use super::evex::Register;
use super::rex::{LegacyPrefixes, OpcodeMap};
use super::ByteSink;
use crate::isa::x64::encoding::rex::encode_modrm;
pub struct VexInstruction {
length: VexVectorLength,
prefix: LegacyPrefixes,
map: OpcodeMap,
opcode: u8,
w: bool,
reg: u8,
rm: Register,
vvvv: Option<Register>,
imm: Option<u8>,
}
impl Default for VexInstruction {
fn default() -> Self {
Self {
length: VexVectorLength::default(),
prefix: LegacyPrefixes::None,
map: OpcodeMap::None,
opcode: 0x00,
w: false,
reg: 0x00,
rm: Register::default(),
vvvv: None,
imm: None,
}
}
}
impl VexInstruction {
pub fn new() -> Self {
Self::default()
}
#[inline(always)]
pub fn length(mut self, length: VexVectorLength) -> Self {
self.length = length;
self
}
#[inline(always)]
pub fn prefix(mut self, prefix: LegacyPrefixes) -> Self {
debug_assert!(
prefix == LegacyPrefixes::None
|| prefix == LegacyPrefixes::_66
|| prefix == LegacyPrefixes::_F2
|| prefix == LegacyPrefixes::_F3
);
self.prefix = prefix;
self
}
#[inline(always)]
pub fn map(mut self, map: OpcodeMap) -> Self {
self.map = map;
self
}
#[inline(always)]
pub fn w(mut self, w: bool) -> Self {
self.w = w;
self
}
#[inline(always)]
pub fn opcode(mut self, opcode: u8) -> Self {
self.opcode = opcode;
self
}
#[inline(always)]
pub fn reg(mut self, reg: impl Into<Register>) -> Self {
self.reg = reg.into().into();
self
}
#[inline(always)]
pub fn opcode_ext(mut self, n: u8) -> Self {
self.reg = n;
self
}
#[inline(always)]
pub fn rm(mut self, reg: impl Into<Register>) -> Self {
self.rm = reg.into();
self
}
#[allow(dead_code)]
#[inline(always)]
pub fn vvvv(mut self, reg: impl Into<Register>) -> Self {
self.vvvv = Some(reg.into());
self
}
#[inline(always)]
pub fn imm_reg(mut self, reg: impl Into<Register>) -> Self {
let reg: u8 = reg.into().into();
self.imm = Some((reg & 0xf) << 4);
self
}
#[inline(always)]
pub fn imm(mut self, imm: u8) -> Self {
self.imm = Some(imm);
self
}
#[inline(always)]
fn r_bit(&self) -> u8 {
(!(self.reg >> 3)) & 1
}
#[inline(always)]
fn x_bit(&self) -> u8 {
(!0) & 1
}
#[inline(always)]
fn b_bit(&self) -> u8 {
let rm: u8 = self.rm.into();
(!(rm >> 3)) & 1
}
#[inline(always)]
fn use_2byte_prefix(&self) -> bool {
self.b_bit() == 1 && self.x_bit() == 1 &&
self.w == false &&
!(self.map == OpcodeMap::_0F3A || self.map == OpcodeMap::_0F38)
}
#[inline(always)]
fn prefix_last_byte(&self) -> u8 {
let vvvv = self.vvvv.map(|r| r.into()).unwrap_or(0x00);
let mut byte = 0x00;
byte |= self.prefix.bits();
byte |= self.length.bits() << 2;
byte |= ((!vvvv) & 0xF) << 3;
byte
}
#[inline(always)]
fn encode_2byte_prefix<CS: ByteSink + ?Sized>(&self, sink: &mut CS) {
let last_byte = self.prefix_last_byte() | (self.r_bit() << 7);
sink.put1(0xC5);
sink.put1(last_byte);
}
#[inline(always)]
fn encode_3byte_prefix<CS: ByteSink + ?Sized>(&self, sink: &mut CS) {
let mut second_byte = 0x00;
second_byte |= self.map.bits(); second_byte |= self.b_bit() << 5;
second_byte |= self.x_bit() << 6;
second_byte |= self.r_bit() << 7;
let w_bit = self.w as u8;
let last_byte = self.prefix_last_byte() | (w_bit << 7);
sink.put1(0xC4);
sink.put1(second_byte);
sink.put1(last_byte);
}
pub fn encode<CS: ByteSink + ?Sized>(&self, sink: &mut CS) {
if self.use_2byte_prefix() {
self.encode_2byte_prefix(sink);
} else {
self.encode_3byte_prefix(sink);
}
sink.put1(self.opcode);
let rm: u8 = self.rm.into();
sink.put1(encode_modrm(3, self.reg & 7, rm & 7));
if let Some(imm) = self.imm {
sink.put1(imm);
}
}
}
#[allow(dead_code, missing_docs)] pub enum VexVectorLength {
V128,
V256,
}
impl VexVectorLength {
fn bits(&self) -> u8 {
match self {
Self::V128 => 0b0,
Self::V256 => 0b1,
}
}
}
impl Default for VexVectorLength {
fn default() -> Self {
Self::V128
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::isa::x64::inst::regs;
use std::vec::Vec;
#[test]
fn vpslldq() {
let dst = regs::xmm1().to_real_reg().unwrap().hw_enc();
let src = regs::xmm2().to_real_reg().unwrap().hw_enc();
let mut sink0 = Vec::new();
VexInstruction::new()
.length(VexVectorLength::V128)
.prefix(LegacyPrefixes::_66)
.map(OpcodeMap::_0F)
.opcode(0x73)
.opcode_ext(7)
.vvvv(dst)
.rm(src)
.imm(0x17)
.encode(&mut sink0);
assert_eq!(sink0, vec![0xc5, 0xf1, 0x73, 0xfa, 0x17]);
}
#[test]
fn vblendvpd() {
let dst = regs::xmm1().to_real_reg().unwrap().hw_enc();
let a = regs::xmm2().to_real_reg().unwrap().hw_enc();
let b = regs::xmm3().to_real_reg().unwrap().hw_enc();
let c = regs::xmm4().to_real_reg().unwrap().hw_enc();
let mut sink0 = Vec::new();
VexInstruction::new()
.length(VexVectorLength::V128)
.prefix(LegacyPrefixes::_66)
.map(OpcodeMap::_0F3A)
.w(false)
.opcode(0x4B)
.reg(dst)
.vvvv(a)
.rm(b)
.imm_reg(c)
.encode(&mut sink0);
assert_eq!(sink0, vec![0xc4, 0xe3, 0x69, 0x4b, 0xcb, 0x40]);
}
#[test]
fn vcmpps() {
let dst = regs::xmm10().to_real_reg().unwrap().hw_enc();
let a = regs::xmm11().to_real_reg().unwrap().hw_enc();
let b = regs::xmm12().to_real_reg().unwrap().hw_enc();
let mut sink0 = Vec::new();
VexInstruction::new()
.length(VexVectorLength::V256)
.prefix(LegacyPrefixes::None)
.map(OpcodeMap::_0F)
.opcode(0xC2)
.reg(dst)
.vvvv(a)
.rm(b)
.imm(4)
.encode(&mut sink0);
assert_eq!(sink0, vec![0xc4, 0x41, 0x24, 0xc2, 0xd4, 0x04]);
}
}