use crate::codegen::{MachineFunction, MachineInstr, MachineOperand};
use crate::mc_streamer::x86_opcodes;
use crate::x86::x86_instr_info::X86Opcode;
use crate::x86::x86_subtarget::X86Subtarget;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86Mode {
Mode16,
Mode32,
Mode64,
}
impl X86Mode {
pub fn is_64bit(self) -> bool {
matches!(self, X86Mode::Mode64)
}
pub fn default_operand_size(self) -> u8 {
match self {
X86Mode::Mode16 => 2,
X86Mode::Mode32 => 4,
X86Mode::Mode64 => 4, }
}
pub fn default_address_size(self) -> u8 {
match self {
X86Mode::Mode16 => 2,
X86Mode::Mode32 => 4,
X86Mode::Mode64 => 8,
}
}
}
pub mod prefixes {
pub const LOCK: u8 = 0xF0;
pub const REPNE: u8 = 0xF2;
pub const REP: u8 = 0xF3;
pub const CS_OVERRIDE: u8 = 0x2E;
pub const SS_OVERRIDE: u8 = 0x36;
pub const DS_OVERRIDE: u8 = 0x3E;
pub const ES_OVERRIDE: u8 = 0x26;
pub const FS_OVERRIDE: u8 = 0x64;
pub const GS_OVERRIDE: u8 = 0x65;
pub const OPERAND_SIZE_OVERRIDE: u8 = 0x66;
pub const ADDRESS_SIZE_OVERRIDE: u8 = 0x67;
}
pub mod mod_field {
pub const MEM_NO_DISP: u8 = 0b00;
pub const MEM_DISP8: u8 = 0b01;
pub const MEM_DISP32: u8 = 0b10;
pub const REG_DIRECT: u8 = 0b11;
}
pub struct X86MCEncoder {
pub output: Vec<u8>,
pub mode: X86Mode,
pub subtarget: X86Subtarget,
}
impl X86MCEncoder {
pub fn new(mode: X86Mode, subtarget: X86Subtarget) -> Self {
Self {
output: Vec::new(),
mode,
subtarget,
}
}
pub fn encode_function(&mut self, mf: &MachineFunction) -> Vec<u8> {
self.output.clear();
for bb in &mf.blocks {
for mi in &bb.instructions {
let bytes = self.encode_instruction(mi);
self.output.extend_from_slice(&bytes);
}
}
self.output.clone()
}
pub fn encode_instruction(&self, mi: &MachineInstr) -> Vec<u8> {
let mut bytes = Vec::new();
let prefixes = self.encode_prefixes(mi);
bytes.extend_from_slice(&prefixes);
let opcode = self.encode_opcode(mi);
bytes.extend_from_slice(&opcode);
if self.requires_modrm(mi) {
let modrm = self.encode_modrm_for_instr(mi);
bytes.push(modrm);
if self.requires_sib(mi) {
let sib = self.encode_sib_for_instr(mi);
bytes.push(sib);
}
let disp = self.encode_displacement_for_instr(mi);
bytes.extend_from_slice(&disp);
}
let imm = self.encode_immediate_for_instr(mi);
bytes.extend_from_slice(&imm);
bytes
}
pub fn encode_prefixes(&self, mi: &MachineInstr) -> Vec<u8> {
let mut prefixes = Vec::new();
let (has_16bit_operand, _has_64bit_operand) = self.analyze_operand_sizes(mi);
if has_16bit_operand && !self.mode.is_64bit() {
prefixes.push(prefixes::OPERAND_SIZE_OVERRIDE);
}
if self.requires_rex(mi) {
let rex = self.encode_rex_prefix_for_instr(mi);
prefixes.push(rex);
}
prefixes
}
fn analyze_operand_sizes(&self, mi: &MachineInstr) -> (bool, bool) {
let mut has_16bit = false;
let mut _has_64bit = false;
for op in &mi.operands {
if let MachineOperand::PhysReg(reg) = op {
let reg_id = *reg as u16;
if (32..=47).contains(®_id) {
has_16bit = true;
}
if self.mode.is_64bit() && reg_id < 16 {
_has_64bit = true;
}
}
}
(has_16bit, _has_64bit)
}
pub fn requires_rex(&self, mi: &MachineInstr) -> bool {
if !self.mode.is_64bit() {
return false;
}
for op in &mi.operands {
if let MachineOperand::PhysReg(reg) = op {
let reg_id = *reg as u16;
if reg_id >= 8 && reg_id <= 15 {
eprintln!(
"DEBUG_REX: reg 8-15: opcode={}, reg_id={}",
mi.opcode, reg_id
);
return true; }
if reg_id >= 24 && reg_id <= 31 {
eprintln!(
"DEBUG_REX: reg 24-31: opcode={}, reg_id={}",
mi.opcode, reg_id
);
return true; }
if reg_id >= 40 && reg_id <= 47 {
return true; }
if reg_id >= 56 && reg_id <= 63 {
return true; }
if reg_id >= 52 && reg_id <= 55 {
return true; }
if reg_id >= 106 && reg_id <= 113 {
return true; }
if reg_id >= 114 {
return true; }
}
}
if self.mode.is_64bit() && mi.size != 4 {
if let Some(MachineOperand::PhysReg(reg)) = mi.operands.first() {
let reg_id = *reg as u16;
let is_shift = mi.opcode == x86_opcodes::SHL
|| mi.opcode == x86_opcodes::SHR
|| mi.opcode == x86_opcodes::SAR;
if reg_id < 16 && !is_shift {
eprintln!(
"DEBUG_REX: reg<16+non-shift: opcode={}, reg_id={}, size={}",
mi.opcode, reg_id, mi.size
);
return true; }
if reg_id < 16 && is_shift {
eprintln!(
"DEBUG_REX: reg<16+SHIFT: opcode={}, reg_id={}, size={} (NO REX)",
mi.opcode, reg_id, mi.size
);
}
}
}
false
}
fn encode_rex_prefix_for_instr(&self, mi: &MachineInstr) -> u8 {
let (w, r, x, b) = self.compute_rex_fields(mi);
self.encode_rex_prefix(w, r, x, b)
}
fn compute_rex_fields(&self, mi: &MachineInstr) -> (bool, bool, bool, bool) {
let mut w = false;
let mut r = false;
let x = false;
let mut b = false;
for op in &mi.operands {
if let MachineOperand::PhysReg(reg) = op {
let reg_id = *reg as u16;
let is_shift = mi.opcode == x86_opcodes::SHL
|| mi.opcode == x86_opcodes::SHR
|| mi.opcode == x86_opcodes::SAR;
if reg_id < 16 && mi.size != 4 && !is_shift {
eprintln!(
"DEBUG: non-shift opcode={}, reg_id={}, mi.size={}, SETTING W=true",
mi.opcode, reg_id, mi.size
);
w = true;
} else if reg_id < 16 && mi.size != 4 && is_shift {
eprintln!(
"DEBUG: shift opcode={}, reg_id={}, mi.size={}, NOT setting W",
mi.opcode, reg_id, mi.size
);
}
if reg_id >= 8 && reg_id <= 15 {
r = true;
}
if reg_id >= 24 && reg_id <= 31 {
r = true;
}
if reg_id >= 106 && reg_id <= 113 {
r = true;
}
if reg_id >= 114 {
r = true;
}
if reg_id >= 8 && reg_id <= 15 {
b = true;
}
if reg_id >= 24 && reg_id <= 31 {
b = true;
}
}
}
(w, r, x, b)
}
pub fn encode_rex_prefix(&self, w: bool, r: bool, x: bool, b: bool) -> u8 {
let mut rex: u8 = 0x40;
if w {
rex |= 0x08;
}
if r {
rex |= 0x04;
}
if x {
rex |= 0x02;
}
if b {
rex |= 0x01;
}
rex
}
pub fn encode_opcode(&self, mi: &MachineInstr) -> Vec<u8> {
let opcode = mi.opcode;
match opcode {
x86_opcodes::NOP => vec![0x90],
x86_opcodes::MOV => self.encode_mov_opcode(mi),
x86_opcodes::ADD => self.encode_alu_opcode(mi, 0x01, 0x03, 0),
x86_opcodes::SUB => self.encode_alu_opcode(mi, 0x29, 0x2B, 5),
x86_opcodes::AND => self.encode_alu_opcode(mi, 0x21, 0x23, 4),
x86_opcodes::OR => self.encode_alu_opcode(mi, 0x09, 0x0B, 1),
x86_opcodes::XOR => self.encode_alu_opcode(mi, 0x31, 0x33, 6),
x86_opcodes::CMP => self.encode_alu_opcode(mi, 0x39, 0x3B, 7),
x86_opcodes::LEA => vec![0x8D],
x86_opcodes::PUSH => self.encode_push_pop_opcode(mi, true),
x86_opcodes::POP => self.encode_push_pop_opcode(mi, false),
x86_opcodes::CALL => vec![0xE8], x86_opcodes::RET => vec![0xC3],
x86_opcodes::JMP => self.encode_jmp_opcode(mi),
x86_opcodes::JE => self.encode_jcc_opcode(mi, 0x84),
x86_opcodes::JNE => self.encode_jcc_opcode(mi, 0x85),
x86_opcodes::SHL => self.encode_shift_opcode(4),
x86_opcodes::SHR => self.encode_shift_opcode(5),
x86_opcodes::INC => self.encode_inc_dec_opcode(mi, 0),
x86_opcodes::DEC => self.encode_inc_dec_opcode(mi, 1),
x86_opcodes::NOT => self.encode_unary_opcode(2),
x86_opcodes::NEG => self.encode_unary_opcode(3),
x86_opcodes::MUL => self.encode_mul_div_opcode(4),
x86_opcodes::DIV => self.encode_mul_div_opcode(6),
_ => {
vec![0x0F, 0x0B]
}
}
}
fn encode_mov_opcode(&self, mi: &MachineInstr) -> Vec<u8> {
if mi.operands.len() < 2 {
return vec![0x89]; }
let imm_is_last = mi
.operands
.last()
.map_or(false, |op| matches!(op, MachineOperand::Imm(_)));
let dst_is_reg = matches!(mi.operands.first(), Some(MachineOperand::PhysReg(_)));
if imm_is_last && dst_is_reg && mi.operands.len() == 2 {
if let Some(MachineOperand::PhysReg(reg)) = mi.operands.first() {
let reg_field = self.get_reg_field(*reg as u16) & 0x07;
return vec![0xB8 | reg_field];
}
}
vec![0x89]
}
fn encode_alu_opcode(
&self,
mi: &MachineInstr,
op_rm_r: u8,
_op_r_rm: u8,
_group_reg: u8,
) -> Vec<u8> {
if mi.operands.len() < 2 {
return vec![op_rm_r]; }
let imm_is_last = mi
.operands
.last()
.map_or(false, |op| matches!(op, MachineOperand::Imm(_)));
if imm_is_last {
if let Some(MachineOperand::Imm(imm)) = mi.operands.last() {
if *imm >= -128 && *imm <= 127 {
vec![0x83]
} else {
vec![0x81]
}
} else {
vec![0x83]
}
} else {
vec![op_rm_r]
}
}
fn encode_push_pop_opcode(&self, mi: &MachineInstr, is_push: bool) -> Vec<u8> {
if let Some(MachineOperand::PhysReg(reg)) = mi.operands.first() {
let reg_field = self.get_reg_field(*reg as u16);
if is_push {
vec![0x50 | reg_field]
} else {
vec![0x58 | reg_field]
}
} else {
if is_push {
vec![0xFF]
} else {
vec![0x8F]
}
}
}
fn encode_jmp_opcode(&self, mi: &MachineInstr) -> Vec<u8> {
if let Some(MachineOperand::Imm(offset)) = mi.operands.first() {
if *offset >= -128 && *offset <= 127 {
vec![0xEB]
} else {
vec![0xE9]
}
} else if mi.operands.first().is_some() {
vec![0xE9]
} else {
vec![0xE9]
}
}
fn encode_jcc_opcode(&self, _mi: &MachineInstr, opcode_2byte: u8) -> Vec<u8> {
vec![0x0F, opcode_2byte]
}
fn encode_shift_opcode(&self, _group_reg: u8) -> Vec<u8> {
vec![0xD3]
}
fn encode_inc_dec_opcode(&self, _mi: &MachineInstr, _group_reg: u8) -> Vec<u8> {
vec![0xFF]
}
fn encode_unary_opcode(&self, _group_reg: u8) -> Vec<u8> {
vec![0xF7]
}
fn encode_mul_div_opcode(&self, _group_reg: u8) -> Vec<u8> {
vec![0xF7]
}
pub fn requires_modrm(&self, mi: &MachineInstr) -> bool {
let opcode = mi.opcode;
match opcode {
x86_opcodes::NOP => false,
x86_opcodes::RET => false,
x86_opcodes::CALL => {
false
}
x86_opcodes::JMP | x86_opcodes::JE | x86_opcodes::JNE => {
false
}
x86_opcodes::PUSH | x86_opcodes::POP => {
matches!(mi.operands.first(), Some(MachineOperand::Imm(_))) || mi.operands.len() > 1
}
x86_opcodes::MOV => {
let imm_is_last = mi
.operands
.last()
.map_or(false, |op| matches!(op, MachineOperand::Imm(_)));
let dst_is_reg = matches!(mi.operands.first(), Some(MachineOperand::PhysReg(_)));
if imm_is_last && dst_is_reg && mi.operands.len() == 2 {
false } else {
true
}
}
_ => {
!mi.operands.is_empty()
}
}
}
fn encode_modrm_for_instr(&self, mi: &MachineInstr) -> u8 {
let (mod_field_val, reg_opcode, rm_field) = self.compute_modrm_fields(mi);
self.encode_modrm(mod_field_val, reg_opcode, rm_field)
}
pub fn encode_modrm(&self, mod_field: u8, reg_opcode: u8, rm: u8) -> u8 {
((mod_field & 0x03) << 6) | ((reg_opcode & 0x07) << 3) | (rm & 0x07)
}
fn compute_modrm_fields(&self, mi: &MachineInstr) -> (u8, u8, u8) {
let opcode = mi.opcode;
match opcode {
x86_opcodes::ADD
| x86_opcodes::SUB
| x86_opcodes::AND
| x86_opcodes::OR
| x86_opcodes::XOR
| x86_opcodes::CMP
| x86_opcodes::MOV => {
if mi.operands.len() >= 2 {
let dst_reg = self.get_phys_reg_field(mi, 0);
let src_reg = self.get_phys_reg_field(mi, 1);
let src_is_imm = mi
.operands
.last()
.map_or(false, |op| matches!(op, MachineOperand::Imm(_)));
if src_is_imm {
let group_reg = self.get_group_reg(opcode);
(mod_field::REG_DIRECT, group_reg, dst_reg & 0x07)
} else if opcode == x86_opcodes::MOV {
(mod_field::REG_DIRECT, src_reg & 0x07, dst_reg & 0x07)
} else {
(mod_field::REG_DIRECT, src_reg & 0x07, dst_reg & 0x07)
}
} else {
(mod_field::REG_DIRECT, 0, 0)
}
}
x86_opcodes::LEA => {
if mi.operands.len() >= 2 {
let dst_reg = self.get_phys_reg_field(mi, 0);
let src_reg = self.get_phys_reg_field(mi, 1);
(mod_field::REG_DIRECT, dst_reg & 0x07, src_reg & 0x07)
} else {
(mod_field::REG_DIRECT, 0, 0)
}
}
x86_opcodes::SHL | x86_opcodes::SHR => {
let group_reg = if opcode == x86_opcodes::SHL { 4 } else { 5 };
if let Some(r) = mi.operands.first() {
let rm = self.get_operand_reg_field(r);
(mod_field::REG_DIRECT, group_reg, rm & 0x07)
} else {
(mod_field::REG_DIRECT, group_reg, 0)
}
}
x86_opcodes::INC | x86_opcodes::DEC => {
let group_reg = if opcode == x86_opcodes::INC { 0 } else { 1 };
if let Some(r) = mi.operands.first() {
let rm = self.get_operand_reg_field(r);
(mod_field::REG_DIRECT, group_reg, rm & 0x07)
} else {
(mod_field::REG_DIRECT, group_reg, 0)
}
}
x86_opcodes::NOT | x86_opcodes::NEG => {
let group_reg = if opcode == x86_opcodes::NOT { 2 } else { 3 };
if let Some(r) = mi.operands.first() {
let rm = self.get_operand_reg_field(r);
(mod_field::REG_DIRECT, group_reg, rm & 0x07)
} else {
(mod_field::REG_DIRECT, group_reg, 0)
}
}
x86_opcodes::MUL | x86_opcodes::DIV => {
let group_reg = if opcode == x86_opcodes::MUL { 4 } else { 6 };
if let Some(r) = mi.operands.first() {
let rm = self.get_operand_reg_field(r);
(mod_field::REG_DIRECT, group_reg, rm & 0x07)
} else {
(mod_field::REG_DIRECT, group_reg, 0)
}
}
_ => (mod_field::REG_DIRECT, 0, 0),
}
}
fn get_group_reg(&self, opcode: u32) -> u8 {
match opcode {
x86_opcodes::ADD => 0,
x86_opcodes::OR => 1,
x86_opcodes::ADC => 2, x86_opcodes::SBB => 3,
x86_opcodes::AND => 4,
x86_opcodes::SUB => 5,
x86_opcodes::XOR => 6,
x86_opcodes::CMP => 7,
_ => 0,
}
}
fn get_phys_reg_field(&self, mi: &MachineInstr, op_index: usize) -> u8 {
mi.operands
.get(op_index)
.map(|op| self.get_operand_reg_field(op))
.unwrap_or(0)
}
fn get_operand_reg_field(&self, op: &MachineOperand) -> u8 {
match op {
MachineOperand::PhysReg(reg) => self.get_reg_field(*reg as u16),
MachineOperand::Reg(_vr) => {
0
}
_ => 0,
}
}
pub fn get_reg_field(&self, reg: u16) -> u8 {
if reg <= 15 {
return (reg & 0x07) as u8;
}
if reg <= 31 {
return ((reg - 16) & 0x07) as u8;
}
if reg <= 47 {
return ((reg - 32) & 0x07) as u8;
}
if reg <= 63 {
return ((reg - 48) & 0x07) as u8;
}
if reg <= 67 {
return ((reg - 60) & 0x07) as u8;
}
if (98..=129).contains(®) {
return ((reg - 98) & 0x07) as u8;
}
if (130..=161).contains(®) {
return ((reg - 130) & 0x07) as u8;
}
if (162..=193).contains(®) {
return ((reg - 162) & 0x07) as u8;
}
0
}
pub fn requires_sib(&self, mi: &MachineInstr) -> bool {
for op in &mi.operands {
if let MachineOperand::PhysReg(reg) = op {
let reg_id = *reg as u16;
let reg_field = self.get_reg_field(reg_id);
if reg_field == 4 {
continue;
}
}
}
false
}
fn encode_sib_for_instr(&self, _mi: &MachineInstr) -> u8 {
self.encode_sib(0, 4, 4)
}
pub fn encode_sib(&self, scale: u8, index: u8, base: u8) -> u8 {
((scale & 0x03) << 6) | ((index & 0x07) << 3) | (base & 0x07)
}
fn encode_displacement_for_instr(&self, _mi: &MachineInstr) -> Vec<u8> {
vec![]
}
pub fn encode_displacement(&self, disp: i64, mod_bits: u8) -> Vec<u8> {
match mod_bits {
mod_field::MEM_DISP8 => {
vec![disp as u8]
}
mod_field::MEM_DISP32 => {
if self.mode == X86Mode::Mode16 {
(disp as i16).to_le_bytes().to_vec()
} else {
(disp as i32).to_le_bytes().to_vec()
}
}
_ => {
vec![]
}
}
}
fn encode_immediate_for_instr(&self, mi: &MachineInstr) -> Vec<u8> {
for op in mi.operands.iter().rev() {
if let MachineOperand::Imm(imm) = op {
let size = self.determine_immediate_size(mi, *imm);
return self.encode_immediate(*imm, size);
}
}
vec![]
}
fn determine_immediate_size(&self, mi: &MachineInstr, imm: i64) -> u8 {
let opcode = mi.opcode;
if opcode == x86_opcodes::MOV {
let imm_is_last = mi
.operands
.last()
.map_or(false, |op| matches!(op, MachineOperand::Imm(_)));
let dst_is_reg = matches!(mi.operands.first(), Some(MachineOperand::PhysReg(_)));
if imm_is_last && dst_is_reg && mi.operands.len() == 2 {
if let Some(MachineOperand::PhysReg(reg)) = mi.operands.first() {
let reg_id = *reg as u16;
if reg_id < 16 {
return 8;
}
if reg_id < 32 {
return 4;
}
if reg_id < 48 {
return 2;
}
return 1;
}
}
}
match opcode {
x86_opcodes::CALL | x86_opcodes::JMP | x86_opcodes::JE | x86_opcodes::JNE => {
if imm >= -128 && imm <= 127 {
1
} else {
4
}
}
_ => {
if imm >= -128 && imm <= 127 {
1
} else if imm >= -(1i64 << 31) && imm <= (1i64 << 31) - 1 {
4
} else {
8
}
}
}
}
pub fn encode_immediate(&self, imm: i64, size: u8) -> Vec<u8> {
match size {
1 => vec![imm as u8],
2 => (imm as i16).to_le_bytes().to_vec(),
4 => (imm as i32).to_le_bytes().to_vec(),
8 => imm.to_le_bytes().to_vec(),
_ => (imm as i32).to_le_bytes().to_vec(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ModRMEntry {
pub byte: u8,
pub mod_field: u8,
pub reg: u8,
pub rm: u8,
pub desc: &'static str,
}
pub const MODRM_EFFECTIVE_ADDRESS: [[&str; 8]; 4] = [
[
"[RAX]", "[RCX]", "[RDX]", "[RBX]", "[--][--]", "[RIP+disp32]", "[RSI]", "[RDI]", ],
[
"[RAX+disp8]",
"[RCX+disp8]",
"[RDX+disp8]",
"[RBX+disp8]",
"[--][--]+disp8", "[RBP+disp8]",
"[RSI+disp8]",
"[RDI+disp8]",
],
[
"[RAX+disp32]",
"[RCX+disp32]",
"[RDX+disp32]",
"[RBX+disp32]",
"[--][--]+disp32", "[RBP+disp32]",
"[RSI+disp32]",
"[RDI+disp32]",
],
[
"RAX/EAX/AX/AL/MM0/XMM0",
"RCX/ECX/CX/CL/MM1/XMM1",
"RDX/EDX/DX/DL/MM2/XMM2",
"RBX/EBX/BX/BL/MM3/XMM3",
"RSP/ESP/SP/AH/MM4/XMM4",
"RBP/EBP/BP/CH/MM5/XMM5",
"RSI/ESI/SI/DH/MM6/XMM6",
"RDI/EDI/DI/BH/MM7/XMM7",
],
];
pub fn modrm_entry(mod_field: u8, reg: u8, rm: u8) -> ModRMEntry {
let byte = (mod_field << 6) | ((reg & 0x07) << 3) | (rm & 0x07);
let desc = MODRM_EFFECTIVE_ADDRESS[mod_field as usize & 0x03][rm as usize & 0x07];
ModRMEntry {
byte,
mod_field,
reg,
rm,
desc,
}
}
pub fn generate_modrm_table() -> Vec<ModRMEntry> {
let mut table = Vec::with_capacity(256);
for byte in 0u8..=255u8 {
let mod_field = (byte >> 6) & 0x03;
let reg = (byte >> 3) & 0x07;
let rm = byte & 0x07;
let desc = MODRM_EFFECTIVE_ADDRESS[mod_field as usize][rm as usize];
table.push(ModRMEntry {
byte,
mod_field,
reg,
rm,
desc,
});
}
table
}
pub fn lookup_modrm(mod_field: u8, reg: u8, rm: u8) -> ModRMEntry {
modrm_entry(mod_field, reg, rm)
}
pub mod sib_scale {
pub const SCALE_1: u8 = 0b00;
pub const SCALE_2: u8 = 0b01;
pub const SCALE_4: u8 = 0b10;
pub const SCALE_8: u8 = 0b11;
}
pub const SIB_NO_INDEX: u8 = 0b100;
pub const SIB_NO_BASE_DISP32: u8 = 0b101;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SIBEntry {
pub byte: u8,
pub scale: u8,
pub index: u8,
pub base: u8,
pub no_index: bool,
pub no_base: bool,
pub scale_multiplier: u8,
}
pub fn sib_entry(scale: u8, index: u8, base: u8) -> SIBEntry {
let byte = ((scale & 0x03) << 6) | ((index & 0x07) << 3) | (base & 0x07);
let no_index = index == SIB_NO_INDEX;
let no_base = base == SIB_NO_BASE_DISP32;
let scale_multiplier = match scale & 0x03 {
sib_scale::SCALE_1 => 1,
sib_scale::SCALE_2 => 2,
sib_scale::SCALE_4 => 4,
sib_scale::SCALE_8 => 8,
_ => 1,
};
SIBEntry {
byte,
scale,
index,
base,
no_index,
no_base,
scale_multiplier,
}
}
pub fn scale_to_sib_field(scale: u8) -> u8 {
match scale {
1 => sib_scale::SCALE_1,
2 => sib_scale::SCALE_2,
4 => sib_scale::SCALE_4,
8 => sib_scale::SCALE_8,
_ => sib_scale::SCALE_1,
}
}
pub mod rex_fields {
pub const BASE: u8 = 0x40;
pub const BIT_W: u8 = 0x08;
pub const BIT_R: u8 = 0x04;
pub const BIT_X: u8 = 0x02;
pub const BIT_B: u8 = 0x01;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct REXDescriptor {
pub byte: u8,
pub w: bool,
pub r: bool,
pub x: bool,
pub b: bool,
pub description: &'static str,
}
pub const REX_TABLE: [REXDescriptor; 16] = [
REXDescriptor {
byte: 0x40,
w: false,
r: false,
x: false,
b: false,
description: "REX: no extensions",
},
REXDescriptor {
byte: 0x41,
w: false,
r: false,
x: false,
b: true,
description: "REX.B: extend ModR/M r/m",
},
REXDescriptor {
byte: 0x42,
w: false,
r: false,
x: true,
b: false,
description: "REX.X: extend SIB index",
},
REXDescriptor {
byte: 0x43,
w: false,
r: false,
x: true,
b: true,
description: "REX.XB",
},
REXDescriptor {
byte: 0x44,
w: false,
r: true,
x: false,
b: false,
description: "REX.R: extend ModR/M reg",
},
REXDescriptor {
byte: 0x45,
w: false,
r: true,
x: false,
b: true,
description: "REX.RB",
},
REXDescriptor {
byte: 0x46,
w: false,
r: true,
x: true,
b: false,
description: "REX.RX",
},
REXDescriptor {
byte: 0x47,
w: false,
r: true,
x: true,
b: true,
description: "REX.RXB",
},
REXDescriptor {
byte: 0x48,
w: true,
r: false,
x: false,
b: false,
description: "REX.W: 64-bit operand size",
},
REXDescriptor {
byte: 0x49,
w: true,
r: false,
x: false,
b: true,
description: "REX.WB",
},
REXDescriptor {
byte: 0x4A,
w: true,
r: false,
x: true,
b: false,
description: "REX.WX",
},
REXDescriptor {
byte: 0x4B,
w: true,
r: false,
x: true,
b: true,
description: "REX.WXB",
},
REXDescriptor {
byte: 0x4C,
w: true,
r: true,
x: false,
b: false,
description: "REX.WR",
},
REXDescriptor {
byte: 0x4D,
w: true,
r: true,
x: false,
b: true,
description: "REX.WRB",
},
REXDescriptor {
byte: 0x4E,
w: true,
r: true,
x: true,
b: false,
description: "REX.WRX",
},
REXDescriptor {
byte: 0x4F,
w: true,
r: true,
x: true,
b: true,
description: "REX.WRXB: all extensions",
},
];
pub fn rex_descriptor(byte: u8) -> Option<&'static REXDescriptor> {
if (0x40..=0x4F).contains(&byte) {
Some(&REX_TABLE[(byte - 0x40) as usize])
} else {
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VEXDescriptor {
pub three_byte: bool,
pub bytes: [u8; 3],
pub count: u8,
pub not_r: bool,
pub not_x: bool,
pub not_b: bool,
pub map_select: u8,
pub w: bool,
pub vvvv: u8,
pub l: bool,
pub pp: u8,
}
pub mod vex_map {
pub const MAP_0F: u8 = 1; pub const MAP_0F38: u8 = 2; pub const MAP_0F3A: u8 = 3; }
pub mod vex_pp {
pub const NONE: u8 = 0b00; pub const P66: u8 = 0b01; pub const PF3: u8 = 0b10; pub const PF2: u8 = 0b11; }
pub fn build_vex_2byte(not_r: bool, vvvv: u8, l: bool, pp: u8) -> [u8; 2] {
let byte1 = (if not_r { 0x80 } else { 0x00 })
| ((!vvvv & 0x0F) << 3)
| (if l { 0x04 } else { 0x00 })
| (pp & 0x03);
[0xC5, byte1]
}
pub fn build_vex_3byte(
not_r: bool,
not_x: bool,
not_b: bool,
map_select: u8,
w: bool,
vvvv: u8,
l: bool,
pp: u8,
) -> [u8; 3] {
let byte1 = (if not_r { 0x80 } else { 0x00 })
| (if not_x { 0x40 } else { 0x00 })
| (if not_b { 0x20 } else { 0x00 })
| (map_select & 0x1F);
let byte2 = (if w { 0x80 } else { 0x00 })
| ((!vvvv & 0x0F) << 3)
| (if l { 0x04 } else { 0x00 })
| (pp & 0x03);
[0xC4, byte1, byte2]
}
pub fn requires_vex_3byte(not_x: bool, not_b: bool, w: bool, map_select: u8) -> bool {
not_x || not_b || w || map_select != vex_map::MAP_0F
}
pub fn generate_vex2_table() -> Vec<VEXDescriptor> {
let mut table = Vec::with_capacity(256);
for b1 in 0u8..=255u8 {
let not_r = (b1 & 0x80) != 0;
let vvvv = (!b1 >> 3) & 0x0F;
let l = (b1 & 0x04) != 0;
let pp = b1 & 0x03;
table.push(VEXDescriptor {
three_byte: false,
bytes: [0xC5, b1, 0],
count: 2,
not_r,
not_x: false,
not_b: false,
map_select: vex_map::MAP_0F,
w: false,
vvvv,
l,
pp,
});
}
table
}
pub mod evex_layout {
pub const MAGIC: u8 = 0x62;
pub const P1_FIXED_BIT: u8 = 0x04; pub const P2_FIXED_BIT: u8 = 0x08; }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EVEXDescriptor {
pub bytes: [u8; 4],
pub r_prime: bool,
pub x_prime: bool,
pub b_prime: bool,
pub not_r: bool,
pub not_x: bool,
pub not_b: bool,
pub v_prime: bool,
pub w: bool,
pub vvvv: u8,
pub pp: u8,
pub z: bool,
pub ll: u8,
pub b_cast: bool,
pub aaa: u8,
pub mmmm: u8,
}
pub fn build_evex_prefix(
r_prime: bool,
_x_prime: bool,
_b_prime: bool,
not_r: bool,
not_x: bool,
not_b: bool,
mmmm: u8,
w: bool,
vvvv: u8,
pp: u8,
z: bool,
ll: u8,
b_cast: bool,
v_prime: bool,
aaa: u8,
) -> [u8; 4] {
let p0 = (if not_r { 0x00 } else { 0x80 })
| (if not_x { 0x00 } else { 0x40 })
| (if not_b { 0x00 } else { 0x20 })
| (if r_prime { 0x00 } else { 0x10 })
| (mmmm & 0x07);
let p1 = (if w { 0x80 } else { 0x00 })
| ((!vvvv & 0x0F) << 3)
| evex_layout::P1_FIXED_BIT
| (pp & 0x03);
let p2 = (if z { 0x80 } else { 0x00 })
| ((ll & 0x03) << 5)
| (if b_cast { 0x10 } else { 0x00 })
| (if v_prime { 0x00 } else { 0x08 })
| (aaa & 0x07);
[evex_layout::MAGIC, p0, p1, p2]
}
pub mod evex_vector_length {
pub const VL128: u8 = 0b00; pub const VL256: u8 = 0b01; pub const VL512: u8 = 0b10; }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EVEXTupleType {
Full,
Half,
Quarter,
Eighth,
Mem128,
Movddup,
}
impl EVEXTupleType {
pub fn ncd8_multiplier(self, vl: u8) -> u8 {
use evex_vector_length::*;
use EVEXTupleType::*;
match (self, vl) {
(Full, VL128) | (Full, VL256) | (Full, VL512) => 1,
(Half, VL128) => 1,
(Half, VL256) => 2,
(Half, VL512) => 4,
(Quarter, VL128) => 1,
(Quarter, VL256) => 2,
(Quarter, VL512) => 4,
(Eighth, VL128) => 1,
(Eighth, VL256) => 2,
(Eighth, VL512) => 4,
(Mem128, _) => 16,
(Movddup, VL128) => 8,
(Movddup, VL256) => 32,
(Movddup, VL512) => 64,
_ => 1, }
}
}
pub mod xop_layout {
pub const XOP_MAGIC: u8 = 0x8F;
}
pub fn build_xop_prefix(
not_r: bool,
not_x: bool,
not_b: bool,
map_select: u8,
w: bool,
vvvv: u8,
l: bool,
pp: u8,
) -> [u8; 3] {
let byte1 = (if not_r { 0x80 } else { 0x00 })
| (if not_x { 0x40 } else { 0x00 })
| (if not_b { 0x20 } else { 0x00 })
| (map_select & 0x1F);
let byte2 = (if w { 0x80 } else { 0x00 })
| ((!vvvv & 0x0F) << 3)
| (if l { 0x04 } else { 0x00 })
| (pp & 0x03);
[xop_layout::XOP_MAGIC, byte1, byte2]
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NCD8Entry {
pub multiplier: u8,
pub tuple: &'static str,
}
pub const NCD8_TABLE: [NCD8Entry; 7] = [
NCD8Entry {
multiplier: 1,
tuple: "Full",
},
NCD8Entry {
multiplier: 2,
tuple: "Half",
},
NCD8Entry {
multiplier: 4,
tuple: "Quarter",
},
NCD8Entry {
multiplier: 8,
tuple: "Eighth",
},
NCD8Entry {
multiplier: 16,
tuple: "Mem128",
},
NCD8Entry {
multiplier: 32,
tuple: "HalfMem",
},
NCD8Entry {
multiplier: 64,
tuple: "QuarterMem",
},
];
pub fn ncd8_multiplier(broadcast: bool, vector_length: u8, tuple_type: &str) -> u8 {
match (broadcast, vector_length, tuple_type) {
(false, 0, "Full") => 1,
(false, 1, "Full") => 1,
(false, 2, "Full") => 1,
(false, 0, "Half") => 1,
(false, 1, "Half") => 2,
(false, 2, "Half") => 4,
(false, 0, "Quarter") => 1,
(false, 1, "Quarter") => 2,
(false, 2, "Quarter") => 4,
(false, 0, "Eighth") => 1,
(false, 1, "Eighth") => 2,
(false, 2, "Eighth") => 4,
(false, _, "Mem128") => 16,
(true, 0, "Full") => 1,
(true, 1, "Full") => 8, (true, 2, "Full") => 16, (true, 1, "Half") => 4, (true, 2, "Half") => 8, (true, 1, "Quarter") => 2, (true, 2, "Quarter") => 4, _ => 1,
}
}
pub fn encode_ncd8_displacement(disp: i64, multiplier: u8) -> Option<u8> {
let m = multiplier as i64;
if disp % m == 0 {
let compressed = disp / m;
if (-128i64..=127i64).contains(&compressed) {
return Some(compressed as u8);
}
}
None
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BranchDisplacement {
Short(i8),
Near(i32),
}
impl BranchDisplacement {
pub fn to_bytes(self) -> Vec<u8> {
match self {
BranchDisplacement::Short(disp) => vec![disp as u8],
BranchDisplacement::Near(disp) => disp.to_le_bytes().to_vec(),
}
}
pub fn byte_count(self) -> usize {
match self {
BranchDisplacement::Short(_) => 1,
BranchDisplacement::Near(_) => 4,
}
}
pub fn is_short(self) -> bool {
matches!(self, BranchDisplacement::Short(_))
}
pub fn is_near(self) -> bool {
matches!(self, BranchDisplacement::Near(_))
}
}
pub fn relax_branch_displacement(target_offset: i64, current_pc: i64) -> BranchDisplacement {
let rel = target_offset - current_pc;
if (-128i64..=127i64).contains(&rel) {
BranchDisplacement::Short(rel as i8)
} else {
BranchDisplacement::Near(rel as i32)
}
}
pub fn branch_instruction_size(_opcode: u8, disp: BranchDisplacement) -> usize {
1 + disp.byte_count() }
#[derive(Debug, Clone)]
pub struct InstructionEncoding {
pub opcode_bytes: Vec<u8>,
pub has_modrm: bool,
pub has_sib: bool,
pub requires_rex: bool,
pub requires_vex: bool,
pub requires_evex: bool,
pub immediate_size: u8,
pub implied_prefix: u8,
pub opcode_map: u8,
pub displacement_size: u8,
}
impl Default for InstructionEncoding {
fn default() -> Self {
InstructionEncoding {
opcode_bytes: Vec::new(),
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct EncodingLookupEntry {
pub bytes: Vec<u8>,
pub encoding: InstructionEncoding,
}
pub fn alu_encoding(op_rm_r: u8, _op_r_rm: u8, _group_reg: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_rm_r],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
}
}
pub fn sse_encoding(_op_0f: u8, op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x0F, op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0x66,
opcode_map: 0,
displacement_size: 0,
}
}
pub fn vex_encoding(map: u8, op_suffix: u8, pp: u8, _is_256bit: bool) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: true,
requires_evex: false,
immediate_size: 0,
implied_prefix: pp,
opcode_map: map,
displacement_size: 0,
}
}
pub fn evex_encoding(map: u8, op_suffix: u8, pp: u8, _is_512bit: bool) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: true,
immediate_size: 0,
implied_prefix: pp,
opcode_map: map,
displacement_size: 0,
}
}
#[derive(Debug, Clone)]
pub struct NamedEncoding {
pub opcode: u32,
pub mnemonic: &'static str,
pub encoding: InstructionEncoding,
}
pub fn common_encoding_table() -> Vec<NamedEncoding> {
vec![
NamedEncoding {
opcode: 1,
mnemonic: "mov",
encoding: mov_encoding(),
},
NamedEncoding {
opcode: 35,
mnemonic: "movsx",
encoding: sse_encoding(0xBE, 0),
},
NamedEncoding {
opcode: 36,
mnemonic: "movzx",
encoding: sse_encoding(0xB6, 0),
},
NamedEncoding {
opcode: 2,
mnemonic: "add",
encoding: alu_encoding(0x01, 0x03, 0),
},
NamedEncoding {
opcode: 3,
mnemonic: "sub",
encoding: alu_encoding(0x29, 0x2B, 5),
},
NamedEncoding {
opcode: 6,
mnemonic: "and",
encoding: alu_encoding(0x21, 0x23, 4),
},
NamedEncoding {
opcode: 7,
mnemonic: "or",
encoding: alu_encoding(0x09, 0x0B, 1),
},
NamedEncoding {
opcode: 8,
mnemonic: "xor",
encoding: alu_encoding(0x31, 0x33, 6),
},
NamedEncoding {
opcode: 18,
mnemonic: "cmp",
encoding: alu_encoding(0x39, 0x3B, 7),
},
NamedEncoding {
opcode: 24,
mnemonic: "adc",
encoding: alu_encoding(0x11, 0x13, 2),
},
NamedEncoding {
opcode: 25,
mnemonic: "sbb",
encoding: alu_encoding(0x19, 0x1B, 3),
},
NamedEncoding {
opcode: 27,
mnemonic: "imul",
encoding: sse_encoding(0xAF, 0),
},
NamedEncoding {
opcode: 19,
mnemonic: "lea",
encoding: InstructionEncoding {
opcode_bytes: vec![0x8D],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 9,
mnemonic: "shl",
encoding: shift_encoding(),
},
NamedEncoding {
opcode: 10,
mnemonic: "shr",
encoding: shift_encoding(),
},
NamedEncoding {
opcode: 29,
mnemonic: "sar",
encoding: shift_encoding(),
},
NamedEncoding {
opcode: 13,
mnemonic: "call",
encoding: InstructionEncoding {
opcode_bytes: vec![0xE8],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 4,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 14,
mnemonic: "ret",
encoding: InstructionEncoding {
opcode_bytes: vec![0xC3],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 15,
mnemonic: "jmp",
encoding: InstructionEncoding {
opcode_bytes: vec![0xE9],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 4,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 11,
mnemonic: "push",
encoding: InstructionEncoding {
opcode_bytes: vec![0xFF],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 12,
mnemonic: "pop",
encoding: InstructionEncoding {
opcode_bytes: vec![0x8F],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 26,
mnemonic: "test",
encoding: InstructionEncoding {
opcode_bytes: vec![0x85],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 72,
mnemonic: "bt",
encoding: sse_encoding(0xA3, 0),
},
NamedEncoding {
opcode: 73,
mnemonic: "bts",
encoding: sse_encoding(0xAB, 0),
},
NamedEncoding {
opcode: 74,
mnemonic: "btr",
encoding: sse_encoding(0xB3, 0),
},
NamedEncoding {
opcode: 75,
mnemonic: "btc",
encoding: sse_encoding(0xBB, 0),
},
NamedEncoding {
opcode: 76,
mnemonic: "bsf",
encoding: sse_encoding(0xBC, 0),
},
NamedEncoding {
opcode: 77,
mnemonic: "bsr",
encoding: sse_encoding(0xBD, 0),
},
NamedEncoding {
opcode: 70,
mnemonic: "bswap",
encoding: InstructionEncoding {
opcode_bytes: vec![0x0F, 0xC8],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 71,
mnemonic: "xchg",
encoding: InstructionEncoding {
opcode_bytes: vec![0x87],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 78,
mnemonic: "cbw",
encoding: InstructionEncoding {
opcode_bytes: vec![0x98],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 79,
mnemonic: "cwde",
encoding: InstructionEncoding {
opcode_bytes: vec![0x98],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 80,
mnemonic: "cdqe",
encoding: InstructionEncoding {
opcode_bytes: vec![0x48, 0x98],
has_modrm: false,
has_sib: false,
requires_rex: true,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 84,
mnemonic: "lahf",
encoding: InstructionEncoding {
opcode_bytes: vec![0x9F],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 85,
mnemonic: "sahf",
encoding: InstructionEncoding {
opcode_bytes: vec![0x9E],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 97,
mnemonic: "syscall",
encoding: InstructionEncoding {
opcode_bytes: vec![0x0F, 0x05],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 99,
mnemonic: "hlt",
encoding: InstructionEncoding {
opcode_bytes: vec![0xF4],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 100,
mnemonic: "rdtsc",
encoding: InstructionEncoding {
opcode_bytes: vec![0x0F, 0x31],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 101,
mnemonic: "cpuid",
encoding: InstructionEncoding {
opcode_bytes: vec![0x0F, 0xA2],
has_modrm: false,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
},
},
NamedEncoding {
opcode: 42,
mnemonic: "cmove",
encoding: sse_encoding(0x44, 0),
},
NamedEncoding {
opcode: 43,
mnemonic: "cmovne",
encoding: sse_encoding(0x45, 0),
},
NamedEncoding {
opcode: 50,
mnemonic: "cmovl",
encoding: sse_encoding(0x4C, 0),
},
NamedEncoding {
opcode: 53,
mnemonic: "cmovg",
encoding: sse_encoding(0x4F, 0),
},
NamedEncoding {
opcode: 117,
mnemonic: "addss",
encoding: sse33_encoding(0x58),
},
NamedEncoding {
opcode: 118,
mnemonic: "addsd",
encoding: ssef2_encoding(0x58),
},
NamedEncoding {
opcode: 119,
mnemonic: "subss",
encoding: sse33_encoding(0x5C),
},
NamedEncoding {
opcode: 120,
mnemonic: "subsd",
encoding: ssef2_encoding(0x5C),
},
NamedEncoding {
opcode: 121,
mnemonic: "mulss",
encoding: sse33_encoding(0x59),
},
NamedEncoding {
opcode: 122,
mnemonic: "mulsd",
encoding: ssef2_encoding(0x59),
},
NamedEncoding {
opcode: 123,
mnemonic: "divss",
encoding: sse33_encoding(0x5E),
},
NamedEncoding {
opcode: 124,
mnemonic: "divsd",
encoding: ssef2_encoding(0x5E),
},
NamedEncoding {
opcode: 125,
mnemonic: "sqrtss",
encoding: sse33_encoding(0x51),
},
NamedEncoding {
opcode: 126,
mnemonic: "sqrtsd",
encoding: ssef2_encoding(0x51),
},
NamedEncoding {
opcode: 127,
mnemonic: "minss",
encoding: sse33_encoding(0x5D),
},
NamedEncoding {
opcode: 128,
mnemonic: "minsd",
encoding: ssef2_encoding(0x5D),
},
NamedEncoding {
opcode: 129,
mnemonic: "maxss",
encoding: sse33_encoding(0x5F),
},
NamedEncoding {
opcode: 130,
mnemonic: "maxsd",
encoding: ssef2_encoding(0x5F),
},
NamedEncoding {
opcode: 131,
mnemonic: "cmpss",
encoding: sse33_imm8_encoding(0xC2),
},
NamedEncoding {
opcode: 132,
mnemonic: "cmpsd",
encoding: ssef2_imm8_encoding(0xC2),
},
NamedEncoding {
opcode: 133,
mnemonic: "cvtsi2ss",
encoding: sse33_encoding(0x2A),
},
NamedEncoding {
opcode: 134,
mnemonic: "cvtsi2sd",
encoding: ssef2_encoding(0x2A),
},
NamedEncoding {
opcode: 135,
mnemonic: "cvtss2si",
encoding: sse33_encoding(0x2D),
},
NamedEncoding {
opcode: 136,
mnemonic: "cvtsd2si",
encoding: ssef2_encoding(0x2D),
},
NamedEncoding {
opcode: 137,
mnemonic: "cvttss2si",
encoding: sse33_encoding(0x2C),
},
NamedEncoding {
opcode: 138,
mnemonic: "cvttsd2si",
encoding: ssef2_encoding(0x2C),
},
NamedEncoding {
opcode: 244,
mnemonic: "addps",
encoding: sse0f_encoding(0x58),
},
NamedEncoding {
opcode: 245,
mnemonic: "addpd",
encoding: sse66_encoding(0x58),
},
NamedEncoding {
opcode: 246,
mnemonic: "subps",
encoding: sse0f_encoding(0x5C),
},
NamedEncoding {
opcode: 247,
mnemonic: "subpd",
encoding: sse66_encoding(0x5C),
},
NamedEncoding {
opcode: 248,
mnemonic: "mulps",
encoding: sse0f_encoding(0x59),
},
NamedEncoding {
opcode: 249,
mnemonic: "mulpd",
encoding: sse66_encoding(0x59),
},
NamedEncoding {
opcode: 250,
mnemonic: "divps",
encoding: sse0f_encoding(0x5E),
},
NamedEncoding {
opcode: 251,
mnemonic: "divpd",
encoding: sse66_encoding(0x5E),
},
NamedEncoding {
opcode: 139,
mnemonic: "andps",
encoding: sse0f_encoding(0x54),
},
NamedEncoding {
opcode: 140,
mnemonic: "andnps",
encoding: sse0f_encoding(0x55),
},
NamedEncoding {
opcode: 141,
mnemonic: "orps",
encoding: sse0f_encoding(0x56),
},
NamedEncoding {
opcode: 142,
mnemonic: "xorps",
encoding: sse0f_encoding(0x57),
},
NamedEncoding {
opcode: X86Opcode::ANDPD as u32,
mnemonic: "andpd",
encoding: sse66_encoding(0x54),
},
NamedEncoding {
opcode: X86Opcode::ORPD as u32,
mnemonic: "orpd",
encoding: sse66_encoding(0x56),
},
NamedEncoding {
opcode: X86Opcode::XORPD as u32,
mnemonic: "xorpd",
encoding: sse66_encoding(0x57),
},
NamedEncoding {
opcode: X86Opcode::ANDNPD as u32,
mnemonic: "andnpd",
encoding: sse66_encoding(0x55),
},
NamedEncoding {
opcode: 143,
mnemonic: "shufps",
encoding: sse0f_imm8_encoding(0xC6),
},
NamedEncoding {
opcode: 144,
mnemonic: "shufpd",
encoding: sse66_imm8_encoding(0xC6),
},
NamedEncoding {
opcode: 148,
mnemonic: "unpcklps",
encoding: sse0f_encoding(0x14),
},
NamedEncoding {
opcode: 149,
mnemonic: "unpckhps",
encoding: sse0f_encoding(0x15),
},
NamedEncoding {
opcode: 150,
mnemonic: "unpcklpd",
encoding: sse66_encoding(0x14),
},
NamedEncoding {
opcode: 151,
mnemonic: "unpckhpd",
encoding: sse66_encoding(0x15),
},
NamedEncoding {
opcode: 152,
mnemonic: "movhlps",
encoding: sse0f_encoding(0x12),
},
NamedEncoding {
opcode: 153,
mnemonic: "movlhps",
encoding: sse0f_encoding(0x16),
},
NamedEncoding {
opcode: 238,
mnemonic: "cvtss2sd",
encoding: sse33_encoding(0x5A),
},
NamedEncoding {
opcode: 239,
mnemonic: "cvtsd2ss",
encoding: ssef2_encoding(0x5A),
},
NamedEncoding {
opcode: X86Opcode::CVTDQ2PS as u32,
mnemonic: "cvtdq2ps",
encoding: sse0f_encoding(0x5B),
},
NamedEncoding {
opcode: X86Opcode::CVTPS2DQ as u32,
mnemonic: "cvtps2dq",
encoding: sse66_encoding(0x5B),
},
NamedEncoding {
opcode: X86Opcode::CVTTPS2DQ as u32,
mnemonic: "cvttps2dq",
encoding: sse33_encoding(0x5B),
},
NamedEncoding {
opcode: 263,
mnemonic: "movsldup",
encoding: sse33_encoding(0x12),
},
NamedEncoding {
opcode: 264,
mnemonic: "movshdup",
encoding: sse33_encoding(0x16),
},
NamedEncoding {
opcode: 265,
mnemonic: "movddup",
encoding: ssef2_encoding(0x12),
},
NamedEncoding {
opcode: 285,
mnemonic: "dpps",
encoding: sse38_imm8_encoding(0x40, 0x66),
},
NamedEncoding {
opcode: 286,
mnemonic: "dppd",
encoding: sse38_imm8_encoding(0x41, 0x66),
},
NamedEncoding {
opcode: 281,
mnemonic: "blendps",
encoding: sse3a_imm8_encoding(0x0C, 0x66),
},
NamedEncoding {
opcode: 282,
mnemonic: "blendpd",
encoding: sse3a_imm8_encoding(0x0D, 0x66),
},
NamedEncoding {
opcode: 283,
mnemonic: "blendvps",
encoding: sse38_encoding(0x14, 0x66),
},
NamedEncoding {
opcode: 284,
mnemonic: "blendvpd",
encoding: sse38_encoding(0x15, 0x66),
},
NamedEncoding {
opcode: X86Opcode::ROUNDPS as u32,
mnemonic: "roundps",
encoding: sse3a_imm8_encoding(0x08, 0x66),
},
NamedEncoding {
opcode: X86Opcode::ROUNDPD as u32,
mnemonic: "roundpd",
encoding: sse3a_imm8_encoding(0x09, 0x66),
},
NamedEncoding {
opcode: 287,
mnemonic: "extractps",
encoding: sse3a_imm8_encoding(0x17, 0x66),
},
NamedEncoding {
opcode: 288,
mnemonic: "insertps",
encoding: sse3a_imm8_encoding(0x21, 0x66),
},
NamedEncoding {
opcode: 289,
mnemonic: "pmulld",
encoding: sse38_encoding(0x40, 0x66),
},
NamedEncoding {
opcode: 290,
mnemonic: "pmuldq",
encoding: sse38_encoding(0x28, 0x66),
},
NamedEncoding {
opcode: 291,
mnemonic: "pminsb",
encoding: sse38_encoding(0x38, 0x66),
},
NamedEncoding {
opcode: 292,
mnemonic: "pminsd",
encoding: sse38_encoding(0x39, 0x66),
},
NamedEncoding {
opcode: 293,
mnemonic: "pminuw",
encoding: sse38_encoding(0x3A, 0x66),
},
NamedEncoding {
opcode: 294,
mnemonic: "pminud",
encoding: sse38_encoding(0x3B, 0x66),
},
NamedEncoding {
opcode: 295,
mnemonic: "pmaxsb",
encoding: sse38_encoding(0x3C, 0x66),
},
NamedEncoding {
opcode: 296,
mnemonic: "pmaxsd",
encoding: sse38_encoding(0x3D, 0x66),
},
NamedEncoding {
opcode: 297,
mnemonic: "pmaxuw",
encoding: sse38_encoding(0x3E, 0x66),
},
NamedEncoding {
opcode: 298,
mnemonic: "pmaxud",
encoding: sse38_encoding(0x3F, 0x66),
},
NamedEncoding {
opcode: 299,
mnemonic: "packusdw",
encoding: sse38_encoding(0x2B, 0x66),
},
NamedEncoding {
opcode: 300,
mnemonic: "pcmpeqq",
encoding: sse38_encoding(0x29, 0x66),
},
NamedEncoding {
opcode: 154,
mnemonic: "pextrb",
encoding: sse3a_imm8_encoding(0x14, 0x66),
},
NamedEncoding {
opcode: 155,
mnemonic: "pextrw",
encoding: sse3a_imm8_encoding(0x15, 0x66),
},
NamedEncoding {
opcode: 156,
mnemonic: "pextrd",
encoding: sse3a_imm8_encoding(0x16, 0x66),
},
NamedEncoding {
opcode: 157,
mnemonic: "pextrq",
encoding: sse3a_imm8_encoding_rexw(0x16, 0x66),
},
NamedEncoding {
opcode: 158,
mnemonic: "pinsrb",
encoding: sse3a_imm8_encoding(0x20, 0x66),
},
NamedEncoding {
opcode: 159,
mnemonic: "pinsrw",
encoding: sse3a_imm8_encoding(0xC4, 0x66),
},
NamedEncoding {
opcode: 160,
mnemonic: "pinsrd",
encoding: sse3a_imm8_encoding(0x22, 0x66),
},
NamedEncoding {
opcode: 161,
mnemonic: "pinsrq",
encoding: sse3a_imm8_encoding_rexw(0x22, 0x66),
},
NamedEncoding {
opcode: 301,
mnemonic: "crc32",
encoding: crc32_encoding(),
},
NamedEncoding {
opcode: X86Opcode::AESENC as u32,
mnemonic: "aesenc",
encoding: aes_encoding(0xDC),
},
NamedEncoding {
opcode: X86Opcode::AESENCLAST as u32,
mnemonic: "aesenclast",
encoding: aes_encoding(0xDD),
},
NamedEncoding {
opcode: X86Opcode::AESDEC as u32,
mnemonic: "aesdec",
encoding: aes_encoding(0xDE),
},
NamedEncoding {
opcode: X86Opcode::AESDECLAST as u32,
mnemonic: "aesdeclast",
encoding: aes_encoding(0xDF),
},
NamedEncoding {
opcode: X86Opcode::AESIMC as u32,
mnemonic: "aesimc",
encoding: aes_encoding(0xDB),
},
NamedEncoding {
opcode: X86Opcode::AESKEYGENASSIST as u32,
mnemonic: "aeskeygenassist",
encoding: aes_keygen_encoding(),
},
NamedEncoding {
opcode: X86Opcode::PCLMULQDQ as u32,
mnemonic: "pclmulqdq",
encoding: pclmul_encoding(),
},
NamedEncoding {
opcode: X86Opcode::SHA1RNDS4 as u32,
mnemonic: "sha1rnds4",
encoding: sha_imm8_encoding(0xCC),
},
NamedEncoding {
opcode: X86Opcode::SHA1NEXTE as u32,
mnemonic: "sha1nexte",
encoding: sha_encoding(0xC8),
},
NamedEncoding {
opcode: X86Opcode::SHA1MSG1 as u32,
mnemonic: "sha1msg1",
encoding: sha_encoding(0xC9),
},
NamedEncoding {
opcode: X86Opcode::SHA1MSG2 as u32,
mnemonic: "sha1msg2",
encoding: sha_encoding(0xCA),
},
NamedEncoding {
opcode: X86Opcode::SHA256RNDS2 as u32,
mnemonic: "sha256rnds2",
encoding: sha_encoding(0xCB),
},
NamedEncoding {
opcode: X86Opcode::SHA256MSG1 as u32,
mnemonic: "sha256msg1",
encoding: sha_encoding(0xCC),
},
NamedEncoding {
opcode: X86Opcode::SHA256MSG2 as u32,
mnemonic: "sha256msg2",
encoding: sha_encoding(0xCD),
},
NamedEncoding {
opcode: 306,
mnemonic: "vaddss",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x58, 0xF3),
},
NamedEncoding {
opcode: 307,
mnemonic: "vaddsd",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x58, 0xF2),
},
NamedEncoding {
opcode: X86Opcode::VSUBSS as u32,
mnemonic: "vsubss",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x5C, 0xF3),
},
NamedEncoding {
opcode: X86Opcode::VSUBSD as u32,
mnemonic: "vsubsd",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x5C, 0xF2),
},
NamedEncoding {
opcode: X86Opcode::VMULSS as u32,
mnemonic: "vmulss",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x59, 0xF3),
},
NamedEncoding {
opcode: X86Opcode::VMULSD as u32,
mnemonic: "vmulsd",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x59, 0xF2),
},
NamedEncoding {
opcode: X86Opcode::VDIVSS as u32,
mnemonic: "vdivss",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x5E, 0xF3),
},
NamedEncoding {
opcode: X86Opcode::VDIVSD as u32,
mnemonic: "vdivsd",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x5E, 0xF2),
},
NamedEncoding {
opcode: 304,
mnemonic: "vaddps",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x58, 0x00),
},
NamedEncoding {
opcode: 305,
mnemonic: "vaddpd",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x58, 0x66),
},
NamedEncoding {
opcode: 308,
mnemonic: "vsubps",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x5C, 0x00),
},
NamedEncoding {
opcode: 309,
mnemonic: "vsubpd",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x5C, 0x66),
},
NamedEncoding {
opcode: 310,
mnemonic: "vmulps",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x59, 0x00),
},
NamedEncoding {
opcode: 311,
mnemonic: "vmulpd",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x59, 0x66),
},
NamedEncoding {
opcode: 312,
mnemonic: "vdivps",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x5E, 0x00),
},
NamedEncoding {
opcode: 313,
mnemonic: "vdivpd",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x5E, 0x66),
},
NamedEncoding {
opcode: 314,
mnemonic: "vandps",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x54, 0x00),
},
NamedEncoding {
opcode: 315,
mnemonic: "vandnps",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x55, 0x00),
},
NamedEncoding {
opcode: 316,
mnemonic: "vorps",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x56, 0x00),
},
NamedEncoding {
opcode: 317,
mnemonic: "vxorps",
encoding: vex_128_encoding(vex_map::MAP_0F, 0x57, 0x00),
},
NamedEncoding {
opcode: 350,
mnemonic: "vfmadd132ps",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0x98, 0x66),
},
NamedEncoding {
opcode: 351,
mnemonic: "vfmadd213ps",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0xA8, 0x66),
},
NamedEncoding {
opcode: 352,
mnemonic: "vfmadd231ps",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0xB8, 0x66),
},
NamedEncoding {
opcode: 338,
mnemonic: "vfmadd132pd",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0x98, 0x66),
},
NamedEncoding {
opcode: 339,
mnemonic: "vfmadd213pd",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0xA8, 0x66),
},
NamedEncoding {
opcode: 340,
mnemonic: "vfmadd231pd",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0xB8, 0x66),
},
NamedEncoding {
opcode: 353,
mnemonic: "vfmadd132ss",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0x99, 0x66),
},
NamedEncoding {
opcode: 354,
mnemonic: "vfmadd213ss",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0xA9, 0x66),
},
NamedEncoding {
opcode: 355,
mnemonic: "vfmadd231ss",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0xB9, 0x66),
},
NamedEncoding {
opcode: 356,
mnemonic: "vfmadd132sd",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0x99, 0xF2),
},
NamedEncoding {
opcode: 357,
mnemonic: "vfmadd213sd",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0xA9, 0xF2),
},
NamedEncoding {
opcode: 358,
mnemonic: "vfmadd231sd",
encoding: vex_128_encoding(vex_map::MAP_0F38, 0xB9, 0xF2),
},
NamedEncoding {
opcode: X86Opcode::VPADDB as u32,
mnemonic: "vpaddb",
encoding: vex_256_encoding(vex_map::MAP_0F, 0xFC, 0x66),
},
NamedEncoding {
opcode: X86Opcode::VPADDW as u32,
mnemonic: "vpaddw",
encoding: vex_256_encoding(vex_map::MAP_0F, 0xFD, 0x66),
},
NamedEncoding {
opcode: X86Opcode::VPADDD as u32,
mnemonic: "vpaddd",
encoding: vex_256_encoding(vex_map::MAP_0F, 0xFE, 0x66),
},
NamedEncoding {
opcode: X86Opcode::VPADDQ as u32,
mnemonic: "vpaddq",
encoding: vex_256_encoding(vex_map::MAP_0F, 0xD4, 0x66),
},
NamedEncoding {
opcode: X86Opcode::VPSUBB as u32,
mnemonic: "vpsubb",
encoding: vex_256_encoding(vex_map::MAP_0F, 0xF8, 0x66),
},
NamedEncoding {
opcode: X86Opcode::VPSUBW as u32,
mnemonic: "vpsubw",
encoding: vex_256_encoding(vex_map::MAP_0F, 0xF9, 0x66),
},
NamedEncoding {
opcode: X86Opcode::VPSUBD as u32,
mnemonic: "vpsubd",
encoding: vex_256_encoding(vex_map::MAP_0F, 0xFA, 0x66),
},
NamedEncoding {
opcode: X86Opcode::VPSUBQ as u32,
mnemonic: "vpsubq",
encoding: vex_256_encoding(vex_map::MAP_0F, 0xFB, 0x66),
},
NamedEncoding {
opcode: X86Opcode::VPMULLD as u32,
mnemonic: "vpmulld",
encoding: vex_256_encoding(vex_map::MAP_0F38, 0x40, 0x66),
},
NamedEncoding {
opcode: X86Opcode::VPMULUDQ as u32,
mnemonic: "vpmuludq",
encoding: vex_256_encoding(vex_map::MAP_0F, 0xF4, 0x66),
},
NamedEncoding {
opcode: X86Opcode::VPMADDWD as u32,
mnemonic: "vpmaddwd",
encoding: vex_256_encoding(vex_map::MAP_0F, 0xF5, 0x66),
},
]
}
fn sse0f_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x0F, op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
}
}
fn sse0f_imm8_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x0F, op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 1,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
}
}
fn sse66_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x0F, op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0x66,
opcode_map: 0,
displacement_size: 0,
}
}
fn sse66_imm8_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x0F, op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 1,
implied_prefix: 0x66,
opcode_map: 0,
displacement_size: 0,
}
}
fn sse33_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x0F, op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0xF3,
opcode_map: 0,
displacement_size: 0,
}
}
fn sse33_imm8_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x0F, op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 1,
implied_prefix: 0xF3,
opcode_map: 0,
displacement_size: 0,
}
}
fn ssef2_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x0F, op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0xF2,
opcode_map: 0,
displacement_size: 0,
}
}
fn ssef2_imm8_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x0F, op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 1,
implied_prefix: 0xF2,
opcode_map: 0,
displacement_size: 0,
}
}
fn sse38_encoding(op_suffix: u8, prefix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: prefix,
opcode_map: 2,
displacement_size: 0,
}
}
fn sse38_imm8_encoding(op_suffix: u8, prefix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 1,
implied_prefix: prefix,
opcode_map: 2,
displacement_size: 0,
}
}
fn sse3a_imm8_encoding(op_suffix: u8, prefix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 1,
implied_prefix: prefix,
opcode_map: 3,
displacement_size: 0,
}
}
fn sse3a_imm8_encoding_rexw(op_suffix: u8, prefix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: true,
requires_vex: false,
requires_evex: false,
immediate_size: 1,
implied_prefix: prefix,
opcode_map: 3,
displacement_size: 0,
}
}
fn vex_128_encoding(map: u8, op_suffix: u8, pp: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: true,
requires_evex: false,
immediate_size: 0,
implied_prefix: pp,
opcode_map: map,
displacement_size: 0,
}
}
fn vex_256_encoding(map: u8, op_suffix: u8, pp: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: true,
requires_evex: false,
immediate_size: 0,
implied_prefix: pp,
opcode_map: map,
displacement_size: 0,
}
}
fn crc32_encoding() -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0xF0],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0xF2,
opcode_map: 2,
displacement_size: 0,
}
}
fn aes_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0x66,
opcode_map: 2,
displacement_size: 0,
}
}
fn aes_keygen_encoding() -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0xDF],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 1,
implied_prefix: 0x66,
opcode_map: 3,
displacement_size: 0,
}
}
fn pclmul_encoding() -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x44],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 1,
implied_prefix: 0x66,
opcode_map: 3,
displacement_size: 0,
}
}
fn sha_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 2,
displacement_size: 0,
}
}
fn sha_imm8_encoding(op_suffix: u8) -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![op_suffix],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 1,
implied_prefix: 0,
opcode_map: 3,
displacement_size: 0,
}
}
fn mov_encoding() -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0x89],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
}
}
fn shift_encoding() -> InstructionEncoding {
InstructionEncoding {
opcode_bytes: vec![0xD3],
has_modrm: true,
has_sib: false,
requires_rex: false,
requires_vex: false,
requires_evex: false,
immediate_size: 0,
implied_prefix: 0,
opcode_map: 0,
displacement_size: 0,
}
}
pub fn encode_imm8(value: i64) -> [u8; 1] {
[(value & 0xFF) as u8]
}
pub fn encode_imm16(value: i64) -> [u8; 2] {
(value as i16).to_le_bytes()
}
pub fn encode_imm32(value: i64) -> [u8; 4] {
(value as i32).to_le_bytes()
}
pub fn encode_imm64(value: i64) -> [u8; 8] {
value.to_le_bytes()
}
pub fn min_immediate_size(value: i64) -> u8 {
if (-128i64..=127i64).contains(&value) {
1
} else if (-32768i64..=32767i64).contains(&value) {
2
} else if (-2147483648i64..=2147483647i64).contains(&value) {
4
} else {
8
}
}
pub fn imm_fits_imm8(value: i64) -> bool {
(-128i64..=127i64).contains(&value)
}
pub fn imm_fits_imm32(value: i64) -> bool {
(-2147483648i64..=2147483647i64).contains(&value)
}
pub fn can_use_sign_extended_imm8(value: i64) -> bool {
(-128i64..=127i64).contains(&value)
}
pub fn instruction_length(encoding: &InstructionEncoding) -> usize {
let mut len = 0usize;
if encoding.implied_prefix != 0 {
len += 1;
}
if encoding.requires_rex {
len += 1;
}
if encoding.requires_vex {
len += if encoding.opcode_map == vex_map::MAP_0F {
2
} else {
3
};
}
if encoding.requires_evex {
len += 4;
}
len += encoding.opcode_bytes.len();
if encoding.has_modrm {
len += 1;
}
if encoding.has_sib {
len += 1;
}
len += encoding.displacement_size as usize;
len += encoding.immediate_size as usize;
len
}
pub fn is_high_byte_reg(reg_field: u8) -> bool {
matches!(reg_field, 4 | 5 | 6 | 7)
}
pub fn inc_reg_opcode(reg_field: u8) -> u8 {
0x40 | (reg_field & 0x07)
}
pub fn push_reg_opcode(reg_field: u8) -> u8 {
0x50 | (reg_field & 0x07)
}
pub fn pop_reg_opcode(reg_field: u8) -> u8 {
0x58 | (reg_field & 0x07)
}
pub fn xchg_rax_opcode(reg_field: u8) -> u8 {
0x90 | (reg_field & 0x07)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codegen::{MachineBasicBlock, MachineFunction};
use crate::x86::x86_register_info::*;
fn make_subtarget(mode: X86Mode) -> X86Subtarget {
let triple = if mode.is_64bit() {
"x86_64-unknown-none"
} else {
"i686-unknown-none"
};
let mut st = X86Subtarget::new(triple, "generic", "");
st.is_64_bit = mode.is_64bit();
st
}
fn make_instr(opcode: u32, operands: Vec<MachineOperand>) -> MachineInstr {
MachineInstr {
opcode,
operands,
def: None,
}
}
fn phys_reg(id: u16) -> MachineOperand {
MachineOperand::PhysReg(id as u32)
}
fn imm(val: i64) -> MachineOperand {
MachineOperand::Imm(val)
}
#[test]
fn test_encode_rex_prefix_no_flags() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let rex = enc.encode_rex_prefix(false, false, false, false);
assert_eq!(rex, 0x40);
}
#[test]
fn test_encode_rex_prefix_w() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let rex = enc.encode_rex_prefix(true, false, false, false);
assert_eq!(rex, 0x48);
}
#[test]
fn test_encode_rex_prefix_r() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let rex = enc.encode_rex_prefix(false, true, false, false);
assert_eq!(rex, 0x44);
}
#[test]
fn test_encode_rex_prefix_x() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let rex = enc.encode_rex_prefix(false, false, true, false);
assert_eq!(rex, 0x42);
}
#[test]
fn test_encode_rex_prefix_b() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let rex = enc.encode_rex_prefix(false, false, false, true);
assert_eq!(rex, 0x41);
}
#[test]
fn test_encode_rex_prefix_all() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let rex = enc.encode_rex_prefix(true, true, true, true);
assert_eq!(rex, 0x4F);
}
#[test]
fn test_get_reg_field_gpr64() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
assert_eq!(enc.get_reg_field(RAX), 0);
assert_eq!(enc.get_reg_field(RCX), 1);
assert_eq!(enc.get_reg_field(RDX), 2);
assert_eq!(enc.get_reg_field(RBX), 3);
assert_eq!(enc.get_reg_field(RSP), 4);
assert_eq!(enc.get_reg_field(RBP), 5);
assert_eq!(enc.get_reg_field(RSI), 6);
assert_eq!(enc.get_reg_field(RDI), 7);
assert_eq!(enc.get_reg_field(R8), 0);
assert_eq!(enc.get_reg_field(R9), 1);
assert_eq!(enc.get_reg_field(R15), 7);
}
#[test]
fn test_get_reg_field_gpr32() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
assert_eq!(enc.get_reg_field(EAX), 0); assert_eq!(enc.get_reg_field(ECX), 1); assert_eq!(enc.get_reg_field(EDX), 2); assert_eq!(enc.get_reg_field(R15D), 7); }
#[test]
fn test_get_reg_field_gpr16() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
assert_eq!(enc.get_reg_field(AX), 0); assert_eq!(enc.get_reg_field(BP), 5); }
#[test]
fn test_get_reg_field_gpr8() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
assert_eq!(enc.get_reg_field(AL), 0); assert_eq!(enc.get_reg_field(CL), 1); assert_eq!(enc.get_reg_field(AH), 4); assert_eq!(enc.get_reg_field(BH), 7); }
#[test]
fn test_get_reg_field_xmm() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
assert_eq!(enc.get_reg_field(XMM0), 0);
assert_eq!(enc.get_reg_field(XMM7), 7);
assert_eq!(enc.get_reg_field(XMM8), 0); assert_eq!(enc.get_reg_field(XMM15), 7);
}
#[test]
fn test_encode_modrm_reg_direct() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let b = enc.encode_modrm(mod_field::REG_DIRECT, 0, 0);
assert_eq!(b, 0xC0); }
#[test]
fn test_encode_modrm_mem_no_disp() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let b = enc.encode_modrm(mod_field::MEM_NO_DISP, 0, 5);
assert_eq!(b, 0x05); }
#[test]
fn test_encode_modrm_mem_disp8() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let b = enc.encode_modrm(mod_field::MEM_DISP8, 3, 7);
assert_eq!(b, 0x5F); }
#[test]
fn test_encode_modrm_mem_disp32() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let b = enc.encode_modrm(mod_field::MEM_DISP32, 7, 0);
assert_eq!(b, 0xB8); }
#[test]
fn test_encode_sib() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let sib = enc.encode_sib(0, 0, 4);
assert_eq!(sib, 0x04); }
#[test]
fn test_encode_sib_scale_4_index_rcx_base_rbx() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let sib = enc.encode_sib(2, 1, 3);
assert_eq!(sib, 0x8B); }
#[test]
fn test_encode_sib_no_index() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let sib = enc.encode_sib(0, 4, 5);
assert_eq!(sib, 0x25); }
#[test]
fn test_encode_displacement_disp8() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let bytes = enc.encode_displacement(0x42, mod_field::MEM_DISP8);
assert_eq!(bytes, vec![0x42]);
}
#[test]
fn test_encode_displacement_disp8_negative() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let bytes = enc.encode_displacement(-8, mod_field::MEM_DISP8);
assert_eq!(bytes, vec![0xF8]);
}
#[test]
fn test_encode_displacement_disp32() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let bytes = enc.encode_displacement(0x12345678, mod_field::MEM_DISP32);
assert_eq!(bytes, vec![0x78, 0x56, 0x34, 0x12]);
}
#[test]
fn test_encode_displacement_no_disp() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let bytes = enc.encode_displacement(0, mod_field::MEM_NO_DISP);
assert!(bytes.is_empty());
}
#[test]
fn test_encode_immediate_imm8() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let bytes = enc.encode_immediate(42, 1);
assert_eq!(bytes, vec![42]);
}
#[test]
fn test_encode_immediate_imm8_negative() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let bytes = enc.encode_immediate(-1, 1);
assert_eq!(bytes, vec![0xFF]);
}
#[test]
fn test_encode_immediate_imm32() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let bytes = enc.encode_immediate(0xDEADBEEF, 4);
assert_eq!(bytes, vec![0xEF, 0xBE, 0xAD, 0xDE]);
}
#[test]
fn test_encode_immediate_imm64() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let bytes = enc.encode_immediate(0x0102030405060708, 8);
assert_eq!(bytes, vec![0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
}
#[test]
fn test_encode_nop() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::NOP, vec![]);
let bytes = enc.encode_opcode(&mi);
assert_eq!(bytes, vec![0x90]);
}
#[test]
fn test_encode_ret() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::RET, vec![]);
let bytes = enc.encode_opcode(&mi);
assert_eq!(bytes, vec![0xC3]);
}
#[test]
fn test_encode_push_rax() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::PUSH, vec![phys_reg(RAX)]);
let bytes = enc.encode_opcode(&mi);
assert_eq!(bytes, vec![0x50]);
}
#[test]
fn test_encode_pop_rax() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::POP, vec![phys_reg(RAX)]);
let bytes = enc.encode_opcode(&mi);
assert_eq!(bytes, vec![0x58]);
}
#[test]
fn test_encode_push_rbp() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::PUSH, vec![phys_reg(RBP)]);
let bytes = enc.encode_opcode(&mi);
assert_eq!(bytes, vec![0x55]);
}
#[test]
fn test_encode_call() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::CALL, vec![imm(0)]);
let bytes = enc.encode_opcode(&mi);
assert_eq!(bytes, vec![0xE8]);
}
#[test]
fn test_encode_jmp_rel8() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::JMP, vec![imm(10)]);
let bytes = enc.encode_opcode(&mi);
assert_eq!(bytes, vec![0xEB]);
}
#[test]
fn test_encode_jmp_rel32() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::JMP, vec![imm(1000)]);
let bytes = enc.encode_opcode(&mi);
assert_eq!(bytes, vec![0xE9]);
}
#[test]
fn test_encode_mov_reg_reg() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::MOV, vec![phys_reg(EAX), phys_reg(ECX)]);
let opcode = enc.encode_opcode(&mi);
assert_eq!(opcode, vec![0x89]);
}
#[test]
fn test_encode_mov_imm_to_reg() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::MOV, vec![phys_reg(RAX), imm(42)]);
let opcode = enc.encode_opcode(&mi);
assert_eq!(opcode, vec![0xB8]);
}
#[test]
fn test_encode_add_reg_reg() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::ADD, vec![phys_reg(RAX), phys_reg(RCX)]);
let opcode = enc.encode_opcode(&mi);
assert_eq!(opcode, vec![0x01]);
}
#[test]
fn test_encode_add_imm() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::ADD, vec![phys_reg(RAX), imm(5)]);
let opcode = enc.encode_opcode(&mi);
assert_eq!(opcode, vec![0x83]);
}
#[test]
fn test_encode_sub_reg_reg() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::SUB, vec![phys_reg(RAX), phys_reg(RDX)]);
let opcode = enc.encode_opcode(&mi);
assert_eq!(opcode, vec![0x29]);
}
#[test]
fn test_encode_full_nop() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::NOP, vec![]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x90]);
}
#[test]
fn test_encode_full_ret() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::RET, vec![]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0xC3]);
}
#[test]
fn test_encode_full_push_rax() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::PUSH, vec![phys_reg(RAX)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x48, 0x50]);
}
#[test]
fn test_encode_full_add_rax_rcx() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::ADD, vec![phys_reg(RAX), phys_reg(RCX)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x48, 0x01, 0xC8]);
}
#[test]
fn test_encode_full_add_eax_ecx() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::ADD, vec![phys_reg(EAX), phys_reg(ECX)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x01, 0xC8]);
}
#[test]
fn test_encode_full_add_rax_imm5() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::ADD, vec![phys_reg(RAX), imm(5)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x48, 0x83, 0xC0, 0x05]);
}
#[test]
fn test_encode_full_mov_rax_rcx() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::MOV, vec![phys_reg(RAX), phys_reg(RCX)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x48, 0x89, 0xC8]);
}
#[test]
fn test_encode_full_mov_rax_imm42() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::MOV, vec![phys_reg(RAX), imm(42)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(
bytes,
vec![0x48, 0xB8, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
);
}
#[test]
fn test_encode_full_sub_rax_rdx() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::SUB, vec![phys_reg(RAX), phys_reg(RDX)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x48, 0x29, 0xD0]);
}
#[test]
fn test_encode_full_and_rax_rbx() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::AND, vec![phys_reg(RAX), phys_reg(RBX)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x48, 0x21, 0xD8]);
}
#[test]
fn test_encode_full_or_rax_rcx() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::OR, vec![phys_reg(RAX), phys_reg(RCX)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x48, 0x09, 0xC8]);
}
#[test]
fn test_encode_full_xor_rax_rax() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::XOR, vec![phys_reg(RAX), phys_reg(RAX)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x48, 0x31, 0xC0]);
}
#[test]
fn test_encode_full_cmp_rax_rcx() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::CMP, vec![phys_reg(RAX), phys_reg(RCX)]);
let bytes = enc.encode_instruction(&mi);
assert_eq!(bytes, vec![0x48, 0x39, 0xC8]);
}
#[test]
fn test_requires_rex_64bit_gpr() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::MOV, vec![phys_reg(RAX), phys_reg(RCX)]);
assert!(enc.requires_rex(&mi));
}
#[test]
fn test_requires_rex_32bit() {
let enc = X86MCEncoder::new(X86Mode::Mode32, make_subtarget(X86Mode::Mode32));
let mi = make_instr(x86_opcodes::MOV, vec![phys_reg(RAX), phys_reg(RCX)]);
assert!(!enc.requires_rex(&mi));
}
#[test]
fn test_requires_rex_r8() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::MOV, vec![phys_reg(R8), phys_reg(R9)]);
assert!(enc.requires_rex(&mi));
}
#[test]
fn test_requires_modrm_nop() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::NOP, vec![]);
assert!(!enc.requires_modrm(&mi));
}
#[test]
fn test_requires_modrm_ret() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::RET, vec![]);
assert!(!enc.requires_modrm(&mi));
}
#[test]
fn test_requires_modrm_add() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::ADD, vec![phys_reg(RAX), phys_reg(RCX)]);
assert!(enc.requires_modrm(&mi));
}
#[test]
fn test_requires_modrm_push_reg() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(x86_opcodes::PUSH, vec![phys_reg(RAX)]);
assert!(!enc.requires_modrm(&mi));
}
#[test]
fn test_x86_mode_is_64bit() {
assert!(X86Mode::Mode64.is_64bit());
assert!(!X86Mode::Mode32.is_64bit());
assert!(!X86Mode::Mode16.is_64bit());
}
#[test]
fn test_x86_mode_default_operand_size() {
assert_eq!(X86Mode::Mode16.default_operand_size(), 2);
assert_eq!(X86Mode::Mode32.default_operand_size(), 4);
assert_eq!(X86Mode::Mode64.default_operand_size(), 4);
}
#[test]
fn test_x86_mode_default_address_size() {
assert_eq!(X86Mode::Mode16.default_address_size(), 2);
assert_eq!(X86Mode::Mode32.default_address_size(), 4);
assert_eq!(X86Mode::Mode64.default_address_size(), 8);
}
#[test]
fn test_encode_unknown_opcode() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = make_instr(999, vec![]);
let bytes = enc.encode_opcode(&mi);
assert_eq!(bytes, vec![0x0F, 0x0B]);
}
#[test]
fn test_encode_function_simple() {
let _enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mbb = MachineBasicBlock {
name: String::new(),
instructions: vec![
make_instr(x86_opcodes::PUSH, vec![phys_reg(RBP)]),
make_instr(x86_opcodes::MOV, vec![phys_reg(RBP), phys_reg(RSP)]),
make_instr(x86_opcodes::MOV, vec![phys_reg(RAX), imm(0)]),
make_instr(x86_opcodes::POP, vec![phys_reg(RBP)]),
make_instr(x86_opcodes::RET, vec![]),
],
successors: vec![],
};
let mf = MachineFunction {
name: "test_func".into(),
blocks: vec![mbb],
virt_reg_counter: 0,
};
let bytes = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64))
.encode_function(&mf);
assert!(!bytes.is_empty());
assert_eq!(bytes[0], 0x48);
assert_eq!(bytes[1], 0x55);
assert_eq!(bytes[bytes.len() - 1], 0xC3);
}
#[test]
fn test_encode_empty_instruction() {
let enc = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64));
let mi = MachineInstr::new(x86_opcodes::NOP);
let bytes = enc.encode_instruction(&mi);
assert!(!bytes.is_empty());
}
#[test]
fn test_encode_function_empty() {
let mf = MachineFunction::new("empty_func");
let bytes = X86MCEncoder::new(X86Mode::Mode64, make_subtarget(X86Mode::Mode64))
.encode_function(&mf);
assert!(bytes.is_empty());
}
}