use crate::x86::x86_instr_info::{X86MemOperand, X86Opcode};
use crate::x86::x86_register_info::{self, X86RegisterInfo, BP, BX, DI, SI};
use std::collections::{BTreeMap, HashMap, HashSet};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AddrWidth {
Addr16,
Addr32,
Addr64,
}
impl AddrWidth {
pub fn default_data_size(&self) -> u8 {
match self {
AddrWidth::Addr16 => 2,
AddrWidth::Addr32 => 4,
AddrWidth::Addr64 => 8,
}
}
pub fn max_disp_bits(&self) -> u32 {
match self {
AddrWidth::Addr16 => 16,
AddrWidth::Addr32 | AddrWidth::Addr64 => 32,
}
}
pub fn supports_rip_relative(&self) -> bool {
matches!(self, AddrWidth::Addr64)
}
pub fn uses_sib(&self) -> bool {
matches!(self, AddrWidth::Addr32 | AddrWidth::Addr64)
}
}
#[derive(Debug, Clone)]
pub struct X86AddressingFull {
pub addr_width: AddrWidth,
pub reg_info: X86RegisterInfo,
pub intel_syntax: bool,
pub code_model: CodeModel,
pub is_pic: bool,
pub tls_model: TlsModel,
pub opt_level: OptLevel,
}
impl Default for X86AddressingFull {
fn default() -> Self {
Self {
addr_width: AddrWidth::Addr64,
reg_info: X86RegisterInfo,
intel_syntax: false,
code_model: CodeModel::Small,
is_pic: true,
tls_model: TlsModel::InitialExec,
opt_level: OptLevel::Default,
}
}
}
impl X86AddressingFull {
pub fn new(addr_width: AddrWidth) -> Self {
Self {
addr_width,
..Default::default()
}
}
pub fn new_x86_64() -> Self {
Self::new(AddrWidth::Addr64)
}
pub fn new_x86_32() -> Self {
Self::new(AddrWidth::Addr32)
}
pub fn new_x86_16() -> Self {
Self::new(AddrWidth::Addr16)
}
pub fn with_code_model(mut self, model: CodeModel) -> Self {
self.code_model = model;
self
}
pub fn with_pic(mut self, pic: bool) -> Self {
self.is_pic = pic;
self
}
pub fn with_tls_model(mut self, model: TlsModel) -> Self {
self.tls_model = model;
self
}
pub fn with_opt_level(mut self, level: OptLevel) -> Self {
self.opt_level = level;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CodeModel {
Small,
Kernel,
Medium,
Large,
}
impl Default for CodeModel {
fn default() -> Self {
CodeModel::Small
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TlsModel {
GeneralDynamic,
LocalDynamic,
InitialExec,
LocalExec,
}
impl Default for TlsModel {
fn default() -> Self {
TlsModel::InitialExec
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OptLevel {
None,
Default,
Aggressive,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct X86AddressMode {
pub width: AddrWidth,
pub base: Option<u16>,
pub index: Option<u16>,
pub scale: u8,
pub displacement: i64,
pub segment: Option<u16>,
pub is_rip_relative: bool,
pub is_frame_relative: bool,
pub symbol: Option<String>,
pub is_got_relative: bool,
pub is_absolute_16: bool,
pub is_absolute_32: bool,
pub form_16bit: Option<X86Addr16Form>,
}
impl Default for X86AddressMode {
fn default() -> Self {
Self {
width: AddrWidth::Addr64,
base: None,
index: None,
scale: 0,
displacement: 0,
segment: None,
is_rip_relative: false,
is_frame_relative: false,
symbol: None,
is_got_relative: false,
is_absolute_16: false,
is_absolute_32: false,
form_16bit: None,
}
}
}
impl X86AddressMode {
pub fn base64(base: u16) -> Self {
Self {
width: AddrWidth::Addr64,
base: Some(base),
..Default::default()
}
}
pub fn base_disp64(base: u16, disp: i64) -> Self {
Self {
width: AddrWidth::Addr64,
base: Some(base),
displacement: disp,
..Default::default()
}
}
pub fn full64(base: u16, index: u16, scale: u8, disp: i64) -> Self {
Self {
width: AddrWidth::Addr64,
base: Some(base),
index: Some(index),
scale,
displacement: disp,
..Default::default()
}
}
pub fn rip_relative(disp: i64) -> Self {
Self {
width: AddrWidth::Addr64,
displacement: disp,
is_rip_relative: true,
..Default::default()
}
}
pub fn rip_symbol(symbol: &str, disp: i64) -> Self {
Self {
width: AddrWidth::Addr64,
displacement: disp,
is_rip_relative: true,
symbol: Some(symbol.to_string()),
..Default::default()
}
}
pub fn base32(base: u16) -> Self {
Self {
width: AddrWidth::Addr32,
base: Some(base),
..Default::default()
}
}
pub fn base_disp32(base: u16, disp: i64) -> Self {
Self {
width: AddrWidth::Addr32,
base: Some(base),
displacement: disp,
..Default::default()
}
}
pub fn full32(base: u16, index: u16, scale: u8, disp: i64) -> Self {
Self {
width: AddrWidth::Addr32,
base: Some(base),
index: Some(index),
scale,
displacement: disp,
..Default::default()
}
}
pub fn absolute32(addr: i64) -> Self {
Self {
width: AddrWidth::Addr32,
displacement: addr,
is_absolute_32: true,
..Default::default()
}
}
pub fn base16(base: u16) -> Self {
Self {
width: AddrWidth::Addr16,
base: Some(base),
form_16bit: match base {
r if r == BX => Some(X86Addr16Form::Bx),
r if r == BP => Some(X86Addr16Form::Bp),
r if r == SI => Some(X86Addr16Form::Si),
r if r == DI => Some(X86Addr16Form::Di),
_ => None,
},
..Default::default()
}
}
pub fn base_index16(base: u16, index: u16) -> Self {
let form = match (base, index) {
(b, i) if b == BX && i == SI => Some(X86Addr16Form::BxSi),
(b, i) if b == BX && i == DI => Some(X86Addr16Form::BxDi),
(b, i) if b == BP && i == SI => Some(X86Addr16Form::BpSi),
(b, i) if b == BP && i == DI => Some(X86Addr16Form::BpDi),
_ => None,
};
Self {
width: AddrWidth::Addr16,
base: Some(base),
index: Some(index),
form_16bit: form,
..Default::default()
}
}
pub fn absolute16(addr: i64) -> Self {
Self {
width: AddrWidth::Addr16,
displacement: addr,
is_absolute_16: true,
form_16bit: Some(X86Addr16Form::Disp16),
..Default::default()
}
}
pub fn with_segment(mut self, seg: u16) -> Self {
self.segment = Some(seg);
self
}
pub fn with_frame_relative(mut self, fr: bool) -> Self {
self.is_frame_relative = fr;
self
}
pub fn with_symbol(mut self, sym: &str) -> Self {
self.symbol = Some(sym.to_string());
self
}
pub fn with_got_relative(mut self, gr: bool) -> Self {
self.is_got_relative = gr;
self
}
pub fn has_base(&self) -> bool {
self.base.is_some()
}
pub fn has_index(&self) -> bool {
self.index.is_some() && self.scale > 0
}
pub fn has_disp_zero(&self) -> bool {
self.displacement == 0
}
pub fn fits_disp8(&self) -> bool {
self.displacement >= -128 && self.displacement <= 127
}
pub fn fits_disp32(&self) -> bool {
self.displacement >= -2_147_483_648 && self.displacement <= 2_147_483_647
}
pub fn fits_disp16(&self) -> bool {
self.displacement >= -32_768 && self.displacement <= 32_767
}
pub fn classify(&self) -> AddressingForm {
if self.is_rip_relative {
return AddressingForm::RipRelative;
}
if self.is_absolute_16 {
return AddressingForm::Absolute16;
}
if self.is_absolute_32 {
return AddressingForm::Absolute32;
}
match (self.has_base(), self.has_index(), self.has_disp_zero()) {
(false, false, _) if self.displacement == 0 => AddressingForm::None,
(false, false, _) => AddressingForm::Absolute32,
(true, false, true) => AddressingForm::BaseOnly,
(true, false, false) if self.fits_disp8() => AddressingForm::BaseDisp8,
(true, false, false) => AddressingForm::BaseDisp32,
(false, true, true) => AddressingForm::IndexScale,
(false, true, false) => AddressingForm::IndexScaleDisp,
(true, true, true) => AddressingForm::BaseIndexScale,
(true, true, false) if self.fits_disp8() => AddressingForm::BaseIndexScaleDisp8,
(true, true, false) => AddressingForm::BaseIndexScaleDisp32,
}
}
pub fn requires_sib(&self) -> bool {
if !self.width.uses_sib() {
return false;
}
if self.is_rip_relative || self.is_absolute_32 {
return false;
}
if self.has_index() {
return true;
}
if let Some(b) = self.base {
let base_enc = b & 0x7;
if base_enc == 4 {
return true;
}
if base_enc == 5 && self.width == AddrWidth::Addr64 {
return true;
}
}
false
}
pub fn requires_rex(&self) -> bool {
if self.width != AddrWidth::Addr64 {
return false;
}
if let Some(b) = self.base {
if b >= 8 {
return true;
}
}
if let Some(i) = self.index {
if i >= 8 {
return true;
}
}
false
}
pub fn is_special_sib_case(&self) -> bool {
if !self.width.uses_sib() || self.has_index() {
return false;
}
if let Some(b) = self.base {
let enc = b & 0x7;
enc == 4 || (enc == 5 && self.width == AddrWidth::Addr64 && !self.is_rip_relative)
} else {
false
}
}
pub fn compute_modrm_parts(&self) -> (u8, u8) {
let (mod_field, rm_field): (u8, u8) = if self.is_rip_relative {
(Mod00, 5)
} else if self.is_absolute_32 {
if self.width == AddrWidth::Addr64 {
(Mod00, 5) } else {
(Mod00, 5) }
} else if self.is_absolute_16 {
(Mod00, 6)
} else if self.requires_sib() {
if self.has_disp_zero() {
(Mod00, 4)
} else if self.fits_disp8() {
(Mod01, 4)
} else {
(Mod10, 4)
}
} else if let Some(base) = self.base {
let rm_enc: u8 = (base & 0x7) as u8;
if self.has_disp_zero() && rm_enc != 5 {
(Mod00, rm_enc)
} else if self.has_disp_zero() && rm_enc == 5 {
(Mod01, rm_enc)
} else if self.fits_disp8() {
(Mod01, rm_enc)
} else {
(Mod10, rm_enc)
}
} else {
(Mod00, 5)
};
(mod_field, rm_field)
}
pub fn compute_sib_parts(&self) -> (u8, u8, u8) {
let scale_field: u8 = match self.scale {
2 => 1,
4 => 2,
8 => 3,
_ => 0, };
let index_field: u8 = if self.has_index() {
(self.index.unwrap() & 0x7) as u8
} else {
4 };
let base_field: u8 = if let Some(b) = self.base {
(b & 0x7) as u8
} else {
5 };
(scale_field, index_field, base_field)
}
pub fn encode_modrm(&self, reg_field: u8) -> u8 {
let (mod_field, rm_field) = self.compute_modrm_parts();
(mod_field << 6) | ((reg_field & 0x7) << 3) | (rm_field & 0x7)
}
pub fn encode_sib(&self) -> u8 {
let (scale, index, base) = self.compute_sib_parts();
(scale << 6) | ((index & 0x7) << 3) | (base & 0x7)
}
pub fn displacement_size(&self) -> u8 {
if self.has_disp_zero() && !self.is_special_disp8_zero_case() {
0
} else if self.fits_disp8() {
1
} else if self.fits_disp16() && self.width == AddrWidth::Addr16 {
2
} else {
4
}
}
fn is_special_disp8_zero_case(&self) -> bool {
if self.has_disp_zero() {
if let Some(b) = self.base {
let enc = b & 0x7;
if enc == 5 {
return self.width == AddrWidth::Addr32 && !self.requires_sib();
}
}
}
false
}
pub fn encode_displacement(&self) -> Vec<u8> {
let size = self.displacement_size();
let disp = self.displacement;
match size {
0 => vec![],
1 => vec![disp as u8],
2 => vec![(disp & 0xFF) as u8, ((disp >> 8) & 0xFF) as u8],
4 => vec![
(disp & 0xFF) as u8,
((disp >> 8) & 0xFF) as u8,
((disp >> 16) & 0xFF) as u8,
((disp >> 24) & 0xFF) as u8,
],
_ => vec![],
}
}
pub fn encode_full(&self, reg_field: u8) -> Vec<u8> {
let mut bytes = Vec::with_capacity(6);
bytes.push(self.encode_modrm(reg_field));
if self.requires_sib() {
bytes.push(self.encode_sib());
}
bytes.extend(self.encode_displacement());
bytes
}
pub fn can_be_lea(&self) -> bool {
self.has_base() || self.has_index()
}
pub fn is_simple(&self) -> bool {
!self.requires_sib()
}
pub fn complexity_score(&self) -> u32 {
let mut score: u32 = 0;
if self.has_base() {
score += 1;
}
if self.has_index() {
score += 2;
}
if self.scale > 1 {
score += 1;
}
let disp_size = self.displacement_size();
score += disp_size as u32;
if self.requires_sib() {
score += 1;
}
if self.requires_rex() {
score += 1;
}
if self.segment.is_some() {
score += 1;
}
if self.is_rip_relative {
score += 1;
}
score
}
pub fn to_mem_operand(&self) -> X86MemOperand {
X86MemOperand {
base: self.base.unwrap_or(0),
index: self.index.unwrap_or(0),
scale: self.scale,
displacement: self.displacement as i32,
segment: self.segment.unwrap_or(0),
}
}
pub fn from_mem_operand(mem: &X86MemOperand, width: AddrWidth) -> Self {
Self {
width,
base: if mem.base != 0 { Some(mem.base) } else { None },
index: if mem.index != 0 {
Some(mem.index)
} else {
None
},
scale: mem.scale,
displacement: mem.displacement as i64,
segment: if mem.segment != 0 {
Some(mem.segment)
} else {
None
},
..Default::default()
}
}
pub fn hash_key(&self) -> u64 {
let mut key: u64 = (self.width as u64) << 56;
if let Some(b) = self.base {
key ^= (b as u64) << 8;
}
if let Some(i) = self.index {
key ^= (i as u64) << 16;
}
key ^= (self.scale as u64) << 24;
key ^= (self.displacement as u64).wrapping_mul(0x9E3779B97F4A7C15);
if self.is_rip_relative {
key ^= 1 << 63;
}
if let Some(seg) = self.segment {
key ^= (seg as u64) << 40;
}
key
}
}
pub mod mod_field {
pub const MOD00: u8 = 0;
pub const MOD01: u8 = 1;
pub const MOD10: u8 = 2;
pub const MOD11: u8 = 3;
}
pub const Mod00: u8 = mod_field::MOD00;
pub const Mod01: u8 = mod_field::MOD01;
pub const Mod10: u8 = mod_field::MOD10;
pub const Mod11: u8 = mod_field::MOD11;
pub mod sib_scale {
pub const TIMES_1: u8 = 0;
pub const TIMES_2: u8 = 1;
pub const TIMES_4: u8 = 2;
pub const TIMES_8: u8 = 3;
}
pub mod sib_field {
pub const NO_INDEX: u8 = 4;
pub const DISP32_BASE: u8 = 5;
pub const RSP_BASE: u8 = 4;
pub const RBP_BASE: u8 = 5;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86Addr16Form {
BxSi,
BxDi,
BpSi,
BpDi,
Si,
Di,
Bp,
Bx,
BxSiDisp8,
BxDiDisp8,
BpSiDisp8,
BpDiDisp8,
SiDisp8,
DiDisp8,
BpDisp8,
BxDisp8,
BxSiDisp16,
BxDiDisp16,
BpSiDisp16,
BpDiDisp16,
SiDisp16,
DiDisp16,
BpDisp16,
BxDisp16,
Disp16,
}
impl X86Addr16Form {
pub fn rm_field(&self) -> u8 {
match self {
X86Addr16Form::BxSi => 0,
X86Addr16Form::BxDi => 1,
X86Addr16Form::BpSi => 2,
X86Addr16Form::BpDi => 3,
X86Addr16Form::Si => 4,
X86Addr16Form::Di => 5,
X86Addr16Form::Bp => 6,
X86Addr16Form::Bx => 7,
X86Addr16Form::BxSiDisp8 => 0,
X86Addr16Form::BxDiDisp8 => 1,
X86Addr16Form::BpSiDisp8 => 2,
X86Addr16Form::BpDiDisp8 => 3,
X86Addr16Form::SiDisp8 => 4,
X86Addr16Form::DiDisp8 => 5,
X86Addr16Form::BpDisp8 => 6,
X86Addr16Form::BxDisp8 => 7,
X86Addr16Form::BxSiDisp16 => 0,
X86Addr16Form::BxDiDisp16 => 1,
X86Addr16Form::BpSiDisp16 => 2,
X86Addr16Form::BpDiDisp16 => 3,
X86Addr16Form::SiDisp16 => 4,
X86Addr16Form::DiDisp16 => 5,
X86Addr16Form::BpDisp16 => 6,
X86Addr16Form::BxDisp16 => 7,
X86Addr16Form::Disp16 => 6,
}
}
pub fn mod_field(&self) -> u8 {
match self {
X86Addr16Form::BxSi
| X86Addr16Form::BxDi
| X86Addr16Form::BpSi
| X86Addr16Form::BpDi
| X86Addr16Form::Si
| X86Addr16Form::Di
| X86Addr16Form::Bx
| X86Addr16Form::Disp16 => Mod00,
X86Addr16Form::Bp => Mod01, X86Addr16Form::BxSiDisp8
| X86Addr16Form::BxDiDisp8
| X86Addr16Form::BpSiDisp8
| X86Addr16Form::BpDiDisp8
| X86Addr16Form::SiDisp8
| X86Addr16Form::DiDisp8
| X86Addr16Form::BpDisp8
| X86Addr16Form::BxDisp8 => Mod01,
X86Addr16Form::BxSiDisp16
| X86Addr16Form::BxDiDisp16
| X86Addr16Form::BpSiDisp16
| X86Addr16Form::BpDiDisp16
| X86Addr16Form::SiDisp16
| X86Addr16Form::DiDisp16
| X86Addr16Form::BpDisp16
| X86Addr16Form::BxDisp16 => Mod10,
}
}
pub fn has_displacement(&self) -> bool {
matches!(
self,
X86Addr16Form::BxSiDisp8
| X86Addr16Form::BxDiDisp8
| X86Addr16Form::BpSiDisp8
| X86Addr16Form::BpDiDisp8
| X86Addr16Form::SiDisp8
| X86Addr16Form::DiDisp8
| X86Addr16Form::BpDisp8
| X86Addr16Form::BxDisp8
| X86Addr16Form::BxSiDisp16
| X86Addr16Form::BxDiDisp16
| X86Addr16Form::BpSiDisp16
| X86Addr16Form::BpDiDisp16
| X86Addr16Form::SiDisp16
| X86Addr16Form::DiDisp16
| X86Addr16Form::BpDisp16
| X86Addr16Form::BxDisp16
| X86Addr16Form::Disp16
| X86Addr16Form::Bp
)
}
pub fn disp_size(&self) -> u8 {
match self {
X86Addr16Form::Bx
| X86Addr16Form::BxSi
| X86Addr16Form::BxDi
| X86Addr16Form::BpSi
| X86Addr16Form::BpDi
| X86Addr16Form::Si
| X86Addr16Form::Di => 0,
X86Addr16Form::Bp
| X86Addr16Form::BxSiDisp8
| X86Addr16Form::BxDiDisp8
| X86Addr16Form::BpSiDisp8
| X86Addr16Form::BpDiDisp8
| X86Addr16Form::SiDisp8
| X86Addr16Form::DiDisp8
| X86Addr16Form::BpDisp8
| X86Addr16Form::BxDisp8 => 1,
X86Addr16Form::BxSiDisp16
| X86Addr16Form::BxDiDisp16
| X86Addr16Form::BpSiDisp16
| X86Addr16Form::BpDiDisp16
| X86Addr16Form::SiDisp16
| X86Addr16Form::DiDisp16
| X86Addr16Form::BpDisp16
| X86Addr16Form::BxDisp16
| X86Addr16Form::Disp16 => 2,
}
}
pub fn effective_base(&self) -> Option<u16> {
match self {
X86Addr16Form::BxSi
| X86Addr16Form::Bx
| X86Addr16Form::BxSiDisp8
| X86Addr16Form::BxDisp8
| X86Addr16Form::BxSiDisp16
| X86Addr16Form::BxDisp16 => Some(BX),
X86Addr16Form::BxDi | X86Addr16Form::BxDiDisp8 | X86Addr16Form::BxDiDisp16 => Some(BX),
X86Addr16Form::BpSi
| X86Addr16Form::BpDi
| X86Addr16Form::Bp
| X86Addr16Form::BpSiDisp8
| X86Addr16Form::BpDiDisp8
| X86Addr16Form::BpDisp8
| X86Addr16Form::BpSiDisp16
| X86Addr16Form::BpDiDisp16
| X86Addr16Form::BpDisp16 => Some(BP),
X86Addr16Form::Si | X86Addr16Form::SiDisp8 | X86Addr16Form::SiDisp16 => Some(SI),
X86Addr16Form::Di | X86Addr16Form::DiDisp8 | X86Addr16Form::DiDisp16 => Some(DI),
X86Addr16Form::Disp16 => None,
}
}
pub fn effective_index(&self) -> Option<u16> {
match self {
X86Addr16Form::BxSi
| X86Addr16Form::BpSi
| X86Addr16Form::BxSiDisp8
| X86Addr16Form::BpSiDisp8
| X86Addr16Form::BxSiDisp16
| X86Addr16Form::BpSiDisp16 => Some(SI),
X86Addr16Form::BxDi
| X86Addr16Form::BpDi
| X86Addr16Form::BxDiDisp8
| X86Addr16Form::BpDiDisp8
| X86Addr16Form::BxDiDisp16
| X86Addr16Form::BpDiDisp16 => Some(DI),
_ => None,
}
}
pub fn name(&self) -> &'static str {
match self {
X86Addr16Form::BxSi => "[BX+SI]",
X86Addr16Form::BxDi => "[BX+DI]",
X86Addr16Form::BpSi => "[BP+SI]",
X86Addr16Form::BpDi => "[BP+DI]",
X86Addr16Form::Si => "[SI]",
X86Addr16Form::Di => "[DI]",
X86Addr16Form::Bp => "[BP]",
X86Addr16Form::Bx => "[BX]",
X86Addr16Form::BxSiDisp8 => "[BX+SI+disp8]",
X86Addr16Form::BxDiDisp8 => "[BX+DI+disp8]",
X86Addr16Form::BpSiDisp8 => "[BP+SI+disp8]",
X86Addr16Form::BpDiDisp8 => "[BP+DI+disp8]",
X86Addr16Form::SiDisp8 => "[SI+disp8]",
X86Addr16Form::DiDisp8 => "[DI+disp8]",
X86Addr16Form::BpDisp8 => "[BP+disp8]",
X86Addr16Form::BxDisp8 => "[BX+disp8]",
X86Addr16Form::BxSiDisp16 => "[BX+SI+disp16]",
X86Addr16Form::BxDiDisp16 => "[BX+DI+disp16]",
X86Addr16Form::BpSiDisp16 => "[BP+SI+disp16]",
X86Addr16Form::BpDiDisp16 => "[BP+DI+disp16]",
X86Addr16Form::SiDisp16 => "[SI+disp16]",
X86Addr16Form::DiDisp16 => "[DI+disp16]",
X86Addr16Form::BpDisp16 => "[BP+disp16]",
X86Addr16Form::BxDisp16 => "[BX+disp16]",
X86Addr16Form::Disp16 => "[disp16]",
}
}
pub fn decode(modrm: u8) -> Self {
let mod_field = (modrm >> 6) & 0x3;
let rm_field = modrm & 0x7;
match (mod_field, rm_field) {
(0, 0) => X86Addr16Form::BxSi,
(0, 1) => X86Addr16Form::BxDi,
(0, 2) => X86Addr16Form::BpSi,
(0, 3) => X86Addr16Form::BpDi,
(0, 4) => X86Addr16Form::Si,
(0, 5) => X86Addr16Form::Di,
(0, 6) => X86Addr16Form::Disp16,
(0, 7) => X86Addr16Form::Bx,
(1, 0) => X86Addr16Form::BxSiDisp8,
(1, 1) => X86Addr16Form::BxDiDisp8,
(1, 2) => X86Addr16Form::BpSiDisp8,
(1, 3) => X86Addr16Form::BpDiDisp8,
(1, 4) => X86Addr16Form::SiDisp8,
(1, 5) => X86Addr16Form::DiDisp8,
(1, 6) => X86Addr16Form::BpDisp8,
(1, 7) => X86Addr16Form::BxDisp8,
(2, 0) => X86Addr16Form::BxSiDisp16,
(2, 1) => X86Addr16Form::BxDiDisp16,
(2, 2) => X86Addr16Form::BpSiDisp16,
(2, 3) => X86Addr16Form::BpDiDisp16,
(2, 4) => X86Addr16Form::SiDisp16,
(2, 5) => X86Addr16Form::DiDisp16,
(2, 6) => X86Addr16Form::BpDisp16,
(2, 7) => X86Addr16Form::BxDisp16,
(3, _) => X86Addr16Form::Bx, _ => X86Addr16Form::BxSi, }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AddressingForm {
None,
BaseOnly,
BaseDisp8,
BaseDisp32,
IndexScale,
IndexScaleDisp,
BaseIndexScale,
BaseIndexScaleDisp8,
BaseIndexScaleDisp32,
RipRelative,
Absolute16,
Absolute32,
}
impl AddressingForm {
pub fn encoding_overhead(&self) -> u8 {
match self {
AddressingForm::None => 0,
AddressingForm::BaseOnly => 1, AddressingForm::BaseDisp8 => 2, AddressingForm::BaseDisp32 => 5, AddressingForm::IndexScale => 2, AddressingForm::IndexScaleDisp => 6, AddressingForm::BaseIndexScale => 2, AddressingForm::BaseIndexScaleDisp8 => 3, AddressingForm::BaseIndexScaleDisp32 => 6, AddressingForm::RipRelative => 5, AddressingForm::Absolute16 => 3, AddressingForm::Absolute32 => 5, }
}
pub fn uses_sib(&self) -> bool {
matches!(
self,
AddressingForm::IndexScale
| AddressingForm::IndexScaleDisp
| AddressingForm::BaseIndexScale
| AddressingForm::BaseIndexScaleDisp8
| AddressingForm::BaseIndexScaleDisp32
)
}
pub fn name(&self) -> &'static str {
match self {
AddressingForm::None => "none",
AddressingForm::BaseOnly => "[base]",
AddressingForm::BaseDisp8 => "[base+disp8]",
AddressingForm::BaseDisp32 => "[base+disp32]",
AddressingForm::IndexScale => "[index*scale]",
AddressingForm::IndexScaleDisp => "[index*scale+disp32]",
AddressingForm::BaseIndexScale => "[base+index*scale]",
AddressingForm::BaseIndexScaleDisp8 => "[base+index*scale+disp8]",
AddressingForm::BaseIndexScaleDisp32 => "[base+index*scale+disp32]",
AddressingForm::RipRelative => "[rip+disp32]",
AddressingForm::Absolute16 => "[disp16]",
AddressingForm::Absolute32 => "[disp32]",
}
}
}
#[derive(Debug, Clone)]
pub struct X86AddressingModeBuilder {
width: AddrWidth,
base: Option<u16>,
index: Option<u16>,
scale: u8,
displacement: i64,
segment: Option<u16>,
is_rip_relative: bool,
is_frame_relative: bool,
symbol: Option<String>,
is_got_relative: bool,
}
impl X86AddressingModeBuilder {
pub fn new(width: AddrWidth) -> Self {
Self {
width,
base: None,
index: None,
scale: 0,
displacement: 0,
segment: None,
is_rip_relative: false,
is_frame_relative: false,
symbol: None,
is_got_relative: false,
}
}
pub fn new64() -> Self {
Self::new(AddrWidth::Addr64)
}
pub fn new32() -> Self {
Self::new(AddrWidth::Addr32)
}
pub fn new16() -> Self {
Self::new(AddrWidth::Addr16)
}
pub fn with_base(mut self, reg: u16) -> Self {
self.base = Some(reg);
self
}
pub fn with_base_opt(mut self, reg: Option<u16>) -> Self {
self.base = reg;
self
}
pub fn with_index(mut self, reg: u16, scale: u8) -> Self {
self.index = Some(reg);
self.scale = Self::canonicalize_scale(scale);
self
}
pub fn with_index_opt(mut self, reg: Option<u16>, scale: u8) -> Self {
self.index = reg;
if reg.is_some() {
self.scale = Self::canonicalize_scale(scale);
} else {
self.scale = 0;
}
self
}
fn canonicalize_scale(scale: u8) -> u8 {
match scale {
1 | 2 | 4 | 8 => scale,
s if s < 2 => 1,
s if s < 4 => 2,
s if s < 8 => 4,
_ => 8,
}
}
pub fn with_disp(mut self, disp: i64) -> Self {
self.displacement = disp;
self
}
pub fn with_segment(mut self, seg: u16) -> Self {
self.segment = Some(seg);
self
}
pub fn with_rip_relative(mut self, rr: bool) -> Self {
self.is_rip_relative = rr;
if rr {
self.base = None;
}
self
}
pub fn with_frame_relative(mut self, fr: bool) -> Self {
self.is_frame_relative = fr;
self
}
pub fn with_symbol(mut self, sym: &str) -> Self {
self.symbol = Some(sym.to_string());
self
}
pub fn with_got_relative(mut self, gr: bool) -> Self {
self.is_got_relative = gr;
self
}
pub fn canonicalize(mut self) -> Self {
if self.index.is_some() && self.scale == 0 {
self.index = None;
}
if self.displacement == 0 {
if let Some(base) = self.base {
let base_enc: u8 = (base & 0x7) as u8;
if base_enc == 5
&& self.width == AddrWidth::Addr32
&& !self.requires_sib_fast(base_enc)
{
}
}
}
if self.displacement != 0 {
match self.width {
AddrWidth::Addr16 => {
if !(-32768..=32767).contains(&self.displacement) {
self.displacement &= 0xFFFF;
if self.displacement > 32767 {
self.displacement -= 65536;
}
}
}
_ => {
if !(-2_147_483_648i64..=2_147_483_647i64).contains(&self.displacement) {
self.displacement &= 0xFFFF_FFFF;
if self.displacement > 2_147_483_647 {
self.displacement -= 4_294_967_296;
}
}
}
}
}
self
}
fn requires_sib_fast(&self, base_enc: u8) -> bool {
base_enc == 4 || (base_enc == 5 && self.width == AddrWidth::Addr64 && !self.is_rip_relative)
}
pub fn fold_displacement_into_base(mut self, base_reg: u16) -> Self {
if self.base.is_none() {
self.base = Some(base_reg);
}
self
}
pub fn add_disp(mut self, offset: i64) -> Self {
self.displacement = self.displacement.wrapping_add(offset);
self.canonicalize()
}
pub fn complexity_score(&self) -> u32 {
let mut score: u32 = 0;
if self.base.is_some() {
score += 1;
}
if self.index.is_some() && self.scale > 0 {
score += 2;
}
if self.scale > 1 {
score += 1;
}
if self.displacement != 0 {
if self.displacement >= -128 && self.displacement <= 127 {
score += 1;
} else {
score += 4;
}
}
if self.segment.is_some() {
score += 1;
}
if self.is_rip_relative {
score += 1;
}
score
}
pub fn select_best_form(&self) -> AddressingForm {
if self.is_rip_relative {
return AddressingForm::RipRelative;
}
match (self.base, self.index, self.scale > 0, self.displacement) {
(None, None, _, 0) => AddressingForm::None,
(None, None, _, _) if self.width == AddrWidth::Addr16 => AddressingForm::Absolute16,
(None, None, _, _) => AddressingForm::Absolute32,
(Some(_), None, _, 0) => AddressingForm::BaseOnly,
(Some(_), None, _, d) if d >= -128 && d <= 127 => AddressingForm::BaseDisp8,
(Some(_), None, _, _) => AddressingForm::BaseDisp32,
(None, Some(_), true, 0) => AddressingForm::IndexScale,
(None, Some(_), true, _) => AddressingForm::IndexScaleDisp,
(Some(_), Some(_), true, 0) => AddressingForm::BaseIndexScale,
(Some(_), Some(_), true, d) if d >= -128 && d <= 127 => {
AddressingForm::BaseIndexScaleDisp8
}
(Some(_), Some(_), true, _) => AddressingForm::BaseIndexScaleDisp32,
_ => AddressingForm::BaseOnly,
}
}
pub fn build(mut self) -> X86AddressMode {
self = self.canonicalize();
let mut mode = X86AddressMode {
width: self.width,
base: self.base,
index: self.index,
scale: self.scale,
displacement: self.displacement,
segment: self.segment,
is_rip_relative: self.is_rip_relative,
is_frame_relative: self.is_frame_relative,
symbol: self.symbol.clone(),
is_got_relative: self.is_got_relative,
..Default::default()
};
if self.width == AddrWidth::Addr16 {
mode.form_16bit = self.determine_16bit_form();
mode.is_absolute_16 = mode.form_16bit == Some(X86Addr16Form::Disp16);
}
if self.width == AddrWidth::Addr32 && self.base.is_none() && self.index.is_none() {
mode.is_absolute_32 = self.displacement != 0;
}
mode
}
fn determine_16bit_form(&self) -> Option<X86Addr16Form> {
use crate::x86::x86_register_info::{BP, BX, DI, SI};
let base = self.base;
let index = self.index;
let has_disp = self.displacement != 0;
let disp8 = self.displacement >= -128 && self.displacement <= 127;
match (base, index, has_disp, disp8) {
(None, None, true, _) => Some(X86Addr16Form::Disp16),
(None, None, false, _) => None,
(Some(b), None, false, _) => match b {
r if r == BX => Some(X86Addr16Form::Bx),
r if r == BP => Some(X86Addr16Form::Bp),
r if r == SI => Some(X86Addr16Form::Si),
r if r == DI => Some(X86Addr16Form::Di),
_ => None,
},
(Some(b), None, true, true) => match b {
r if r == BX => Some(X86Addr16Form::BxDisp8),
r if r == BP => Some(X86Addr16Form::BpDisp8),
r if r == SI => Some(X86Addr16Form::SiDisp8),
r if r == DI => Some(X86Addr16Form::DiDisp8),
_ => None,
},
(Some(b), None, true, false) => match b {
r if r == BX => Some(X86Addr16Form::BxDisp16),
r if r == BP => Some(X86Addr16Form::BpDisp16),
r if r == SI => Some(X86Addr16Form::SiDisp16),
r if r == DI => Some(X86Addr16Form::DiDisp16),
_ => None,
},
(Some(b), Some(i), false, _) => match (b, i) {
(b_val, i_val) if b_val == BX && i_val == SI => Some(X86Addr16Form::BxSi),
(b_val, i_val) if b_val == BX && i_val == DI => Some(X86Addr16Form::BxDi),
(b_val, i_val) if b_val == BP && i_val == SI => Some(X86Addr16Form::BpSi),
(b_val, i_val) if b_val == BP && i_val == DI => Some(X86Addr16Form::BpDi),
_ => None,
},
(Some(b), Some(i), true, true) => match (b, i) {
(b_val, i_val) if b_val == BX && i_val == SI => Some(X86Addr16Form::BxSiDisp8),
(b_val, i_val) if b_val == BX && i_val == DI => Some(X86Addr16Form::BxDiDisp8),
(b_val, i_val) if b_val == BP && i_val == SI => Some(X86Addr16Form::BpSiDisp8),
(b_val, i_val) if b_val == BP && i_val == DI => Some(X86Addr16Form::BpDiDisp8),
_ => None,
},
(Some(b), Some(i), true, false) => match (b, i) {
(b_val, i_val) if b_val == BX && i_val == SI => Some(X86Addr16Form::BxSiDisp16),
(b_val, i_val) if b_val == BX && i_val == DI => Some(X86Addr16Form::BxDiDisp16),
(b_val, i_val) if b_val == BP && i_val == SI => Some(X86Addr16Form::BpSiDisp16),
(b_val, i_val) if b_val == BP && i_val == DI => Some(X86Addr16Form::BpDiDisp16),
_ => None,
},
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct X86MemoryLowering {
pub engine: X86AddressingFull,
pub use_frame_pointer: bool,
pub stack_size: i64,
pub frame_base_offset: i64,
pub use_absolute_addressing: bool,
}
impl X86MemoryLowering {
pub fn new(engine: X86AddressingFull) -> Self {
Self {
engine,
use_frame_pointer: true,
stack_size: 0,
frame_base_offset: 0,
use_absolute_addressing: false,
}
}
pub fn new_x86_64_pic() -> Self {
Self::new(X86AddressingFull::new_x86_64().with_pic(true))
}
pub fn new_x86_64_non_pic() -> Self {
let mut s = Self::new(X86AddressingFull::new_x86_64().with_pic(false));
s.use_absolute_addressing = true;
s
}
pub fn new_x86_32() -> Self {
Self::new(X86AddressingFull::new_x86_32().with_pic(false))
}
pub fn lower_load(
&self,
addr: &X86AddressMode,
size: u8,
is_signed: bool,
dest_size: Option<u8>,
) -> LoadLoweringResult {
let effective_addr = self.resolve_address(addr);
let opcode = if size <= dest_size.unwrap_or(size) && dest_size.is_some() {
if is_signed {
match (size, dest_size.unwrap()) {
(1, 2) => X86Opcode::MOVSX,
(1, 4) => X86Opcode::MOVSX,
(1, 8) => X86Opcode::MOVSX,
(2, 4) => X86Opcode::MOVSX,
(2, 8) => X86Opcode::MOVSX,
(4, 8) => X86Opcode::MOVSXD,
_ => self.select_mov_opcode(size),
}
} else {
match (size, dest_size.unwrap()) {
(1, 2) => X86Opcode::MOVZX,
(1, 4) => X86Opcode::MOVZX,
(1, 8) => X86Opcode::MOVZX,
(2, 4) => X86Opcode::MOVZX,
(2, 8) => X86Opcode::MOVZX,
_ => self.select_mov_opcode(size),
}
}
} else {
self.select_mov_opcode(size)
};
let needs_rex = effective_addr.requires_rex();
let needs_sib = effective_addr.requires_sib();
LoadLoweringResult {
address_mode: effective_addr,
opcode,
size,
needs_rex,
needs_sib,
}
}
pub fn lower_simple_load(&self, addr: &X86AddressMode, size: u8) -> LoadLoweringResult {
let effective_addr = self.resolve_address(addr);
let needs_rex = effective_addr.requires_rex();
let needs_sib = effective_addr.requires_sib();
LoadLoweringResult {
opcode: self.select_mov_opcode(size),
address_mode: effective_addr,
size,
needs_rex,
needs_sib,
}
}
fn select_mov_opcode(&self, size: u8) -> X86Opcode {
match size {
1 => X86Opcode::MOV, 2 => X86Opcode::MOV, 4 => X86Opcode::MOV, 8 => X86Opcode::MOV, 16 => X86Opcode::VMOVDQA32_Z, 32 => X86Opcode::VMOVDQA32_Z, 64 => X86Opcode::VMOVDQA32_Z, _ => X86Opcode::MOV,
}
}
pub fn lower_store(
&self,
addr: &X86AddressMode,
size: u8,
value_is_float: bool,
) -> StoreLoweringResult {
let effective_addr = self.resolve_address(addr);
let opcode = if value_is_float {
match size {
4 => X86Opcode::MOVSS,
8 => X86Opcode::MOVSD,
_ => self.select_mov_store_opcode(size),
}
} else {
self.select_mov_store_opcode(size)
};
let needs_rex = effective_addr.requires_rex();
let needs_sib = effective_addr.requires_sib();
StoreLoweringResult {
address_mode: effective_addr,
opcode,
size,
needs_rex,
needs_sib,
}
}
fn select_mov_store_opcode(&self, size: u8) -> X86Opcode {
match size {
1 => X86Opcode::MOV,
2 => X86Opcode::MOV,
4 => X86Opcode::MOV,
8 => X86Opcode::MOV,
16 => X86Opcode::VMOVDQA32_Z,
32 => X86Opcode::VMOVDQA32_Z,
64 => X86Opcode::VMOVDQA32_Z,
_ => X86Opcode::MOV,
}
}
pub fn lower_frame_index(&self, frame_index: i32, _size: u8) -> X86AddressMode {
let offset = self.compute_frame_offset(frame_index);
let (base_reg, is_neg) = if self.use_frame_pointer {
match self.engine.addr_width {
AddrWidth::Addr64 => (x86_register_info::RBP, true),
AddrWidth::Addr32 => (x86_register_info::EBP, true),
AddrWidth::Addr16 => (x86_register_info::BP, true),
}
} else {
match self.engine.addr_width {
AddrWidth::Addr64 => (x86_register_info::RSP, false),
AddrWidth::Addr32 => (x86_register_info::ESP, false),
AddrWidth::Addr16 => (x86_register_info::SP, false),
}
};
let disp = if is_neg { -offset } else { offset };
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(base_reg)
.with_disp(disp)
.with_frame_relative(true)
.build()
}
fn compute_frame_offset(&self, frame_index: i32) -> i64 {
let base = if frame_index < 0 {
self.frame_base_offset + (-frame_index as i64 * 8) + 8
} else {
(frame_index as i64 + 2) * 8
};
base
}
pub fn lower_global_address(
&self,
symbol: &str,
offset: i64,
is_external: bool,
) -> X86AddressMode {
match self.engine.addr_width {
AddrWidth::Addr64 => {
if self.engine.is_pic {
X86AddressMode::rip_symbol(symbol, offset).with_got_relative(is_external)
} else if self.engine.code_model == CodeModel::Small {
X86AddressMode::rip_symbol(symbol, offset)
} else {
X86AddressMode {
width: AddrWidth::Addr64,
displacement: offset,
symbol: Some(symbol.to_string()),
is_absolute_32: true,
..Default::default()
}
}
}
AddrWidth::Addr32 => {
if self.engine.is_pic {
X86AddressMode {
width: AddrWidth::Addr32,
displacement: offset,
symbol: Some(format!("{}@GOT", symbol)),
is_got_relative: true,
..Default::default()
}
} else {
X86AddressMode {
width: AddrWidth::Addr32,
displacement: offset,
symbol: Some(symbol.to_string()),
is_absolute_32: true,
..Default::default()
}
}
}
AddrWidth::Addr16 => {
X86AddressMode {
width: AddrWidth::Addr16,
displacement: offset,
symbol: Some(symbol.to_string()),
is_absolute_16: true,
..Default::default()
}
}
}
}
pub fn lower_global_got(&self, symbol: &str) -> X86AddressMode {
X86AddressMode {
width: AddrWidth::Addr64,
displacement: 0,
is_rip_relative: true,
symbol: Some(format!("{}@GOTPCREL", symbol)),
is_got_relative: true,
..Default::default()
}
}
pub fn lower_tls_address(&self, symbol: &str, offset: i64, is_local: bool) -> X86AddressMode {
let segment = self.get_tls_segment_reg();
match self.engine.tls_model {
TlsModel::LocalExec => {
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_disp(offset)
.with_segment(segment)
.with_symbol(symbol)
.build()
}
TlsModel::InitialExec => {
X86AddressMode {
width: self.engine.addr_width,
displacement: offset,
segment: Some(segment),
symbol: Some(format!("{}@TPOFF", symbol)),
is_got_relative: true,
..Default::default()
}
}
TlsModel::GeneralDynamic => {
X86AddressMode {
width: self.engine.addr_width,
symbol: Some(format!("{}@TLSGD", symbol)),
is_rip_relative: true,
..Default::default()
}
}
TlsModel::LocalDynamic => {
X86AddressMode {
width: self.engine.addr_width,
symbol: Some(format!("{}@TLSLD", symbol)),
is_rip_relative: true,
..Default::default()
}
}
}
}
fn get_tls_segment_reg(&self) -> u16 {
match self.engine.addr_width {
AddrWidth::Addr64 => {
x86_register_info::FS
}
AddrWidth::Addr32 => {
x86_register_info::GS
}
AddrWidth::Addr16 => {
0
}
}
}
pub fn lower_constant_pool(&self, pool_index: u32, offset: i64) -> X86AddressMode {
let label = format!(".LCPI{}", pool_index);
if self.engine.addr_width == AddrWidth::Addr64 {
X86AddressMode::rip_symbol(&label, offset)
} else {
X86AddressMode {
width: AddrWidth::Addr32,
displacement: offset,
symbol: Some(label),
is_absolute_32: true,
..Default::default()
}
}
}
pub fn lower_jump_table(&self, table_index: u32, offset: i64) -> X86AddressMode {
let label = format!(".LJTI{}", table_index);
if self.engine.addr_width == AddrWidth::Addr64 {
X86AddressMode::rip_symbol(&label, offset)
} else {
X86AddressMode {
width: AddrWidth::Addr32,
displacement: offset,
symbol: Some(label),
is_absolute_32: true,
..Default::default()
}
}
}
fn resolve_address(&self, addr: &X86AddressMode) -> X86AddressMode {
let mut resolved = addr.clone();
if addr.is_frame_relative {
resolved.displacement += self.frame_base_offset;
}
if addr.symbol.is_some() && self.engine.is_pic && addr.width == AddrWidth::Addr64 {
if !addr.is_got_relative && !addr.is_rip_relative {
resolved.is_got_relative = true;
}
}
resolved
}
}
#[derive(Debug, Clone)]
pub struct LoadLoweringResult {
pub address_mode: X86AddressMode,
pub opcode: X86Opcode,
pub size: u8,
pub needs_rex: bool,
pub needs_sib: bool,
}
#[derive(Debug, Clone)]
pub struct StoreLoweringResult {
pub address_mode: X86AddressMode,
pub opcode: X86Opcode,
pub size: u8,
pub needs_rex: bool,
pub needs_sib: bool,
}
#[derive(Debug, Clone)]
pub struct X86AddressOptimization {
engine: X86AddressingFull,
optimize_for_size: bool,
enable_lea_formation: bool,
enable_sinking: bool,
enable_factoring: bool,
enable_hoisting: bool,
max_sink_distance: usize,
}
impl X86AddressOptimization {
pub fn new(engine: X86AddressingFull) -> Self {
Self {
engine,
optimize_for_size: false,
enable_lea_formation: true,
enable_sinking: true,
enable_factoring: true,
enable_hoisting: true,
max_sink_distance: 4,
}
}
pub fn with_size_opt(mut self, size_opt: bool) -> Self {
self.optimize_for_size = size_opt;
self
}
pub fn without_lea_formation(mut self) -> Self {
self.enable_lea_formation = false;
self
}
pub fn try_form_lea(
&self,
opcode: X86Opcode,
dst: u16,
src1: u16,
src2: Option<u64>,
) -> Option<X86AddressMode> {
if !self.enable_lea_formation {
return None;
}
match opcode {
X86Opcode::ADD | X86Opcode::SUB => {
if src2.is_none() {
return Some(
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(dst)
.with_index(src1, 1)
.build(),
);
}
let imm = src2.unwrap() as i64;
let scaled_imm = if opcode == X86Opcode::SUB { -imm } else { imm };
return Some(
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(dst)
.with_disp(scaled_imm)
.build(),
);
}
X86Opcode::SHL => {
if let Some(shift) = src2 {
let scale = 1u8 << shift;
if scale == 2 || scale == 4 || scale == 8 {
return Some(
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(dst)
.with_index(src1, scale)
.build(),
);
}
}
}
X86Opcode::MUL | X86Opcode::IMUL => {
return None;
}
_ => {}
}
None
}
pub fn try_form_complex_lea(
&self,
base: u16,
index: u16,
scale: u8,
displacement: i64,
) -> Option<X86AddressMode> {
if !self.enable_lea_formation {
return None;
}
if scale != 1 && scale != 2 && scale != 4 && scale != 8 {
return None;
}
let disp_fits = displacement >= -2_147_483_648 && displacement <= 2_147_483_647;
if !disp_fits {
return None;
}
Some(
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(base)
.with_index(index, scale)
.with_disp(displacement)
.build(),
)
}
pub fn should_use_lea(&self, _addr_mode: &X86AddressMode, num_adds: usize) -> bool {
if !self.enable_lea_formation {
return false;
}
if self.optimize_for_size {
return num_adds >= 2;
}
num_adds >= 2
}
pub fn compute_sink_distance(
&self,
addr_mode: &X86AddressMode,
num_intervening_uses: usize,
) -> usize {
if !self.enable_sinking {
return 0;
}
if num_intervening_uses == 0 {
return 0;
}
let max_dist = self.max_sink_distance;
let complexity = addr_mode.complexity_score();
if complexity <= 2 {
max_dist
} else if complexity <= 4 {
max_dist / 2
} else {
0 }
}
pub fn can_sink_to(&self, addr_mode: &X86AddressMode, is_in_loop: bool) -> bool {
if !self.enable_sinking {
return false;
}
if !is_in_loop {
return false;
}
if addr_mode.is_frame_relative || addr_mode.symbol.is_some() {
return false;
}
addr_mode.complexity_score() <= 3
}
pub fn factor_common_base(
&self,
addresses: &[X86AddressMode],
) -> (Option<u16>, Vec<X86AddressMode>) {
if !self.enable_factoring || addresses.is_empty() {
return (None, addresses.to_vec());
}
let mut base_counts: HashMap<u16, usize> = HashMap::new();
for addr in addresses {
if let Some(base) = addr.base {
*base_counts.entry(base).or_insert(0) += 1;
}
}
let threshold = (addresses.len() + 1) / 2;
let mut best_base: Option<u16> = None;
let mut best_count: usize = 0;
for (base, count) in &base_counts {
if *count > best_count && *count >= 2 && *count >= threshold {
best_count = *count;
best_base = Some(*base);
}
}
if let Some(common_base) = best_base {
let new_addresses: Vec<X86AddressMode> = addresses
.iter()
.map(|addr| {
let mut new_addr = addr.clone();
if new_addr.base == Some(common_base) {
} else if new_addr.base.is_some() {
}
new_addr
})
.collect();
(Some(common_base), new_addresses)
} else {
(None, addresses.to_vec())
}
}
pub fn factor_common_index(
&self,
addresses: &[X86AddressMode],
) -> (Option<(u16, u8)>, Vec<X86AddressMode>) {
if !self.enable_factoring || addresses.is_empty() {
return (None, addresses.to_vec());
}
let mut index_counts: HashMap<(u16, u8), usize> = HashMap::new();
for addr in addresses {
if let Some(index) = addr.index {
if addr.scale > 0 {
*index_counts.entry((index, addr.scale)).or_insert(0) += 1;
}
}
}
let threshold = (addresses.len() + 1) / 2;
let mut best_index: Option<(u16, u8)> = None;
let mut best_count: usize = 0;
for (idx, count) in &index_counts {
if *count > best_count && *count >= 2 && *count >= threshold {
best_count = *count;
best_index = Some(*idx);
}
}
(best_index, addresses.to_vec())
}
pub fn is_loop_invariant(&self, addr_mode: &X86AddressMode) -> bool {
if addr_mode.is_frame_relative || addr_mode.symbol.is_some() {
return true;
}
if addr_mode.is_absolute_16 || addr_mode.is_absolute_32 || addr_mode.is_rip_relative {
return true;
}
if !addr_mode.has_index() {
if let Some(base) = addr_mode.base {
let base_enc = base & 0x7;
if base_enc == 4 || base_enc == 5 {
return true;
}
}
}
false
}
pub fn hoist_address(&self, addr_mode: &X86AddressMode) -> Option<X86AddressMode> {
if !self.enable_hoisting {
return None;
}
if self.is_loop_invariant(addr_mode) {
Some(addr_mode.clone())
} else {
None
}
}
pub fn optimize(&self, addr_mode: &X86AddressMode) -> X86AddressMode {
let mut optimized = addr_mode.clone();
optimized = self.fold_zero_disp(optimized);
optimized = self.try_disp8(optimized);
optimized = self.canonicalize_form(optimized);
optimized
}
fn fold_zero_disp(&self, addr: X86AddressMode) -> X86AddressMode {
if addr.displacement == 0 && !addr.is_rip_relative {
if let Some(base) = addr.base {
let base_enc = base & 0x7;
if base_enc == 5 && addr.width == AddrWidth::Addr32 && !addr.requires_sib() {
}
}
}
addr
}
fn try_disp8(&self, addr: X86AddressMode) -> X86AddressMode {
if addr.displacement == 0 {
return addr;
}
if addr.fits_disp8() {
return addr;
}
addr
}
fn canonicalize_form(&self, addr: X86AddressMode) -> X86AddressMode {
if addr.scale == 0 && addr.index.is_some() {
let mut a = addr;
a.index = None;
return a;
}
addr
}
pub fn select_best(&self, a: &X86AddressMode, b: &X86AddressMode) -> X86AddressMode {
let form_a = a.classify();
let form_b = b.classify();
let overhead_a = form_a.encoding_overhead();
let overhead_b = form_b.encoding_overhead();
if overhead_a < overhead_b {
return a.clone();
}
if overhead_b < overhead_a {
return b.clone();
}
let score_a = a.complexity_score();
let score_b = b.complexity_score();
if score_a <= score_b {
a.clone()
} else {
b.clone()
}
}
}
#[derive(Debug, Clone)]
pub struct X86SegmentHandling {
engine: X86AddressingFull,
is_64bit: bool,
default_ds: u16,
tls_segment: u16,
}
impl X86SegmentHandling {
pub fn new(engine: X86AddressingFull) -> Self {
let is_64bit = engine.addr_width == AddrWidth::Addr64;
let (default_ds, tls_segment) = if is_64bit {
(x86_register_info::DS, x86_register_info::FS)
} else {
(x86_register_info::DS, x86_register_info::GS)
};
Self {
engine,
is_64bit,
default_ds,
tls_segment,
}
}
pub fn default_segment_for_base(&self, base: u16) -> u16 {
use crate::x86::x86_register_info::{BP, DI, EBP, EDI, EIP, ESP, RBP, RIP, RSP, SP};
match base {
r if r == RSP || r == RBP || r == ESP || r == EBP || r == SP || r == BP => {
x86_register_info::SS
}
r if r == RIP || r == EIP => x86_register_info::CS,
r if r == DI || r == EDI => x86_register_info::ES,
_ => x86_register_info::DS,
}
}
pub fn default_segment(&self, addr: &X86AddressMode) -> u16 {
if let Some(base) = addr.base {
self.default_segment_for_base(base)
} else {
x86_register_info::DS
}
}
pub fn needs_segment_override(&self, addr: &X86AddressMode) -> bool {
if let Some(seg) = addr.segment {
let default = self.default_segment(addr);
seg != default
} else {
false
}
}
pub fn segment_override_prefix(&self, addr: &X86AddressMode) -> Option<u8> {
if !self.needs_segment_override(addr) {
return None;
}
let seg = addr.segment?;
Some(self.encode_segment_prefix(seg))
}
pub fn encode_segment_prefix(&self, seg_reg: u16) -> u8 {
use crate::x86::x86_register_info::{CS, DS, ES, FS, GS, SS};
match seg_reg {
r if r == CS => 0x2E,
r if r == SS => 0x36,
r if r == DS => 0x3E,
r if r == ES => 0x26,
r if r == FS => 0x64,
r if r == GS => 0x65,
_ => 0x00, }
}
pub fn decode_segment_prefix(&self, prefix: u8) -> Option<u16> {
use crate::x86::x86_register_info::{CS, DS, ES, FS, GS, SS};
match prefix {
0x2E => Some(CS),
0x36 => Some(SS),
0x3E => Some(DS),
0x26 => Some(ES),
0x64 => Some(FS),
0x65 => Some(GS),
_ => None,
}
}
pub fn tls_segment_reg(&self) -> u16 {
self.tls_segment
}
pub fn create_tls_address(&self, offset: i64) -> X86AddressMode {
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_disp(offset)
.with_segment(self.tls_segment)
.build()
}
pub fn create_tls_symbol_address(&self, symbol: &str, offset: i64) -> X86AddressMode {
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_disp(offset)
.with_segment(self.tls_segment)
.with_symbol(symbol)
.build()
}
pub fn is_stack_access(&self, addr: &X86AddressMode) -> bool {
self.default_segment(addr) == x86_register_info::SS
}
pub fn create_stack_address(&self, base: u16, offset: i64) -> X86AddressMode {
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(base)
.with_disp(offset)
.with_segment(x86_register_info::SS)
.with_frame_relative(true)
.build()
}
pub fn get_available_segments_32(&self) -> Vec<u16> {
vec![
x86_register_info::CS,
x86_register_info::DS,
x86_register_info::SS,
x86_register_info::ES,
x86_register_info::FS,
x86_register_info::GS,
]
}
pub fn get_available_segments_64(&self) -> Vec<u16> {
vec![x86_register_info::FS, x86_register_info::GS]
}
pub fn get_available_segments(&self) -> Vec<u16> {
if self.is_64bit {
self.get_available_segments_64()
} else {
self.get_available_segments_32()
}
}
pub fn generate_prefixes(&self, addr: &X86AddressMode) -> Vec<u8> {
let mut prefixes = Vec::new();
if let Some(seg_prefix) = self.segment_override_prefix(addr) {
prefixes.push(seg_prefix);
}
if self.is_64bit && addr.width == AddrWidth::Addr32 {
prefixes.push(0x67); } else if !self.is_64bit
&& addr.width == AddrWidth::Addr16
&& self.engine.addr_width == AddrWidth::Addr32
{
prefixes.push(0x67); }
if addr.requires_rex() {
let mut rex: u8 = 0x40; if self.is_64bit {
rex |= 0x08; }
if let Some(idx) = addr.index {
if idx >= 8 {
rex |= 0x02; }
}
if let Some(base) = addr.base {
if base >= 8 {
rex |= 0x01; }
}
if rex != 0x48 {
prefixes.push(rex);
} else {
prefixes.push(0x48); }
} else if self.is_64bit && self.needs_rex_w(addr) {
prefixes.push(0x48); }
prefixes
}
fn needs_rex_w(&self, _addr: &X86AddressMode) -> bool {
self.is_64bit
}
}
#[derive(Debug, Clone)]
pub struct AddressingModeMatcher {
width: AddrWidth,
}
impl AddressingModeMatcher {
pub fn new(width: AddrWidth) -> Self {
Self { width }
}
pub fn decode(
&self,
modrm: u8,
sib: Option<u8>,
displacement: &[u8],
has_rex: bool,
rex_b: bool,
rex_x: bool,
) -> X86AddressMode {
let _mod_field = (modrm >> 6) & 0x3;
let _reg_field = (modrm >> 3) & 0x7;
let _rm_field = modrm & 0x7;
match self.width {
AddrWidth::Addr16 => self.decode_16bit(modrm, displacement),
AddrWidth::Addr32 | AddrWidth::Addr64 => {
self.decode_32_64(modrm, sib, displacement, has_rex, rex_b, rex_x)
}
}
}
fn decode_16bit(&self, _modrm: u8, displacement: &[u8]) -> X86AddressMode {
let form = X86Addr16Form::decode(_modrm);
let disp = match displacement.len() {
0 => 0i64,
1 => displacement[0] as i8 as i64,
2 => {
let val = u16::from_le_bytes([displacement[0], displacement[1]]);
val as i16 as i64
}
_ => 0i64,
};
let mut mode = X86AddressMode {
width: AddrWidth::Addr16,
base: form.effective_base(),
index: form.effective_index(),
scale: 1, displacement: disp,
form_16bit: Some(form),
..Default::default()
};
if form == X86Addr16Form::Disp16 {
mode.is_absolute_16 = true;
}
mode
}
fn decode_32_64(
&self,
modrm: u8,
sib: Option<u8>,
displacement: &[u8],
has_rex: bool,
rex_b: bool,
rex_x: bool,
) -> X86AddressMode {
let mod_field = (modrm >> 6) & 0x3;
let rm_field = modrm & 0x7;
let disp: i64 = match displacement.len() {
0 => 0,
1 => displacement[0] as i8 as i64,
4 => {
let val = u32::from_le_bytes([
displacement[0],
displacement[1],
displacement[2],
displacement[3],
]);
val as i32 as i64
}
_ => 0,
};
if self.width == AddrWidth::Addr64 && mod_field == 0 && rm_field == 5 {
return X86AddressMode {
width: AddrWidth::Addr64,
displacement: disp,
is_rip_relative: true,
..Default::default()
};
}
if self.width == AddrWidth::Addr32 && mod_field == 0 && rm_field == 5 {
return X86AddressMode {
width: AddrWidth::Addr32,
displacement: disp,
is_absolute_32: true,
..Default::default()
};
}
if rm_field == 4 {
return self.decode_sib(modrm, sib, displacement, has_rex, rex_b, rex_x);
}
let base_id: u16 = if has_rex && rex_b {
rm_field as u16 + 8
} else {
rm_field as u16
};
X86AddressMode {
width: self.width,
base: Some(base_id),
displacement: disp,
..Default::default()
}
}
fn decode_sib(
&self,
_modrm: u8,
sib: Option<u8>,
displacement: &[u8],
has_rex: bool,
rex_b: bool,
rex_x: bool,
) -> X86AddressMode {
let mod_field = (_modrm >> 6) & 0x3;
let sib_byte = sib.unwrap_or(0x24);
let scale_field = (sib_byte >> 6) & 0x3;
let index_field = (sib_byte >> 3) & 0x7;
let base_field = sib_byte & 0x7;
let scale: u8 = match scale_field {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
_ => 1,
};
let index: Option<u16> = if index_field == 4 {
None } else {
let id = if has_rex && rex_x {
index_field as u16 + 8
} else {
index_field as u16
};
Some(id)
};
let base: Option<u16> = if mod_field == 0 && base_field == 5 {
None
} else {
let id = if has_rex && rex_b {
base_field as u16 + 8
} else {
base_field as u16
};
Some(id)
};
let disp: i64 = match displacement.len() {
0 => 0,
1 => displacement[0] as i8 as i64,
4 => {
let val = u32::from_le_bytes([
displacement[0],
displacement[1],
displacement[2],
displacement[3],
]);
val as i32 as i64
}
_ => 0,
};
X86AddressMode {
width: self.width,
base,
index,
scale,
displacement: disp,
is_absolute_32: base.is_none() && index.is_none(),
..Default::default()
}
}
}
pub fn generate_all_modrm_combinations_32_64(width: AddrWidth) -> Vec<ModRMEntry> {
let mut entries = Vec::new();
for rm in 0..8u8 {
if rm == 4 {
continue;
}
if rm == 5 {
if width == AddrWidth::Addr64 {
entries.push(ModRMEntry {
modrm: (Mod00 << 6) | rm,
form: ModRMForm::RIPRelative,
base: None,
index: None,
scale: 1,
displacement: 0,
});
} else {
entries.push(ModRMEntry {
modrm: (Mod00 << 6) | rm,
form: ModRMForm::Absolute,
base: None,
index: None,
scale: 1,
displacement: 0,
});
}
continue;
}
entries.push(ModRMEntry {
modrm: (Mod00 << 6) | rm,
form: ModRMForm::BaseOnly,
base: Some(rm as u16),
index: None,
scale: 1,
displacement: 0,
});
}
for base in 0..8u8 {
for index in 0..8u8 {
if index == 4 && base == 5 {
entries.push(ModRMEntry {
modrm: (Mod00 << 6) | 4,
form: ModRMForm::Absolute,
base: None,
index: None,
scale: 1,
displacement: 0,
});
continue;
}
for scale in 0..4u8 {
let _sib = (scale << 6) | ((index & 0x7) << 3) | (base & 0x7);
let actual_base = if base == 5 && index == 4 {
None } else {
Some(base as u16)
};
let actual_index = if index == 4 { None } else { Some(index as u16) };
let actual_scale: u8 = match scale {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
_ => 1,
};
let form = match (actual_base, actual_index) {
(Some(_), Some(_)) => ModRMForm::BaseIndexScale,
(Some(_), None) => ModRMForm::BaseOnly,
(None, Some(_)) => ModRMForm::IndexScale,
(None, None) => ModRMForm::Absolute,
};
entries.push(ModRMEntry {
modrm: (Mod00 << 6) | 4,
form,
base: actual_base,
index: actual_index,
scale: actual_scale,
displacement: 0,
});
}
}
}
for rm in 0..8u8 {
if rm == 4 {
continue;
}
entries.push(ModRMEntry {
modrm: (Mod01 << 6) | rm,
form: ModRMForm::BaseDisp8,
base: Some(rm as u16),
index: None,
scale: 1,
displacement: 0,
});
}
entries.push(ModRMEntry {
modrm: (Mod01 << 6) | 4,
form: ModRMForm::BaseDisp8,
base: Some(4), index: None,
scale: 1,
displacement: 0,
});
for rm in 0..8u8 {
if rm == 4 {
continue;
}
entries.push(ModRMEntry {
modrm: (Mod10 << 6) | rm,
form: ModRMForm::BaseDisp32,
base: Some(rm as u16),
index: None,
scale: 1,
displacement: 0,
});
}
entries
}
#[derive(Debug, Clone)]
pub struct ModRMEntry {
pub modrm: u8,
pub form: ModRMForm,
pub base: Option<u16>,
pub index: Option<u16>,
pub scale: u8,
pub displacement: i64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ModRMForm {
BaseOnly,
BaseDisp8,
BaseDisp32,
IndexScale,
BaseIndexScale,
BaseIndexScaleDisp8,
BaseIndexScaleDisp32,
RIPRelative,
Absolute,
RegisterDirect,
}
pub fn generate_all_sib_combinations() -> Vec<SIBEntry> {
let mut entries = Vec::new();
for scale in 0..4u8 {
for index in 0..8u8 {
for base in 0..8u8 {
let sib = (scale << 6) | ((index & 0x7) << 3) | (base & 0x7);
let scale_val: u8 = match scale {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
_ => 1,
};
let has_index = index != 4;
entries.push(SIBEntry {
sib,
scale: scale_val,
index: if has_index { Some(index as u16) } else { None },
base: Some(base as u16),
is_valid: !(index == 4 && base == 5), });
}
}
}
entries
}
#[derive(Debug, Clone)]
pub struct SIBEntry {
pub sib: u8,
pub scale: u8,
pub index: Option<u16>,
pub base: Option<u16>,
pub is_valid: bool,
}
#[derive(Debug, Clone)]
pub struct AddressModeSelector {
engine: X86AddressingFull,
prefer_disp8: bool,
prefer_no_sib: bool,
allow_rip_relative: bool,
allow_sib: bool,
max_disp_bits: u8,
}
impl AddressModeSelector {
pub fn new(engine: X86AddressingFull) -> Self {
Self {
max_disp_bits: engine.addr_width.max_disp_bits() as u8,
engine,
prefer_disp8: true,
prefer_no_sib: true,
allow_rip_relative: true,
allow_sib: true,
}
}
pub fn select_base_disp(&self, base: u16, disp: i64) -> X86AddressMode {
let builder = X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(base)
.with_disp(disp)
.canonicalize();
builder.build()
}
pub fn select_full(&self, base: u16, index: u16, scale: u8, disp: i64) -> X86AddressMode {
let mut builder = X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(base)
.with_index(index, scale)
.with_disp(disp);
if self.prefer_no_sib && index == 0 && scale <= 1 {
builder = builder.with_index_opt(None, 0);
}
if self.prefer_disp8 && disp != 0 {
if disp >= -128 && disp <= 127 {
} else if disp >= -120 && disp <= 120 {
}
}
builder.canonicalize().build()
}
pub fn select_rip_relative(&self, disp: i64) -> X86AddressMode {
if !self.allow_rip_relative {
return X86AddressMode {
width: self.engine.addr_width,
displacement: disp,
is_absolute_32: true,
..Default::default()
};
}
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_disp(disp)
.with_rip_relative(true)
.build()
}
pub fn select_absolute(&self, addr: i64) -> X86AddressMode {
match self.engine.addr_width {
AddrWidth::Addr16 => X86AddressMode::absolute16(addr),
AddrWidth::Addr32 => X86AddressMode::absolute32(addr),
AddrWidth::Addr64 => {
if self.allow_rip_relative {
X86AddressMode::rip_relative(addr)
} else {
X86AddressMode {
width: AddrWidth::Addr64,
displacement: addr,
is_absolute_32: true,
..Default::default()
}
}
}
}
}
pub fn select_16bit(&self, base: Option<u16>, index: Option<u16>, disp: i64) -> X86AddressMode {
use crate::x86::x86_register_info::{BP, BX, DI, SI};
let disp_fits_8 = disp >= -128 && disp <= 127;
let disp_fits_16 = disp >= -32768 && disp <= 32767;
let has_disp = disp != 0;
let form = match (base, index, has_disp, disp_fits_8) {
(None, None, true, _) => X86Addr16Form::Disp16,
(Some(b), None, false, _) => match b {
r if r == BX => X86Addr16Form::Bx,
r if r == BP => X86Addr16Form::Bp,
r if r == SI => X86Addr16Form::Si,
r if r == DI => X86Addr16Form::Di,
_ => X86Addr16Form::Disp16,
},
(Some(b), None, true, true) => match b {
r if r == BX => X86Addr16Form::BxDisp8,
r if r == BP => X86Addr16Form::BpDisp8,
r if r == SI => X86Addr16Form::SiDisp8,
r if r == DI => X86Addr16Form::DiDisp8,
_ => X86Addr16Form::Disp16,
},
(Some(b), None, true, false) => match b {
r if r == BX => X86Addr16Form::BxDisp16,
r if r == BP => X86Addr16Form::BpDisp16,
r if r == SI => X86Addr16Form::SiDisp16,
r if r == DI => X86Addr16Form::DiDisp16,
_ => X86Addr16Form::Disp16,
},
(Some(b), Some(i), false, _) => match (b, i) {
(b_val, i_val) if b_val == BX && i_val == SI => X86Addr16Form::BxSi,
(b_val, i_val) if b_val == BX && i_val == DI => X86Addr16Form::BxDi,
(b_val, i_val) if b_val == BP && i_val == SI => X86Addr16Form::BpSi,
(b_val, i_val) if b_val == BP && i_val == DI => X86Addr16Form::BpDi,
_ => X86Addr16Form::Disp16,
},
(Some(b), Some(i), true, true) => match (b, i) {
(b_val, i_val) if b_val == BX && i_val == SI => X86Addr16Form::BxSiDisp8,
(b_val, i_val) if b_val == BX && i_val == DI => X86Addr16Form::BxDiDisp8,
(b_val, i_val) if b_val == BP && i_val == SI => X86Addr16Form::BpSiDisp8,
(b_val, i_val) if b_val == BP && i_val == DI => X86Addr16Form::BpDiDisp8,
_ => X86Addr16Form::Disp16,
},
(Some(b), Some(i), true, false) => match (b, i) {
(b_val, i_val) if b_val == BX && i_val == SI => X86Addr16Form::BxSiDisp16,
(b_val, i_val) if b_val == BX && i_val == DI => X86Addr16Form::BxDiDisp16,
(b_val, i_val) if b_val == BP && i_val == SI => X86Addr16Form::BpSiDisp16,
(b_val, i_val) if b_val == BP && i_val == DI => X86Addr16Form::BpDiDisp16,
_ => X86Addr16Form::Disp16,
},
_ => X86Addr16Form::Disp16,
};
X86AddressMode {
width: AddrWidth::Addr16,
base,
index,
scale: 1,
displacement: disp,
form_16bit: Some(form),
is_absolute_16: form == X86Addr16Form::Disp16,
..Default::default()
}
}
}
pub fn is_valid_base_reg(reg: u16, width: AddrWidth) -> bool {
match width {
AddrWidth::Addr64 => {
use crate::x86::x86_register_info::{R10, R11, R12, R13, R14, R15, R8, R9};
matches!(
reg,
x86_register_info::RAX
| x86_register_info::RCX
| x86_register_info::RDX
| x86_register_info::RBX
| x86_register_info::RSP
| x86_register_info::RBP
| x86_register_info::RSI
| x86_register_info::RDI
| R8
| R9
| R10
| R11
| R12
| R13
| R14
| R15
)
}
AddrWidth::Addr32 => {
use crate::x86::x86_register_info::{R10D, R11D, R12D, R13D, R14D, R15D, R8D, R9D};
matches!(
reg,
x86_register_info::EAX
| x86_register_info::ECX
| x86_register_info::EDX
| x86_register_info::EBX
| x86_register_info::ESP
| x86_register_info::EBP
| x86_register_info::ESI
| x86_register_info::EDI
| R8D
| R9D
| R10D
| R11D
| R12D
| R13D
| R14D
| R15D
)
}
AddrWidth::Addr16 => {
use crate::x86::x86_register_info::{BP, BX, DI, SI};
matches!(reg, BX | BP | SI | DI)
}
}
}
pub fn is_valid_index_reg(reg: u16, width: AddrWidth) -> bool {
match width {
AddrWidth::Addr64 => {
use crate::x86::x86_register_info::{R10, R11, R12, R13, R14, R15, R8, R9};
matches!(
reg,
x86_register_info::RAX
| x86_register_info::RCX
| x86_register_info::RDX
| x86_register_info::RBX
| x86_register_info::RBP
| x86_register_info::RSI
| x86_register_info::RDI
| R8
| R9
| R10
| R11
| R12
| R13
| R14
| R15
)
}
AddrWidth::Addr32 => {
use crate::x86::x86_register_info::{R10D, R11D, R12D, R13D, R14D, R15D, R8D, R9D};
matches!(
reg,
x86_register_info::EAX
| x86_register_info::ECX
| x86_register_info::EDX
| x86_register_info::EBX
| x86_register_info::EBP
| x86_register_info::ESI
| x86_register_info::EDI
| R8D
| R9D
| R10D
| R11D
| R12D
| R13D
| R14D
| R15D
)
}
AddrWidth::Addr16 => {
use crate::x86::x86_register_info::{DI, SI};
matches!(reg, SI | DI)
}
}
}
pub fn is_valid_scale(scale: u8) -> bool {
matches!(scale, 1 | 2 | 4 | 8)
}
pub fn compute_effective_address(
base_val: Option<u64>,
index_val: Option<u64>,
scale: u8,
disp: i64,
) -> u64 {
let b = base_val.unwrap_or(0);
let i = index_val.unwrap_or(0);
let s = scale as u64;
b.wrapping_add(i.wrapping_mul(s)).wrapping_add(disp as u64)
}
pub fn are_addresses_equal(a: &X86AddressMode, b: &X86AddressMode) -> bool {
a.base == b.base
&& a.index == b.index
&& a.scale == b.scale
&& a.displacement == b.displacement
&& a.segment == b.segment
&& a.is_rip_relative == b.is_rip_relative
&& (a.symbol.as_deref() == b.symbol.as_deref())
}
pub fn address_difference(a: &X86AddressMode, b: &X86AddressMode) -> Option<i64> {
if a.base != b.base
|| a.index != b.index
|| a.scale != b.scale
|| a.segment != b.segment
|| a.is_rip_relative != b.is_rip_relative
{
return None;
}
Some(b.displacement - a.displacement)
}
pub fn merge_addresses(
a: &X86AddressMode,
b: &X86AddressMode,
) -> Option<(X86AddressMode, i64, i64)> {
if a.base != b.base || a.index != b.index || a.scale != b.scale {
return None;
}
let base_disp = a.displacement.min(b.displacement);
let mut common = a.clone();
common.displacement = base_disp;
Some((
common,
a.displacement - base_disp,
b.displacement - base_disp,
))
}
pub fn encode_addressing_mode(addr: &X86AddressMode, reg_field: u8) -> Vec<u8> {
let mut bytes = Vec::new();
let seg_handler = X86SegmentHandling::new(X86AddressingFull::new(addr.width));
if let Some(pref) = seg_handler.segment_override_prefix(addr) {
bytes.push(pref);
}
if addr.requires_rex() {
let mut rex: u8 = 0x40;
if addr.width == AddrWidth::Addr64 {
rex |= 0x08; }
if let Some(idx) = addr.index {
if idx >= 8 {
rex |= 0x02; }
}
if let Some(base) = addr.base {
if base >= 8 {
rex |= 0x01; }
}
bytes.push(rex);
}
bytes.push(addr.encode_modrm(reg_field));
if addr.requires_sib() {
bytes.push(addr.encode_sib());
}
bytes.extend(addr.encode_displacement());
bytes
}
pub fn decode_addressing_mode(
bytes: &[u8],
offset: usize,
width: AddrWidth,
has_rex: bool,
rex_b: bool,
rex_x: bool,
) -> Option<(X86AddressMode, usize)> {
if offset >= bytes.len() {
return None;
}
let matcher = AddressingModeMatcher::new(width);
let modrm = bytes[offset];
let mod_field = (modrm >> 6) & 0x3;
let rm_field = modrm & 0x7;
let mut consumed: usize = 1;
let needs_sib = rm_field == 4;
let sib = if needs_sib {
if offset + 1 >= bytes.len() {
return None;
}
consumed += 1;
Some(bytes[offset + 1])
} else {
None
};
let disp_size: usize = if mod_field == 0 && rm_field == 5 {
4
} else if mod_field == 1 {
1
} else if mod_field == 2 {
4
} else if mod_field == 0 && sib.is_some() {
let sib_byte = sib.unwrap();
let sib_base = sib_byte & 0x7;
if sib_base == 5 {
4
} else {
0
}
} else {
0
};
let disp_bytes = if disp_size > 0 && offset + consumed + disp_size <= bytes.len() {
bytes[offset + consumed..offset + consumed + disp_size].to_vec()
} else {
vec![]
};
consumed += disp_size;
let addr = matcher.decode(modrm, sib, &disp_bytes, has_rex, rex_b, rex_x);
Some((addr, consumed))
}
pub fn encode_16bit_modrm(form: X86Addr16Form, reg_field: u8) -> u8 {
(form.mod_field() << 6) | ((reg_field & 0x7) << 3) | (form.rm_field() & 0x7)
}
pub fn format_intel(addr: &X86AddressMode, _reg_info: &X86RegisterInfo) -> String {
let mut s = String::new();
match addr.width {
AddrWidth::Addr64 => s.push_str("qword ptr "),
AddrWidth::Addr32 => s.push_str("dword ptr "),
AddrWidth::Addr16 => s.push_str("word ptr "),
}
s.push('[');
if let Some(seg) = addr.segment {
s.push_str(X86RegisterInfo::get_intel_name(seg));
s.push(':');
}
if addr.is_rip_relative {
s.push_str("rip");
} else if let Some(base) = addr.base {
s.push_str(X86RegisterInfo::get_intel_name(base));
}
if let Some(index) = addr.index {
if addr.base.is_some() || addr.is_rip_relative {
s.push_str(" + ");
}
s.push_str(X86RegisterInfo::get_intel_name(index));
if addr.scale > 1 {
s.push_str(&format!("*{}", addr.scale));
}
}
if addr.displacement != 0 {
if addr.base.is_some() || addr.index.is_some() || addr.is_rip_relative {
if addr.displacement > 0 {
s.push_str(&format!(" + {}", addr.displacement));
} else {
s.push_str(&format!(" - {}", -addr.displacement));
}
} else {
s.push_str(&format!("{}", addr.displacement));
}
}
s.push(']');
s
}
pub fn format_att(addr: &X86AddressMode, _reg_info: &X86RegisterInfo) -> String {
let mut s = String::new();
if let Some(seg) = addr.segment {
s.push_str(&format!("%{}:", X86RegisterInfo::get_asm_name(seg)));
}
if addr.displacement != 0 {
s.push_str(&format!("{}", addr.displacement));
}
s.push('(');
if addr.is_rip_relative {
s.push_str("%rip");
} else if let Some(base) = addr.base {
s.push_str(&format!("%{}", X86RegisterInfo::get_asm_name(base)));
}
if let Some(index) = addr.index {
s.push(',');
s.push_str(&format!("%{}", X86RegisterInfo::get_asm_name(index)));
if addr.scale > 1 {
s.push_str(&format!(",{}", addr.scale));
}
}
s.push(')');
s
}
#[derive(Debug, Clone)]
pub struct ConstantPoolEntry {
pub index: u32,
pub alignment: u8,
pub size: u16,
pub is_float: bool,
pub rip_offset: i64,
}
#[derive(Debug, Clone, Default)]
pub struct ConstantPool {
entries: Vec<ConstantPoolEntry>,
}
impl ConstantPool {
pub fn new() -> Self {
Self { entries: vec![] }
}
pub fn add_entry(&mut self, alignment: u8, size: u16, is_float: bool) -> u32 {
let index = self.entries.len() as u32;
self.entries.push(ConstantPoolEntry {
index,
alignment,
size,
is_float,
rip_offset: 0,
});
index
}
pub fn get_address(&self, index: u32) -> Option<X86AddressMode> {
self.entries.get(index as usize).map(|entry| {
let label = format!(".LCPI{}", entry.index);
X86AddressMode::rip_symbol(&label, entry.rip_offset)
})
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct AddressCostModel {
pub byte_cost: u32,
pub uop_cost: u32,
pub sib_penalty: u32,
pub rex_penalty: u32,
pub cache_line_penalty: u32,
pub segment_penalty: u32,
pub fuse_complex: bool,
pub fuse_rip_relative: bool,
}
impl Default for AddressCostModel {
fn default() -> Self {
Self {
byte_cost: 1,
uop_cost: 10,
sib_penalty: 5,
rex_penalty: 2,
cache_line_penalty: 20,
segment_penalty: 8,
fuse_complex: true,
fuse_rip_relative: true,
}
}
}
impl AddressCostModel {
pub fn skylake() -> Self {
Self {
byte_cost: 1,
uop_cost: 10,
sib_penalty: 2,
rex_penalty: 1,
cache_line_penalty: 18,
segment_penalty: 5,
fuse_complex: true,
fuse_rip_relative: true,
}
}
pub fn zen() -> Self {
Self {
byte_cost: 1,
uop_cost: 12,
sib_penalty: 3,
rex_penalty: 1,
cache_line_penalty: 15,
segment_penalty: 6,
fuse_complex: true,
fuse_rip_relative: true,
}
}
pub fn size_optimized() -> Self {
Self {
byte_cost: 100,
uop_cost: 1,
sib_penalty: 50,
rex_penalty: 30,
cache_line_penalty: 0,
segment_penalty: 30,
fuse_complex: false,
fuse_rip_relative: false,
}
}
pub fn compute_cost(&self, addr: &X86AddressMode) -> u64 {
let mut cost: u64 = 0;
let encoding_size = addr.classify().encoding_overhead() as u64;
cost += encoding_size * self.byte_cost as u64;
if addr.requires_sib() {
cost += self.sib_penalty as u64;
}
if addr.requires_rex() {
cost += self.rex_penalty as u64;
}
if addr.segment.is_some() {
cost += self.segment_penalty as u64;
}
let uops = self.estimate_uops(addr);
cost += uops * self.uop_cost as u64;
if self.may_cross_cache_line(addr) {
cost += self.cache_line_penalty as u64;
}
cost
}
fn estimate_uops(&self, addr: &X86AddressMode) -> u64 {
if addr.is_rip_relative {
if self.fuse_rip_relative {
return 1; }
return 2; }
if !addr.has_index() {
return 1;
}
if self.fuse_complex {
match addr.scale {
1 | 2 | 4 | 8 => 1, _ => 2, }
} else {
2 }
}
fn may_cross_cache_line(&self, addr: &X86AddressMode) -> bool {
if addr.displacement == 0 {
return false;
}
let offset = (addr.displacement as u64) & 0x3F;
offset > 56
}
pub fn select_cheaper(&self, a: &X86AddressMode, b: &X86AddressMode) -> X86AddressMode {
let cost_a = self.compute_cost(a);
let cost_b = self.compute_cost(b);
if cost_a <= cost_b {
a.clone()
} else {
b.clone()
}
}
}
#[derive(Debug, Clone)]
pub struct RegisterPressureAnalysis {
pub available_gprs: u8,
pub current_pressure: f64,
pub pressure_threshold: f64,
}
impl Default for RegisterPressureAnalysis {
fn default() -> Self {
Self {
available_gprs: 14, current_pressure: 0.0,
pressure_threshold: 0.7,
}
}
}
impl RegisterPressureAnalysis {
pub fn new64() -> Self {
Self {
available_gprs: 14,
..Default::default()
}
}
pub fn new32() -> Self {
Self {
available_gprs: 6, ..Default::default()
}
}
pub fn can_use_index(&self) -> bool {
self.current_pressure < self.pressure_threshold
}
pub fn should_use_lea(&self, num_adds: usize) -> bool {
if self.current_pressure > 0.9 {
return false;
}
if self.current_pressure > 0.6 && num_adds <= 1 {
return false;
}
true
}
pub fn estimate_pressure(&self, addr: &X86AddressMode, live_regs: usize) -> (f64, f64) {
let current = live_regs as f64 / self.available_gprs as f64;
let additional: f64 = if addr.has_index() && addr.index != addr.base {
1.0 / self.available_gprs as f64
} else {
0.0
};
(current + additional, additional)
}
pub fn register_friendliness(&self, addr: &X86AddressMode) -> u32 {
let mut score: u32 = 10;
if addr.has_index() {
score -= 3; }
if addr.has_base() {
score -= 1; }
if addr.is_rip_relative {
score += 5; }
if addr.is_absolute_32 || addr.is_absolute_16 {
score += 3; }
score
}
}
#[derive(Debug, Clone)]
pub struct MemoryFoldRules {
pub can_fold_load: HashSet<X86Opcode>,
pub can_fold_rmw: HashSet<X86Opcode>,
pub can_fold_complex: HashSet<X86Opcode>,
pub max_fold_disp: i64,
}
impl Default for MemoryFoldRules {
fn default() -> Self {
let mut fold_load = HashSet::new();
fold_load.insert(X86Opcode::ADD);
fold_load.insert(X86Opcode::ADC);
fold_load.insert(X86Opcode::SUB);
fold_load.insert(X86Opcode::SBB);
fold_load.insert(X86Opcode::AND);
fold_load.insert(X86Opcode::OR);
fold_load.insert(X86Opcode::XOR);
fold_load.insert(X86Opcode::CMP);
fold_load.insert(X86Opcode::TEST);
fold_load.insert(X86Opcode::MOV);
fold_load.insert(X86Opcode::MOVSX);
fold_load.insert(X86Opcode::MOVZX);
fold_load.insert(X86Opcode::IMUL);
fold_load.insert(X86Opcode::BT);
fold_load.insert(X86Opcode::BTC);
fold_load.insert(X86Opcode::BTR);
fold_load.insert(X86Opcode::BTS);
fold_load.insert(X86Opcode::BSF);
fold_load.insert(X86Opcode::BSR);
fold_load.insert(X86Opcode::NEG);
fold_load.insert(X86Opcode::NOT);
fold_load.insert(X86Opcode::MUL);
fold_load.insert(X86Opcode::PUSH);
fold_load.insert(X86Opcode::ADDSS);
fold_load.insert(X86Opcode::ADDSD);
fold_load.insert(X86Opcode::SUBSS);
fold_load.insert(X86Opcode::SUBSD);
fold_load.insert(X86Opcode::MULSS);
fold_load.insert(X86Opcode::MULSD);
fold_load.insert(X86Opcode::DIVSS);
fold_load.insert(X86Opcode::DIVSD);
let mut fold_rmw = HashSet::new();
fold_rmw.insert(X86Opcode::ADD);
fold_rmw.insert(X86Opcode::ADC);
fold_rmw.insert(X86Opcode::SUB);
fold_rmw.insert(X86Opcode::SBB);
fold_rmw.insert(X86Opcode::AND);
fold_rmw.insert(X86Opcode::OR);
fold_rmw.insert(X86Opcode::XOR);
fold_rmw.insert(X86Opcode::INC);
fold_rmw.insert(X86Opcode::DEC);
fold_rmw.insert(X86Opcode::NEG);
fold_rmw.insert(X86Opcode::NOT);
fold_rmw.insert(X86Opcode::SHL);
fold_rmw.insert(X86Opcode::SHR);
fold_rmw.insert(X86Opcode::SAR);
fold_rmw.insert(X86Opcode::ROL);
fold_rmw.insert(X86Opcode::ROR);
fold_rmw.insert(X86Opcode::RCL);
fold_rmw.insert(X86Opcode::RCR);
fold_rmw.insert(X86Opcode::BTC);
fold_rmw.insert(X86Opcode::BTR);
fold_rmw.insert(X86Opcode::BTS);
Self {
can_fold_load: fold_load,
can_fold_rmw: fold_rmw,
can_fold_complex: HashSet::new(),
max_fold_disp: 2_147_483_647,
}
}
}
impl MemoryFoldRules {
pub fn can_fold_load_into(&self, opcode: X86Opcode) -> bool {
self.can_fold_load.contains(&opcode)
}
pub fn can_fold_rmw_into(&self, opcode: X86Opcode) -> bool {
self.can_fold_rmw.contains(&opcode)
}
pub fn is_foldable(&self, addr: &X86AddressMode) -> bool {
if addr.is_absolute_16 || (addr.is_absolute_32 && addr.width == AddrWidth::Addr32) {
return false;
}
if addr.is_rip_relative {
return true;
}
if addr.has_index() {
return true;
}
addr.displacement.abs() <= self.max_fold_disp
}
pub fn get_fold_form(&self, addr: &X86AddressMode) -> FoldForm {
if !self.is_foldable(addr) {
return FoldForm::CannotFold;
}
if addr.has_index() {
if addr.fits_disp8() || addr.has_disp_zero() {
FoldForm::FullWithIndex
} else {
FoldForm::FullWithIndexDisp32
}
} else if addr.has_disp_zero() {
FoldForm::BaseOnly
} else if addr.fits_disp8() {
FoldForm::BaseDisp8
} else {
FoldForm::BaseDisp32
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FoldForm {
CannotFold,
BaseOnly,
BaseDisp8,
BaseDisp32,
FullWithIndex,
FullWithIndexDisp32,
}
#[derive(Debug, Clone)]
pub struct LeaComplexityDB {
pub latencies: [u32; 5],
pub class3_any_port: bool,
pub class4_fast: bool,
}
impl LeaComplexityDB {
pub fn intel_skylake_client() -> Self {
Self {
latencies: [0, 1, 1, 1, 3],
class3_any_port: false,
class4_fast: false,
}
}
pub fn intel_ice_lake() -> Self {
Self {
latencies: [0, 1, 1, 1, 3],
class3_any_port: false,
class4_fast: false,
}
}
pub fn amd_zen() -> Self {
Self {
latencies: [0, 1, 1, 1, 1],
class3_any_port: true,
class4_fast: true,
}
}
pub fn classify_lea(&self, addr: &X86AddressMode) -> u8 {
let components = if addr.has_base() { 1 } else { 0 }
+ if addr.has_index() { 1 } else { 0 }
+ if addr.displacement != 0 { 1 } else { 0 };
let has_scale = addr.scale > 1;
match (components, has_scale) {
(0, _) => 1, (1, false) => 1, (2, false) => 2, (2, true) => 3, (3, false) => 3, (3, true) => 4, _ => 4,
}
}
pub fn lea_latency(&self, addr: &X86AddressMode) -> u32 {
let class = self.classify_lea(addr) as usize;
if class < self.latencies.len() {
self.latencies[class]
} else {
3
}
}
}
#[derive(Debug, Clone)]
pub struct AddressSelectionDecisionTree {
engine: X86AddressingFull,
cost_model: AddressCostModel,
pressure: RegisterPressureAnalysis,
lea_db: LeaComplexityDB,
fold_rules: MemoryFoldRules,
}
impl AddressSelectionDecisionTree {
pub fn new(engine: X86AddressingFull) -> Self {
Self {
engine,
cost_model: AddressCostModel::default(),
pressure: RegisterPressureAnalysis::default(),
lea_db: LeaComplexityDB::intel_skylake_client(),
fold_rules: MemoryFoldRules::default(),
}
}
pub fn select_best_memory(
&self,
base: Option<u16>,
index: Option<u16>,
scale: u8,
disp: i64,
is_frame_idx: bool,
is_global: bool,
is_tls: bool,
) -> X86AddressMode {
if is_tls {
return self.select_tls_mode(disp);
}
if is_global {
return self.select_global_mode(disp);
}
if is_frame_idx {
return self.select_frame_mode(disp);
}
self.select_generic_mode(base, index, scale, disp)
}
fn select_tls_mode(&self, offset: i64) -> X86AddressMode {
let seg = if self.engine.addr_width == AddrWidth::Addr64 {
x86_register_info::FS
} else {
x86_register_info::GS
};
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_disp(offset)
.with_segment(seg)
.build()
}
fn select_global_mode(&self, disp: i64) -> X86AddressMode {
if self.engine.is_pic && self.engine.addr_width == AddrWidth::Addr64 {
X86AddressMode::rip_relative(disp).with_got_relative(true)
} else if self.engine.addr_width == AddrWidth::Addr64 {
X86AddressMode::rip_relative(disp)
} else {
X86AddressMode::absolute32(disp)
}
}
fn select_frame_mode(&self, offset: i64) -> X86AddressMode {
let base = match self.engine.addr_width {
AddrWidth::Addr64 => x86_register_info::RBP,
AddrWidth::Addr32 => x86_register_info::EBP,
AddrWidth::Addr16 => x86_register_info::BP,
};
X86AddressMode::base_disp64(base, -offset).with_frame_relative(true)
}
fn select_generic_mode(
&self,
base: Option<u16>,
index: Option<u16>,
scale: u8,
disp: i64,
) -> X86AddressMode {
if let Some(b) = base {
if index.is_none() || scale <= 1 {
let simple = X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(b)
.with_disp(disp)
.build();
let complex = X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(b)
.with_index_opt(index, scale)
.with_disp(disp)
.build();
let simple_cost = self.cost_model.compute_cost(&simple);
let complex_cost = self.cost_model.compute_cost(&complex);
if simple_cost <= complex_cost || !self.pressure.can_use_index() {
return simple;
}
return complex;
}
}
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base_opt(base)
.with_index_opt(index, scale)
.with_disp(disp)
.build()
}
pub fn select_best_lea(
&self,
base: u16,
index: Option<u16>,
scale: u8,
disp: i64,
) -> X86AddressMode {
X86AddressingModeBuilder::new(self.engine.addr_width)
.with_base(base)
.with_index_opt(index, scale)
.with_disp(disp)
.build()
}
}
#[derive(Debug, Clone)]
pub struct PrefixInteraction {
pub addr_width: AddrWidth,
}
impl PrefixInteraction {
pub fn new(addr_width: AddrWidth) -> Self {
Self { addr_width }
}
pub fn encode_rex(&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 decode_rex(&self, rex: u8) -> (bool, bool, bool, bool) {
let w = (rex & 0x08) != 0;
let r = (rex & 0x04) != 0;
let x = (rex & 0x02) != 0;
let b = (rex & 0x01) != 0;
(w, r, x, b)
}
pub fn needs_rex(&self, addr: &X86AddressMode) -> bool {
if self.addr_width != AddrWidth::Addr64 {
return false;
}
if let Some(base) = addr.base {
if base >= 8 {
return true; }
}
if let Some(index) = addr.index {
if index >= 8 {
return true; }
}
true
}
pub fn compute_rex_bits(&self, addr: &X86AddressMode) -> (bool, bool, bool) {
let r: bool = false; let mut x: bool = false;
let mut b: bool = false;
if let Some(index) = addr.index {
if index >= 8 {
x = true;
}
}
if let Some(base) = addr.base {
if base >= 8 {
b = true;
}
}
(r, x, b)
}
pub fn is_rex_valid_with_reg(&self, _reg: u16) -> bool {
!matches!(_reg, 64 | 65 | 66 | 67)
}
pub fn needs_addr_size_override(&self, addr: &X86AddressMode) -> bool {
match self.addr_width {
AddrWidth::Addr64 => addr.width == AddrWidth::Addr32,
AddrWidth::Addr32 => addr.width == AddrWidth::Addr16,
AddrWidth::Addr16 => false,
}
}
pub fn encode_all_prefixes(&self, addr: &X86AddressMode, reg_field: u16) -> Vec<u8> {
let mut prefixes = Vec::new();
if let Some(_seg) = addr.segment {
let seg_handler = X86SegmentHandling::new(X86AddressingFull::new(self.addr_width));
if let Some(pfx) = seg_handler.segment_override_prefix(addr) {
prefixes.push(pfx);
}
}
if self.needs_addr_size_override(addr) {
prefixes.push(0x67);
}
if self.needs_rex(addr) {
let (_r, x, b) = self.compute_rex_bits(addr);
let r_bit = reg_field >= 8;
let rex = self.encode_rex(true, r_bit, x, b);
prefixes.push(rex);
}
prefixes
}
}
#[derive(Debug, Clone)]
pub struct AddressingModeValidator {
width: AddrWidth,
}
impl AddressingModeValidator {
pub fn new(width: AddrWidth) -> Self {
Self { width }
}
pub fn is_valid(&self, addr: &X86AddressMode) -> Result<(), AddressingError> {
if addr.width != self.width {
return Err(AddressingError::WidthMismatch);
}
if let Some(base) = addr.base {
if !is_valid_base_reg(base, self.width) {
return Err(AddressingError::InvalidBaseRegister(base));
}
}
if let Some(index) = addr.index {
if !is_valid_index_reg(index, self.width) {
return Err(AddressingError::InvalidIndexRegister(index));
}
}
if addr.scale > 0 && !is_valid_scale(addr.scale) {
return Err(AddressingError::InvalidScale(addr.scale));
}
if !self.is_displacement_valid(addr.displacement) {
return Err(AddressingError::DisplacementOutOfRange(addr.displacement));
}
if addr.is_rip_relative {
if self.width != AddrWidth::Addr64 {
return Err(AddressingError::RipRelativeNotSupported);
}
if addr.base.is_some() || addr.index.is_some() {
return Err(AddressingError::RipRelativeWithBaseIndex);
}
}
if self.width == AddrWidth::Addr16 {
if addr.scale > 1 {
return Err(AddressingError::ScaleNotSupportedIn16Bit);
}
if addr.base.is_some() && addr.index.is_some() {
let valid = Self::is_valid_16bit_pair(addr.base.unwrap(), addr.index.unwrap());
if !valid {
return Err(AddressingError::Invalid16BitPair);
}
}
}
if let Some(idx) = addr.index {
let idx_enc = idx & 0x7;
if idx_enc == 4 && self.width != AddrWidth::Addr16 {
return Err(AddressingError::RspCannotBeIndex);
}
}
Ok(())
}
fn is_valid_16bit_pair(base: u16, index: u16) -> bool {
use crate::x86::x86_register_info::{BP, BX, DI, SI};
match (base, index) {
(b, i) if b == BX && i == SI => true,
(b, i) if b == BX && i == DI => true,
(b, i) if b == BP && i == SI => true,
(b, i) if b == BP && i == DI => true,
_ => false,
}
}
fn is_displacement_valid(&self, disp: i64) -> bool {
match self.width {
AddrWidth::Addr16 => disp >= -32768 && disp <= 32767,
AddrWidth::Addr32 | AddrWidth::Addr64 => {
disp >= -2_147_483_648 && disp <= 2_147_483_647
}
}
}
pub fn suggest_fix(&self, error: &AddressingError) -> Option<String> {
match error {
AddressingError::RspCannotBeIndex => {
Some("Use a different index register; RSP cannot be an index.".into())
}
AddressingError::InvalidScale(s) => {
let nearest = match s {
s if *s <= 1 => 1,
s if *s <= 2 => 2,
s if *s <= 4 => 4,
_ => 8,
};
Some(format!(
"Scale {} is invalid. Use 1, 2, 4, or 8. Nearest: {}",
s, nearest
))
}
AddressingError::RipRelativeNotSupported => {
Some("RIP-relative addressing is only available in 64-bit mode.".into())
}
AddressingError::ScaleNotSupportedIn16Bit => {
Some("16-bit mode does not support scaled index addressing.".into())
}
AddressingError::Invalid16BitPair => {
Some("Valid 16-bit pairs: [BX+SI], [BX+DI], [BP+SI], [BP+DI].".into())
}
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AddressingError {
WidthMismatch,
InvalidBaseRegister(u16),
InvalidIndexRegister(u16),
InvalidScale(u8),
DisplacementOutOfRange(i64),
RipRelativeNotSupported,
RipRelativeWithBaseIndex,
ScaleNotSupportedIn16Bit,
Invalid16BitPair,
RspCannotBeIndex,
RbpWithZeroDisplacement,
SegmentOverrideNotAllowed,
}
impl std::fmt::Display for AddressingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AddressingError::WidthMismatch => {
write!(f, "Addressing mode width does not match current mode")
}
AddressingError::InvalidBaseRegister(r) => {
write!(f, "Register {} is not a valid base register", r)
}
AddressingError::InvalidIndexRegister(r) => {
write!(f, "Register {} is not a valid index register", r)
}
AddressingError::InvalidScale(s) => {
write!(f, "Scale {} is invalid (must be 1, 2, 4, or 8)", s)
}
AddressingError::DisplacementOutOfRange(d) => {
write!(f, "Displacement {} is out of range", d)
}
AddressingError::RipRelativeNotSupported => {
write!(
f,
"RIP-relative addressing is only supported in 64-bit mode"
)
}
AddressingError::RipRelativeWithBaseIndex => {
write!(
f,
"RIP-relative addressing cannot have a base or index register"
)
}
AddressingError::ScaleNotSupportedIn16Bit => {
write!(f, "Scaled index addressing is not supported in 16-bit mode")
}
AddressingError::Invalid16BitPair => {
write!(f, "Invalid base+index pair for 16-bit mode")
}
AddressingError::RspCannotBeIndex => {
write!(f, "RSP/ESP cannot be used as an index register")
}
AddressingError::RbpWithZeroDisplacement => {
write!(f, "[RBP] with zero displacement is not encodable")
}
AddressingError::SegmentOverrideNotAllowed => {
write!(f, "Segment override is not allowed in this context")
}
}
}
}
impl std::error::Error for AddressingError {}
#[derive(Debug, Clone)]
pub struct CompleteModRMTable {
pub entries: Vec<CompleteModRMEntry>,
pub by_modrm: BTreeMap<u8, Vec<CompleteModRMEntry>>,
pub by_form: HashMap<ModRMForm, Vec<CompleteModRMEntry>>,
}
#[derive(Debug, Clone)]
pub struct CompleteModRMEntry {
pub modrm: u8,
pub mod_field: u8,
pub reg_field: u8,
pub rm_field: u8,
pub form: ModRMForm,
pub base: Option<u8>,
pub index: Option<u8>,
pub scale: u8,
pub has_sib: bool,
pub sib_byte: Option<u8>,
pub has_disp: bool,
pub disp_size: u8,
pub description: &'static str,
}
impl CompleteModRMTable {
pub fn generate_64bit() -> Self {
Self::generate(AddrWidth::Addr64)
}
pub fn generate_32bit() -> Self {
Self::generate(AddrWidth::Addr32)
}
pub fn generate(width: AddrWidth) -> Self {
let mut entries = Vec::new();
let mut by_modrm: BTreeMap<u8, Vec<CompleteModRMEntry>> = BTreeMap::new();
let mut by_form: HashMap<ModRMForm, Vec<CompleteModRMEntry>> = HashMap::new();
for mod_field in 0u8..4 {
for reg_field in 0u8..8 {
for rm_field in 0u8..8 {
let modrm = (mod_field << 6) | (reg_field << 3) | rm_field;
if mod_field == 3 {
let entry = CompleteModRMEntry {
modrm,
mod_field,
reg_field,
rm_field,
form: ModRMForm::RegisterDirect,
base: Some(rm_field),
index: None,
scale: 1,
has_sib: false,
sib_byte: None,
has_disp: false,
disp_size: 0,
description: "register-direct",
};
entries.push(entry);
continue;
}
if rm_field == 4 && width.uses_sib() {
Self::generate_sib_entries(
mod_field,
reg_field,
rm_field,
modrm,
width,
&mut entries,
);
} else {
Self::generate_direct_entries(
mod_field,
reg_field,
rm_field,
modrm,
width,
&mut entries,
);
}
}
}
}
for entry in &entries {
by_modrm.entry(entry.modrm).or_default().push(entry.clone());
by_form.entry(entry.form).or_default().push(entry.clone());
}
Self {
entries,
by_modrm,
by_form,
}
}
fn generate_direct_entries(
mod_field: u8,
reg_field: u8,
rm_field: u8,
modrm: u8,
width: AddrWidth,
entries: &mut Vec<CompleteModRMEntry>,
) {
let (form, has_disp, disp_size, description) = match (mod_field, rm_field, width) {
(0, 5, AddrWidth::Addr64) => (ModRMForm::RIPRelative, true, 4, "[RIP + disp32]"),
(0, 5, AddrWidth::Addr32) => (ModRMForm::Absolute, true, 4, "[disp32]"),
(0, _, _) => (ModRMForm::BaseOnly, false, 0, "[base]"),
(1, _, _) => (ModRMForm::BaseDisp8, true, 1, "[base + disp8]"),
(2, _, _) => (ModRMForm::BaseDisp32, true, 4, "[base + disp32]"),
_ => (ModRMForm::BaseOnly, false, 0, "[base]"),
};
entries.push(CompleteModRMEntry {
modrm,
mod_field,
reg_field,
rm_field,
form,
base: if form != ModRMForm::Absolute && form != ModRMForm::RIPRelative {
Some(rm_field)
} else {
None
},
index: None,
scale: 1,
has_sib: false,
sib_byte: None,
has_disp,
disp_size,
description,
});
}
fn generate_sib_entries(
mod_field: u8,
reg_field: u8,
_rm_field: u8,
modrm: u8,
width: AddrWidth,
entries: &mut Vec<CompleteModRMEntry>,
) {
for scale_bits in 0u8..4 {
for index_field in 0u8..8 {
for base_field in 0u8..8 {
if mod_field == 0 && base_field == 5 && index_field == 4 {
entries.push(CompleteModRMEntry {
modrm,
mod_field,
reg_field,
rm_field: 4,
form: if width == AddrWidth::Addr64 {
ModRMForm::RIPRelative
} else {
ModRMForm::Absolute
},
base: None,
index: None,
scale: 1,
has_sib: true,
sib_byte: Some(
(scale_bits << 6) | ((index_field & 0x7) << 3) | (base_field & 0x7),
),
has_disp: true,
disp_size: 4,
description: "[disp32 via SIB]",
});
continue;
}
let sib = (scale_bits << 6) | ((index_field & 0x7) << 3) | (base_field & 0x7);
let scale: u8 = match scale_bits {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
_ => 1,
};
let has_index = index_field != 4;
let base_is_disp32 = mod_field == 0 && base_field == 5;
let actual_base = if base_is_disp32 {
None
} else {
Some(base_field)
};
let actual_index = if has_index { Some(index_field) } else { None };
let (form, has_disp, disp_size, description) =
match (mod_field, base_is_disp32, has_index) {
(0, _, true) => {
(ModRMForm::BaseIndexScale, false, 0, "[base + index*scale]")
}
(0, false, false) => (ModRMForm::BaseOnly, false, 0, "[base] via SIB"),
(1, _, true) => (
ModRMForm::BaseIndexScaleDisp8,
true,
1,
"[base + index*scale + disp8]",
),
(1, false, false) => {
(ModRMForm::BaseDisp8, true, 1, "[base + disp8] via SIB")
}
(2, _, true) => (
ModRMForm::BaseIndexScaleDisp32,
true,
4,
"[base + index*scale + disp32]",
),
(2, false, false) => {
(ModRMForm::BaseDisp32, true, 4, "[base + disp32] via SIB")
}
_ => (ModRMForm::BaseOnly, false, 0, "[base] via SIB"),
};
entries.push(CompleteModRMEntry {
modrm,
mod_field,
reg_field,
rm_field: 4,
form,
base: actual_base,
index: actual_index,
scale,
has_sib: true,
sib_byte: Some(sib),
has_disp,
disp_size,
description,
});
}
}
}
}
pub fn lookup_modrm(&self, modrm: u8) -> Option<&CompleteModRMEntry> {
self.by_modrm.get(&modrm).and_then(|v| v.first())
}
pub fn entries_by_form(&self, form: ModRMForm) -> &[CompleteModRMEntry] {
self.by_form.get(&form).map(|v| v.as_slice()).unwrap_or(&[])
}
pub fn total_entries(&self) -> usize {
self.entries.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::x86::x86_register_info;
fn make_reg_info() -> X86RegisterInfo {
X86RegisterInfo
}
#[test]
fn test_addr_width_default_data_size() {
assert_eq!(AddrWidth::Addr16.default_data_size(), 2);
assert_eq!(AddrWidth::Addr32.default_data_size(), 4);
assert_eq!(AddrWidth::Addr64.default_data_size(), 8);
}
#[test]
fn test_addr_width_max_disp_bits() {
assert_eq!(AddrWidth::Addr16.max_disp_bits(), 16);
assert_eq!(AddrWidth::Addr32.max_disp_bits(), 32);
assert_eq!(AddrWidth::Addr64.max_disp_bits(), 32);
}
#[test]
fn test_addr_width_rip_relative_support() {
assert!(!AddrWidth::Addr16.supports_rip_relative());
assert!(!AddrWidth::Addr32.supports_rip_relative());
assert!(AddrWidth::Addr64.supports_rip_relative());
}
#[test]
fn test_addr_width_uses_sib() {
assert!(!AddrWidth::Addr16.uses_sib());
assert!(AddrWidth::Addr32.uses_sib());
assert!(AddrWidth::Addr64.uses_sib());
}
#[test]
fn test_addressing_full_new() {
let e = X86AddressingFull::new_x86_64();
assert_eq!(e.addr_width, AddrWidth::Addr64);
let e32 = X86AddressingFull::new_x86_32();
assert_eq!(e32.addr_width, AddrWidth::Addr32);
let e16 = X86AddressingFull::new_x86_16();
assert_eq!(e16.addr_width, AddrWidth::Addr16);
}
#[test]
fn test_addressing_full_with_code_model() {
let e = X86AddressingFull::new_x86_64().with_code_model(CodeModel::Large);
assert_eq!(e.code_model, CodeModel::Large);
}
#[test]
fn test_addressing_full_with_pic() {
let e = X86AddressingFull::new_x86_64().with_pic(false);
assert!(!e.is_pic);
}
#[test]
fn test_addressing_full_with_tls_model() {
let e = X86AddressingFull::new_x86_64().with_tls_model(TlsModel::LocalExec);
assert_eq!(e.tls_model, TlsModel::LocalExec);
}
#[test]
fn test_addressing_full_default() {
let e = X86AddressingFull::default();
assert_eq!(e.addr_width, AddrWidth::Addr64);
assert!(e.is_pic);
assert_eq!(e.tls_model, TlsModel::InitialExec);
}
#[test]
fn test_address_mode_base64() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(m.width, AddrWidth::Addr64);
assert_eq!(m.base, Some(x86_register_info::RAX));
assert_eq!(m.index, None);
assert_eq!(m.displacement, 0);
}
#[test]
fn test_address_mode_base_disp64() {
let m = X86AddressMode::base_disp64(x86_register_info::RBX, 16);
assert_eq!(m.base, Some(x86_register_info::RBX));
assert_eq!(m.displacement, 16);
}
#[test]
fn test_address_mode_full64() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 8);
assert_eq!(m.base, Some(x86_register_info::RBX));
assert_eq!(m.index, Some(x86_register_info::RSI));
assert_eq!(m.scale, 4);
assert_eq!(m.displacement, 8);
}
#[test]
fn test_address_mode_rip_relative() {
let m = X86AddressMode::rip_relative(42);
assert!(m.is_rip_relative);
assert_eq!(m.displacement, 42);
assert_eq!(m.base, None);
}
#[test]
fn test_address_mode_rip_symbol() {
let m = X86AddressMode::rip_symbol("foo", 0);
assert!(m.is_rip_relative);
assert_eq!(m.symbol, Some("foo".to_string()));
}
#[test]
fn test_address_mode_base32() {
let m = X86AddressMode::base32(x86_register_info::EAX);
assert_eq!(m.width, AddrWidth::Addr32);
assert_eq!(m.base, Some(x86_register_info::EAX));
}
#[test]
fn test_address_mode_absolute32() {
let m = X86AddressMode::absolute32(0x1000);
assert!(m.is_absolute_32);
assert_eq!(m.displacement, 0x1000);
}
#[test]
fn test_address_mode_base16() {
let m = X86AddressMode::base16(x86_register_info::BX);
assert_eq!(m.width, AddrWidth::Addr16);
assert_eq!(m.base, Some(x86_register_info::BX));
assert_eq!(m.form_16bit, Some(X86Addr16Form::Bx));
}
#[test]
fn test_address_mode_base_index16() {
let m = X86AddressMode::base_index16(x86_register_info::BX, x86_register_info::SI);
assert_eq!(m.width, AddrWidth::Addr16);
assert_eq!(m.base, Some(x86_register_info::BX));
assert_eq!(m.index, Some(x86_register_info::SI));
assert_eq!(m.form_16bit, Some(X86Addr16Form::BxSi));
}
#[test]
fn test_address_mode_absolute16() {
let m = X86AddressMode::absolute16(0x200);
assert!(m.is_absolute_16);
assert_eq!(m.displacement, 0x200);
assert_eq!(m.form_16bit, Some(X86Addr16Form::Disp16));
}
#[test]
fn test_address_mode_with_segment() {
let m = X86AddressMode::base64(x86_register_info::RAX).with_segment(x86_register_info::FS);
assert_eq!(m.segment, Some(x86_register_info::FS));
}
#[test]
fn test_address_mode_with_frame_relative() {
let m = X86AddressMode::base64(x86_register_info::RBP).with_frame_relative(true);
assert!(m.is_frame_relative);
}
#[test]
fn test_address_mode_with_symbol() {
let m = X86AddressMode::rip_relative(0).with_symbol("my_var");
assert_eq!(m.symbol, Some("my_var".to_string()));
}
#[test]
fn test_address_mode_with_got_relative() {
let m = X86AddressMode::rip_relative(0).with_got_relative(true);
assert!(m.is_got_relative);
}
#[test]
fn test_has_base() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert!(m.has_base());
let n = X86AddressMode::default();
assert!(!n.has_base());
}
#[test]
fn test_has_index() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 2, 0);
assert!(m.has_index());
let n = X86AddressMode::base64(x86_register_info::RAX);
assert!(!n.has_index());
}
#[test]
fn test_fits_disp8() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 42);
assert!(m.fits_disp8());
let n = X86AddressMode::base_disp64(x86_register_info::RAX, 200);
assert!(!n.fits_disp8());
let z = X86AddressMode::base_disp64(x86_register_info::RAX, -128);
assert!(z.fits_disp8());
}
#[test]
fn test_fits_disp32() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 2_147_483_647);
assert!(m.fits_disp32());
let n = X86AddressMode::base_disp64(x86_register_info::RAX, 3_000_000_000i64);
assert!(!n.fits_disp32());
}
#[test]
fn test_fits_disp16() {
let m = X86AddressMode::base16(x86_register_info::BX);
let m2 = X86AddressMode {
displacement: 32767,
..m.clone()
};
assert!(m2.fits_disp16());
let m3 = X86AddressMode {
displacement: 32768,
..m
};
assert!(!m3.fits_disp16());
}
#[test]
fn test_classify_base_only() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(m.classify(), AddressingForm::BaseOnly);
}
#[test]
fn test_classify_base_disp8() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 42);
assert_eq!(m.classify(), AddressingForm::BaseDisp8);
}
#[test]
fn test_classify_base_disp32() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 200);
assert_eq!(m.classify(), AddressingForm::BaseDisp32);
}
#[test]
fn test_classify_rip_relative() {
let m = X86AddressMode::rip_relative(42);
assert_eq!(m.classify(), AddressingForm::RipRelative);
}
#[test]
fn test_classify_absolute16() {
let m = X86AddressMode::absolute16(0x200);
assert_eq!(m.classify(), AddressingForm::Absolute16);
}
#[test]
fn test_classify_absolute32() {
let m = X86AddressMode::absolute32(0x1000);
assert_eq!(m.classify(), AddressingForm::Absolute32);
}
#[test]
fn test_classify_base_index_scale() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
assert_eq!(m.classify(), AddressingForm::BaseIndexScale);
}
#[test]
fn test_classify_base_index_scale_disp8() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 42);
assert_eq!(m.classify(), AddressingForm::BaseIndexScaleDisp8);
}
#[test]
fn test_classify_base_index_scale_disp32() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 200);
assert_eq!(m.classify(), AddressingForm::BaseIndexScaleDisp32);
}
#[test]
fn test_requires_sib_with_index() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
assert!(m.requires_sib());
}
#[test]
fn test_requires_sib_no_index_no_sib() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert!(!m.requires_sib());
}
#[test]
fn test_requires_sib_rsp() {
let m = X86AddressMode::base64(x86_register_info::RSP);
assert!(m.requires_sib());
}
#[test]
fn test_requires_sib_r12() {
let m = X86AddressMode::base64(x86_register_info::R12);
assert!(m.requires_sib());
}
#[test]
fn test_requires_sib_rbp_64bit() {
let m = X86AddressMode::base64(x86_register_info::RBP);
assert!(m.requires_sib());
}
#[test]
fn test_requires_sib_r13_64bit() {
let m = X86AddressMode::base64(x86_register_info::R13);
assert!(m.requires_sib());
}
#[test]
fn test_requires_sib_16bit() {
let m = X86AddressMode::base16(x86_register_info::BX);
assert!(!m.requires_sib());
}
#[test]
fn test_requires_rex_r8() {
let m = X86AddressMode::base64(x86_register_info::R8);
assert!(m.requires_rex());
}
#[test]
fn test_requires_rex_r8_index() {
let m = X86AddressMode::full64(x86_register_info::RAX, x86_register_info::R8, 1, 0);
assert!(m.requires_rex());
}
#[test]
fn test_requires_rex_none_for_low_regs() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert!(!m.requires_rex());
}
#[test]
fn test_encode_modrm_base_only() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(m.encode_modrm(0), 0x00);
}
#[test]
fn test_encode_modrm_rip_relative() {
let m = X86AddressMode::rip_relative(42);
assert_eq!(m.encode_modrm(0), 0x05);
}
#[test]
fn test_encode_modrm_base_disp8() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 42);
assert_eq!(m.encode_modrm(0), 0x40);
}
#[test]
fn test_encode_modrm_base_disp32() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 200);
assert_eq!(m.encode_modrm(0), 0x80);
}
#[test]
fn test_encode_modrm_with_reg_field() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(m.encode_modrm(1), 0x08);
}
#[test]
fn test_encode_sib() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
let sib = m.encode_sib();
assert_eq!((sib >> 6) & 0x3, 2); assert_eq!((sib >> 3) & 0x7, x86_register_info::RSI & 0x7); assert_eq!(sib & 0x7, x86_register_info::RBX & 0x7); }
#[test]
fn test_encode_sib_no_index() {
let m = X86AddressMode::base64(x86_register_info::RSP);
let sib = m.encode_sib();
assert_eq!(sib, 0x24);
}
#[test]
fn test_displacement_size_zero() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(m.displacement_size(), 0);
}
#[test]
fn test_displacement_size_8() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 42);
assert_eq!(m.displacement_size(), 1);
}
#[test]
fn test_displacement_size_32() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 200);
assert_eq!(m.displacement_size(), 4);
}
#[test]
fn test_encode_displacement_zero() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(m.encode_displacement(), Vec::<u8>::new());
}
#[test]
fn test_encode_displacement_8() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 42);
assert_eq!(m.encode_displacement(), vec![42]);
}
#[test]
fn test_encode_displacement_32() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 0x12345678);
let expected = vec![0x78, 0x56, 0x34, 0x12];
assert_eq!(m.encode_displacement(), expected);
}
#[test]
fn test_encode_displacement_negative() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, -1);
let expected = vec![0xFF, 0xFF, 0xFF, 0xFF];
assert_eq!(m.encode_displacement(), expected);
}
#[test]
fn test_encode_full() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(m.encode_full(0), vec![0x00]);
let m2 = X86AddressMode::base_disp64(x86_register_info::RAX, 42);
assert_eq!(m2.encode_full(0), vec![0x40, 0x2A]);
let m3 = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
let result = m3.encode_full(0);
assert_eq!(result.len(), 2); assert_eq!(result[0] & 0xC7, 0x04); }
#[test]
fn test_complexity_simple() {
let m = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(m.complexity_score(), 1);
}
#[test]
fn test_complexity_with_disp8() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 42);
assert_eq!(m.complexity_score(), 2); }
#[test]
fn test_complexity_full() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 200);
let score = m.complexity_score();
assert!(score >= 5); }
#[test]
fn test_to_mem_operand() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 8);
let mem = m.to_mem_operand();
assert_eq!(mem.base, x86_register_info::RBX);
assert_eq!(mem.index, x86_register_info::RSI);
assert_eq!(mem.scale, 4);
assert_eq!(mem.displacement, 8);
assert_eq!(mem.segment, 0);
}
#[test]
fn test_from_mem_operand() {
let mem = X86MemOperand::full(x86_register_info::RBX, x86_register_info::RSI, 4, 8);
let m = X86AddressMode::from_mem_operand(&mem, AddrWidth::Addr64);
assert_eq!(m.base, Some(x86_register_info::RBX));
assert_eq!(m.index, Some(x86_register_info::RSI));
assert_eq!(m.scale, 4);
assert_eq!(m.displacement, 8);
}
#[test]
fn test_addr16_form_rm_field() {
assert_eq!(X86Addr16Form::BxSi.rm_field(), 0);
assert_eq!(X86Addr16Form::BxDi.rm_field(), 1);
assert_eq!(X86Addr16Form::BpSi.rm_field(), 2);
assert_eq!(X86Addr16Form::BpDi.rm_field(), 3);
assert_eq!(X86Addr16Form::Si.rm_field(), 4);
assert_eq!(X86Addr16Form::Di.rm_field(), 5);
assert_eq!(X86Addr16Form::Bp.rm_field(), 6);
assert_eq!(X86Addr16Form::Bx.rm_field(), 7);
}
#[test]
fn test_addr16_form_mod_field() {
assert_eq!(X86Addr16Form::BxSi.mod_field(), Mod00);
assert_eq!(X86Addr16Form::Bp.mod_field(), Mod01);
assert_eq!(X86Addr16Form::BxDisp8.mod_field(), Mod01);
assert_eq!(X86Addr16Form::BxDisp16.mod_field(), Mod10);
}
#[test]
fn test_addr16_form_has_displacement() {
assert!(!X86Addr16Form::BxSi.has_displacement());
assert!(X86Addr16Form::Bp.has_displacement());
assert!(X86Addr16Form::BxDisp8.has_displacement());
assert!(X86Addr16Form::Disp16.has_displacement());
}
#[test]
fn test_addr16_form_disp_size() {
assert_eq!(X86Addr16Form::BxSi.disp_size(), 0);
assert_eq!(X86Addr16Form::Bp.disp_size(), 1);
assert_eq!(X86Addr16Form::BxDisp8.disp_size(), 1);
assert_eq!(X86Addr16Form::BxDisp16.disp_size(), 2);
assert_eq!(X86Addr16Form::Disp16.disp_size(), 2);
}
#[test]
fn test_addr16_form_effective_base() {
assert_eq!(
X86Addr16Form::BxSi.effective_base(),
Some(x86_register_info::BX)
);
assert_eq!(
X86Addr16Form::BpSi.effective_base(),
Some(x86_register_info::BP)
);
assert_eq!(
X86Addr16Form::Si.effective_base(),
Some(x86_register_info::SI)
);
assert_eq!(
X86Addr16Form::Di.effective_base(),
Some(x86_register_info::DI)
);
assert_eq!(
X86Addr16Form::Bx.effective_base(),
Some(x86_register_info::BX)
);
assert_eq!(X86Addr16Form::Disp16.effective_base(), None);
}
#[test]
fn test_addr16_form_effective_index() {
assert_eq!(
X86Addr16Form::BxSi.effective_index(),
Some(x86_register_info::SI)
);
assert_eq!(
X86Addr16Form::BxDi.effective_index(),
Some(x86_register_info::DI)
);
assert_eq!(X86Addr16Form::Si.effective_index(), None);
assert_eq!(X86Addr16Form::Bx.effective_index(), None);
}
#[test]
fn test_addr16_form_name() {
assert_eq!(X86Addr16Form::BxSi.name(), "[BX+SI]");
assert_eq!(X86Addr16Form::Bx.name(), "[BX]");
assert_eq!(X86Addr16Form::Disp16.name(), "[disp16]");
}
#[test]
fn test_addr16_form_decode() {
assert_eq!(X86Addr16Form::decode(0x00), X86Addr16Form::BxSi);
assert_eq!(X86Addr16Form::decode(0x47), X86Addr16Form::BxDisp8);
assert_eq!(X86Addr16Form::decode(0x8E), X86Addr16Form::BpDisp16);
assert_eq!(X86Addr16Form::decode(0x06), X86Addr16Form::Disp16);
}
#[test]
fn test_addr16_form_decode_all() {
let mut seen = HashSet::new();
for rm in 0..8u8 {
let modrm = (Mod00 << 6) | rm;
seen.insert(X86Addr16Form::decode(modrm));
}
for rm in 0..8u8 {
let modrm = (Mod01 << 6) | rm;
seen.insert(X86Addr16Form::decode(modrm));
}
for rm in 0..8u8 {
let modrm = (Mod10 << 6) | rm;
seen.insert(X86Addr16Form::decode(modrm));
}
assert_eq!(seen.len(), 24);
}
#[test]
fn test_addressing_form_encoding_overhead() {
assert_eq!(AddressingForm::BaseOnly.encoding_overhead(), 1);
assert_eq!(AddressingForm::BaseDisp8.encoding_overhead(), 2);
assert_eq!(AddressingForm::BaseDisp32.encoding_overhead(), 5);
assert_eq!(AddressingForm::BaseIndexScale.encoding_overhead(), 2);
assert_eq!(AddressingForm::BaseIndexScaleDisp8.encoding_overhead(), 3);
assert_eq!(AddressingForm::BaseIndexScaleDisp32.encoding_overhead(), 6);
assert_eq!(AddressingForm::RipRelative.encoding_overhead(), 5);
assert_eq!(AddressingForm::Absolute32.encoding_overhead(), 5);
}
#[test]
fn test_addressing_form_uses_sib() {
assert!(!AddressingForm::BaseOnly.uses_sib());
assert!(AddressingForm::BaseIndexScale.uses_sib());
assert!(AddressingForm::BaseIndexScaleDisp8.uses_sib());
assert!(!AddressingForm::RipRelative.uses_sib());
}
#[test]
fn test_builder_new() {
let b = X86AddressingModeBuilder::new64();
assert_eq!(b.build().width, AddrWidth::Addr64);
}
#[test]
fn test_builder_simple() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RAX)
.build();
assert_eq!(m.base, Some(x86_register_info::RAX));
assert_eq!(m.displacement, 0);
}
#[test]
fn test_builder_full() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBX)
.with_index(x86_register_info::RSI, 4)
.with_disp(16)
.build();
assert_eq!(m.base, Some(x86_register_info::RBX));
assert_eq!(m.index, Some(x86_register_info::RSI));
assert_eq!(m.scale, 4);
assert_eq!(m.displacement, 16);
}
#[test]
fn test_builder_canonicalize_scale() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBX)
.with_index(x86_register_info::RSI, 3)
.build();
assert_eq!(m.scale, 4);
let m2 = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBX)
.with_index(x86_register_info::RSI, 0)
.build();
assert_eq!(m2.scale, 1);
}
#[test]
fn test_builder_canonicalize_zero_index_scale() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RAX)
.with_index_opt(Some(x86_register_info::RSI), 0)
.build();
assert_eq!(m.index, None);
assert_eq!(m.scale, 0);
}
#[test]
fn test_builder_rip_relative() {
let m = X86AddressingModeBuilder::new64()
.with_rip_relative(true)
.with_disp(42)
.build();
assert!(m.is_rip_relative);
assert_eq!(m.base, None);
assert_eq!(m.displacement, 42);
}
#[test]
fn test_builder_segment() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RAX)
.with_segment(x86_register_info::FS)
.build();
assert_eq!(m.segment, Some(x86_register_info::FS));
}
#[test]
fn test_builder_symbol() {
let m = X86AddressingModeBuilder::new64()
.with_symbol("test_var")
.with_rip_relative(true)
.build();
assert_eq!(m.symbol, Some("test_var".to_string()));
}
#[test]
fn test_builder_got_relative() {
let m = X86AddressingModeBuilder::new64()
.with_got_relative(true)
.with_rip_relative(true)
.build();
assert!(m.is_got_relative);
}
#[test]
fn test_builder_frame_relative() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBP)
.with_frame_relative(true)
.build();
assert!(m.is_frame_relative);
}
#[test]
fn test_builder_complexity_score() {
let score_simple = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RAX)
.complexity_score();
assert_eq!(score_simple, 1);
let score_complex = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBX)
.with_index(x86_register_info::RSI, 4)
.with_disp(200)
.complexity_score();
assert!(score_complex > 4);
}
#[test]
fn test_builder_select_best_form() {
let form_simple = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RAX)
.select_best_form();
assert_eq!(form_simple, AddressingForm::BaseOnly);
let form_rip = X86AddressingModeBuilder::new64()
.with_rip_relative(true)
.with_disp(42)
.select_best_form();
assert_eq!(form_rip, AddressingForm::RipRelative);
let form_complex = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBX)
.with_index(x86_register_info::RSI, 4)
.with_disp(42)
.select_best_form();
assert_eq!(form_complex, AddressingForm::BaseIndexScaleDisp8);
}
#[test]
fn test_builder_determine_16bit_form() {
use crate::x86::x86_register_info::{BP, BX, DI, SI};
let b = X86AddressingModeBuilder::new16()
.with_base(BX)
.with_index(SI, 1);
let m = b.build();
assert_eq!(m.form_16bit, Some(X86Addr16Form::BxSi));
let b2 = X86AddressingModeBuilder::new16()
.with_base(BX)
.with_disp(42);
let m2 = b2.build();
assert_eq!(m2.form_16bit, Some(X86Addr16Form::BxDisp8));
let b3 = X86AddressingModeBuilder::new16().with_disp(0x200);
let m3 = b3.build();
assert_eq!(m3.form_16bit, Some(X86Addr16Form::Disp16));
assert!(m3.is_absolute_16);
}
#[test]
fn test_builder_add_disp() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RAX)
.with_disp(10)
.add_disp(5)
.build();
assert_eq!(m.displacement, 15);
}
#[test]
fn test_builder_add_disp_overflow() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RAX)
.with_disp(i64::MAX)
.add_disp(1)
.build();
assert_eq!(m.displacement, i64::MIN);
}
#[test]
fn test_builder_fold_displacement_into_base() {
let m = X86AddressingModeBuilder::new64()
.with_disp(16)
.fold_displacement_into_base(x86_register_info::RBX)
.build();
assert_eq!(m.base, Some(x86_register_info::RBX));
assert_eq!(m.displacement, 16);
}
#[test]
fn test_lower_simple_load_8() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = X86AddressMode::base64(x86_register_info::RAX);
let result = lower.lower_simple_load(&addr, 8);
assert_eq!(result.size, 8);
assert_eq!(result.address_mode.base, Some(x86_register_info::RAX));
}
#[test]
fn test_lower_simple_load_4() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = X86AddressMode::base64(x86_register_info::RBX);
let result = lower.lower_simple_load(&addr, 4);
assert_eq!(result.size, 4);
}
#[test]
fn test_lower_load_signed_ext() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = X86AddressMode::base64(x86_register_info::RAX);
let result = lower.lower_load(&addr, 4, true, Some(8));
assert_eq!(result.opcode, X86Opcode::MOVSXD);
}
#[test]
fn test_lower_load_zero_ext() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = X86AddressMode::base64(x86_register_info::RAX);
let result = lower.lower_load(&addr, 2, false, Some(4));
assert_eq!(result.opcode, X86Opcode::MOVZX);
}
#[test]
fn test_lower_store_int() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = X86AddressMode::base64(x86_register_info::RAX);
let result = lower.lower_store(&addr, 8, false);
assert_eq!(result.size, 8);
assert_eq!(result.address_mode.base, Some(x86_register_info::RAX));
}
#[test]
fn test_lower_store_float() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = X86AddressMode::base64(x86_register_info::RAX);
let result = lower.lower_store(&addr, 4, true);
assert_eq!(result.opcode, X86Opcode::MOVSS);
}
#[test]
fn test_lower_store_double() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = X86AddressMode::base64(x86_register_info::RAX);
let result = lower.lower_store(&addr, 8, true);
assert_eq!(result.opcode, X86Opcode::MOVSD);
}
#[test]
fn test_lower_frame_index_with_fp() {
let mut lower = X86MemoryLowering::new_x86_64_pic();
lower.use_frame_pointer = true;
let addr = lower.lower_frame_index(0, 8);
assert_eq!(addr.base, Some(x86_register_info::RBP));
assert!(addr.is_frame_relative);
}
#[test]
fn test_lower_frame_index_without_fp() {
let mut lower = X86MemoryLowering::new_x86_64_pic();
lower.use_frame_pointer = false;
let addr = lower.lower_frame_index(0, 8);
assert_eq!(addr.base, Some(x86_register_info::RSP));
}
#[test]
fn test_lower_frame_index_32bit() {
let lower_32 = X86MemoryLowering::new_x86_32();
let addr = lower_32.lower_frame_index(0, 4);
assert!(
addr.base == Some(x86_register_info::EBP) || addr.base == Some(x86_register_info::ESP)
);
}
#[test]
fn test_lower_global_address_64bit_pic() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = lower.lower_global_address("my_var", 0, false);
assert!(addr.is_rip_relative || addr.is_got_relative);
assert!(addr.symbol.is_some());
}
#[test]
fn test_lower_global_address_64bit_non_pic() {
let lower = X86MemoryLowering::new_x86_64_non_pic();
let addr = lower.lower_global_address("my_var", 0, false);
assert!(addr.is_rip_relative);
}
#[test]
fn test_lower_global_address_32bit() {
let lower = X86MemoryLowering::new_x86_32();
let addr = lower.lower_global_address("my_var", 0, false);
assert!(addr.is_absolute_32 || addr.is_got_relative);
}
#[test]
fn test_lower_global_got() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = lower.lower_global_got("my_var");
assert!(addr.is_rip_relative);
assert!(addr.is_got_relative);
assert!(addr.symbol.unwrap().contains("GOTPCREL"));
}
#[test]
fn test_lower_tls_local_exec_64() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = lower.lower_tls_address("tls_var", 8, true);
assert!(addr.segment == Some(x86_register_info::FS));
assert_eq!(addr.displacement, 8);
}
#[test]
fn test_lower_tls_local_exec_32() {
let lower = X86MemoryLowering::new_x86_32();
let addr = lower.lower_tls_address("tls_var", 4, true);
assert!(addr.segment == Some(x86_register_info::GS));
}
#[test]
fn test_lower_tls_initial_exec() {
let mut engine = X86AddressingFull::new_x86_64();
engine.tls_model = TlsModel::InitialExec;
let lower = X86MemoryLowering::new(engine);
let addr = lower.lower_tls_address("tls_var", 0, false);
assert!(addr.is_got_relative);
assert!(addr.symbol.is_some());
}
#[test]
fn test_lower_tls_general_dynamic() {
let mut engine = X86AddressingFull::new_x86_64();
engine.tls_model = TlsModel::GeneralDynamic;
let lower = X86MemoryLowering::new(engine);
let addr = lower.lower_tls_address("tls_var", 0, false);
assert!(addr.symbol.as_ref().map_or(false, |s| s.contains("TLSGD")));
assert!(addr.is_rip_relative);
}
#[test]
fn test_lower_constant_pool_64() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = lower.lower_constant_pool(0, 0);
assert!(addr.is_rip_relative);
assert!(addr.symbol.unwrap().contains("LCPI"));
}
#[test]
fn test_lower_constant_pool_32() {
let lower = X86MemoryLowering::new_x86_32();
let addr = lower.lower_constant_pool(0, 0);
assert!(addr.is_absolute_32);
assert!(addr.symbol.unwrap().contains("LCPI"));
}
#[test]
fn test_lower_jump_table() {
let lower = X86MemoryLowering::new_x86_64_pic();
let addr = lower.lower_jump_table(0, 0);
assert!(addr.symbol.unwrap().contains("LJTI"));
}
#[test]
fn test_constant_pool_empty() {
let pool = ConstantPool::new();
assert!(pool.is_empty());
assert_eq!(pool.len(), 0);
}
#[test]
fn test_constant_pool_add_entry() {
let mut pool = ConstantPool::new();
let idx = pool.add_entry(8, 4, false);
assert_eq!(idx, 0);
assert_eq!(pool.len(), 1);
assert!(!pool.is_empty());
}
#[test]
fn test_constant_pool_get_address() {
let mut pool = ConstantPool::new();
let idx = pool.add_entry(8, 8, true);
let addr = pool.get_address(idx).unwrap();
assert!(addr.is_rip_relative);
assert!(addr.symbol.unwrap().contains("LCPI0"));
}
#[test]
fn test_try_form_lea_add_reg() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let result = opt.try_form_lea(
X86Opcode::ADD,
x86_register_info::RAX,
x86_register_info::RBX,
None,
);
assert!(result.is_some());
let addr = result.unwrap();
assert_eq!(addr.base, Some(x86_register_info::RAX));
assert_eq!(addr.index, Some(x86_register_info::RBX));
}
#[test]
fn test_try_form_lea_add_imm() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let result = opt.try_form_lea(X86Opcode::ADD, x86_register_info::RAX, 0, Some(16));
assert!(result.is_some());
let addr = result.unwrap();
assert_eq!(addr.base, Some(x86_register_info::RAX));
assert_eq!(addr.displacement, 16);
}
#[test]
fn test_try_form_lea_sub_imm() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let result = opt.try_form_lea(X86Opcode::SUB, x86_register_info::RAX, 0, Some(8));
assert!(result.is_some());
let addr = result.unwrap();
assert_eq!(addr.displacement, -8);
}
#[test]
fn test_try_form_lea_no_lea_for_mul() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let result = opt.try_form_lea(
X86Opcode::MUL,
x86_register_info::RAX,
x86_register_info::RBX,
None,
);
assert!(result.is_none());
}
#[test]
fn test_should_use_lea() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode::base_disp64(x86_register_info::RAX, 16);
assert!(!opt.should_use_lea(&addr, 1));
assert!(opt.should_use_lea(&addr, 2));
}
#[test]
fn test_compute_sink_distance_simple() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode::base64(x86_register_info::RAX);
let dist = opt.compute_sink_distance(&addr, 2);
assert_eq!(dist, 4); }
#[test]
fn test_compute_sink_distance_complex() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 200);
let dist = opt.compute_sink_distance(&addr, 2);
assert_eq!(dist, 0); }
#[test]
fn test_can_sink_to() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode::base64(x86_register_info::RAX);
assert!(opt.can_sink_to(&addr, true));
assert!(!opt.can_sink_to(&addr, false)); }
#[test]
fn test_factor_common_base() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addrs = vec![
X86AddressMode::base_disp64(x86_register_info::RBX, 0),
X86AddressMode::base_disp64(x86_register_info::RBX, 4),
X86AddressMode::base_disp64(x86_register_info::RBX, 8),
];
let (common, _) = opt.factor_common_base(&addrs);
assert_eq!(common, Some(x86_register_info::RBX));
}
#[test]
fn test_factor_common_base_different() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addrs = vec![
X86AddressMode::base_disp64(x86_register_info::RAX, 0),
X86AddressMode::base_disp64(x86_register_info::RBX, 4),
X86AddressMode::base_disp64(x86_register_info::RCX, 8),
];
let (common, _) = opt.factor_common_base(&addrs);
assert_eq!(common, None); }
#[test]
fn test_factor_common_index() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addrs = vec![
X86AddressMode::full64(x86_register_info::RAX, x86_register_info::RSI, 4, 0),
X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 4),
X86AddressMode::full64(x86_register_info::RCX, x86_register_info::RSI, 4, 8),
];
let (common, _) = opt.factor_common_index(&addrs);
assert_eq!(common, Some((x86_register_info::RSI, 4)));
}
#[test]
fn test_is_loop_invariant_frame() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode {
width: AddrWidth::Addr64,
base: Some(x86_register_info::RBP),
is_frame_relative: true,
..Default::default()
};
assert!(opt.is_loop_invariant(&addr));
}
#[test]
fn test_is_loop_invariant_global() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode::rip_relative(0).with_symbol("foo");
assert!(opt.is_loop_invariant(&addr));
}
#[test]
fn test_is_loop_invariant_rsp() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode::base64(x86_register_info::RSP);
assert!(opt.is_loop_invariant(&addr));
}
#[test]
fn test_is_not_loop_invariant_gpr() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode::base64(x86_register_info::RAX);
assert!(!opt.is_loop_invariant(&addr));
}
#[test]
fn test_optimize_zero_disp() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode::base64(x86_register_info::RAX);
let optimized = opt.optimize(&addr);
assert_eq!(optimized.displacement, 0);
}
#[test]
fn test_select_best_smaller_encoding() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let a = X86AddressMode::base64(x86_register_info::RAX); let b = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0); let best = opt.select_best(&a, &b);
assert_eq!(best.classify(), AddressingForm::BaseOnly);
}
#[test]
fn test_segment_default_for_base() {
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
assert_eq!(
handler.default_segment_for_base(x86_register_info::RSP),
x86_register_info::SS
);
assert_eq!(
handler.default_segment_for_base(x86_register_info::RBP),
x86_register_info::SS
);
assert_eq!(
handler.default_segment_for_base(x86_register_info::RAX),
x86_register_info::DS
);
}
#[test]
fn test_segment_override_prefix() {
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
let addr =
X86AddressMode::base64(x86_register_info::RAX).with_segment(x86_register_info::FS);
assert_eq!(handler.segment_override_prefix(&addr), Some(0x64));
let addr_gs =
X86AddressMode::base64(x86_register_info::RAX).with_segment(x86_register_info::GS);
assert_eq!(handler.segment_override_prefix(&addr_gs), Some(0x65));
}
#[test]
fn test_needs_segment_override() {
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
let addr =
X86AddressMode::base64(x86_register_info::RAX).with_segment(x86_register_info::DS);
assert!(!handler.needs_segment_override(&addr));
let addr_fs =
X86AddressMode::base64(x86_register_info::RAX).with_segment(x86_register_info::FS);
assert!(handler.needs_segment_override(&addr_fs));
}
#[test]
fn test_encode_decode_segment_prefix() {
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
let prefixes = [0x2E, 0x36, 0x3E, 0x26, 0x64, 0x65];
for p in &prefixes {
let seg = handler.decode_segment_prefix(*p);
assert!(seg.is_some());
let encoded = handler.encode_segment_prefix(seg.unwrap());
assert_eq!(encoded, *p);
}
}
#[test]
fn test_tls_segment_reg() {
let engine64 = X86AddressingFull::new_x86_64();
let handler64 = X86SegmentHandling::new(engine64);
assert_eq!(handler64.tls_segment_reg(), x86_register_info::FS);
let engine32 = X86AddressingFull::new_x86_32();
let handler32 = X86SegmentHandling::new(engine32);
assert_eq!(handler32.tls_segment_reg(), x86_register_info::GS);
}
#[test]
fn test_create_tls_address() {
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
let addr = handler.create_tls_address(16);
assert_eq!(addr.segment, Some(x86_register_info::FS));
assert_eq!(addr.displacement, 16);
}
#[test]
fn test_is_stack_access() {
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
let stack_addr = X86AddressMode::base64(x86_register_info::RSP);
assert!(handler.is_stack_access(&stack_addr));
let data_addr = X86AddressMode::base64(x86_register_info::RAX);
assert!(!handler.is_stack_access(&data_addr));
}
#[test]
fn test_generate_prefixes() {
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
let addr = X86AddressMode::base64(x86_register_info::RAX);
let prefixes = handler.generate_prefixes(&addr);
assert_eq!(prefixes, vec![0x48]);
let addr_r8 = X86AddressMode::base64(x86_register_info::R8);
let prefixes_r8 = handler.generate_prefixes(&addr_r8);
assert!(prefixes_r8.contains(&0x49)); }
#[test]
fn test_available_segments() {
let engine64 = X86AddressingFull::new_x86_64();
let handler64 = X86SegmentHandling::new(engine64);
let segs64 = handler64.get_available_segments();
assert_eq!(segs64.len(), 2);
let engine32 = X86AddressingFull::new_x86_32();
let handler32 = X86SegmentHandling::new(engine32);
let segs32 = handler32.get_available_segments();
assert_eq!(segs32.len(), 6); }
#[test]
fn test_matcher_decode_64bit_rip_relative() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr64);
let disp = vec![0x78, 0x56, 0x34, 0x12]; let addr = matcher.decode(0x05, None, &disp, false, false, false);
assert!(addr.is_rip_relative);
assert_eq!(addr.displacement, 0x12345678);
}
#[test]
fn test_matcher_decode_32bit_absolute() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr32);
let disp = vec![0x00, 0x10, 0x00, 0x00]; let addr = matcher.decode(0x05, None, &disp, false, false, false);
assert!(addr.is_absolute_32);
assert_eq!(addr.displacement, 0x1000);
}
#[test]
fn test_matcher_decode_sib() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr64);
let sib: u8 = (2 << 6) | (6 << 3) | 3; let addr = matcher.decode(0x04, Some(sib), &[], false, false, false);
assert_eq!(addr.base, Some(3)); assert_eq!(addr.index, Some(6)); assert_eq!(addr.scale, 4);
}
#[test]
fn test_matcher_decode_sib_no_index() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr64);
let sib: u8 = (0 << 6) | (4 << 3) | 4; let addr = matcher.decode(0x04, Some(sib), &[], false, false, false);
assert_eq!(addr.base, Some(4)); assert_eq!(addr.index, None);
}
#[test]
fn test_matcher_decode_16bit() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr16);
let addr = matcher.decode(0x47, None, &[0x10], false, false, false);
assert_eq!(addr.form_16bit, Some(X86Addr16Form::BxDisp8));
assert_eq!(addr.displacement, 16);
}
#[test]
fn test_matcher_decode_16bit_disp16() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr16);
let disp: Vec<u8> = vec![0x00, 0x20]; let addr = matcher.decode(0x06, None, &disp, false, false, false);
assert!(addr.is_absolute_16);
assert_eq!(addr.displacement, 0x2000);
}
#[test]
fn test_generate_all_modrm_combinations_64() {
let entries = generate_all_modrm_combinations_32_64(AddrWidth::Addr64);
assert!(entries.len() > 50);
let rip_entries: Vec<_> = entries
.iter()
.filter(|e| e.form == ModRMForm::RIPRelative)
.collect();
assert!(!rip_entries.is_empty());
}
#[test]
fn test_generate_all_modrm_combinations_32() {
let entries = generate_all_modrm_combinations_32_64(AddrWidth::Addr32);
assert!(entries.len() > 50);
let abs_entries: Vec<_> = entries
.iter()
.filter(|e| e.form == ModRMForm::Absolute && e.modrm == 0x05)
.collect();
assert!(!abs_entries.is_empty());
}
#[test]
fn test_generate_all_sib_combinations() {
let entries = generate_all_sib_combinations();
assert_eq!(entries.len(), 256);
let sibs: HashSet<u8> = entries.iter().map(|e| e.sib).collect();
assert_eq!(sibs.len(), 256);
}
#[test]
fn test_sib_entry_invalid() {
let entries = generate_all_sib_combinations();
let invalid: Vec<_> = entries.iter().filter(|e| !e.is_valid).collect();
assert!(!invalid.is_empty());
}
#[test]
fn test_selector_select_base_disp() {
let engine = X86AddressingFull::new_x86_64();
let selector = AddressModeSelector::new(engine);
let addr = selector.select_base_disp(x86_register_info::RAX, 42);
assert_eq!(addr.base, Some(x86_register_info::RAX));
assert_eq!(addr.displacement, 42);
}
#[test]
fn test_selector_select_full() {
let engine = X86AddressingFull::new_x86_64();
let selector = AddressModeSelector::new(engine);
let addr = selector.select_full(x86_register_info::RBX, x86_register_info::RSI, 4, 16);
assert_eq!(addr.base, Some(x86_register_info::RBX));
assert_eq!(addr.index, Some(x86_register_info::RSI));
assert_eq!(addr.scale, 4);
}
#[test]
fn test_selector_select_rip_relative() {
let engine = X86AddressingFull::new_x86_64();
let selector = AddressModeSelector::new(engine);
let addr = selector.select_rip_relative(42);
assert!(addr.is_rip_relative);
}
#[test]
fn test_selector_select_absolute_64() {
let engine = X86AddressingFull::new_x86_64();
let selector = AddressModeSelector::new(engine);
let addr = selector.select_absolute(0x1000);
assert!(addr.is_rip_relative);
}
#[test]
fn test_selector_select_absolute_32() {
let engine = X86AddressingFull::new_x86_32();
let selector = AddressModeSelector::new(engine);
let addr = selector.select_absolute(0x1000);
assert!(addr.is_absolute_32);
}
#[test]
fn test_selector_select_16bit_base() {
let engine = X86AddressingFull::new_x86_16();
let selector = AddressModeSelector::new(engine);
let addr = selector.select_16bit(Some(x86_register_info::BX), None, 0);
assert_eq!(addr.form_16bit, Some(X86Addr16Form::Bx));
}
#[test]
fn test_selector_select_16bit_dual() {
let engine = X86AddressingFull::new_x86_16();
let selector = AddressModeSelector::new(engine);
let addr =
selector.select_16bit(Some(x86_register_info::BX), Some(x86_register_info::SI), 0);
assert_eq!(addr.form_16bit, Some(X86Addr16Form::BxSi));
}
#[test]
fn test_selector_select_16bit_with_disp() {
let engine = X86AddressingFull::new_x86_16();
let selector = AddressModeSelector::new(engine);
let addr = selector.select_16bit(Some(x86_register_info::BX), None, 42);
assert_eq!(addr.form_16bit, Some(X86Addr16Form::BxDisp8));
}
#[test]
fn test_selector_select_16bit_disp16_absolute() {
let engine = X86AddressingFull::new_x86_16();
let selector = AddressModeSelector::new(engine);
let addr = selector.select_16bit(None, None, 0x500);
assert_eq!(addr.form_16bit, Some(X86Addr16Form::Disp16));
assert!(addr.is_absolute_16);
}
#[test]
fn test_is_valid_base_reg_64() {
assert!(is_valid_base_reg(x86_register_info::RAX, AddrWidth::Addr64));
assert!(is_valid_base_reg(x86_register_info::R8, AddrWidth::Addr64));
assert!(is_valid_base_reg(x86_register_info::R15, AddrWidth::Addr64));
assert!(!is_valid_base_reg(
x86_register_info::XMM0,
AddrWidth::Addr64
));
}
#[test]
fn test_is_valid_base_reg_32() {
assert!(is_valid_base_reg(x86_register_info::EAX, AddrWidth::Addr32));
assert!(is_valid_base_reg(x86_register_info::R8D, AddrWidth::Addr32));
}
#[test]
fn test_is_valid_base_reg_16() {
assert!(is_valid_base_reg(x86_register_info::BX, AddrWidth::Addr16));
assert!(is_valid_base_reg(x86_register_info::BP, AddrWidth::Addr16));
assert!(is_valid_base_reg(x86_register_info::SI, AddrWidth::Addr16));
assert!(is_valid_base_reg(x86_register_info::DI, AddrWidth::Addr16));
assert!(!is_valid_base_reg(x86_register_info::AX, AddrWidth::Addr16));
}
#[test]
fn test_is_valid_index_reg_64() {
assert!(is_valid_index_reg(
x86_register_info::RAX,
AddrWidth::Addr64
));
assert!(!is_valid_index_reg(
x86_register_info::RSP,
AddrWidth::Addr64
));
assert!(is_valid_index_reg(x86_register_info::R8, AddrWidth::Addr64));
}
#[test]
fn test_is_valid_index_reg_16() {
assert!(is_valid_index_reg(x86_register_info::SI, AddrWidth::Addr16));
assert!(is_valid_index_reg(x86_register_info::DI, AddrWidth::Addr16));
assert!(!is_valid_index_reg(
x86_register_info::BX,
AddrWidth::Addr16
));
}
#[test]
fn test_is_valid_scale() {
assert!(is_valid_scale(1));
assert!(is_valid_scale(2));
assert!(is_valid_scale(4));
assert!(is_valid_scale(8));
assert!(!is_valid_scale(3));
assert!(!is_valid_scale(0));
assert!(!is_valid_scale(16));
}
#[test]
fn test_compute_effective_address() {
let result = compute_effective_address(Some(0x1000), Some(0x8), 4, 16);
assert_eq!(result, 0x1000 + (0x8 * 4) + 16);
let result_no_base = compute_effective_address(None, Some(0x10), 8, 0x100);
assert_eq!(result_no_base, 0 + (0x10 * 8) + 0x100);
let result_simple = compute_effective_address(Some(0x1000), None, 0, 0);
assert_eq!(result_simple, 0x1000);
}
#[test]
fn test_are_addresses_equal() {
let a = X86AddressMode::base_disp64(x86_register_info::RAX, 16);
let b = X86AddressMode::base_disp64(x86_register_info::RAX, 16);
assert!(are_addresses_equal(&a, &b));
let c = X86AddressMode::base_disp64(x86_register_info::RAX, 32);
assert!(!are_addresses_equal(&a, &c));
}
#[test]
fn test_address_difference() {
let a = X86AddressMode::base_disp64(x86_register_info::RAX, 0);
let b = X86AddressMode::base_disp64(x86_register_info::RAX, 16);
assert_eq!(address_difference(&a, &b), Some(16));
let c = X86AddressMode::base_disp64(x86_register_info::RBX, 16);
assert_eq!(address_difference(&a, &c), None); }
#[test]
fn test_merge_addresses() {
let a = X86AddressMode::base_disp64(x86_register_info::RBX, 8);
let b = X86AddressMode::base_disp64(x86_register_info::RBX, 16);
let result = merge_addresses(&a, &b);
assert!(result.is_some());
let (common, off_a, off_b) = result.unwrap();
assert_eq!(common.displacement, 8);
assert_eq!(off_a, 0);
assert_eq!(off_b, 8);
}
#[test]
fn test_merge_addresses_different_base() {
let a = X86AddressMode::base_disp64(x86_register_info::RAX, 8);
let b = X86AddressMode::base_disp64(x86_register_info::RBX, 16);
let result = merge_addresses(&a, &b);
assert!(result.is_none());
}
#[test]
fn test_encode_addressing_mode_simple() {
let addr = X86AddressMode::base64(x86_register_info::RAX);
let bytes = encode_addressing_mode(&addr, 0);
assert!(bytes.contains(&0x00)); }
#[test]
fn test_encode_addressing_mode_with_sib() {
let addr = X86AddressMode::base64(x86_register_info::RSP);
let bytes = encode_addressing_mode(&addr, 0);
assert!(bytes.len() >= 3);
}
#[test]
fn test_decode_addressing_mode_roundtrip() {
let original = X86AddressMode::base_disp64(x86_register_info::RAX, 42);
let encoded = encode_addressing_mode(&original, 0);
let (decoded, consumed) =
decode_addressing_mode(&encoded, 1, AddrWidth::Addr64, true, false, false).unwrap();
assert!(consumed > 0);
assert_eq!(decoded.base, original.base);
assert_eq!(decoded.displacement, original.displacement);
}
#[test]
fn test_encode_16bit_modrm() {
let modrm = encode_16bit_modrm(X86Addr16Form::BxSi, 0);
assert_eq!(modrm, (Mod00 << 6) | 0);
let modrm2 = encode_16bit_modrm(X86Addr16Form::BxDisp8, 1);
assert_eq!(modrm2, (Mod01 << 6) | (1 << 3) | 7);
}
#[test]
fn test_format_intel_simple() {
let m = X86AddressMode::base64(x86_register_info::RAX);
let reg_info = make_reg_info();
let s = format_intel(&m, ®_info);
assert!(s.contains("["));
assert!(s.contains("]"));
assert!(s.contains("rax") || s.contains("RAX"));
}
#[test]
fn test_format_intel_full() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 8);
let reg_info = make_reg_info();
let s = format_intel(&m, ®_info);
assert!(s.contains("rbx") || s.contains("RBX"));
assert!(s.contains("rsi") || s.contains("RSI"));
assert!(s.contains("4"));
assert!(s.contains("8"));
}
#[test]
fn test_format_intel_rip_relative() {
let m = X86AddressMode::rip_relative(42);
let reg_info = make_reg_info();
let s = format_intel(&m, ®_info);
assert!(s.contains("rip") || s.contains("RIP"));
}
#[test]
fn test_format_att_simple() {
let m = X86AddressMode::base64(x86_register_info::RAX);
let reg_info = make_reg_info();
let s = format_att(&m, ®_info);
assert!(s.contains("("));
assert!(s.contains(")"));
assert!(s.contains("rax") || s.contains("RAX"));
}
#[test]
fn test_format_att_with_index() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 8);
let reg_info = make_reg_info();
let s = format_att(&m, ®_info);
assert!(s.contains("8"));
assert!(s.contains("rbx") || s.contains("RBX"));
assert!(s.contains("rsi") || s.contains("RSI"));
assert!(s.contains("4"));
}
#[test]
fn test_rsp_sib_encoding() {
let m = X86AddressMode::base64(x86_register_info::RSP);
assert!(m.requires_sib());
let sib = m.encode_sib();
assert_eq!(sib, 0x24);
}
#[test]
fn test_rbp_64bit_sib_encoding() {
let m = X86AddressMode::base64(x86_register_info::RBP);
assert!(m.requires_sib());
let sib = m.encode_sib();
assert_eq!(sib & 0x7, 5); assert_eq!((sib >> 3) & 0x7, 4); }
#[test]
fn test_r12_sib_encoding() {
let m = X86AddressMode::base64(x86_register_info::R12);
assert!(m.requires_sib());
}
#[test]
fn test_r13_64bit_sib_encoding() {
let m = X86AddressMode::base64(x86_register_info::R13);
assert!(m.requires_sib());
}
#[test]
fn test_rip_relative_no_sib() {
let m = X86AddressMode::rip_relative(42);
assert!(!m.requires_sib());
assert_eq!(m.encode_modrm(0), 0x05);
}
#[test]
fn test_absolute_32_no_sib() {
let m = X86AddressMode::absolute32(0x1000);
assert!(!m.requires_sib());
}
#[test]
fn test_zero_disp_folded() {
let builder = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RAX)
.with_disp(0);
let m = builder.build();
assert_eq!(m.displacement, 0);
assert_eq!(m.classify(), AddressingForm::BaseOnly);
}
#[test]
fn test_disp8_promoted_to_disp32() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, 200);
assert_eq!(m.classify(), AddressingForm::BaseDisp32);
}
#[test]
fn test_displacement_negative() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, -16);
assert!(m.fits_disp8());
assert_eq!(m.displacement_size(), 1);
}
#[test]
fn test_displacement_negative_32() {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, -200);
assert!(!m.fits_disp8());
assert_eq!(m.displacement_size(), 4);
}
#[test]
fn test_address_mode_clone() {
let m = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 8);
let cloned = m.clone();
assert_eq!(m.base, cloned.base);
assert_eq!(m.index, cloned.index);
assert_eq!(m.scale, cloned.scale);
assert_eq!(m.displacement, cloned.displacement);
}
#[test]
fn test_address_mode_default() {
let m = X86AddressMode::default();
assert_eq!(m.width, AddrWidth::Addr64);
assert_eq!(m.base, None);
assert_eq!(m.index, None);
assert_eq!(m.scale, 0);
assert_eq!(m.displacement, 0);
assert!(!m.is_rip_relative);
}
#[test]
fn test_hash_key_unique() {
let a = X86AddressMode::base64(x86_register_info::RAX);
let b = X86AddressMode::base64(x86_register_info::RBX);
assert_ne!(a.hash_key(), b.hash_key());
let c = X86AddressMode::base_disp64(x86_register_info::RAX, 0);
let d = X86AddressMode::base_disp64(x86_register_info::RAX, 16);
assert_ne!(c.hash_key(), d.hash_key());
}
#[test]
fn test_code_model_default() {
assert_eq!(CodeModel::default(), CodeModel::Small);
}
#[test]
fn test_tls_model_default() {
assert_eq!(TlsModel::default(), TlsModel::InitialExec);
}
#[test]
fn test_all_sib_combinations_are_unique() {
let entries = generate_all_sib_combinations();
let mut seen = HashSet::new();
for e in &entries {
assert!(seen.insert(e.sib), "Duplicate SIB: {:02X}", e.sib);
}
}
#[test]
fn test_all_16bit_forms_decode_roundtrip() {
let forms = [
X86Addr16Form::BxSi,
X86Addr16Form::BxDi,
X86Addr16Form::BpSi,
X86Addr16Form::BpDi,
X86Addr16Form::Si,
X86Addr16Form::Di,
X86Addr16Form::Bp,
X86Addr16Form::Bx,
];
for &form in &forms {
let modrm = (form.mod_field() << 6) | (form.rm_field());
let decoded = X86Addr16Form::decode(modrm);
assert_eq!(
decoded, form,
"Form {:?} mismatch: encoded {:02X} → decoded {:?}",
form, modrm, decoded
);
}
}
#[test]
fn test_all_64bit_base_registers_work() {
let regs: Vec<u16> = vec![
x86_register_info::RAX,
x86_register_info::RCX,
x86_register_info::RDX,
x86_register_info::RBX,
x86_register_info::RSP,
x86_register_info::RBP,
x86_register_info::RSI,
x86_register_info::RDI,
x86_register_info::R8,
x86_register_info::R9,
x86_register_info::R10,
x86_register_info::R11,
x86_register_info::R12,
x86_register_info::R13,
x86_register_info::R14,
x86_register_info::R15,
];
for reg in ®s {
let m = X86AddressMode::base64(*reg);
let modrm = m.encode_modrm(0);
assert!(
modrm < 0xC0,
"Invalid ModR/M for register {}: {:02X}",
reg,
modrm
);
}
}
#[test]
fn test_all_scale_factors_work() {
let scales = [1, 2, 4, 8];
for &scale in &scales {
let m =
X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, scale, 0);
let sib = m.encode_sib();
let decoded_scale = match (sib >> 6) & 0x3 {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
_ => 0,
};
assert_eq!(
decoded_scale, scale,
"Scale {} encoded incorrectly in SIB {:02X}",
scale, sib
);
}
}
#[test]
fn test_displacement_range_8bit() {
for disp in -128i64..=127i64 {
let m = X86AddressMode::base_disp64(x86_register_info::RAX, disp);
assert!(m.fits_disp8());
assert_eq!(
m.displacement_size(),
if disp == 0 && m.requires_sib() {
0
} else if disp != 0 {
1
} else {
0
}
);
}
}
#[test]
fn test_tls_all_models() {
let models = [
TlsModel::GeneralDynamic,
TlsModel::LocalDynamic,
TlsModel::InitialExec,
TlsModel::LocalExec,
];
for &model in &models {
let mut engine = X86AddressingFull::new_x86_64();
engine.tls_model = model;
let lower = X86MemoryLowering::new(engine);
let addr = lower.lower_tls_address("tls_var", 0, true);
assert!(addr.width == AddrWidth::Addr64);
}
}
#[test]
fn test_code_model_all() {
let models = [
CodeModel::Small,
CodeModel::Kernel,
CodeModel::Medium,
CodeModel::Large,
];
for &model in &models {
let engine = X86AddressingFull::new_x86_64().with_code_model(model);
assert_eq!(engine.code_model, model);
}
}
#[test]
fn test_opt_level_all() {
let levels = [OptLevel::None, OptLevel::Default, OptLevel::Aggressive];
for &level in &levels {
let engine = X86AddressingFull::new_x86_64().with_opt_level(level);
assert_eq!(engine.opt_level, level);
}
}
#[test]
fn test_roundtrip_base_only() {
for reg in &[
x86_register_info::RAX,
x86_register_info::RCX,
x86_register_info::RDX,
x86_register_info::RBX,
x86_register_info::RSI,
x86_register_info::RDI,
] {
let original = X86AddressMode::base64(*reg);
let modrm = original.encode_modrm(0);
let matcher = AddressingModeMatcher::new(AddrWidth::Addr64);
let decoded = matcher.decode(modrm, None, &[], false, false, false);
assert_eq!(
decoded.base, original.base,
"Round-trip failed for register {}",
reg
);
assert_eq!(decoded.displacement, original.displacement);
}
}
#[test]
fn test_roundtrip_rip_relative() {
let disp: i64 = 0x12345678;
let original = X86AddressMode::rip_relative(disp);
let modrm = original.encode_modrm(0);
let disp_bytes = original.encode_displacement();
let matcher = AddressingModeMatcher::new(AddrWidth::Addr64);
let decoded = matcher.decode(modrm, None, &disp_bytes, false, false, false);
assert!(decoded.is_rip_relative);
assert_eq!(decoded.displacement, disp);
}
#[test]
fn test_roundtrip_sib_index() {
let original = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
let modrm = original.encode_modrm(0);
let sib = Some(original.encode_sib());
let matcher = AddressingModeMatcher::new(AddrWidth::Addr64);
let decoded = matcher.decode(modrm, sib, &[], false, false, false);
assert_eq!(decoded.base, original.base);
assert_eq!(decoded.index, original.index);
assert_eq!(decoded.scale, original.scale);
}
#[test]
fn test_bulk_sib_all_scale_index_base_combos() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr64);
for scale in [0u8, 1, 2, 3] {
for index in 0..8u8 {
for base in 0..8u8 {
if index == 4 && base == 5 {
continue;
}
let sib = (scale << 6) | ((index & 0x7) << 3) | (base & 0x7);
if index == 4 && base == 5 {
continue; }
let addr = matcher.decode(
(Mod00 << 6) | 4, Some(sib),
&[],
false,
false,
false,
);
if base == 5 {
assert_eq!(addr.base, None);
} else {
assert_eq!(addr.base, Some(base as u16));
}
if index == 4 {
assert_eq!(addr.index, None);
} else {
assert_eq!(addr.index, Some(index as u16));
}
let expected_scale: u8 = match scale {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
_ => 1,
};
assert_eq!(addr.scale, expected_scale);
}
}
}
}
#[test]
fn test_bulk_16bit_all_modrm_forms() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr16);
for mod_field in 0u8..3 {
for rm in 0u8..8 {
let modrm = (mod_field << 6) | rm;
let form = X86Addr16Form::decode(modrm);
let disp: Vec<u8> = match form.disp_size() {
0 => vec![],
1 => vec![0x10],
2 => vec![0x00, 0x20],
_ => vec![],
};
let addr = matcher.decode(modrm, None, &disp, false, false, false);
if form.disp_size() == 0 {
assert!(addr.form_16bit.is_some());
assert_eq!(addr.form_16bit, Some(form));
}
}
}
}
#[test]
fn test_cost_model_default() {
let model = AddressCostModel::default();
let addr = X86AddressMode::base64(x86_register_info::RAX);
let cost = model.compute_cost(&addr);
assert!(cost > 0);
}
#[test]
fn test_cost_model_simple_vs_complex() {
let model = AddressCostModel::default();
let simple = X86AddressMode::base64(x86_register_info::RAX);
let complex =
X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 200);
let cost_simple = model.compute_cost(&simple);
let cost_complex = model.compute_cost(&complex);
assert!(cost_simple < cost_complex);
}
#[test]
fn test_cost_model_size_optimized() {
let model = AddressCostModel::size_optimized();
let addr = X86AddressMode::base64(x86_register_info::RAX);
let cost = model.compute_cost(&addr);
assert!(cost > 0);
}
#[test]
fn test_cost_model_select_cheaper() {
let model = AddressCostModel::default();
let simple = X86AddressMode::base64(x86_register_info::RAX);
let complex =
X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 200);
let selected = model.select_cheaper(&simple, &complex);
assert_eq!(selected.base, simple.base);
}
#[test]
fn test_cost_model_skylake() {
let model = AddressCostModel::skylake();
let addr = X86AddressMode::base64(x86_register_info::RAX);
let cost = model.compute_cost(&addr);
assert!(cost > 0);
}
#[test]
fn test_cost_model_zen() {
let model = AddressCostModel::zen();
let addr = X86AddressMode::base64(x86_register_info::RAX);
let cost = model.compute_cost(&addr);
assert!(cost > 0);
}
#[test]
fn test_pressure_analysis_default() {
let pa = RegisterPressureAnalysis::default();
assert!(pa.can_use_index());
}
#[test]
fn test_pressure_analysis_high_pressure() {
let mut pa = RegisterPressureAnalysis::default();
pa.current_pressure = 0.9;
assert!(!pa.can_use_index());
}
#[test]
fn test_pressure_should_use_lea() {
let pa = RegisterPressureAnalysis::default();
assert!(pa.should_use_lea(2));
assert!(!pa.should_use_lea(1));
let mut pa2 = RegisterPressureAnalysis::default();
pa2.current_pressure = 0.95;
assert!(!pa2.should_use_lea(2));
}
#[test]
fn test_pressure_estimate() {
let pa = RegisterPressureAnalysis::new64();
let addr = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
let (pressure, change) = pa.estimate_pressure(&addr, 5);
assert!(change > 0.0);
assert!(pressure > 0.0);
}
#[test]
fn test_pressure_register_friendliness() {
let pa = RegisterPressureAnalysis::default();
let simple = X86AddressMode::base64(x86_register_info::RAX);
let complex = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
let rip = X86AddressMode::rip_relative(0);
assert!(pa.register_friendliness(&simple) > pa.register_friendliness(&complex));
assert!(pa.register_friendliness(&rip) > pa.register_friendliness(&simple));
}
#[test]
fn test_fold_rules_default_load() {
let rules = MemoryFoldRules::default();
assert!(rules.can_fold_load_into(X86Opcode::ADD));
assert!(rules.can_fold_load_into(X86Opcode::CMP));
assert!(!rules.can_fold_load_into(X86Opcode::JMP));
}
#[test]
fn test_fold_rules_default_rmw() {
let rules = MemoryFoldRules::default();
assert!(rules.can_fold_rmw_into(X86Opcode::ADD));
assert!(rules.can_fold_rmw_into(X86Opcode::INC));
assert!(!rules.can_fold_rmw_into(X86Opcode::JMP));
}
#[test]
fn test_fold_rules_is_foldable() {
let rules = MemoryFoldRules::default();
let addr = X86AddressMode::base64(x86_register_info::RAX);
assert!(rules.is_foldable(&addr));
let rip = X86AddressMode::rip_relative(0);
assert!(rules.is_foldable(&rip));
let complex = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
assert!(rules.is_foldable(&complex));
}
#[test]
fn test_fold_rules_get_fold_form() {
let rules = MemoryFoldRules::default();
let base = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(rules.get_fold_form(&base), FoldForm::BaseOnly);
let disp8 = X86AddressMode::base_disp64(x86_register_info::RAX, 42);
assert_eq!(rules.get_fold_form(&disp8), FoldForm::BaseDisp8);
let disp32 = X86AddressMode::base_disp64(x86_register_info::RAX, 200);
assert_eq!(rules.get_fold_form(&disp32), FoldForm::BaseDisp32);
let index = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
assert_eq!(rules.get_fold_form(&index), FoldForm::FullWithIndex);
}
#[test]
fn test_lea_complexity_class_1() {
let db = LeaComplexityDB::intel_skylake_client();
let addr = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(db.classify_lea(&addr), 1);
assert_eq!(db.lea_latency(&addr), 1);
}
#[test]
fn test_lea_complexity_class_2() {
let db = LeaComplexityDB::intel_skylake_client();
let addr = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 1, 0);
assert_eq!(db.classify_lea(&addr), 2);
assert_eq!(db.lea_latency(&addr), 1);
}
#[test]
fn test_lea_complexity_class_3() {
let db = LeaComplexityDB::intel_skylake_client();
let addr = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
assert_eq!(db.classify_lea(&addr), 3);
assert_eq!(db.lea_latency(&addr), 1);
}
#[test]
fn test_lea_complexity_class_4() {
let db = LeaComplexityDB::intel_skylake_client();
let addr = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 16);
assert_eq!(db.classify_lea(&addr), 4);
assert_eq!(db.lea_latency(&addr), 3);
}
#[test]
fn test_lea_complexity_zen_class_4_fast() {
let db = LeaComplexityDB::amd_zen();
let addr = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 16);
assert_eq!(db.classify_lea(&addr), 4);
assert_eq!(db.lea_latency(&addr), 1);
}
#[test]
fn test_decision_tree_select_frame() {
let engine = X86AddressingFull::new_x86_64();
let tree = AddressSelectionDecisionTree::new(engine);
let addr = tree.select_best_memory(
None, None, 0, 8, true, false, false, );
assert!(addr.is_frame_relative);
assert_eq!(addr.base, Some(x86_register_info::RBP));
}
#[test]
fn test_decision_tree_select_global() {
let engine = X86AddressingFull::new_x86_64().with_pic(true);
let tree = AddressSelectionDecisionTree::new(engine);
let addr = tree.select_best_memory(
None, None, 0, 0, false, true, false,
);
assert!(addr.is_rip_relative);
assert!(addr.is_got_relative);
}
#[test]
fn test_decision_tree_select_tls() {
let engine = X86AddressingFull::new_x86_64();
let tree = AddressSelectionDecisionTree::new(engine);
let addr = tree.select_best_memory(
None, None, 0, 8, false, false, true, );
assert_eq!(addr.segment, Some(x86_register_info::FS));
}
#[test]
fn test_decision_tree_select_generic() {
let engine = X86AddressingFull::new_x86_64();
let tree = AddressSelectionDecisionTree::new(engine);
let addr = tree.select_best_memory(
Some(x86_register_info::RAX),
Some(x86_register_info::RSI),
4,
16,
false,
false,
false,
);
assert_eq!(addr.base, Some(x86_register_info::RAX));
assert_eq!(addr.index, Some(x86_register_info::RSI));
assert_eq!(addr.scale, 4);
}
#[test]
fn test_prefix_encode_rex_all_bits() {
let pi = PrefixInteraction::new(AddrWidth::Addr64);
assert_eq!(pi.encode_rex(false, false, false, false), 0x40);
assert_eq!(pi.encode_rex(true, false, false, false), 0x48);
assert_eq!(pi.encode_rex(true, true, true, true), 0x4F);
}
#[test]
fn test_prefix_decode_rex() {
let pi = PrefixInteraction::new(AddrWidth::Addr64);
let (w, r, x, b) = pi.decode_rex(0x4F);
assert!(w);
assert!(r);
assert!(x);
assert!(b);
}
#[test]
fn test_prefix_needs_rex_r8() {
let pi = PrefixInteraction::new(AddrWidth::Addr64);
let addr = X86AddressMode::base64(x86_register_info::R8);
assert!(pi.needs_rex(&addr));
}
#[test]
fn test_prefix_needs_rex_low_reg() {
let pi = PrefixInteraction::new(AddrWidth::Addr64);
let addr = X86AddressMode::base64(x86_register_info::RAX);
assert!(pi.needs_rex(&addr));
}
#[test]
fn test_prefix_compute_rex_bits() {
let pi = PrefixInteraction::new(AddrWidth::Addr64);
let addr = X86AddressMode::full64(x86_register_info::R8, x86_register_info::R9, 1, 0);
let (r, x, b) = pi.compute_rex_bits(&addr);
assert!(!r);
assert!(x);
assert!(b);
}
#[test]
fn test_prefix_needs_addr_size_override() {
let pi64 = PrefixInteraction::new(AddrWidth::Addr64);
let addr32 = X86AddressMode::base32(x86_register_info::EAX);
assert!(pi64.needs_addr_size_override(&addr32));
let pi32 = PrefixInteraction::new(AddrWidth::Addr32);
let addr16 = X86AddressMode::base16(x86_register_info::BX);
assert!(pi32.needs_addr_size_override(&addr16));
}
#[test]
fn test_prefix_encode_all() {
let pi = PrefixInteraction::new(AddrWidth::Addr64);
let addr = X86AddressMode::base64(x86_register_info::RAX);
let prefixes = pi.encode_all_prefixes(&addr, 0);
assert!(!prefixes.is_empty());
assert!(prefixes.contains(&0x48));
}
#[test]
fn test_validator_valid_simple() {
let v = AddressingModeValidator::new(AddrWidth::Addr64);
let addr = X86AddressMode::base64(x86_register_info::RAX);
assert!(v.is_valid(&addr).is_ok());
}
#[test]
fn test_validator_valid_complex() {
let v = AddressingModeValidator::new(AddrWidth::Addr64);
let addr = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 16);
assert!(v.is_valid(&addr).is_ok());
}
#[test]
fn test_validator_valid_rip() {
let v = AddressingModeValidator::new(AddrWidth::Addr64);
let addr = X86AddressMode::rip_relative(42);
assert!(v.is_valid(&addr).is_ok());
}
#[test]
fn test_validator_invalid_scale() {
let v = AddressingModeValidator::new(AddrWidth::Addr64);
let addr = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 3, 0);
let result = v.is_valid(&addr);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), AddressingError::InvalidScale(3));
}
#[test]
fn test_validator_rip_with_base() {
let v = AddressingModeValidator::new(AddrWidth::Addr64);
let mut addr = X86AddressMode::rip_relative(42);
addr.base = Some(x86_register_info::RAX);
let result = v.is_valid(&addr);
assert!(result.is_err());
}
#[test]
fn test_validator_rsp_as_index() {
let v = AddressingModeValidator::new(AddrWidth::Addr64);
let addr = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSP, 1, 0);
let result = v.is_valid(&addr);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), AddressingError::RspCannotBeIndex);
}
#[test]
fn test_validator_16bit_valid() {
let v = AddressingModeValidator::new(AddrWidth::Addr16);
let addr = X86AddressMode::base_index16(x86_register_info::BX, x86_register_info::SI);
assert!(v.is_valid(&addr).is_ok());
}
#[test]
fn test_validator_16bit_invalid_pair() {
let v = AddressingModeValidator::new(AddrWidth::Addr16);
let addr = X86AddressMode::base_index16(x86_register_info::AX, x86_register_info::CX);
let result = v.is_valid(&addr);
assert!(result.is_err());
}
#[test]
fn test_validator_16bit_no_scale() {
let v = AddressingModeValidator::new(AddrWidth::Addr16);
let addr = X86AddressMode {
width: AddrWidth::Addr16,
base: Some(x86_register_info::BX),
scale: 4,
..Default::default()
};
let result = v.is_valid(&addr);
assert!(result.is_err());
}
#[test]
fn test_validator_width_mismatch() {
let v = AddressingModeValidator::new(AddrWidth::Addr32);
let addr = X86AddressMode::base64(x86_register_info::RAX);
let result = v.is_valid(&addr);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), AddressingError::WidthMismatch);
}
#[test]
fn test_validator_suggest_fix() {
let v = AddressingModeValidator::new(AddrWidth::Addr64);
let suggestion = v.suggest_fix(&AddressingError::InvalidScale(3));
assert!(suggestion.is_some());
assert!(suggestion.unwrap().contains("4"));
let suggestion2 = v.suggest_fix(&AddressingError::RspCannotBeIndex);
assert!(suggestion2.is_some());
assert!(suggestion2.unwrap().contains("RSP"));
}
#[test]
fn test_addressing_error_display() {
let err = AddressingError::InvalidScale(0);
let s = format!("{}", err);
assert!(s.contains("0"));
let err2 = AddressingError::RipRelativeNotSupported;
let s2 = format!("{}", err2);
assert!(s2.contains("64-bit"));
}
#[test]
fn test_complete_table_64bit() {
let table = CompleteModRMTable::generate_64bit();
assert!(table.total_entries() > 1000);
}
#[test]
fn test_complete_table_32bit() {
let table = CompleteModRMTable::generate_32bit();
assert!(table.total_entries() > 1000);
}
#[test]
fn test_complete_table_lookup() {
let table = CompleteModRMTable::generate_64bit();
let entry = table.lookup_modrm(0x00);
assert!(entry.is_some());
let e = entry.unwrap();
assert_eq!(e.form, ModRMForm::BaseOnly);
assert_eq!(e.base, Some(0));
}
#[test]
fn test_complete_table_lookup_rip_relative() {
let table = CompleteModRMTable::generate_64bit();
let entry = table.lookup_modrm(0x05);
assert!(entry.is_some());
let e = entry.unwrap();
assert_eq!(e.form, ModRMForm::RIPRelative);
}
#[test]
fn test_complete_table_entries_by_form() {
let table = CompleteModRMTable::generate_64bit();
let entries = table.entries_by_form(ModRMForm::BaseOnly);
assert!(!entries.is_empty());
}
#[test]
fn test_bulk_all_64bit_base_registers_with_disp8() {
let regs = [
x86_register_info::RAX,
x86_register_info::RCX,
x86_register_info::RDX,
x86_register_info::RBX,
x86_register_info::RSI,
x86_register_info::RDI,
x86_register_info::R8,
x86_register_info::R9,
x86_register_info::R10,
x86_register_info::R11,
x86_register_info::R14,
x86_register_info::R15,
];
for ® in ®s {
let addr = X86AddressMode::base_disp64(reg, 42);
let modrm = addr.encode_modrm(0);
let mod_field = (modrm >> 6) & 0x3;
assert_eq!(mod_field, 1, "Expected mod=01 for disp8, reg={}", reg);
let rm = modrm & 0x7;
assert_eq!(rm, reg & 0x7, "rm field mismatch for reg={}", reg);
}
}
#[test]
fn test_bulk_all_64bit_base_registers_with_disp32() {
let regs = [
x86_register_info::RAX,
x86_register_info::RCX,
x86_register_info::RDX,
x86_register_info::RBX,
x86_register_info::RSI,
x86_register_info::RDI,
x86_register_info::R8,
x86_register_info::R9,
x86_register_info::R10,
x86_register_info::R11,
x86_register_info::R14,
x86_register_info::R15,
];
for ® in ®s {
let addr = X86AddressMode::base_disp64(reg, 200);
let modrm = addr.encode_modrm(0);
let mod_field = (modrm >> 6) & 0x3;
assert_eq!(mod_field, 2, "Expected mod=10 for disp32, reg={}", reg);
}
}
#[test]
fn test_bulk_all_index_registers_with_scales() {
let indices = [
x86_register_info::RAX,
x86_register_info::RCX,
x86_register_info::RDX,
x86_register_info::RBX,
x86_register_info::RBP,
x86_register_info::RSI,
x86_register_info::RDI,
x86_register_info::R8,
x86_register_info::R9,
x86_register_info::R10,
x86_register_info::R11,
x86_register_info::R12,
x86_register_info::R13,
x86_register_info::R14,
x86_register_info::R15,
];
let scales = [1u8, 2, 4, 8];
for &idx in &indices {
for &sc in &scales {
let addr = X86AddressMode::full64(x86_register_info::RBX, idx, sc, 0);
assert!(addr.requires_sib());
let sib = addr.encode_sib();
let sib_index = (sib >> 3) & 0x7;
assert_eq!(
sib_index,
idx & 0x7,
"Index mismatch for idx={}, scale={}",
idx,
sc
);
let sib_scale = (sib >> 6) & 0x3;
let decoded_scale: u8 = match sib_scale {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
_ => 0,
};
assert_eq!(
decoded_scale, sc,
"Scale mismatch for idx={}, scale={}",
idx, sc
);
}
}
}
#[test]
fn test_convert_64_to_32() {
let m64 = X86AddressMode::base64(x86_register_info::RAX);
let mut m32 = m64.clone();
m32.width = AddrWidth::Addr32;
m32.base = Some(x86_register_info::EAX);
assert_eq!(m32.width, AddrWidth::Addr32);
assert_eq!(m32.base, Some(x86_register_info::EAX));
}
#[test]
fn test_convert_64_to_16() {
let m16 = X86AddressingModeBuilder::new16()
.with_base(x86_register_info::BX)
.build();
assert_eq!(m16.width, AddrWidth::Addr16);
assert_eq!(m16.base, Some(x86_register_info::BX));
}
#[test]
fn test_frame_pointer_rbp_with_negative_offset() {
let addr = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBP)
.with_disp(-8)
.with_frame_relative(true)
.build();
assert!(addr.is_frame_relative);
assert_eq!(addr.displacement, -8);
assert!(addr.fits_disp8());
}
#[test]
fn test_frame_pointer_ebp_with_offset() {
let addr = X86AddressingModeBuilder::new32()
.with_base(x86_register_info::EBP)
.with_disp(-4)
.with_frame_relative(true)
.build();
assert!(addr.is_frame_relative);
assert_eq!(addr.base, Some(x86_register_info::EBP));
}
#[test]
fn test_stack_pointer_rsp_no_disp() {
let addr = X86AddressMode::base64(x86_register_info::RSP);
assert!(addr.requires_sib());
let sib = addr.encode_sib();
assert_eq!(sib, 0x24);
}
#[test]
fn test_stack_pointer_rsp_with_disp8() {
let addr = X86AddressMode::base_disp64(x86_register_info::RSP, 16);
assert!(addr.requires_sib());
let modrm = addr.encode_modrm(0);
let mod_field = (modrm >> 6) & 0x3;
assert_eq!(mod_field, 1);
assert_eq!(modrm & 0x7, 4);
}
#[test]
fn test_disp_negative_1() {
let addr = X86AddressMode::base_disp64(x86_register_info::RAX, -1);
assert!(addr.fits_disp8());
assert_eq!(addr.displacement_size(), 1);
assert_eq!(addr.encode_displacement(), vec![0xFF]);
}
#[test]
fn test_disp_negative_128() {
let addr = X86AddressMode::base_disp64(x86_register_info::RAX, -128);
assert!(addr.fits_disp8());
assert_eq!(addr.displacement_size(), 1);
assert_eq!(addr.encode_displacement(), vec![0x80]);
}
#[test]
fn test_disp_negative_129() {
let addr = X86AddressMode::base_disp64(x86_register_info::RAX, -129);
assert!(!addr.fits_disp8());
assert_eq!(addr.displacement_size(), 4);
}
#[test]
fn test_disp_positive_127() {
let addr = X86AddressMode::base_disp64(x86_register_info::RAX, 127);
assert!(addr.fits_disp8());
assert_eq!(addr.displacement_size(), 1);
}
#[test]
fn test_disp_positive_128() {
let addr = X86AddressMode::base_disp64(x86_register_info::RAX, 128);
assert!(!addr.fits_disp8());
assert_eq!(addr.displacement_size(), 4);
}
#[test]
fn test_sib_r12_no_index() {
let addr = X86AddressMode::base64(x86_register_info::R12);
assert!(addr.requires_sib());
assert!(addr.is_special_sib_case());
let sib = addr.encode_sib();
assert_eq!((sib >> 3) & 0x7, 4);
assert_eq!(sib & 0x7, 4);
}
#[test]
fn test_sib_r13_no_index() {
let addr = X86AddressMode::base64(x86_register_info::R13);
assert!(addr.requires_sib());
assert!(addr.is_special_sib_case());
let sib = addr.encode_sib();
assert_eq!((sib >> 3) & 0x7, 4);
assert_eq!(sib & 0x7, 5);
}
#[test]
fn test_sib_rbp_64bit() {
let addr = X86AddressMode::base64(x86_register_info::RBP);
assert!(addr.requires_sib());
assert!(addr.is_special_sib_case());
}
#[test]
fn test_sib_ebp_32bit() {
let addr = X86AddressMode::base32(x86_register_info::EBP);
assert!(!addr.requires_sib());
}
#[test]
fn test_segment_fs_override() {
let addr =
X86AddressMode::base64(x86_register_info::RAX).with_segment(x86_register_info::FS);
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
assert_eq!(handler.segment_override_prefix(&addr), Some(0x64));
}
#[test]
fn test_segment_gs_override() {
let addr =
X86AddressMode::base64(x86_register_info::RAX).with_segment(x86_register_info::GS);
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
assert_eq!(handler.segment_override_prefix(&addr), Some(0x65));
}
#[test]
fn test_segment_ss_default_for_rsp() {
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
let addr = X86AddressMode::base64(x86_register_info::RSP);
assert_eq!(handler.default_segment(&addr), x86_register_info::SS);
assert!(!handler.needs_segment_override(&addr));
}
#[test]
fn test_segment_ds_default_for_rax() {
let engine = X86AddressingFull::new_x86_64();
let handler = X86SegmentHandling::new(engine);
let addr = X86AddressMode::base64(x86_register_info::RAX);
assert_eq!(handler.default_segment(&addr), x86_register_info::DS);
}
#[test]
fn test_hash_key_different_base() {
let a = X86AddressMode::base64(x86_register_info::RAX);
let b = X86AddressMode::base64(x86_register_info::RBX);
assert_ne!(a.hash_key(), b.hash_key());
}
#[test]
fn test_hash_key_different_disp() {
let a = X86AddressMode::base_disp64(x86_register_info::RAX, 0);
let b = X86AddressMode::base_disp64(x86_register_info::RAX, 8);
assert_ne!(a.hash_key(), b.hash_key());
}
#[test]
fn test_hash_key_different_index() {
let a = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
let b = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RDI, 4, 0);
assert_ne!(a.hash_key(), b.hash_key());
}
#[test]
fn test_hash_key_different_scale() {
let a = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 1, 0);
let b = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
assert_ne!(a.hash_key(), b.hash_key());
}
#[test]
fn test_hash_key_different_segment() {
let a = X86AddressMode::base64(x86_register_info::RAX).with_segment(x86_register_info::FS);
let b = X86AddressMode::base64(x86_register_info::RAX).with_segment(x86_register_info::GS);
assert_ne!(a.hash_key(), b.hash_key());
}
#[test]
fn test_hash_key_rip_vs_absolute() {
let a = X86AddressMode::rip_relative(0);
let b = X86AddressMode::absolute32(0);
assert_ne!(a.hash_key(), b.hash_key());
}
#[test]
fn test_effective_address_overflow() {
let result = compute_effective_address(Some(0xFFFF_FFFF_FFFF_FFFF), Some(1), 8, 0);
assert_eq!(result, 0xFFFF_FFFF_FFFF_FFFFu64.wrapping_add(8));
}
#[test]
fn test_effective_address_all_zero() {
let result = compute_effective_address(None, None, 1, 0);
assert_eq!(result, 0);
}
#[test]
fn test_effective_address_max() {
let result = compute_effective_address(Some(u64::MAX), Some(u64::MAX), 8, i64::MAX as u64);
let _ = result;
}
#[test]
fn test_canonicalize_scale_0_to_1() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBX)
.with_index(x86_register_info::RSI, 0)
.build();
assert_eq!(m.scale, 0);
assert_eq!(m.index, None);
}
#[test]
fn test_canonicalize_scale_3_to_4() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBX)
.with_index(x86_register_info::RSI, 3)
.build();
assert_eq!(m.scale, 4);
}
#[test]
fn test_canonicalize_scale_5_to_4() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBX)
.with_index(x86_register_info::RSI, 5)
.build();
assert_eq!(m.scale, 4);
}
#[test]
fn test_canonicalize_scale_7_to_8() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBX)
.with_index(x86_register_info::RSI, 7)
.build();
assert_eq!(m.scale, 8);
}
#[test]
fn test_canonicalize_scale_9_to_8() {
let m = X86AddressingModeBuilder::new64()
.with_base(x86_register_info::RBX)
.with_index(x86_register_info::RSI, 9)
.build();
assert_eq!(m.scale, 8);
}
#[test]
fn test_prefix_order_segment_before_rex() {
let pi = PrefixInteraction::new(AddrWidth::Addr64);
let addr =
X86AddressMode::base64(x86_register_info::R8).with_segment(x86_register_info::FS);
let prefixes = pi.encode_all_prefixes(&addr, 0);
if let Some(seg_pos) = prefixes.iter().position(|&b| b == 0x64) {
if let Some(rex_pos) = prefixes.iter().position(|&b| b & 0xF0 == 0x40) {
assert!(seg_pos < rex_pos, "Segment override must come before REX");
}
}
}
#[test]
fn test_prefix_order_addr_size_before_rex() {
let pi = PrefixInteraction::new(AddrWidth::Addr64);
let addr = X86AddressMode::base32(x86_register_info::EAX);
let prefixes = pi.encode_all_prefixes(&addr, 0);
if let Some(addr_pos) = prefixes.iter().position(|&b| b == 0x67) {
if let Some(rex_pos) = prefixes.iter().position(|&b| b & 0xF0 == 0x40) {
assert!(
addr_pos < rex_pos,
"Address size override must come before REX"
);
}
}
}
#[test]
fn test_builder_16bit_all_single_registers() {
use crate::x86::x86_register_info::{BP, BX, DI, SI};
let regs = [
(BX, X86Addr16Form::Bx),
(BP, X86Addr16Form::Bp),
(SI, X86Addr16Form::Si),
(DI, X86Addr16Form::Di),
];
for &(reg, expected_form) in ®s {
let m = X86AddressingModeBuilder::new16().with_base(reg).build();
assert_eq!(m.form_16bit, Some(expected_form));
assert!(!m.is_absolute_16);
}
}
#[test]
fn test_builder_16bit_all_dual_registers() {
use crate::x86::x86_register_info::{BP, BX, DI, SI};
let pairs = [
(BX, SI, X86Addr16Form::BxSi),
(BX, DI, X86Addr16Form::BxDi),
(BP, SI, X86Addr16Form::BpSi),
(BP, DI, X86Addr16Form::BpDi),
];
for &(base, index, expected_form) in &pairs {
let m = X86AddressingModeBuilder::new16()
.with_base(base)
.with_index(index, 1)
.build();
assert_eq!(m.form_16bit, Some(expected_form));
}
}
#[test]
fn test_builder_16bit_disp16_absolute() {
let m = X86AddressingModeBuilder::new16().with_disp(0x1000).build();
assert!(m.is_absolute_16);
assert_eq!(m.form_16bit, Some(X86Addr16Form::Disp16));
}
#[test]
fn test_rip_relative_displacement_range() {
let m = X86AddressMode::rip_relative(2_147_483_647i64);
assert!(m.fits_disp32());
assert_eq!(m.displacement_size(), 4);
}
#[test]
fn test_rip_relative_negative_disp() {
let m = X86AddressMode::rip_relative(-2_147_483_648i64);
assert!(m.fits_disp32());
assert_eq!(m.displacement_size(), 4);
}
#[test]
fn test_rip_relative_default_width() {
let m = X86AddressMode::rip_relative(0);
assert_eq!(m.width, AddrWidth::Addr64);
}
#[test]
fn test_lower_global_kernel_model() {
let engine = X86AddressingFull::new_x86_64().with_code_model(CodeModel::Kernel);
let lower = X86MemoryLowering::new(engine);
let addr = lower.lower_global_address("kernel_var", 0, false);
assert!(addr.symbol.is_some());
}
#[test]
fn test_lower_global_medium_model() {
let engine = X86AddressingFull::new_x86_64().with_code_model(CodeModel::Medium);
let lower = X86MemoryLowering::new(engine);
let addr = lower.lower_global_address("data_var", 0, false);
assert!(!addr.is_rip_relative || addr.is_absolute_32);
}
#[test]
fn test_lower_global_large_model() {
let engine = X86AddressingFull::new_x86_64().with_code_model(CodeModel::Large);
let lower = X86MemoryLowering::new(engine);
let addr = lower.lower_global_address("far_var", 0, false);
assert!(addr.is_absolute_32);
}
#[test]
fn test_exhaustive_direct_modrm_roundtrip() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr64);
let base_regs: [u16; 8] = [0, 1, 2, 3, 5, 6, 7, 8];
for &rm in &base_regs {
if rm != 5 {
let modrm = (Mod00 << 6) | rm;
let addr = matcher.decode(modrm, None, &[], false, rm >= 8, false);
if !addr.is_rip_relative {
assert_eq!(addr.base, Some(rm));
assert_eq!(addr.displacement, 0);
}
}
let modrm_disp8 = (Mod01 << 6) | rm;
let disp8_bytes = [0x2A];
let addr_d8 = matcher.decode(modrm_disp8, None, &disp8_bytes, false, rm >= 8, false);
if rm != 5 {
assert_eq!(addr_d8.base, Some(rm));
}
assert!(addr_d8.displacement == 42 || addr_d8.displacement == 0x2A);
let modrm_disp32 = (Mod10 << 6) | rm;
let disp32_bytes = [0x78, 0x56, 0x34, 0x12];
let addr_d32 = matcher.decode(modrm_disp32, None, &disp32_bytes, false, rm >= 8, false);
if rm != 5 {
assert_eq!(addr_d32.base, Some(rm));
}
}
}
#[test]
fn test_stress_many_address_modes_roundtrip() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr64);
let bases = [0u16, 1, 2, 3, 6, 7];
let indices = [0u16, 1, 2, 3, 5, 6, 7];
let scales = [1u8, 2, 4, 8];
let disps = [0i64, 42, -128, 127, 200, -200, 0x1000, -0x1000];
for &base in &bases {
for &index in &indices {
for &scale in &scales {
for &disp in &disps {
let original = X86AddressMode::full64(base, index, scale, disp);
let modrm = original.encode_modrm(0);
let sib = if original.requires_sib() {
Some(original.encode_sib())
} else {
None
};
let disp_bytes = original.encode_displacement();
let decoded = matcher.decode(modrm, sib, &disp_bytes, false, false, false);
assert_eq!(
decoded.base, original.base,
"base mismatch: {:?} != {:?} for base={}, index={}, scale={}, disp={}",
decoded.base, original.base, base, index, scale, disp
);
if original.has_index() {
assert_eq!(
decoded.index, original.index,
"index mismatch for base={}, index={}, scale={}, disp={}",
base, index, scale, disp
);
}
}
}
}
}
}
#[test]
fn test_builder_opt_level_interaction() {
for level in [OptLevel::None, OptLevel::Default, OptLevel::Aggressive] {
let engine = X86AddressingFull::new_x86_64().with_opt_level(level);
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode::base64(x86_register_info::RAX);
let optimized = opt.optimize(&addr);
assert_eq!(optimized.base, Some(x86_register_info::RAX));
}
}
#[test]
fn test_16bit_form_disp_sizes_all_forms() {
let all_forms = [
X86Addr16Form::BxSi,
X86Addr16Form::BxDi,
X86Addr16Form::BpSi,
X86Addr16Form::BpDi,
X86Addr16Form::Si,
X86Addr16Form::Di,
X86Addr16Form::Bp,
X86Addr16Form::Bx,
X86Addr16Form::BxSiDisp8,
X86Addr16Form::BxDiDisp8,
X86Addr16Form::BpSiDisp8,
X86Addr16Form::BpDiDisp8,
X86Addr16Form::SiDisp8,
X86Addr16Form::DiDisp8,
X86Addr16Form::BpDisp8,
X86Addr16Form::BxDisp8,
X86Addr16Form::BxSiDisp16,
X86Addr16Form::BxDiDisp16,
X86Addr16Form::BpSiDisp16,
X86Addr16Form::BpDiDisp16,
X86Addr16Form::SiDisp16,
X86Addr16Form::DiDisp16,
X86Addr16Form::BpDisp16,
X86Addr16Form::BxDisp16,
X86Addr16Form::Disp16,
];
for form in &all_forms {
let disp_size = form.disp_size();
assert!(
disp_size <= 2,
"Form {:?} has invalid disp size {}",
form,
disp_size
);
let mod_field = form.mod_field();
assert!(
mod_field <= 2,
"Form {:?} has invalid mod field {}",
form,
mod_field
);
let rm_field = form.rm_field();
assert!(
rm_field <= 7,
"Form {:?} has invalid rm field {}",
form,
rm_field
);
let name = form.name();
assert!(!name.is_empty(), "Form {:?} has empty name", form);
}
}
#[test]
fn test_modrm_form_register_direct() {
let matcher = AddressingModeMatcher::new(AddrWidth::Addr64);
let modrm: u8 = 0xC0;
let addr = matcher.decode(modrm, None, &[], false, false, false);
assert!(!addr.is_rip_relative);
}
#[test]
fn test_memory_lowering_all_tls_models_64() {
for &model in &[
TlsModel::GeneralDynamic,
TlsModel::LocalDynamic,
TlsModel::InitialExec,
TlsModel::LocalExec,
] {
let mut engine = X86AddressingFull::new_x86_64();
engine.tls_model = model;
let lower = X86MemoryLowering::new(engine);
let addr = lower.lower_tls_address("x", 16, true);
assert_eq!(addr.width, AddrWidth::Addr64);
assert!(
addr.segment == Some(x86_register_info::FS)
|| addr.is_rip_relative
|| addr.symbol.is_some(),
"Model {:?} produced addr with no segment, not RIP-relative, no symbol",
model
);
}
}
#[test]
fn test_memory_lowering_all_tls_models_32() {
for &model in &[
TlsModel::GeneralDynamic,
TlsModel::LocalDynamic,
TlsModel::InitialExec,
TlsModel::LocalExec,
] {
let mut engine = X86AddressingFull::new_x86_32();
engine.tls_model = model;
let lower = X86MemoryLowering::new(engine);
let addr = lower.lower_tls_address("x", 8, true);
assert_eq!(addr.width, AddrWidth::Addr32);
assert!(
addr.segment == Some(x86_register_info::GS)
|| addr.is_rip_relative
|| addr.symbol.is_some(),
"32-bit model {:?} produced addr with no segment, not RIP-relative, no symbol",
model
);
}
}
#[test]
fn test_full_modrm_table_consistency() {
let table = CompleteModRMTable::generate_64bit();
for entry in &table.entries {
let mod_field = (entry.modrm >> 6) & 0x3;
assert_eq!(
mod_field, entry.mod_field,
"Mod field mismatch in entry {:?}",
entry.modrm
);
let reg_field = (entry.modrm >> 3) & 0x7;
assert_eq!(reg_field, entry.reg_field, "Reg field mismatch");
let rm_field = entry.modrm & 0x7;
assert_eq!(rm_field, entry.rm_field, "RM field mismatch");
assert!(
entry.disp_size <= 4,
"Invalid disp size {}",
entry.disp_size
);
if entry.has_sib {
assert!(
entry.sib_byte.is_some(),
"SIB byte missing for entry with has_sib"
);
}
}
}
#[test]
fn test_builder_determines_absolute32() {
let m = X86AddressingModeBuilder::new32().with_disp(0x1000).build();
assert!(m.is_absolute_32);
assert_eq!(m.base, None);
assert_eq!(m.index, None);
}
#[test]
fn test_builder_determines_not_absolute_with_base() {
let m = X86AddressingModeBuilder::new32()
.with_base(x86_register_info::EAX)
.with_disp(0x1000)
.build();
assert!(!m.is_absolute_32);
}
#[test]
fn test_different_widths_produce_different_hash() {
let addr64 = X86AddressMode::base64(x86_register_info::RAX);
let addr32 = X86AddressMode::base32(x86_register_info::EAX);
assert_ne!(addr64.hash_key(), addr32.hash_key());
}
#[test]
fn test_optimization_form_canonicalization_removes_zero_scale() {
let engine = X86AddressingFull::new_x86_64();
let opt = X86AddressOptimization::new(engine);
let addr = X86AddressMode {
width: AddrWidth::Addr64,
base: Some(x86_register_info::RBX),
index: Some(x86_register_info::RSI),
scale: 0,
..Default::default()
};
let optimized = opt.optimize(&addr);
assert_eq!(optimized.index, None);
}
#[test]
fn test_constant_pool_multiple_entries() {
let mut pool = ConstantPool::new();
let idx0 = pool.add_entry(4, 4, false);
let idx1 = pool.add_entry(8, 8, true);
let idx2 = pool.add_entry(16, 16, false);
assert_eq!(idx0, 0);
assert_eq!(idx1, 1);
assert_eq!(idx2, 2);
assert_eq!(pool.len(), 3);
let addr0 = pool.get_address(0).unwrap();
assert!(addr0.symbol.unwrap().contains("LCPI0"));
let addr1 = pool.get_address(1).unwrap();
assert!(addr1.symbol.unwrap().contains("LCPI1"));
let addr2 = pool.get_address(2).unwrap();
assert!(addr2.symbol.unwrap().contains("LCPI2"));
}
#[test]
fn test_constant_pool_get_nonexistent() {
let pool = ConstantPool::new();
assert!(pool.get_address(0).is_none());
assert!(pool.get_address(999).is_none());
}
#[test]
fn test_16bit_absolute_address_is_disp16() {
let m = X86AddressMode::absolute16(0xB800);
assert!(m.is_absolute_16);
assert!(!m.is_absolute_32);
assert!(!m.is_rip_relative);
assert_eq!(m.form_16bit, Some(X86Addr16Form::Disp16));
assert_eq!(m.displacement, 0xB800);
}
#[test]
fn test_fold_form_enum_variants() {
let forms = [
FoldForm::CannotFold,
FoldForm::BaseOnly,
FoldForm::BaseDisp8,
FoldForm::BaseDisp32,
FoldForm::FullWithIndex,
FoldForm::FullWithIndexDisp32,
];
for i in 0..forms.len() {
for j in (i + 1)..forms.len() {
assert_ne!(forms[i], forms[j]);
}
}
}
#[test]
fn test_is_simple_no_sib() {
let addr = X86AddressMode::base64(x86_register_info::RAX);
assert!(addr.is_simple());
}
#[test]
fn test_is_not_simple_with_sib() {
let addr = X86AddressMode::full64(x86_register_info::RBX, x86_register_info::RSI, 4, 0);
assert!(!addr.is_simple());
}
#[test]
fn test_can_be_lea() {
let addr = X86AddressMode::base64(x86_register_info::RAX);
assert!(addr.can_be_lea());
let rip = X86AddressMode::rip_relative(0);
assert!(!rip.can_be_lea());
}
#[test]
fn test_addressing_form_names() {
assert_eq!(AddressingForm::BaseOnly.name(), "[base]");
assert_eq!(AddressingForm::BaseDisp8.name(), "[base+disp8]");
assert_eq!(AddressingForm::RipRelative.name(), "[rip+disp32]");
assert_eq!(AddressingForm::Absolute32.name(), "[disp32]");
assert_eq!(AddressingForm::Absolute16.name(), "[disp16]");
assert_eq!(AddressingForm::None.name(), "none");
}
#[test]
fn test_sib_scale_constants() {
assert_eq!(sib_scale::TIMES_1, 0);
assert_eq!(sib_scale::TIMES_2, 1);
assert_eq!(sib_scale::TIMES_4, 2);
assert_eq!(sib_scale::TIMES_8, 3);
}
#[test]
fn test_sib_field_constants() {
assert_eq!(sib_field::NO_INDEX, 4);
assert_eq!(sib_field::DISP32_BASE, 5);
assert_eq!(sib_field::RSP_BASE, 4);
assert_eq!(sib_field::RBP_BASE, 5);
}
#[test]
fn test_mod_field_constants() {
assert_eq!(mod_field::MOD00, 0);
assert_eq!(mod_field::MOD01, 1);
assert_eq!(mod_field::MOD10, 2);
assert_eq!(mod_field::MOD11, 3);
}
#[test]
fn test_modrm_form_enum_variants_distinct() {
let variants = [
ModRMForm::BaseOnly,
ModRMForm::BaseDisp8,
ModRMForm::BaseDisp32,
ModRMForm::IndexScale,
ModRMForm::BaseIndexScale,
ModRMForm::BaseIndexScaleDisp8,
ModRMForm::BaseIndexScaleDisp32,
ModRMForm::RIPRelative,
ModRMForm::Absolute,
ModRMForm::RegisterDirect,
];
for i in 0..variants.len() {
for j in (i + 1)..variants.len() {
assert_ne!(variants[i], variants[j]);
}
}
}
#[test]
fn test_load_lowering_result_fields() {
let result = LoadLoweringResult {
address_mode: X86AddressMode::base64(x86_register_info::RAX),
opcode: X86Opcode::MOV,
size: 8,
needs_rex: false,
needs_sib: false,
};
assert_eq!(result.size, 8);
assert_eq!(result.opcode, X86Opcode::MOV);
}
#[test]
fn test_store_lowering_result_fields() {
let result = StoreLoweringResult {
address_mode: X86AddressMode::base64(x86_register_info::RAX),
opcode: X86Opcode::MOV,
size: 4,
needs_rex: false,
needs_sib: false,
};
assert_eq!(result.size, 4);
assert_eq!(result.opcode, X86Opcode::MOV);
}
#[test]
fn test_opt_level_enum_distinct() {
assert_ne!(OptLevel::None, OptLevel::Default);
assert_ne!(OptLevel::Default, OptLevel::Aggressive);
assert_ne!(OptLevel::None, OptLevel::Aggressive);
}
#[test]
fn test_code_model_enum_distinct() {
assert_ne!(CodeModel::Small, CodeModel::Kernel);
assert_ne!(CodeModel::Kernel, CodeModel::Medium);
assert_ne!(CodeModel::Medium, CodeModel::Large);
}
#[test]
fn test_tls_model_enum_distinct() {
assert_ne!(TlsModel::GeneralDynamic, TlsModel::LocalDynamic);
assert_ne!(TlsModel::LocalDynamic, TlsModel::InitialExec);
assert_ne!(TlsModel::InitialExec, TlsModel::LocalExec);
}
}