use std::fmt;
pub const VEX2_PREFIX: u8 = 0xC5;
pub const VEX3_PREFIX: u8 = 0xC4;
pub const VEX_MMMMM_0F: u8 = 0x01;
pub const VEX_MMMMM_0F38: u8 = 0x02;
pub const VEX_MMMMM_0F3A: u8 = 0x03;
pub const VEX_PP_NONE: u8 = 0x00;
pub const VEX_PP_66: u8 = 0x01;
pub const VEX_PP_F3: u8 = 0x02;
pub const VEX_PP_F2: u8 = 0x03;
pub const VEX_L_128: bool = false;
pub const VEX_L_256: bool = true;
pub const VEX_REG_MASK: u8 = 0x0F;
pub const VEX_REG_HIGH_BIT: u8 = 0x08;
const VEX3_BYTE1_R_MASK: u8 = 0x80;
const VEX3_BYTE1_X_MASK: u8 = 0x40;
const VEX3_BYTE1_B_MASK: u8 = 0x20;
const VEX3_BYTE1_MMMMM_MASK: u8 = 0x1F;
const VEX3_BYTE2_W_MASK: u8 = 0x80;
const VEX3_BYTE2_VVVV_MASK: u8 = 0x78;
const VEX3_BYTE2_L_MASK: u8 = 0x04;
const VEX3_BYTE2_PP_MASK: u8 = 0x03;
const VEX2_BYTE1_R_MASK: u8 = 0x80;
const VEX2_BYTE1_VVVV_MASK: u8 = 0x78;
const VEX2_BYTE1_L_MASK: u8 = 0x04;
const VEX2_BYTE1_PP_MASK: u8 = 0x03;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VexOpcodeMap {
Map0F = 0x01,
Map0F38 = 0x02,
Map0F3A = 0x03,
}
impl VexOpcodeMap {
pub const fn mmmmm(&self) -> u8 {
*self as u8
}
pub const fn from_mmmmm(mmmmm: u8) -> Option<Self> {
match mmmmm {
0x01 => Some(Self::Map0F),
0x02 => Some(Self::Map0F38),
0x03 => Some(Self::Map0F3A),
_ => None,
}
}
pub const fn name(&self) -> &'static str {
match self {
Self::Map0F => "0F",
Self::Map0F38 => "0F38",
Self::Map0F3A => "0F3A",
}
}
pub const fn escape_byte_count(&self) -> u8 {
match self {
Self::Map0F => 1, Self::Map0F38 => 2, Self::Map0F3A => 2, }
}
pub fn escape_bytes(&self) -> Vec<u8> {
match self {
Self::Map0F => vec![0x0F],
Self::Map0F38 => vec![0x0F, 0x38],
Self::Map0F3A => vec![0x0F, 0x3A],
}
}
}
impl fmt::Display for VexOpcodeMap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VexMandatoryPrefix {
None = 0x00,
P66 = 0x01,
PF3 = 0x02,
PF2 = 0x03,
}
impl VexMandatoryPrefix {
pub const fn pp_bits(&self) -> u8 {
*self as u8
}
pub const fn prefix_byte(&self) -> Option<u8> {
match self {
Self::None => None,
Self::P66 => Some(0x66),
Self::PF3 => Some(0xF3),
Self::PF2 => Some(0xF2),
}
}
pub const fn from_pp(pp: u8) -> Option<Self> {
match pp & 0x03 {
0x00 => Some(Self::None),
0x01 => Some(Self::P66),
0x02 => Some(Self::PF3),
0x03 => Some(Self::PF2),
_ => None,
}
}
pub const fn from_byte(byte: u8) -> Self {
match byte {
0x66 => Self::P66,
0xF3 => Self::PF3,
0xF2 => Self::PF2,
_ => Self::None,
}
}
pub const fn name(&self) -> &'static str {
match self {
Self::None => "none",
Self::P66 => "66",
Self::PF3 => "F3",
Self::PF2 => "F2",
}
}
}
impl fmt::Display for VexMandatoryPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum VexVectorLength {
#[default]
L128 = 0,
L256 = 1,
}
impl VexVectorLength {
pub const fn l_bit(&self) -> bool {
match self {
Self::L128 => false,
Self::L256 => true,
}
}
pub const fn width_bytes(&self) -> u8 {
match self {
Self::L128 => 16,
Self::L256 => 32,
}
}
pub const fn width_bits(&self) -> u16 {
match self {
Self::L128 => 128,
Self::L256 => 256,
}
}
pub const fn from_l_bit(l: bool) -> Self {
if l {
Self::L256
} else {
Self::L128
}
}
pub const fn reg_prefix(&self) -> &'static str {
match self {
Self::L128 => "xmm",
Self::L256 => "ymm",
}
}
}
impl fmt::Display for VexVectorLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.width_bits())
}
}
#[inline]
pub const fn vex_invert_reg(reg: u8) -> u8 {
(!reg) & VEX_REG_MASK
}
#[inline]
pub const fn vex_recover_reg(inverted: u8) -> u8 {
(!inverted) & VEX_REG_MASK
}
#[inline]
pub const fn vex_reg_needs_r(reg: u8) -> bool {
(reg & VEX_REG_HIGH_BIT) != 0
}
#[inline]
pub const fn vex_reg_needs_x(reg: u8) -> bool {
(reg & VEX_REG_HIGH_BIT) != 0
}
#[inline]
pub const fn vex_reg_needs_b(reg: u8) -> bool {
(reg & VEX_REG_HIGH_BIT) != 0
}
#[inline]
pub const fn vex_reg_low3(reg: u8) -> u8 {
reg & 0x07
}
#[inline]
pub fn vex_any_extended_reg(regs: &[u8]) -> bool {
for &r in regs {
if r >= 8 {
return true;
}
}
false
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VexPrefixConfig {
pub r: bool,
pub x: bool,
pub b: bool,
pub w: bool,
pub mmmmm: u8,
pub vvvv: u8,
pub l: bool,
pub pp: u8,
}
impl Default for VexPrefixConfig {
fn default() -> Self {
Self {
r: false,
x: false,
b: false,
w: false,
mmmmm: VEX_MMMMM_0F,
vvvv: 0,
l: false,
pp: VEX_PP_NONE,
}
}
}
impl VexPrefixConfig {
pub const fn new() -> Self {
Self {
r: false,
x: false,
b: false,
w: false,
mmmmm: VEX_MMMMM_0F,
vvvv: 0,
l: false,
pp: VEX_PP_NONE,
}
}
pub fn with_map(mut self, map: VexOpcodeMap) -> Self {
self.mmmmm = map.mmmmm();
self
}
pub fn with_prefix(mut self, pfx: VexMandatoryPrefix) -> Self {
self.pp = pfx.pp_bits();
self
}
pub fn with_vector_length(mut self, vl: VexVectorLength) -> Self {
self.l = vl.l_bit();
self
}
pub fn with_vvvv(mut self, reg: u8) -> Self {
self.vvvv = vex_invert_reg(reg & VEX_REG_MASK);
self
}
pub fn with_reg_r(mut self, reg: u8) -> Self {
self.r = vex_reg_needs_r(reg);
self
}
pub fn with_reg_x(mut self, reg: u8) -> Self {
self.x = vex_reg_needs_x(reg);
self
}
pub fn with_reg_b(mut self, reg: u8) -> Self {
self.b = vex_reg_needs_b(reg);
self
}
pub fn with_w(mut self, w: bool) -> Self {
self.w = w;
self
}
pub fn from_registers(
map: VexOpcodeMap,
prefix: VexMandatoryPrefix,
vl: VexVectorLength,
vvvv_reg: u8,
modrm_reg: u8,
modrm_rm: u8,
sib_index: Option<u8>,
w: bool,
) -> Self {
let mut cfg = Self::new()
.with_map(map)
.with_prefix(prefix)
.with_vector_length(vl)
.with_vvvv(vvvv_reg)
.with_reg_r(modrm_reg)
.with_reg_b(modrm_rm)
.with_w(w);
if let Some(idx) = sib_index {
cfg = cfg.with_reg_x(idx);
}
cfg
}
pub fn can_use_2byte_vex(&self) -> bool {
self.mmmmm == VEX_MMMMM_0F && !self.w && !self.x && !self.b && !self.r
}
pub fn try_compress_to_2byte(&self) -> Option<VexPrefixConfig> {
if self.can_use_2byte_vex() {
let mut compressed = *self;
compressed.x = false;
compressed.w = false;
Some(compressed)
} else {
None
}
}
pub fn prefix_byte_count(&self) -> u8 {
if self.can_use_2byte_vex() {
2
} else {
3
}
}
}
pub struct VexPrefixBuilder;
impl VexPrefixBuilder {
#[inline]
pub fn build_2byte(r: bool, vvvv: u8, l: bool, pp: u8) -> [u8; 2] {
let byte1: u8 = ((!r as u8) << 7)
| ((vvvv & VEX_REG_MASK) << 3)
| ((l as u8) << 2)
| (pp & VEX3_BYTE2_PP_MASK);
[VEX2_PREFIX, byte1]
}
#[inline]
pub fn build_2byte_from_config(config: &VexPrefixConfig) -> [u8; 2] {
Self::build_2byte(config.r, config.vvvv, config.l, config.pp)
}
#[inline]
pub fn build_3byte(
r: bool,
x: bool,
b: bool,
mmmmm: u8,
w: bool,
vvvv: u8,
l: bool,
pp: u8,
) -> [u8; 3] {
let byte1: u8 = ((!r as u8) << 7)
| ((!x as u8) << 6)
| ((!b as u8) << 5)
| (mmmmm & VEX3_BYTE1_MMMMM_MASK);
let byte2: u8 = ((w as u8) << 7)
| ((vvvv & VEX_REG_MASK) << 3)
| ((l as u8) << 2)
| (pp & VEX3_BYTE2_PP_MASK);
[VEX3_PREFIX, byte1, byte2]
}
#[inline]
pub fn build_3byte_from_config(config: &VexPrefixConfig) -> [u8; 3] {
Self::build_3byte(
config.r,
config.x,
config.b,
config.mmmmm,
config.w,
config.vvvv,
config.l,
config.pp,
)
}
pub fn build(config: &VexPrefixConfig) -> Vec<u8> {
if config.can_use_2byte_vex() {
let bytes = Self::build_2byte_from_config(config);
bytes.to_vec()
} else {
let bytes = Self::build_3byte_from_config(config);
bytes.to_vec()
}
}
pub fn build_2byte_from_regs(r_reg: u8, vvvv_reg: u8, l: bool, pp: u8) -> [u8; 2] {
let r_inv = vex_reg_needs_r(r_reg);
let vvvv_inv = vex_invert_reg(vvvv_reg);
Self::build_2byte(r_inv, vvvv_inv, l, pp)
}
pub fn build_3byte_from_regs(
r_reg: u8,
x_reg: u8,
b_reg: u8,
mmmmm: u8,
w: bool,
vvvv_reg: u8,
l: bool,
pp: u8,
) -> [u8; 3] {
let r_inv = vex_reg_needs_r(r_reg);
let x_inv = vex_reg_needs_x(x_reg);
let b_inv = vex_reg_needs_b(b_reg);
let vvvv_inv = vex_invert_reg(vvvv_reg);
Self::build_3byte(r_inv, x_inv, b_inv, mmmmm, w, vvvv_inv, l, pp)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct VexDecodedPrefix {
pub is_3byte: bool,
pub r: bool,
pub x: bool,
pub b: bool,
pub mmmmm: u8,
pub w: bool,
pub vvvv: u8,
pub l: bool,
pub pp: u8,
}
impl VexDecodedPrefix {
pub fn decode_2byte(vex_byte1: u8) -> Self {
Self {
is_3byte: false,
r: (vex_byte1 & VEX2_BYTE1_R_MASK) == 0,
x: false,
b: false,
mmmmm: VEX_MMMMM_0F, w: false, vvvv: (vex_byte1 >> 3) & VEX_REG_MASK,
l: (vex_byte1 & VEX2_BYTE1_L_MASK) != 0,
pp: vex_byte1 & VEX2_BYTE1_PP_MASK,
}
}
pub fn decode_3byte(vex_byte1: u8, vex_byte2: u8) -> Self {
Self {
is_3byte: true,
r: (vex_byte1 & VEX3_BYTE1_R_MASK) == 0,
x: (vex_byte1 & VEX3_BYTE1_X_MASK) == 0,
b: (vex_byte1 & VEX3_BYTE1_B_MASK) == 0,
mmmmm: vex_byte1 & VEX3_BYTE1_MMMMM_MASK,
w: (vex_byte2 & VEX3_BYTE2_W_MASK) != 0,
vvvv: (vex_byte2 >> 3) & VEX_REG_MASK,
l: (vex_byte2 & VEX3_BYTE2_L_MASK) != 0,
pp: vex_byte2 & VEX3_BYTE2_PP_MASK,
}
}
pub fn decode(bytes: &[u8]) -> Option<(Self, usize)> {
if bytes.is_empty() {
return None;
}
match bytes[0] {
VEX2_PREFIX if bytes.len() >= 2 => Some((Self::decode_2byte(bytes[1]), 2)),
VEX3_PREFIX if bytes.len() >= 3 => Some((Self::decode_3byte(bytes[1], bytes[2]), 3)),
_ => None,
}
}
pub fn effective_reg_r(&self, reg: u8) -> u8 {
if self.r {
reg & 0x07
} else {
reg | VEX_REG_HIGH_BIT
}
}
pub fn effective_reg_x(&self, reg: u8) -> u8 {
if self.x {
reg & 0x07
} else {
reg | VEX_REG_HIGH_BIT
}
}
pub fn effective_reg_b(&self, reg: u8) -> u8 {
if self.b {
reg & 0x07
} else {
reg | VEX_REG_HIGH_BIT
}
}
pub fn true_vvvv(&self) -> u8 {
vex_recover_reg(self.vvvv)
}
pub fn opcode_map(&self) -> Option<VexOpcodeMap> {
VexOpcodeMap::from_mmmmm(self.mmmmm)
}
pub fn mandatory_prefix(&self) -> VexMandatoryPrefix {
VexMandatoryPrefix::from_pp(self.pp).unwrap_or(VexMandatoryPrefix::None)
}
pub fn vector_length(&self) -> VexVectorLength {
VexVectorLength::from_l_bit(self.l)
}
pub fn to_config(&self) -> VexPrefixConfig {
VexPrefixConfig {
r: self.r,
x: self.x,
b: self.b,
w: self.w,
mmmmm: self.mmmmm,
vvvv: self.vvvv,
l: self.l,
pp: self.pp,
}
}
}
impl fmt::Display for VexDecodedPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ty = if self.is_3byte { "VEX3" } else { "VEX2" };
write!(
f,
"{} R={} X={} B={} W={} mmmmm={:05b} vvvv={:04b} L={} pp={:02b}",
ty, self.r, self.x, self.b, self.w, self.mmmmm, self.vvvv, self.l, self.pp
)
}
}
#[derive(Debug, Clone)]
pub struct VexInstructionLength;
impl VexInstructionLength {
pub fn vex_prefix_size(config: &VexPrefixConfig) -> u8 {
if config.can_use_2byte_vex() {
2
} else {
3
}
}
pub fn compute(
config: &VexPrefixConfig,
opcode_size: u8,
has_modrm: bool,
has_sib: bool,
disp_size: u8,
imm_size: u8,
) -> u16 {
let vex_size = Self::vex_prefix_size(config) as u16;
let op_size = opcode_size as u16;
let modrm_byte = if has_modrm { 1u16 } else { 0 };
let sib_byte = if has_sib { 1u16 } else { 0 };
let disp_bytes = disp_size as u16;
let imm_bytes = imm_size as u16;
vex_size + op_size + modrm_byte + sib_byte + disp_bytes + imm_bytes
}
pub const MAX_LENGTH: u8 = 15;
pub fn is_valid_length(length: u16) -> bool {
length <= Self::MAX_LENGTH as u16
}
pub fn bytes_saved_vs_legacy(config: &VexPrefixConfig, uses_rex: bool) -> i8 {
let legacy_bytes: i8 = {
let rex = if uses_rex { 1 } else { 0 };
let pfx = if config.pp != VEX_PP_NONE { 1 } else { 0 };
let esc = match config.mmmmm {
VEX_MMMMM_0F => 1,
VEX_MMMMM_0F38 | VEX_MMMMM_0F3A => 2,
_ => 0,
};
rex + pfx + esc
};
let vex_bytes = if config.can_use_2byte_vex() { 2 } else { 3 };
legacy_bytes - vex_bytes
}
pub fn estimate_common(
config: &VexPrefixConfig,
is_memory: bool,
needs_sib: bool,
disp_is_32bit: bool,
has_imm8: bool,
) -> u16 {
let op_size: u16 = 1;
let modrm: u16 = 1;
let sib: u16 = if needs_sib { 1 } else { 0 };
let disp: u16 = if is_memory {
if disp_is_32bit {
4
} else {
1
}
} else {
0
};
let imm: u16 = if has_imm8 { 1 } else { 0 };
let vex = Self::vex_prefix_size(config) as u16;
vex + op_size + modrm + sib + disp + imm
}
}
#[derive(Debug, Clone)]
pub struct VexPrefixCompressor;
impl VexPrefixCompressor {
pub fn can_compress(config: &VexPrefixConfig) -> bool {
config.mmmmm == VEX_MMMMM_0F && !config.w && !config.x && !config.b && !config.r
}
pub fn compress(config: VexPrefixConfig) -> VexPrefixConfig {
if Self::can_compress(&config) {
VexPrefixConfig {
r: false,
x: false,
b: false,
w: false,
mmmmm: VEX_MMMMM_0F,
vvvv: config.vvvv,
l: config.l,
pp: config.pp,
}
} else {
config
}
}
pub fn would_prevent_compression(
modrm_reg: u8,
modrm_rm: u8,
sib_index: Option<u8>,
w: bool,
mmmmm: u8,
) -> bool {
mmmmm != VEX_MMMMM_0F
|| w
|| vex_reg_needs_r(modrm_reg)
|| vex_reg_needs_b(modrm_rm)
|| sib_index.map_or(false, vex_reg_needs_x)
}
pub fn compression_blockers(config: &VexPrefixConfig) -> Vec<&'static str> {
let mut blockers = Vec::new();
if config.mmmmm != VEX_MMMMM_0F {
blockers.push("opcode map is not 0F");
}
if config.w {
blockers.push("W bit is set (64-bit operand)");
}
if config.x {
blockers.push("X bit is set (extended index register)");
}
if config.b {
blockers.push("B bit is set (extended base/r_m register)");
}
if config.r {
blockers.push("R bit is set (extended reg register)");
}
blockers
}
pub fn try_make_2byte(vvvv: u8, l: bool, pp: u8) -> Option<VexPrefixConfig> {
let cfg = VexPrefixConfig {
r: false,
x: false,
b: false,
w: false,
mmmmm: VEX_MMMMM_0F,
vvvv,
l,
pp,
};
if cfg.can_use_2byte_vex() {
Some(cfg)
} else {
None
}
}
}
#[derive(Debug, Clone)]
pub struct VexRegisterRestrictions;
impl VexRegisterRestrictions {
pub const MAX_VEX_REG: u8 = 15;
pub fn is_valid_vex_reg(reg: u8) -> bool {
reg <= Self::MAX_VEX_REG
}
pub fn validate_registers(regs: &[u8]) -> Result<(), String> {
for (i, ®) in regs.iter().enumerate() {
if !Self::is_valid_vex_reg(reg) {
return Err(format!(
"Register operand {} has invalid VEX register number {} (max {})",
i,
reg,
Self::MAX_VEX_REG
));
}
}
Ok(())
}
pub fn can_use_2byte(
dest_reg: u8,
src1_reg: u8,
src2_reg: u8,
opcode_map: VexOpcodeMap,
needs_w: bool,
) -> bool {
if opcode_map != VexOpcodeMap::Map0F || needs_w {
return false;
}
!vex_reg_needs_r(dest_reg) && !vex_reg_needs_b(src2_reg) && !vex_reg_needs_x(src1_reg)
}
pub fn required_vex_type(config: &VexPrefixConfig) -> VexEncodingType {
if config.can_use_2byte_vex() {
VexEncodingType::Vex2Byte
} else {
VexEncodingType::Vex3Byte
}
}
pub fn vvvv_is_high_register(_vvvv: u8) -> bool {
false }
pub const MAX_EXPLICIT_OPERANDS: u8 = 4;
pub fn validate_l_bit_for_registers(l: bool, regs: &[u8]) -> Result<(), String> {
if !l {
Ok(())
} else {
Self::validate_registers(regs)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VexEncodingType {
Vex2Byte,
Vex3Byte,
}
impl VexEncodingType {
pub const fn byte_count(&self) -> u8 {
match self {
Self::Vex2Byte => 2,
Self::Vex3Byte => 3,
}
}
pub const fn name(&self) -> &'static str {
match self {
Self::Vex2Byte => "VEX.128",
Self::Vex3Byte => "VEX.256",
}
}
}
impl fmt::Display for VexEncodingType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone)]
pub struct VexEncodedInstruction {
pub vex_prefix: Vec<u8>,
pub opcode: Vec<u8>,
pub modrm: Option<u8>,
pub sib: Option<u8>,
pub displacement: Vec<u8>,
pub immediate: Vec<u8>,
pub encoding_type: VexEncodingType,
}
impl VexEncodedInstruction {
pub fn new(encoding_type: VexEncodingType) -> Self {
Self {
vex_prefix: Vec::new(),
opcode: Vec::new(),
modrm: None,
sib: None,
displacement: Vec::new(),
immediate: Vec::new(),
encoding_type,
}
}
pub fn assemble(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(self.total_length() as usize);
bytes.extend_from_slice(&self.vex_prefix);
bytes.extend_from_slice(&self.opcode);
if let Some(modrm) = self.modrm {
bytes.push(modrm);
}
if let Some(sib) = self.sib {
bytes.push(sib);
}
bytes.extend_from_slice(&self.displacement);
bytes.extend_from_slice(&self.immediate);
bytes
}
pub fn total_length(&self) -> u16 {
let mut len: u16 = 0;
len += self.vex_prefix.len() as u16;
len += self.opcode.len() as u16;
if self.modrm.is_some() {
len += 1;
}
if self.sib.is_some() {
len += 1;
}
len += self.displacement.len() as u16;
len += self.immediate.len() as u16;
len
}
pub fn is_valid(&self) -> bool {
self.total_length() <= 15
}
pub fn encoding_description(&self) -> &'static str {
self.encoding_type.name()
}
}
impl fmt::Display for VexEncodedInstruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let bytes = self.assemble();
write!(f, "VEX[{}] ", bytes.len())?;
for b in &bytes {
write!(f, "{:02X} ", b)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct VexInstructionTemplate {
pub map: VexOpcodeMap,
pub prefix: VexMandatoryPrefix,
pub vector_length: VexVectorLength,
pub opcode: u8,
pub has_modrm: bool,
pub allows_w: bool,
pub is_commutative: bool,
}
impl VexInstructionTemplate {
pub const fn new(
map: VexOpcodeMap,
prefix: VexMandatoryPrefix,
vector_length: VexVectorLength,
opcode: u8,
has_modrm: bool,
) -> Self {
Self {
map,
prefix,
vector_length,
opcode,
has_modrm,
allows_w: false,
is_commutative: false,
}
}
pub const fn with_w(mut self, w: bool) -> Self {
self.allows_w = w;
self
}
pub const fn commutative(mut self) -> Self {
self.is_commutative = true;
self
}
pub fn build_config(
&self,
dest_reg: u8,
src1_reg: u8,
src2_or_mem_reg: u8,
w: bool,
) -> VexPrefixConfig {
VexPrefixConfig::from_registers(
self.map,
self.prefix,
self.vector_length,
src1_reg,
dest_reg,
src2_or_mem_reg,
None, w,
)
}
pub fn encode_rrr(&self, dest_reg: u8, src1_reg: u8, src2_reg: u8) -> VexEncodedInstruction {
let config = self.build_config(dest_reg, src1_reg, src2_reg, false);
let encoding_type = if config.can_use_2byte_vex() {
VexEncodingType::Vex2Byte
} else {
VexEncodingType::Vex3Byte
};
let vex_bytes = VexPrefixBuilder::build(&config);
let modrm = Self::encode_modrm_reg_reg(dest_reg, src2_reg);
VexEncodedInstruction {
vex_prefix: vex_bytes,
opcode: vec![self.opcode],
modrm: Some(modrm),
sib: None,
displacement: vec![],
immediate: vec![],
encoding_type,
}
}
fn encode_modrm_reg_reg(reg_field: u8, rm_field: u8) -> u8 {
0xC0 | ((reg_field & 0x07) << 3) | (rm_field & 0x07)
}
}
pub struct VexTemplateCatalog;
impl VexTemplateCatalog {
pub const VADDPS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0x58,
true,
);
pub const VADDPS_YMM: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L256,
0x58,
true,
);
pub const VMULPS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0x59,
true,
);
pub const VMULPS_YMM: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L256,
0x59,
true,
);
pub const VSUBPS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0x5C,
true,
);
pub const VDIVPS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0x5E,
true,
);
pub const VANDPS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0x54,
true,
);
pub const VORPS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0x56,
true,
);
pub const VXORPS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0x57,
true,
);
pub const VADDPD: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0x58,
true,
);
pub const VMULPD: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0x59,
true,
);
pub const VPADDB: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xFC,
true,
);
pub const VPADDW: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xFD,
true,
);
pub const VPADDD: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xFE,
true,
);
pub const VPADDQ: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xD4,
true,
);
pub const VPSUBB: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xF8,
true,
);
pub const VPSUBW: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xF9,
true,
);
pub const VPSUBD: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xFA,
true,
);
pub const VPMULLW: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xD5,
true,
);
pub const VPMULLD: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0x40,
true,
);
pub const VFMADD132PS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0x98,
true,
);
pub const VFMADD213PS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xA8,
true,
);
pub const VFMADD231PS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xB8,
true,
);
pub const VPERM2F128: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F3A,
VexMandatoryPrefix::P66,
VexVectorLength::L256,
0x06,
true,
);
pub const VPERMILPS_REG: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0x0C,
true,
);
pub const VBROADCASTSS: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0x18,
true,
);
pub const VZEROUPPER: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0x77,
false,
);
pub const VZEROALL: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L256,
0x77,
false,
);
pub const ANDN: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0xF2,
true,
);
pub const BEXTR: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0xF7,
true,
);
pub const BLSI: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0xF3,
true,
);
pub const BLSMSK: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0xF3,
true,
);
pub const BLSR: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0xF3,
true,
);
pub const BZHI: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0xF5,
true,
);
pub const MULX: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::PF2,
VexVectorLength::L128,
0xF6,
true,
);
pub const PDEP: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::PF2,
VexVectorLength::L128,
0xF5,
true,
);
pub const PEXT: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::PF3,
VexVectorLength::L128,
0xF5,
true,
);
pub const RORX: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F3A,
VexMandatoryPrefix::PF2,
VexVectorLength::L128,
0xF0,
true,
);
pub const SHA1RNDS4: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F3A,
VexMandatoryPrefix::None,
VexVectorLength::L128,
0xCC,
true,
);
pub const VAESENC: VexInstructionTemplate = VexInstructionTemplate::new(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::P66,
VexVectorLength::L128,
0xDC,
true,
);
pub fn all_templates() -> Vec<(&'static str, VexInstructionTemplate)> {
vec![
("vaddps", Self::VADDPS),
("vaddps_ymm", Self::VADDPS_YMM),
("vmulps", Self::VMULPS),
("vmulps_ymm", Self::VMULPS_YMM),
("vsubps", Self::VSUBPS),
("vdivps", Self::VDIVPS),
("vandps", Self::VANDPS),
("vorps", Self::VORPS),
("vxorps", Self::VXORPS),
("vaddpd", Self::VADDPD),
("vmulpd", Self::VMULPD),
("vpaddb", Self::VPADDB),
("vpaddw", Self::VPADDW),
("vpaddd", Self::VPADDD),
("vpaddq", Self::VPADDQ),
("vpsubb", Self::VPSUBB),
("vpsubw", Self::VPSUBW),
("vpsubd", Self::VPSUBD),
("vpmullw", Self::VPMULLW),
("vpmulld", Self::VPMULLD),
("vfmadd132ps", Self::VFMADD132PS),
("vfmadd213ps", Self::VFMADD213PS),
("vfmadd231ps", Self::VFMADD231PS),
("vperm2f128", Self::VPERM2F128),
("vpermilps", Self::VPERMILPS_REG),
("vbroadcastss", Self::VBROADCASTSS),
("vzeroupper", Self::VZEROUPPER),
("vzeroall", Self::VZEROALL),
("andn", Self::ANDN),
("bextr", Self::BEXTR),
("blsi", Self::BLSI),
("blsmsk", Self::BLSMSK),
("blsr", Self::BLSR),
("bzhi", Self::BZHI),
("mulx", Self::MULX),
("pdep", Self::PDEP),
("pext", Self::PEXT),
("rorx", Self::RORX),
("sha1rnds4", Self::SHA1RNDS4),
("vaesenc", Self::VAESENC),
]
}
}
#[derive(Debug, Clone, Default)]
pub struct X86VEXEncoding {
pub templates: std::collections::HashMap<String, VexInstructionTemplate>,
pub default_vector_length: VexVectorLength,
pub prefer_2byte: bool,
}
impl X86VEXEncoding {
pub fn new() -> Self {
let mut templates = std::collections::HashMap::new();
for (name, tmpl) in VexTemplateCatalog::all_templates() {
templates.insert(name.to_string(), tmpl);
}
Self {
templates,
default_vector_length: VexVectorLength::L128,
prefer_2byte: true,
}
}
pub fn with_default_vl(mut self, vl: VexVectorLength) -> Self {
self.default_vector_length = vl;
self
}
pub fn without_2byte_compression(mut self) -> Self {
self.prefer_2byte = false;
self
}
pub fn config_for_rrr(
&self,
map: VexOpcodeMap,
prefix: VexMandatoryPrefix,
vl: VexVectorLength,
src1_reg: u8,
dest_reg: u8,
src2_reg: u8,
w: bool,
) -> VexPrefixConfig {
VexPrefixConfig::from_registers(map, prefix, vl, src1_reg, dest_reg, src2_reg, None, w)
}
pub fn config_for_rrm(
&self,
map: VexOpcodeMap,
prefix: VexMandatoryPrefix,
vl: VexVectorLength,
src1_reg: u8,
dest_reg: u8,
mem_base_reg: u8,
mem_index_reg: Option<u8>,
w: bool,
) -> VexPrefixConfig {
VexPrefixConfig::from_registers(
map,
prefix,
vl,
src1_reg,
dest_reg,
mem_base_reg,
mem_index_reg,
w,
)
}
pub fn encode_vex_prefix(&self, config: &VexPrefixConfig) -> Vec<u8> {
let effective_config = if self.prefer_2byte {
VexPrefixCompressor::compress(*config)
} else {
*config
};
VexPrefixBuilder::build(&effective_config)
}
pub fn encode_vex2(&self, r: bool, vvvv: u8, l: bool, pp: u8) -> [u8; 2] {
VexPrefixBuilder::build_2byte(r, vvvv, l, pp)
}
pub fn encode_vex3(
&self,
r: bool,
x: bool,
b: bool,
mmmmm: u8,
w: bool,
vvvv: u8,
l: bool,
pp: u8,
) -> [u8; 3] {
VexPrefixBuilder::build_3byte(r, x, b, mmmmm, w, vvvv, l, pp)
}
pub fn compute_length(
&self,
config: &VexPrefixConfig,
opcode_size: u8,
has_modrm: bool,
has_sib: bool,
disp_size: u8,
imm_size: u8,
) -> u16 {
VexInstructionLength::compute(config, opcode_size, has_modrm, has_sib, disp_size, imm_size)
}
pub fn encode_from_template(
&self,
template_name: &str,
dest_reg: u8,
src1_reg: u8,
src2_reg: u8,
) -> Option<VexEncodedInstruction> {
let template = self.templates.get(template_name)?;
Some(template.encode_rrr(dest_reg, src1_reg, src2_reg))
}
pub fn can_use_2byte(&self, config: &VexPrefixConfig) -> bool {
config.can_use_2byte_vex()
}
pub fn compression_blockers(&self, config: &VexPrefixConfig) -> Vec<&'static str> {
VexPrefixCompressor::compression_blockers(config)
}
pub fn decode_vex(&self, bytes: &[u8]) -> Option<(VexDecodedPrefix, usize)> {
VexDecodedPrefix::decode(bytes)
}
pub fn validate_registers(&self, regs: &[u8]) -> Result<(), String> {
VexRegisterRestrictions::validate_registers(regs)
}
pub fn bytes_saved(&self, config: &VexPrefixConfig, uses_rex: bool) -> i8 {
VexInstructionLength::bytes_saved_vs_legacy(config, uses_rex)
}
}
pub struct Vex2Byte1Table;
impl Vex2Byte1Table {
pub fn all_combinations() -> Vec<(bool, u8, bool, u8)> {
let mut results = Vec::new();
for byte1 in 0x00u8..=0xFFu8 {
let r = (byte1 & VEX2_BYTE1_R_MASK) == 0;
let vvvv = (byte1 >> 3) & VEX_REG_MASK;
let l = (byte1 & VEX2_BYTE1_L_MASK) != 0;
let pp = byte1 & VEX2_BYTE1_PP_MASK;
results.push((r, vvvv, l, pp));
}
results
}
pub fn decode(byte1: u8) -> (bool, u8, bool, u8) {
let r = (byte1 & VEX2_BYTE1_R_MASK) == 0;
let vvvv = (byte1 >> 3) & VEX_REG_MASK;
let l = (byte1 & VEX2_BYTE1_L_MASK) != 0;
let pp = byte1 & VEX2_BYTE1_PP_MASK;
(r, vvvv, l, pp)
}
}
pub struct Vex3ByteTable;
impl Vex3ByteTable {
pub const VALID_MMMMM: [u8; 3] = [VEX_MMMMM_0F, VEX_MMMMM_0F38, VEX_MMMMM_0F3A];
pub fn decode_to_config(byte1: u8, byte2: u8) -> VexPrefixConfig {
let r = (byte1 & VEX3_BYTE1_R_MASK) == 0;
let x = (byte1 & VEX3_BYTE1_X_MASK) == 0;
let b = (byte1 & VEX3_BYTE1_B_MASK) == 0;
let mmmmm = byte1 & VEX3_BYTE1_MMMMM_MASK;
let w = (byte2 & VEX3_BYTE2_W_MASK) != 0;
let vvvv = (byte2 >> 3) & VEX_REG_MASK;
let l = (byte2 & VEX3_BYTE2_L_MASK) != 0;
let pp = byte2 & VEX3_BYTE2_PP_MASK;
VexPrefixConfig {
r,
x,
b,
w,
mmmmm,
vvvv,
l,
pp,
}
}
pub fn valid_byte1_values(mmmmm: u8) -> Vec<u8> {
let mut result = Vec::new();
for rx_bits in 0u8..8u8 {
let byte1 = ((rx_bits & 0x04) << 5) | ((rx_bits & 0x02) << 5) | ((rx_bits & 0x01) << 5) | (mmmmm & VEX3_BYTE1_MMMMM_MASK);
result.push(byte1);
}
result
}
}
#[derive(Debug, Clone)]
pub struct VexToLegacyTranslator;
impl VexToLegacyTranslator {
pub fn translate_prefix(decoded: &VexDecodedPrefix) -> Vec<u8> {
let mut bytes = Vec::new();
let needs_rex = decoded.r || decoded.x || decoded.b || decoded.w;
if needs_rex {
let mut rex: u8 = 0x40;
if decoded.w {
rex |= 0x08;
}
if decoded.r {
rex |= 0x04;
}
if decoded.x {
rex |= 0x02;
}
if decoded.b {
rex |= 0x01;
}
bytes.push(rex);
}
match decoded.mandatory_prefix() {
VexMandatoryPrefix::P66 => bytes.push(0x66),
VexMandatoryPrefix::PF3 => bytes.push(0xF3),
VexMandatoryPrefix::PF2 => bytes.push(0xF2),
VexMandatoryPrefix::None => {}
}
if let Some(map) = decoded.opcode_map() {
bytes.extend(map.escape_bytes());
}
bytes
}
pub fn legacy_prefix_length(decoded: &VexDecodedPrefix) -> u8 {
Self::translate_prefix(decoded).len() as u8
}
pub fn compare_lengths(decoded: &VexDecodedPrefix) -> i8 {
let vex_len = if decoded.is_3byte { 3 } else { 2 };
let legacy_len = Self::legacy_prefix_length(decoded) as i8;
legacy_len - vex_len
}
}
#[derive(Debug, Clone)]
pub struct VexAssemblyPrinter;
impl VexAssemblyPrinter {
pub fn format_prefix(decoded: &VexDecodedPrefix) -> String {
let mut s = String::new();
s.push_str(if decoded.is_3byte { "VEX3" } else { "VEX2" });
s.push_str(&format!(
" R={} X={} B={} W={} mmmmm={:05b} vvvv={:04b} L={} pp={:02b}",
decoded.r as u8,
decoded.x as u8,
decoded.b as u8,
decoded.w as u8,
decoded.mmmmm,
decoded.vvvv,
decoded.l as u8,
decoded.pp,
));
s
}
pub fn format_config(config: &VexPrefixConfig) -> String {
let form = if config.can_use_2byte_vex() {
"VEX2"
} else {
"VEX3"
};
format!(
"{} R={} X={} B={} W={} mmmmm={:05b} vvvv={:04b} L={} pp={:02b}",
form,
config.r as u8,
config.x as u8,
config.b as u8,
config.w as u8,
config.mmmmm,
config.vvvv,
config.l as u8,
config.pp,
)
}
pub fn format_instruction(instr: &VexEncodedInstruction) -> String {
let bytes = instr.assemble();
let mut s = String::new();
for (i, b) in bytes.iter().enumerate() {
if i > 0 {
s.push(' ');
}
s.push_str(&format!("{:02X}", b));
}
s
}
pub fn disassemble_prefix(bytes: &[u8]) -> Option<String> {
let (decoded, _) = VexDecodedPrefix::decode(bytes)?;
Some(Self::format_prefix(&decoded))
}
pub fn print_intel_syntax(
mnemonic: &str,
decoded: &VexDecodedPrefix,
dest_reg: u8,
src1_reg: u8,
src2_reg: u8,
) -> String {
let vl = decoded.vector_length();
let reg_prefix = vl.reg_prefix();
format!(
"{} {}{}, {}{}, {}{}",
mnemonic,
reg_prefix,
decoded.effective_reg_r(dest_reg),
reg_prefix,
decoded.true_vvvv(),
reg_prefix,
decoded.effective_reg_b(src2_reg),
)
}
}
#[derive(Debug, Clone)]
pub struct VexFieldValidator;
impl VexFieldValidator {
pub fn validate(config: &VexPrefixConfig) -> Result<(), Vec<String>> {
let mut errors = Vec::new();
if config.mmmmm < 1 || config.mmmmm > 3 {
errors.push(format!(
"Invalid mmmmm value: {} (expected 1=0F, 2=0F38, or 3=0F3A)",
config.mmmmm
));
}
if config.pp > 3 {
errors.push(format!("Invalid pp value: {} (expected 0-3)", config.pp));
}
if config.can_use_2byte_vex() {
if config.mmmmm != VEX_MMMMM_0F {
errors.push("2-byte VEX requires mmmmm=1 (map 0F)".to_string());
}
if config.w {
errors.push("2-byte VEX cannot encode W=1".to_string());
}
if config.x {
errors.push("2-byte VEX cannot encode X=1".to_string());
}
if config.b {
errors.push("2-byte VEX cannot encode B=1".to_string());
}
}
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn is_valid_mmmmm(mmmmm: u8) -> bool {
matches!(mmmmm, VEX_MMMMM_0F | VEX_MMMMM_0F38 | VEX_MMMMM_0F3A)
}
pub fn is_valid_pp(pp: u8) -> bool {
pp <= 3
}
}
#[derive(Debug, Clone)]
pub struct VexFamilyEncoder;
impl VexFamilyEncoder {
pub fn encode_avx_128_ps(opcode: u8, dest: u8, src1: u8, src2: u8) -> VexEncodedInstruction {
let config = VexPrefixConfig::from_registers(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L128,
src1,
dest,
src2,
None,
false,
);
let encoding_type = if config.can_use_2byte_vex() {
VexEncodingType::Vex2Byte
} else {
VexEncodingType::Vex3Byte
};
let vex_bytes = VexPrefixBuilder::build(&config);
let modrm = 0xC0 | ((dest & 0x07) << 3) | (src2 & 0x07);
VexEncodedInstruction {
vex_prefix: vex_bytes,
opcode: vec![opcode],
modrm: Some(modrm),
sib: None,
displacement: vec![],
immediate: vec![],
encoding_type,
}
}
pub fn encode_avx_256_ps(opcode: u8, dest: u8, src1: u8, src2: u8) -> VexEncodedInstruction {
let config = VexPrefixConfig::from_registers(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L256,
src1,
dest,
src2,
None,
false,
);
let encoding_type = VexEncodingType::Vex3Byte;
let vex_bytes = VexPrefixBuilder::build_3byte_from_config(&config).to_vec();
let modrm = 0xC0 | ((dest & 0x07) << 3) | (src2 & 0x07);
VexEncodedInstruction {
vex_prefix: vex_bytes,
opcode: vec![opcode],
modrm: Some(modrm),
sib: None,
displacement: vec![],
immediate: vec![],
encoding_type,
}
}
pub fn encode_avx_pd(
opcode: u8,
dest: u8,
src1: u8,
src2: u8,
vl: VexVectorLength,
) -> VexEncodedInstruction {
let config = VexPrefixConfig::from_registers(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::P66,
vl,
src1,
dest,
src2,
None,
false,
);
let encoding_type = if config.can_use_2byte_vex() {
VexEncodingType::Vex2Byte
} else {
VexEncodingType::Vex3Byte
};
let vex_bytes = VexPrefixBuilder::build(&config);
let modrm = 0xC0 | ((dest & 0x07) << 3) | (src2 & 0x07);
VexEncodedInstruction {
vex_prefix: vex_bytes,
opcode: vec![opcode],
modrm: Some(modrm),
sib: None,
displacement: vec![],
immediate: vec![],
encoding_type,
}
}
pub fn encode_avx_int_packed(
opcode: u8,
dest: u8,
src1: u8,
src2: u8,
vl: VexVectorLength,
) -> VexEncodedInstruction {
Self::encode_avx_pd(opcode, dest, src1, src2, vl)
}
pub fn encode_fma3(
opcode: u8,
prefix: VexMandatoryPrefix,
dest: u8,
src1: u8,
src2: u8,
vl: VexVectorLength,
) -> VexEncodedInstruction {
let config = VexPrefixConfig::from_registers(
VexOpcodeMap::Map0F38,
prefix,
vl,
src1,
dest,
src2,
None,
false,
);
let encoding_type = if config.can_use_2byte_vex() {
VexEncodingType::Vex2Byte
} else {
VexEncodingType::Vex3Byte
};
let vex_bytes = VexPrefixBuilder::build(&config);
let modrm = 0xC0 | ((dest & 0x07) << 3) | (src2 & 0x07);
VexEncodedInstruction {
vex_prefix: vex_bytes,
opcode: vec![opcode],
modrm: Some(modrm),
sib: None,
displacement: vec![],
immediate: vec![],
encoding_type,
}
}
pub fn encode_bmi1(
opcode: u8,
prefix: VexMandatoryPrefix,
dest: u8,
src1: u8,
src2: u8,
w: bool,
) -> VexEncodedInstruction {
let config = VexPrefixConfig::from_registers(
VexOpcodeMap::Map0F38,
prefix,
VexVectorLength::L128,
src1,
dest,
src2,
None,
w,
);
let encoding_type = if config.can_use_2byte_vex() {
VexEncodingType::Vex2Byte
} else {
VexEncodingType::Vex3Byte
};
let vex_bytes = VexPrefixBuilder::build(&config);
let modrm = 0xC0 | ((dest & 0x07) << 3) | (src2 & 0x07);
VexEncodedInstruction {
vex_prefix: vex_bytes,
opcode: vec![opcode],
modrm: Some(modrm),
sib: None,
displacement: vec![],
immediate: vec![],
encoding_type,
}
}
pub fn encode_vex_with_imm8(
map: VexOpcodeMap,
prefix: VexMandatoryPrefix,
vl: VexVectorLength,
opcode: u8,
dest: u8,
src1: u8,
src2: u8,
imm8: u8,
) -> VexEncodedInstruction {
let config =
VexPrefixConfig::from_registers(map, prefix, vl, src1, dest, src2, None, false);
let encoding_type = if config.can_use_2byte_vex() {
VexEncodingType::Vex2Byte
} else {
VexEncodingType::Vex3Byte
};
let vex_bytes = VexPrefixBuilder::build(&config);
let modrm = 0xC0 | ((dest & 0x07) << 3) | (src2 & 0x07);
VexEncodedInstruction {
vex_prefix: vex_bytes,
opcode: vec![opcode],
modrm: Some(modrm),
sib: None,
displacement: vec![],
immediate: vec![imm8],
encoding_type,
}
}
pub fn encode_vzeroupper() -> VexEncodedInstruction {
let config = VexPrefixConfig {
r: false,
x: false,
b: false,
w: false,
mmmmm: VEX_MMMMM_0F,
vvvv: 0xF, l: false,
pp: VEX_PP_NONE,
};
let vex_bytes = VexPrefixBuilder::build_2byte_from_config(&config).to_vec();
VexEncodedInstruction {
vex_prefix: vex_bytes,
opcode: vec![0x77],
modrm: None,
sib: None,
displacement: vec![],
immediate: vec![],
encoding_type: VexEncodingType::Vex2Byte,
}
}
pub fn encode_vzeroall() -> VexEncodedInstruction {
let config = VexPrefixConfig {
r: false,
x: false,
b: false,
w: false,
mmmmm: VEX_MMMMM_0F,
vvvv: 0xF,
l: true, pp: VEX_PP_NONE,
};
let vex_bytes = VexPrefixBuilder::build_3byte_from_config(&config).to_vec();
VexEncodedInstruction {
vex_prefix: vex_bytes,
opcode: vec![0x77],
modrm: None,
sib: None,
displacement: vec![],
immediate: vec![],
encoding_type: VexEncodingType::Vex3Byte,
}
}
pub fn encode_vldmxcsr(mem_op: bool) -> VexEncodedInstruction {
let config = VexPrefixConfig {
r: false,
x: false,
b: false,
w: false,
mmmmm: VEX_MMMMM_0F,
vvvv: 0xF,
l: false,
pp: VEX_PP_NONE,
};
let vex_bytes = if config.can_use_2byte_vex() {
VexPrefixBuilder::build_2byte_from_config(&config).to_vec()
} else {
VexPrefixBuilder::build_3byte_from_config(&config).to_vec()
};
let encoding_type = if config.can_use_2byte_vex() {
VexEncodingType::Vex2Byte
} else {
VexEncodingType::Vex3Byte
};
let modrm = if mem_op {
0x04 } else {
0xC0 };
VexEncodedInstruction {
vex_prefix: vex_bytes,
opcode: vec![0xAE],
modrm: Some(modrm),
sib: None,
displacement: vec![],
immediate: vec![],
encoding_type,
}
}
}
#[derive(Debug, Clone)]
pub struct VexWigLigConventions;
impl VexWigLigConventions {
pub const WIG_W_VALUE: bool = false;
pub const LIG_L_VALUE: bool = false;
pub fn apply_wig(config: &mut VexPrefixConfig) {
config.w = false;
}
pub fn apply_lig(config: &mut VexPrefixConfig) {
config.l = false;
}
pub fn apply_wig_lig(config: &mut VexPrefixConfig) {
config.w = false;
config.l = false;
}
pub fn is_likely_wig(map: VexOpcodeMap, _opcode: u8) -> bool {
matches!(map, VexOpcodeMap::Map0F)
}
pub fn is_likely_lig(is_scalar: bool) -> bool {
is_scalar
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vex_constants() {
assert_eq!(VEX2_PREFIX, 0xC5);
assert_eq!(VEX3_PREFIX, 0xC4);
assert_eq!(VEX_MMMMM_0F, 0x01);
assert_eq!(VEX_MMMMM_0F38, 0x02);
assert_eq!(VEX_MMMMM_0F3A, 0x03);
assert_eq!(VEX_PP_NONE, 0x00);
assert_eq!(VEX_PP_66, 0x01);
assert_eq!(VEX_PP_F3, 0x02);
assert_eq!(VEX_PP_F2, 0x03);
}
#[test]
fn test_vex_invert_reg() {
assert_eq!(vex_invert_reg(0), 0x0F);
assert_eq!(vex_invert_reg(1), 0x0E);
assert_eq!(vex_invert_reg(7), 0x08);
assert_eq!(vex_invert_reg(8), 0x07);
assert_eq!(vex_invert_reg(15), 0x00);
}
#[test]
fn test_vex_recover_reg() {
for reg in 0..16u8 {
let inv = vex_invert_reg(reg);
let rec = vex_recover_reg(inv);
assert_eq!(rec, reg, "round-trip failed for reg {}", reg);
}
}
#[test]
fn test_vex_reg_needs_extension() {
assert!(!vex_reg_needs_r(0));
assert!(!vex_reg_needs_r(7));
assert!(vex_reg_needs_r(8));
assert!(vex_reg_needs_r(15));
assert!(!vex_reg_needs_x(0));
assert!(vex_reg_needs_x(8));
assert!(!vex_reg_needs_b(0));
assert!(vex_reg_needs_b(8));
}
#[test]
fn test_vex_opcode_map_mmmmm() {
assert_eq!(VexOpcodeMap::Map0F.mmmmm(), 1);
assert_eq!(VexOpcodeMap::Map0F38.mmmmm(), 2);
assert_eq!(VexOpcodeMap::Map0F3A.mmmmm(), 3);
}
#[test]
fn test_vex_opcode_map_from_mmmmm() {
assert_eq!(VexOpcodeMap::from_mmmmm(1), Some(VexOpcodeMap::Map0F));
assert_eq!(VexOpcodeMap::from_mmmmm(2), Some(VexOpcodeMap::Map0F38));
assert_eq!(VexOpcodeMap::from_mmmmm(3), Some(VexOpcodeMap::Map0F3A));
assert_eq!(VexOpcodeMap::from_mmmmm(0), None);
assert_eq!(VexOpcodeMap::from_mmmmm(4), None);
}
#[test]
fn test_vex_opcode_map_escape_byte_count() {
assert_eq!(VexOpcodeMap::Map0F.escape_byte_count(), 1);
assert_eq!(VexOpcodeMap::Map0F38.escape_byte_count(), 2);
assert_eq!(VexOpcodeMap::Map0F3A.escape_byte_count(), 2);
}
#[test]
fn test_vex_mandatory_prefix_pp_bits() {
assert_eq!(VexMandatoryPrefix::None.pp_bits(), 0);
assert_eq!(VexMandatoryPrefix::P66.pp_bits(), 1);
assert_eq!(VexMandatoryPrefix::PF3.pp_bits(), 2);
assert_eq!(VexMandatoryPrefix::PF2.pp_bits(), 3);
}
#[test]
fn test_vex_mandatory_prefix_from_pp() {
assert_eq!(
VexMandatoryPrefix::from_pp(0),
Some(VexMandatoryPrefix::None)
);
assert_eq!(
VexMandatoryPrefix::from_pp(1),
Some(VexMandatoryPrefix::P66)
);
assert_eq!(
VexMandatoryPrefix::from_pp(2),
Some(VexMandatoryPrefix::PF3)
);
assert_eq!(
VexMandatoryPrefix::from_pp(3),
Some(VexMandatoryPrefix::PF2)
);
}
#[test]
fn test_vex_mandatory_prefix_from_byte() {
assert_eq!(VexMandatoryPrefix::from_byte(0x66), VexMandatoryPrefix::P66);
assert_eq!(VexMandatoryPrefix::from_byte(0xF3), VexMandatoryPrefix::PF3);
assert_eq!(VexMandatoryPrefix::from_byte(0xF2), VexMandatoryPrefix::PF2);
assert_eq!(
VexMandatoryPrefix::from_byte(0x00),
VexMandatoryPrefix::None
);
}
#[test]
fn test_vex_vector_length() {
assert_eq!(VexVectorLength::L128.l_bit(), false);
assert_eq!(VexVectorLength::L256.l_bit(), true);
assert_eq!(VexVectorLength::L128.width_bytes(), 16);
assert_eq!(VexVectorLength::L256.width_bytes(), 32);
assert_eq!(VexVectorLength::L128.width_bits(), 128);
assert_eq!(VexVectorLength::L256.width_bits(), 256);
assert_eq!(VexVectorLength::L128.reg_prefix(), "xmm");
assert_eq!(VexVectorLength::L256.reg_prefix(), "ymm");
assert_eq!(VexVectorLength::from_l_bit(false), VexVectorLength::L128);
assert_eq!(VexVectorLength::from_l_bit(true), VexVectorLength::L256);
}
#[test]
fn test_vex_prefix_config_default() {
let cfg = VexPrefixConfig::default();
assert_eq!(cfg.r, false);
assert_eq!(cfg.x, false);
assert_eq!(cfg.b, false);
assert_eq!(cfg.w, false);
assert_eq!(cfg.mmmmm, VEX_MMMMM_0F);
assert_eq!(cfg.pp, VEX_PP_NONE);
}
#[test]
fn test_vex_prefix_config_can_use_2byte() {
let cfg = VexPrefixConfig::new()
.with_map(VexOpcodeMap::Map0F)
.with_vvvv(1);
assert!(cfg.can_use_2byte_vex());
let cfg = VexPrefixConfig::new().with_map(VexOpcodeMap::Map0F38);
assert!(!cfg.can_use_2byte_vex());
let cfg = VexPrefixConfig::new()
.with_map(VexOpcodeMap::Map0F)
.with_reg_r(8);
assert!(!cfg.can_use_2byte_vex());
let cfg = VexPrefixConfig::new()
.with_map(VexOpcodeMap::Map0F)
.with_w(true);
assert!(!cfg.can_use_2byte_vex());
}
#[test]
fn test_vex_prefix_config_prefix_byte_count() {
let cfg = VexPrefixConfig::new().with_map(VexOpcodeMap::Map0F);
assert_eq!(cfg.prefix_byte_count(), 2);
let cfg = VexPrefixConfig::new().with_map(VexOpcodeMap::Map0F38);
assert_eq!(cfg.prefix_byte_count(), 3);
}
#[test]
fn test_build_vex2_basic() {
let bytes = VexPrefixBuilder::build_2byte(true, 0x0D, false, VEX_PP_NONE);
assert_eq!(bytes[0], 0xC5);
assert_eq!(bytes[1], 0xE8);
}
#[test]
fn test_build_vex2_from_regs() {
let bytes = VexPrefixBuilder::build_2byte_from_regs(0, 1, false, VEX_PP_NONE);
assert_eq!(bytes[0], 0xC5);
assert_eq!(bytes[1], 0x70);
}
#[test]
fn test_build_vex3_basic() {
let bytes = VexPrefixBuilder::build_3byte(
true,
true,
true,
VEX_MMMMM_0F,
false,
0x0E,
false,
VEX_PP_NONE,
);
assert_eq!(bytes[0], 0xC4);
assert_eq!(bytes[1], 0x01);
assert_eq!(bytes[2], 0x70);
}
#[test]
fn test_build_vex3_with_w() {
let bytes = VexPrefixBuilder::build_3byte(
false,
true,
true,
VEX_MMMMM_0F,
true,
0x07,
true,
VEX_PP_F2,
);
assert_eq!(bytes[0], 0xC4);
assert_eq!(bytes[1], 0x81);
assert_eq!(bytes[2], 0xBF);
}
#[test]
fn test_build_vex_auto_select() {
let cfg = VexPrefixConfig::new()
.with_map(VexOpcodeMap::Map0F)
.with_vvvv(1)
.with_reg_r(0)
.with_reg_b(0);
let bytes = VexPrefixBuilder::build(&cfg);
assert_eq!(bytes.len(), 2);
assert_eq!(bytes[0], 0xC5);
let cfg = VexPrefixConfig::new()
.with_map(VexOpcodeMap::Map0F)
.with_vvvv(1)
.with_reg_r(8);
let bytes = VexPrefixBuilder::build(&cfg);
assert_eq!(bytes.len(), 3);
assert_eq!(bytes[0], 0xC4);
}
#[test]
fn test_decode_vex2() {
let decoded = VexDecodedPrefix::decode_2byte(0xE8);
assert!(!decoded.is_3byte);
assert!(decoded.r);
assert!(!decoded.x);
assert!(!decoded.b);
assert_eq!(decoded.mmmmm, VEX_MMMMM_0F);
assert!(!decoded.w);
assert_eq!(decoded.vvvv, 0x0D);
assert_eq!(decoded.true_vvvv(), 2); assert!(!decoded.l);
assert_eq!(decoded.pp, VEX_PP_NONE);
}
#[test]
fn test_decode_vex3() {
let decoded = VexDecodedPrefix::decode_3byte(0x01, 0x70);
assert!(decoded.is_3byte);
assert!(decoded.r);
assert!(decoded.x);
assert!(decoded.b);
assert_eq!(decoded.mmmmm, 1);
assert!(!decoded.w);
assert_eq!(decoded.vvvv, 0x0E);
assert_eq!(decoded.true_vvvv(), 1); assert!(!decoded.l);
assert_eq!(decoded.pp, VEX_PP_NONE);
}
#[test]
fn test_decode_vex_from_slice() {
let (decoded, len) = VexDecodedPrefix::decode(&[0xC5, 0xF8]).unwrap();
assert_eq!(len, 2);
assert!(!decoded.is_3byte);
let (decoded, len) = VexDecodedPrefix::decode(&[0xC4, 0xE1, 0x78]).unwrap();
assert_eq!(len, 3);
assert!(decoded.is_3byte);
assert!(VexDecodedPrefix::decode(&[0x90]).is_none()); assert!(VexDecodedPrefix::decode(&[]).is_none());
}
#[test]
fn test_vex_decoded_effective_reg() {
let decoded = VexDecodedPrefix::decode_2byte(0xE8);
assert_eq!(decoded.effective_reg_r(3), 3);
let decoded = VexDecodedPrefix::decode_3byte(0x81, 0x70);
assert_eq!(decoded.effective_reg_r(3), 11);
}
#[test]
fn test_vex_instruction_length_reg_reg() {
let cfg = VexPrefixConfig::new().with_map(VexOpcodeMap::Map0F);
let len = VexInstructionLength::compute(&cfg, 1, true, false, 0, 0);
assert_eq!(len, 4);
}
#[test]
fn test_vex_instruction_length_with_disp() {
let cfg = VexPrefixConfig::new().with_map(VexOpcodeMap::Map0F);
let len = VexInstructionLength::compute(&cfg, 1, true, true, 4, 0);
assert_eq!(len, 9);
}
#[test]
fn test_vex_instruction_length_3byte_with_imm() {
let cfg = VexPrefixConfig::new()
.with_map(VexOpcodeMap::Map0F)
.with_reg_r(8);
let len = VexInstructionLength::compute(&cfg, 1, true, false, 0, 1);
assert_eq!(len, 6);
}
#[test]
fn test_vex_max_length() {
assert_eq!(VexInstructionLength::MAX_LENGTH, 15);
assert!(VexInstructionLength::is_valid_length(15));
assert!(!VexInstructionLength::is_valid_length(16));
}
#[test]
fn test_vex_compress_3to2() {
let cfg = VexPrefixConfig {
r: false,
x: false,
b: false,
w: false,
mmmmm: VEX_MMMMM_0F,
vvvv: 0x0E,
l: false,
pp: VEX_PP_NONE,
};
let compressed = VexPrefixCompressor::compress(cfg);
assert!(compressed.can_use_2byte_vex());
}
#[test]
fn test_vex_cannot_compress_map_0f38() {
let cfg = VexPrefixConfig {
r: false,
x: false,
b: false,
w: false,
mmmmm: VEX_MMMMM_0F38,
vvvv: 0x0E,
l: false,
pp: VEX_PP_NONE,
};
assert!(!VexPrefixCompressor::can_compress(&cfg));
}
#[test]
fn test_vex_cannot_compress_extended_reg() {
let cfg = VexPrefixConfig {
r: false, x: false,
b: false,
w: false,
mmmmm: VEX_MMMMM_0F,
vvvv: 0x0E,
l: false,
pp: VEX_PP_NONE,
};
assert!(!VexPrefixCompressor::can_compress(&cfg));
}
#[test]
fn test_vex_compression_blockers() {
let cfg = VexPrefixConfig {
r: true,
x: false,
b: true,
w: true,
mmmmm: VEX_MMMMM_0F38,
vvvv: 0x0E,
l: false,
pp: VEX_PP_NONE,
};
let blockers = VexPrefixCompressor::compression_blockers(&cfg);
assert!(blockers.len() >= 3);
}
#[test]
fn test_vex_register_restrictions_valid() {
assert!(VexRegisterRestrictions::validate_registers(&[0, 1, 7, 15]).is_ok());
assert!(VexRegisterRestrictions::validate_registers(&[16]).is_err());
}
#[test]
fn test_vex_max_reg() {
assert_eq!(VexRegisterRestrictions::MAX_VEX_REG, 15);
}
#[test]
fn test_vex_template_catalog_has_templates() {
let templates = VexTemplateCatalog::all_templates();
assert!(templates.len() >= 30);
}
#[test]
fn test_vex_template_encode_vaddps() {
let tmpl = VexTemplateCatalog::VADDPS;
let instr = tmpl.encode_rrr(0, 1, 2);
assert_eq!(instr.encoding_type, VexEncodingType::Vex2Byte);
assert_eq!(instr.opcode, vec![0x58]);
assert!(instr.modrm.is_some());
assert_eq!(instr.total_length(), 4); }
#[test]
fn test_vex_template_encode_vaddps_ymm() {
let tmpl = VexTemplateCatalog::VADDPS_YMM;
let instr = tmpl.encode_rrr(0, 1, 2);
assert_eq!(instr.encoding_type, VexEncodingType::Vex3Byte);
assert_eq!(instr.opcode, vec![0x58]);
assert_eq!(instr.total_length(), 5); }
#[test]
fn test_vex_template_encode_vzeroupper() {
let tmpl = VexTemplateCatalog::VZEROUPPER;
assert!(!tmpl.has_modrm); assert_eq!(tmpl.opcode, 0x77);
assert_eq!(tmpl.map, VexOpcodeMap::Map0F);
assert_eq!(tmpl.vector_length, VexVectorLength::L128);
}
#[test]
fn test_x86_vex_encoding_new() {
let encoding = X86VEXEncoding::new();
assert!(encoding.templates.len() >= 30);
assert!(encoding.prefer_2byte);
}
#[test]
fn test_x86_vex_encoding_encode_from_template() {
let encoding = X86VEXEncoding::new();
let instr = encoding.encode_from_template("vaddps", 0, 1, 2).unwrap();
assert_eq!(instr.opcode, vec![0x58]);
assert_eq!(instr.encoding_type, VexEncodingType::Vex2Byte);
let instr = encoding
.encode_from_template("vperm2f128", 0, 1, 2)
.unwrap();
assert_eq!(instr.opcode, vec![0x06]);
assert_eq!(instr.encoding_type, VexEncodingType::Vex3Byte);
}
#[test]
fn test_x86_vex_encoding_config_for_rrr() {
let encoding = X86VEXEncoding::new();
let cfg = encoding.config_for_rrr(
VexOpcodeMap::Map0F,
VexMandatoryPrefix::None,
VexVectorLength::L128,
1, 0, 2, false,
);
assert!(cfg.can_use_2byte_vex());
}
#[test]
fn test_x86_vex_encoding_validate_registers() {
let encoding = X86VEXEncoding::new();
assert!(encoding.validate_registers(&[0, 7, 15]).is_ok());
assert!(encoding.validate_registers(&[16]).is_err());
}
#[test]
fn test_family_encoder_avx_128_ps() {
let instr = VexFamilyEncoder::encode_avx_128_ps(0x58, 0, 1, 2);
assert_eq!(instr.opcode, vec![0x58]);
assert_eq!(instr.encoding_type, VexEncodingType::Vex2Byte);
assert!(instr.modrm.is_some());
}
#[test]
fn test_family_encoder_avx_256_ps() {
let instr = VexFamilyEncoder::encode_avx_256_ps(0x58, 0, 1, 2);
assert_eq!(instr.opcode, vec![0x58]);
assert_eq!(instr.encoding_type, VexEncodingType::Vex3Byte);
}
#[test]
fn test_family_encoder_avx_pd() {
let instr = VexFamilyEncoder::encode_avx_pd(0x58, 0, 1, 2, VexVectorLength::L128);
assert_eq!(instr.opcode, vec![0x58]);
}
#[test]
fn test_family_encoder_bmi1() {
let instr = VexFamilyEncoder::encode_bmi1(0xF2, VexMandatoryPrefix::None, 0, 3, 1, true);
assert_eq!(instr.opcode, vec![0xF2]);
assert_eq!(instr.encoding_type, VexEncodingType::Vex3Byte);
}
#[test]
fn test_family_encoder_vzeroupper() {
let instr = VexFamilyEncoder::encode_vzeroupper();
assert_eq!(instr.opcode, vec![0x77]);
assert!(instr.modrm.is_none());
assert_eq!(instr.encoding_type, VexEncodingType::Vex2Byte);
}
#[test]
fn test_family_encoder_vzeroall() {
let instr = VexFamilyEncoder::encode_vzeroall();
assert_eq!(instr.opcode, vec![0x77]);
assert!(instr.modrm.is_none());
assert_eq!(instr.encoding_type, VexEncodingType::Vex3Byte);
}
#[test]
fn test_vex_to_legacy_translator() {
let decoded = VexDecodedPrefix::decode_2byte(0xE8);
let legacy = VexToLegacyTranslator::translate_prefix(&decoded);
assert_eq!(legacy, vec![0x0F]);
}
#[test]
fn test_vex_to_legacy_with_rex_and_prefix() {
let decoded = VexDecodedPrefix::decode_3byte(0xE1, 0xB9);
let legacy = VexToLegacyTranslator::translate_prefix(&decoded);
assert_eq!(legacy, vec![0x4F, 0x66, 0x0F]);
}
#[test]
fn test_vex_compare_lengths() {
let decoded = VexDecodedPrefix::decode_2byte(0xE8);
let cmp = VexToLegacyTranslator::compare_lengths(&decoded);
assert_eq!(cmp, -1);
let decoded = VexDecodedPrefix::decode_3byte(0xE2, 0x78);
let cmp = VexToLegacyTranslator::compare_lengths(&decoded);
assert_eq!(cmp, 0); }
#[test]
fn test_assembly_printer() {
let decoded = VexDecodedPrefix::decode_2byte(0xE8);
let s = VexAssemblyPrinter::format_prefix(&decoded);
assert!(s.contains("VEX2"));
assert!(s.contains("R="));
assert!(s.contains("L="));
assert!(s.contains("pp="));
}
#[test]
fn test_assembly_printer_intel_syntax() {
let decoded = VexDecodedPrefix::decode_2byte(0x70); let s = VexAssemblyPrinter::print_intel_syntax("vaddps", &decoded, 0, 1, 2);
assert!(s.contains("vaddps"));
assert!(s.contains("xmm0"));
assert!(s.contains("xmm1"));
assert!(s.contains("xmm2"));
}
#[test]
fn test_field_validator_valid() {
let cfg = VexPrefixConfig::new()
.with_map(VexOpcodeMap::Map0F)
.with_prefix(VexMandatoryPrefix::P66);
assert!(VexFieldValidator::validate(&cfg).is_ok());
}
#[test]
fn test_field_validator_invalid_mmmmm() {
let cfg = VexPrefixConfig {
mmmmm: 0,
..VexPrefixConfig::default()
};
assert!(VexFieldValidator::validate(&cfg).is_err());
}
#[test]
fn test_field_validator_invalid_pp() {
let cfg = VexPrefixConfig {
pp: 4,
..VexPrefixConfig::default()
};
assert!(VexFieldValidator::validate(&cfg).is_err());
}
#[test]
fn test_wig_lig_conventions() {
let mut cfg = VexPrefixConfig::new()
.with_map(VexOpcodeMap::Map0F)
.with_w(true)
.with_vector_length(VexVectorLength::L256);
VexWigLigConventions::apply_wig(&mut cfg);
assert!(!cfg.w);
VexWigLigConventions::apply_lig(&mut cfg);
assert!(!cfg.l);
}
#[test]
fn test_roundtrip_vex2_encode_decode() {
let bytes = VexPrefixBuilder::build_2byte(true, 0x0E, false, VEX_PP_NONE);
assert_eq!(bytes, [0xC5, 0x70]);
let (decoded, len) = VexDecodedPrefix::decode(&bytes).unwrap();
assert_eq!(len, 2);
assert!(!decoded.is_3byte);
assert!(decoded.r);
assert_eq!(decoded.vvvv, 0x0E);
assert_eq!(decoded.true_vvvv(), 1);
assert!(!decoded.l);
assert_eq!(decoded.pp, VEX_PP_NONE);
}
#[test]
fn test_roundtrip_vex3_encode_decode() {
let bytes = VexPrefixBuilder::build_3byte(
false,
true,
true,
VEX_MMMMM_0F38,
true,
0x07,
true,
VEX_PP_66,
);
assert_eq!(bytes[0], 0xC4);
let (decoded, len) = VexDecodedPrefix::decode(&bytes).unwrap();
assert_eq!(len, 3);
assert!(decoded.is_3byte);
assert!(!decoded.r);
assert!(decoded.x);
assert!(decoded.b);
assert_eq!(decoded.mmmmm, VEX_MMMMM_0F38);
assert!(decoded.w);
assert_eq!(decoded.vvvv, 0x07);
assert_eq!(decoded.true_vvvv(), 8);
assert!(decoded.l);
assert_eq!(decoded.pp, VEX_PP_66);
}
#[test]
fn test_full_instruction_assemble() {
let cfg = VexPrefixConfig::new()
.with_map(VexOpcodeMap::Map0F)
.with_vvvv(1)
.with_reg_r(0)
.with_reg_b(2);
let mut instr = VexEncodedInstruction::new(VexEncodingType::Vex2Byte);
instr.vex_prefix = VexPrefixBuilder::build(&cfg);
instr.opcode = vec![0x58];
instr.modrm = Some(0xC0 | ((0 & 0x07) << 3) | (2 & 0x07));
let assembled = instr.assemble();
assert_eq!(assembled.len(), 4);
assert!(instr.is_valid());
}
#[test]
fn test_all_vex_templates_valid() {
for (_name, tmpl) in VexTemplateCatalog::all_templates() {
let instr = tmpl.encode_rrr(0, 1, 2);
assert!(instr.is_valid(), "Template produced invalid instruction");
}
}
#[test]
fn test_vex_byte1_table_all_combinations() {
let combos = Vex2Byte1Table::all_combinations();
assert_eq!(combos.len(), 256);
}
#[test]
fn test_vex3_valid_mmmmm_values() {
assert_eq!(Vex3ByteTable::VALID_MMMMM.len(), 3);
assert!(Vex3ByteTable::VALID_MMMMM.contains(&VEX_MMMMM_0F));
assert!(Vex3ByteTable::VALID_MMMMM.contains(&VEX_MMMMM_0F38));
assert!(Vex3ByteTable::VALID_MMMMM.contains(&VEX_MMMMM_0F3A));
}
#[test]
fn test_vex3_byte1_values_for_each_map() {
for &mmmmm in &Vex3ByteTable::VALID_MMMMM {
let values = Vex3ByteTable::valid_byte1_values(mmmmm);
assert_eq!(values.len(), 8); }
}
#[test]
fn test_vex_encoding_type_byte_count() {
assert_eq!(VexEncodingType::Vex2Byte.byte_count(), 2);
assert_eq!(VexEncodingType::Vex3Byte.byte_count(), 3);
}
#[test]
fn test_all_vex_opcodes_nonzero() {
for (_name, tmpl) in VexTemplateCatalog::all_templates() {
assert!(tmpl.opcode != 0, "Template has zero opcode");
}
}
#[test]
fn test_vex_config_from_registers() {
let cfg = VexPrefixConfig::from_registers(
VexOpcodeMap::Map0F38,
VexMandatoryPrefix::P66,
VexVectorLength::L256,
1, 8, 9, Some(10), true, );
assert!(!cfg.r); assert!(!cfg.x); assert!(!cfg.b); assert!(cfg.w);
assert_eq!(cfg.mmmmm, VEX_MMMMM_0F38);
assert_eq!(cfg.pp, VEX_PP_66);
assert!(cfg.l);
}
#[test]
fn test_vex_bytes_saved() {
let cfg = VexPrefixConfig::new().with_map(VexOpcodeMap::Map0F);
let saved = VexInstructionLength::bytes_saved_vs_legacy(&cfg, false);
assert_eq!(saved, -1);
let cfg = VexPrefixConfig::new()
.with_map(VexOpcodeMap::Map0F38)
.with_prefix(VexMandatoryPrefix::P66);
let saved = VexInstructionLength::bytes_saved_vs_legacy(&cfg, true);
assert_eq!(saved, 1);
}
}