From ef8f0618e8d37614c5964156caf1305372fb3187 Mon Sep 17 00:00:00 2001 From: Connor Horman Date: Thu, 21 Sep 2023 22:11:59 -0400 Subject: [PATCH] feat(arch-ops-x86): Overhaul x86 instruction representation and encoding --- Cargo.lock | 7 + arch-ops/Cargo.toml | 3 +- arch-ops/src/traits.rs | 3 + arch-ops/src/x86.rs | 94 +- arch-ops/src/x86/codegen.rs | 1743 ++++++++++++++++++++++++++++++ arch-ops/src/x86/codegen/insn.rs | 485 +++++++++ arch-ops/src/x86/features.rs | 7 +- arch-ops/src/x86/insn.rs | 45 +- 8 files changed, 2335 insertions(+), 52 deletions(-) create mode 100644 arch-ops/src/x86/codegen.rs create mode 100644 arch-ops/src/x86/codegen/insn.rs diff --git a/Cargo.lock b/Cargo.lock index fa52f84..4e44418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,7 @@ name = "arch-ops" version = "0.1.0" dependencies = [ "lazy_static", + "paste", "target-tuples", ] @@ -108,6 +109,12 @@ dependencies = [ "target-tuples", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "proc-macro2" version = "1.0.66" diff --git a/arch-ops/Cargo.toml b/arch-ops/Cargo.toml index def4e5e..6e2b7f1 100644 --- a/arch-ops/Cargo.toml +++ b/arch-ops/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "arch-ops" version = "0.1.0" -authors = ["chorm", "rdrpenguin"] +authors = ["Connor Horman ", "rdrpenguin"] description = "A library to encode and decode instructions for several architectures" edition = "2018" rust-version = "1.56" @@ -12,6 +12,7 @@ license = "BSD-2-Clause-Patent" [dependencies] lazy_static = "1" target-tuples = "0.5.5" +paste = "1" [features] x86 = [] diff --git a/arch-ops/src/traits.rs b/arch-ops/src/traits.rs index b0cf12a..05339b9 100644 --- a/arch-ops/src/traits.rs +++ b/arch-ops/src/traits.rs @@ -64,6 +64,7 @@ impl InsnRead for Box { } } +/// Writes `count` zeroes to `out` using the default block, in blocks of 1024. pub fn default_write_zeroes(mut out: W, mut count: usize) -> std::io::Result<()> { let val: [u8; 1024] = [0; 1024]; while count >= 1024 { @@ -74,9 +75,11 @@ pub fn default_write_zeroes(mut out: W, mut count: usize) -> std::io:: } pub trait InsnWrite: Write { + /// Writes an address of `size` (in bits) to the writer. This will typically generate a relocation fn write_addr(&mut self, size: usize, addr: Address, rel: bool) -> std::io::Result<()>; fn write_reloc(&mut self, reloc: Reloc) -> std::io::Result<()>; fn offset(&self) -> usize; + /// Writes `count` zero bytes. This is provided as some writer types (such as sections) may implement this more efficiently then the default fn write_zeroes(&mut self, count: usize) -> std::io::Result<()> { default_write_zeroes(self, count) } diff --git a/arch-ops/src/x86.rs b/arch-ops/src/x86.rs index ed98718..369f8e4 100644 --- a/arch-ops/src/x86.rs +++ b/arch-ops/src/x86.rs @@ -1,3 +1,4 @@ +use target_tuples::Target; #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] #[non_exhaustive] pub enum X86RegisterClass { @@ -37,6 +38,19 @@ impl X86RegisterClass { } } + /// Whether or not register numbers >16 can be encoded using REX2 (else use EVEX) + pub fn use_rex2(&self) -> bool { + match self { + Self::ByteRex => true, + Self::Word => true, + Self::Double => true, + Self::Quad => true, + Self::Cr => true, + Self::Dr => true, + _ => false, + } + } + pub fn gpr_size(size: usize, mode: X86Mode) -> Option { match size { 1 if mode == X86Mode::Long => Some(X86RegisterClass::ByteRex), @@ -129,7 +143,7 @@ pub enum X86Register { Rsi, Rdi, - // r64 high (REX.B/REX.R) + // r64 high (REX.B/REX.R/REX.X) R8, R9, R10, @@ -139,6 +153,15 @@ pub enum X86Register { R14, R15, + // r64 extended + R(u8), + // r32 extended, + Rd(u8), + // r16 extended + Rw(u8), + // r8 extended + Rl(u8), + Mmx(u8), Xmm(u8), @@ -184,14 +207,60 @@ macro_rules! define_x86_registers{ use X86Register::*; -use self::insn::X86Mode; +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] + +pub enum X86Mode { + Real, + Protected, + Virtual8086, + Compatibility, + Long, +} + +impl X86Mode { + pub fn default_mode_for(target: &Target) -> Option { + match target.arch() { + target_tuples::Architecture::I86 => Some(X86Mode::Real), + target_tuples::Architecture::I8086 => Some(X86Mode::Real), + target_tuples::Architecture::I086 => Some(X86Mode::Real), + target_tuples::Architecture::I186 => Some(X86Mode::Real), + target_tuples::Architecture::I286 => Some(X86Mode::Real), + target_tuples::Architecture::I386 => Some(X86Mode::Protected), + target_tuples::Architecture::I486 => Some(X86Mode::Protected), + target_tuples::Architecture::I586 => Some(X86Mode::Protected), + target_tuples::Architecture::I686 => Some(X86Mode::Protected), + target_tuples::Architecture::X86_64 => Some(X86Mode::Long), + _ => None, + } + } + + pub fn largest_gpr(&self) -> X86RegisterClass { + match self { + Self::Real | Self::Virtual8086 => X86RegisterClass::Word, + Self::Protected | Self::Compatibility => X86RegisterClass::Double, + Self::Long => X86RegisterClass::Quad, + } + } + + pub fn width(&self) -> u16 { + match self { + Self::Real | Self::Virtual8086 => 16, + Self::Protected | Self::Compatibility => 32, + Self::Long => 64, + } + } +} define_x86_registers! { regs [Al, Cl, Dl, Bl, Ah, Ch, Dh, Bh]: Byte; - regs [Al, Cl, Dl, Bl, Spl, Bpl, Sil, Dil, R8b, R9b, R10b, R11b, R12b, R13b, R14b, R15b]: ByteRex; - regs [Ax, Cx, Dx, Bx, Sp, Bp, Si, Di, R8w, R9w, R10w, R11w, R12w, R13w, R14w, R15w]: Word; - regs [Eax, Ecx, Edx, Ebx, Esp, Ebp, Esi, Edi, R8d, R9d, R10d, R11d, R12d, R13d, R14d, R15d]: Double; - regs [Rax, Rcx, Rdx, Rbx, Rsp, Rbp, Rsi, Rdi, R8, R9, R10, R11, R12, R13, R14, R15]: Quad; + regs [Al, Cl, Dl, Bl, Spl, Bpl, Sil, Dil, R8b, R9b, R10b, R11b, R12b, R13b, R14b, R15b, + Rl(16), Rl(17), Rl(18), Rl(19), Rl(20), Rl(21), Rl(22), Rl(23),Rl(24), Rl(25), Rl(26), Rl(27), Rl(28), Rl(29), Rl(30), Rl(31)]: ByteRex; + regs [Ax, Cx, Dx, Bx, Sp, Bp, Si, Di, R8w, R9w, R10w, R11w, R12w, R13w, R14w, R15w, + Rw(16), Rw(17), Rw(18), Rw(19), Rw(20), Rw(21), Rw(22), Rw(23), Rw(24), Rw(25), Rw(26), Rw(27), Rw(28), Rw(29), Rw(30), Rw(31)]: Word; + regs [Eax, Ecx, Edx, Ebx, Esp, Ebp, Esi, Edi, R8d, R9d, R10d, R11d, R12d, R13d, R14d, R15d, + Rd(16), Rd(17), Rd(18), Rd(19), Rd(20), Rd(21), Rd(22), Rd(23), Rd(24), Rd(25), Rd(26), Rd(27), Rd(28), Rd(29), Rd(30), Rd(31)]: Double; + regs [Rax, Rcx, Rdx, Rbx, Rsp, Rbp, Rsi, Rdi, R8, R9, R10, R11, R12, R13, R14, R15, + R(16), R(17), R(18), R(19), R(20), R(21), R(22), R(23), R(24), R(25), R(26), R(27), R(28), R(29), R(30), R(31)]: Quad; regs [Mmx(0), Mmx(1), Mmx(2), Mmx(3), Mmx(4), Mmx(5), Mmx(6), Mmx(7), Mmx(0), Mmx(1), Mmx(2), Mmx(3), Mmx(4), Mmx(5), Mmx(6), Mmx(7)]: Mmx; regs [Xmm(0), Xmm(1), Xmm(2), Xmm(3), Xmm(4), Xmm(5), Xmm(6), Xmm(7), Xmm(8), Xmm(9), Xmm(10), Xmm(11), Xmm(12), Xmm(13), Xmm(14), Xmm(15), Xmm(16), Xmm(17), Xmm(18), Xmm(19), Xmm(20), Xmm(21), Xmm(22), Xmm(23), Xmm(24), Xmm(25), Xmm(26), Xmm(27), Xmm(28), Xmm(29), Xmm(30), Xmm(31)]: Xmm; @@ -283,6 +352,10 @@ impl X86Register { R13 => 13, R14 => 14, R15 => 15, + R(m) => m, + Rd(m) => m, + Rw(m) => m, + Rl(m) => m, Mmx(m) => m, Xmm(m) => m, Ymm(m) => m, @@ -325,6 +398,7 @@ impl X86Register { R13b => X86RegisterClass::ByteRex, R14b => X86RegisterClass::ByteRex, R15b => X86RegisterClass::ByteRex, + Rl(_) => X86RegisterClass::ByteRex, Ax => X86RegisterClass::Word, Cx => X86RegisterClass::Word, Dx => X86RegisterClass::Word, @@ -341,6 +415,7 @@ impl X86Register { R13w => X86RegisterClass::Word, R14w => X86RegisterClass::Word, R15w => X86RegisterClass::Word, + Rw(_) => X86RegisterClass::Word, Eax => X86RegisterClass::Double, Ecx => X86RegisterClass::Double, Edx => X86RegisterClass::Double, @@ -357,6 +432,7 @@ impl X86Register { R13d => X86RegisterClass::Double, R14d => X86RegisterClass::Double, R15d => X86RegisterClass::Double, + Rd(_) => X86RegisterClass::Double, Rax => X86RegisterClass::Quad, Rcx => X86RegisterClass::Quad, Rdx => X86RegisterClass::Quad, @@ -373,6 +449,7 @@ impl X86Register { R13 => X86RegisterClass::Quad, R14 => X86RegisterClass::Quad, R15 => X86RegisterClass::Quad, + R(_) => X86RegisterClass::Quad, Mmx(_) => X86RegisterClass::Mmx, Xmm(_) => X86RegisterClass::Xmm, Ymm(_) => X86RegisterClass::Ymm, @@ -417,6 +494,7 @@ impl Display for X86Register { R13b => f.write_str("r13b"), R14b => f.write_str("r14b"), R15b => f.write_str("r15b"), + Rl(m) => f.write_fmt(format_args!("r{}b", m)), Ax => f.write_str("ax"), Cx => f.write_str("cx"), Dx => f.write_str("dx"), @@ -433,6 +511,7 @@ impl Display for X86Register { R13w => f.write_str("r13w"), R14w => f.write_str("r14w"), R15w => f.write_str("r15w"), + Rw(m) => f.write_fmt(format_args!("r{}w", m)), Eax => f.write_str("eax"), Ecx => f.write_str("ecx"), Edx => f.write_str("edx"), @@ -449,6 +528,7 @@ impl Display for X86Register { R13d => f.write_str("r13d"), R14d => f.write_str("r14d"), R15d => f.write_str("r15d"), + Rd(m) => f.write_fmt(format_args!("r{}d", m)), Rax => f.write_str("rax"), Rcx => f.write_str("rcx"), Rdx => f.write_str("rdx"), @@ -465,6 +545,7 @@ impl Display for X86Register { R13 => f.write_str("r13"), R14 => f.write_str("r14"), R15 => f.write_str("r15"), + R(m) => f.write_fmt(format_args!("r{}", m)), Mmx(n) => f.write_fmt(format_args!("mm{}", n)), Xmm(n) => f.write_fmt(format_args!("xmm{}", n)), Ymm(n) => f.write_fmt(format_args!("ymm{}", n)), @@ -489,5 +570,6 @@ impl Display for X86Register { pub mod cpu; pub mod features; +pub mod codegen; pub mod insn; pub mod printer; diff --git a/arch-ops/src/x86/codegen.rs b/arch-ops/src/x86/codegen.rs new file mode 100644 index 0000000..c3a9995 --- /dev/null +++ b/arch-ops/src/x86/codegen.rs @@ -0,0 +1,1743 @@ +use crate::traits::{Address, InsnWrite}; +use std::{ + io::{self, Write}, + marker::PhantomData, + mem::MaybeUninit, +}; + +use super::{X86Mode, X86Register, X86RegisterClass}; + +macro_rules! options{ + { + $(#[$meta:meta])* + $vis:vis struct $options_ty:ident $(:$(#[$meta3:meta])* $base_ty:ident)?{ + $($(#[$meta2:meta])* $vis2:vis $field:ident),* $(,)? + } + } => { + $(#[$meta])* + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Default)] + $vis struct $options_ty{ + #[doc(hidden)] + $vis __nonexhausive: (), + $($(#[$meta3])* #[doc(hidden)] $vis __base: $base_ty,)? + $($(#[$meta2])* $vis2 $field: bool),* + } + + impl $options_ty{ + pub const NONE: Self = Self::new(); + + paste::paste!{ + $($vis2 const [<$field:upper>]: Self = Self{$field: true, ..Self::new()};)* + } + + $(pub const fn base(x: $base_ty) -> Self{ + Self{__nonexhausive: (), __base: x, ..Self::new()} + })? + + pub const fn new() -> Self{ + Self{__nonexhausive: (), $(__base: $base_ty :: NONE,)? $($field: false),*} + } + } + + impl core::ops::BitOr for $options_ty{ + type Output = Self; + fn bitor(mut self, rhs: Self) -> Self{ + self |= rhs; + self + } + } + + impl core::ops::BitOrAssign for $options_ty{ + fn bitor_assign(&mut self, rhs: Self){ + $(self. $field |= rhs.$field;)* + } + } + $( + impl core::ops::BitOr for $options_ty where $base_ty: core::ops::BitOrAssign{ + type Output = Self; + fn bitor(mut self, rhs: B) -> Self{ + self |= rhs; + self + } + } + + impl core::ops::BitOr<$options_ty> for $base_ty{ + type Output = $options_ty; + fn bitor(self, mut rhs: $options_ty) -> $options_ty{ + rhs.__base |= self; + + rhs + } + } + + impl core::ops::BitOrAssign for $options_ty where $base_ty: core::ops::BitOrAssign{ + fn bitor_assign(&mut self, rhs: B){ + self.__base |= rhs + } + } + )? + } +} + +options! { + pub struct ModRMOptions{ + pub no_rex_w, + pub ignore_size_mismatch, + pub no_escape, + } +} + +options! { + pub struct AvxModRMOptions : ModRMOptions{ + pub vex_extended_vex_only, + } +} + +options! { + pub struct OpRegOptions{ + pub no_rex_w, + } +} + +options! { + pub struct LegacyEvexEncodingOptions{ + pub no_rex_w, + pub ndd, + pub allow_nf, + } +} + +options! { + pub struct EvexModRMOptions: ModRMOptions{ + pub allow_size_256, + pub allow_size_512, + pub use_mask, + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum X86PrefixGroup { + LegacyControl, +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[non_exhaustive] +pub enum X86EncodingMode { + Prefix(X86PrefixGroup), + OpcodeOnly, + ModRM(ModRMOptions), + ModRMControl(u8, ModRMOptions), + ModRMOp2(u8), + OpReg(OpRegOptions), + AvxModRM(AvxModRMOptions), + EvexModRM(EvexModRMOptions), + LegacyEvexModRM(LegacyEvexEncodingOptions), + OpcodeWithSize(X86RegisterClass), + OffsetImm(usize), +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[non_exhaustive] +pub enum X86InstructionMap { + /// No escape prefix + Legacy, + /// Escape prefix 0F + Extended, + /// Escape prefix 0F 38 + Extended2, + /// Escape prefix 0F 3A + Extended3, +} + +impl X86InstructionMap { + pub const fn from_opcode(opc: u32) -> Option { + match (opc & 0xFFFFFF) >> 8 { + 0 => Some(Self::Legacy), + 0x0F => Some(Self::Extended), + 0x0F38 => Some(Self::Extended2), + 0x0F3A => Some(Self::Extended3), + _ => None, + } + } + + pub const fn from_vex_map(map: u8) -> Option { + match map { + 4 => Some(Self::Legacy), + 1 => Some(Self::Extended), + 2 => Some(Self::Extended2), + 3 => Some(Self::Extended3), + _ => None, + } + } + + pub const fn escape_prefix_bytes(&self) -> &'static [u8] { + match self { + Self::Legacy => &[], + Self::Extended => &[0x0F], + Self::Extended2 => &[0x0F, 0x38], + Self::Extended3 => &[0x0F, 0x3A], + } + } + + pub const fn vex_map_value(&self) -> u8 { + match self { + Self::Legacy => 4, + Self::Extended => 1, + Self::Extended2 => 2, + Self::Extended3 => 3, + } + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[non_exhaustive] +pub enum X86EncodingPrefix { + NoPrefix, + Rex, + Rex2, + Vex, + Evex, + + #[doc(hidden)] + __PrefixCount, +} + +impl X86EncodingPrefix { + pub const PREFIX_COUNT: usize = Self::__PrefixCount as usize; +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum SsePrefix { + PackedDouble, + ScalarDouble, + ScalarSingle, +} + +impl SsePrefix { + pub const fn from_opcode(opc: u32) -> Option { + match opc >> 24 { + 0x66 => Some(SsePrefix::PackedDouble), + 0xF2 => Some(SsePrefix::ScalarDouble), + 0xF3 => Some(SsePrefix::ScalarSingle), + _ => None, + } + } + + pub const fn encoding(&self) -> &'static [u8] { + match self { + Self::PackedDouble => &[0x66], + Self::ScalarDouble => &[0xF2], + Self::ScalarSingle => &[0xF3], + } + } + + pub const fn vex_prefix_value(&self) -> u8 { + match self { + Self::PackedDouble => 1, + Self::ScalarSingle => 2, + Self::ScalarDouble => 3, + } + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct X86Encoding { + pub map: X86InstructionMap, + pub base_opcode: u8, + pub mode: X86EncodingMode, + pub allowed_modes: Option<&'static [X86Mode]>, + pub sse_prefix: Option, + pub imm_size: usize, +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[repr(u8)] +pub enum Segment { + Es = 0x26, + Cs = 0x2E, + Ss = 0x36, + Ds = 0x3E, + Fs = 0x64, + Gs = 0x65, +} + +impl core::fmt::Display for Segment { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Es => f.write_str("es"), + Self::Cs => f.write_str("cs"), + Self::Ss => f.write_str("ss"), + Self::Ds => f.write_str("ds"), + Self::Fs => f.write_str("fs"), + Self::Gs => f.write_str("gs"), + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct MaskSpec { + zero: bool, + mask_reg: X86Register, +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[non_exhaustive] +pub enum X86Operand { + Mask(MaskSpec), + Register(X86Register), + Memory(X86RegisterClass, Option, X86MemoryOperand), + Immediate(i64), + RelOffset(Address), + AbsOffset(Address), +} + +impl core::fmt::Display for X86Operand { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + X86Operand::Mask(mask) => { + f.write_fmt(format_args!("{{{}}}", mask.mask_reg))?; + + if mask.zero { + f.write_str("{z}")?; + } + + Ok(()) + } + X86Operand::Register(reg) => reg.fmt(f), + X86Operand::Memory(size, seg, mem) => { + match size { + X86RegisterClass::Byte => f.write_str("byte ptr ")?, + X86RegisterClass::Word => f.write_str("word ptr ")?, + X86RegisterClass::Double => f.write_str("dword ptr ")?, + X86RegisterClass::Quad => f.write_str("qword ptr ")?, + X86RegisterClass::St => f.write_str("tbyte ptr ")?, + X86RegisterClass::Xmm => f.write_str("xmmword ptr ")?, + X86RegisterClass::Ymm => f.write_str("ymmword ptr ")?, + X86RegisterClass::Zmm => f.write_str("zmmword ptr ")?, + X86RegisterClass::Tmm => f.write_str("tmmword ptr ")?, + r => panic!("Bad size specifier for x86 memory operand {:?}", r), + } + + if let Some(seg) = seg { + f.write_fmt(format_args!("{}:", seg))?; + } + + f.write_fmt(format_args!("[{}]", mem)) + } + X86Operand::Immediate(imm) => imm.fmt(f), + X86Operand::AbsOffset(addr) => f.write_fmt(format_args!("offset {}", addr)), + X86Operand::RelOffset(addr) => f.write_fmt(format_args!("disp {}", addr)), + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +#[non_exhaustive] +pub enum X86MemoryOperand { + Indirect { + reg: X86Register, + disp: Option
, + }, + IndirectSib { + scale: u8, + index: X86Register, + base: Option, + disp: Option
, + }, + RelAddr(Address), + AbsAddr(Address), +} + +impl core::fmt::Display for X86MemoryOperand { + #[allow(unused_assignments)] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::AbsAddr(addr) => addr.fmt(f), + Self::RelAddr(addr) => f.write_fmt(format_args!("{}+rip", addr)), + Self::Indirect { reg, disp } => { + reg.fmt(f)?; + if let Some(disp) = disp { + f.write_str(" + ")?; + disp.fmt(f)? + } + Ok(()) + } + Self::IndirectSib { + base, + scale, + index, + disp, + } => { + let mut sep = ""; + if *scale > 1 { + f.write_fmt(format_args!("{}*", scale))?; + } + index.fmt(f)?; + sep = " + "; + + if let Some(base) = base { + f.write_str(sep)?; + sep = " + "; + base.fmt(f)?; + } + + if let Some(disp) = disp { + f.write_str(sep)?; + disp.fmt(f)?; + } + + Ok(()) + } + } + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +#[non_exhaustive] +pub enum X86OperandMode { + TargetRegister(X86Register), + Reg(X86RegisterClass), + RegRM(X86RegisterClass), + MemOrReg(X86RegisterClass), + MemoryOnly(X86RegisterClass), + MemOffset(X86RegisterClass), + Immediate(X86RegisterClass), + ImmediateVal(i64), + RelAddr(u8), + AbsAddr(u8), + StringAddr(X86RegisterClass, X86Register), + Mask, +} + +impl X86Operand { + pub fn matches_mode bool>(&self, accepter: F) -> bool { + match self { + X86Operand::Mask(_) => accepter(X86OperandMode::Mask), + X86Operand::Register(reg) if reg.class() == X86RegisterClass::ByteRex => [ + X86OperandMode::TargetRegister(*reg), + X86OperandMode::Reg(X86RegisterClass::Byte), + X86OperandMode::MemOrReg(X86RegisterClass::Byte), + X86OperandMode::RegRM(X86RegisterClass::Byte), + ] + .iter() + .copied() + .any(accepter), + X86Operand::Register(reg) => [ + X86OperandMode::TargetRegister(*reg), + X86OperandMode::Reg(reg.class()), + X86OperandMode::MemOrReg(reg.class()), + X86OperandMode::RegRM(reg.class()), + ] + .iter() + .copied() + .any(accepter), + X86Operand::Memory( + cl, + None | Some(Segment::Es), + X86MemoryOperand::Indirect { + reg: reg @ (X86Register::Di | X86Register::Edi | X86Register::Rdi), + disp: None, + }, + ) => [ + X86OperandMode::MemoryOnly(*cl), + X86OperandMode::MemOrReg(*cl), + X86OperandMode::StringAddr(*cl, *reg), + ] + .iter() + .copied() + .any(accepter), + X86Operand::Memory(cl, _, X86MemoryOperand::Indirect { reg, disp: None }) => [ + X86OperandMode::MemoryOnly(*cl), + X86OperandMode::MemOrReg(*cl), + X86OperandMode::StringAddr(*cl, *reg), + ] + .iter() + .copied() + .any(accepter), + X86Operand::Memory(cl, _, X86MemoryOperand::RelAddr(_)) => [ + X86OperandMode::MemOffset(*cl), + X86OperandMode::MemoryOnly(*cl), + X86OperandMode::MemOrReg(*cl), + ] + .iter() + .copied() + .any(accepter), + X86Operand::Memory(cl, _, _) => [ + X86OperandMode::MemoryOnly(*cl), + X86OperandMode::MemOrReg(*cl), + ] + .iter() + .copied() + .any(accepter), + X86Operand::Immediate(x) if -128 <= *x && *x < 128 => [ + X86OperandMode::ImmediateVal(*x), + X86OperandMode::Immediate(X86RegisterClass::Byte), + X86OperandMode::Immediate(X86RegisterClass::Word), + X86OperandMode::Immediate(X86RegisterClass::Double), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::Immediate(x) if -32768 <= *x && *x < 32768 => [ + X86OperandMode::ImmediateVal(*x), + X86OperandMode::Immediate(X86RegisterClass::Word), + X86OperandMode::Immediate(X86RegisterClass::Double), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::Immediate(x) if -2147483648 <= *x && *x < 2147483648 => [ + X86OperandMode::ImmediateVal(*x), + X86OperandMode::Immediate(X86RegisterClass::Double), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::Immediate(x) => [ + X86OperandMode::ImmediateVal(*x), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::RelOffset(Address::Disp(x)) if -128 <= *x && *x < 128 => [ + X86OperandMode::RelAddr(8), + X86OperandMode::RelAddr(16), + X86OperandMode::RelAddr(32), + X86OperandMode::Immediate(X86RegisterClass::Byte), + X86OperandMode::Immediate(X86RegisterClass::Word), + X86OperandMode::Immediate(X86RegisterClass::Double), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::RelOffset(Address::Disp(x)) if -32768 <= *x && *x < 32768 => [ + X86OperandMode::RelAddr(16), + X86OperandMode::RelAddr(32), + X86OperandMode::Immediate(X86RegisterClass::Word), + X86OperandMode::Immediate(X86RegisterClass::Double), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::RelOffset(Address::Disp(x)) if -2147483648 <= *x && *x < 2147483648 => [ + X86OperandMode::RelAddr(32), + X86OperandMode::Immediate(X86RegisterClass::Double), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::RelOffset(_) => [ + X86OperandMode::RelAddr(32), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::AbsOffset(Address::Abs(x)) if *x < 256 => [ + X86OperandMode::AbsAddr(8), + X86OperandMode::AbsAddr(16), + X86OperandMode::AbsAddr(32), + X86OperandMode::Immediate(X86RegisterClass::Byte), + X86OperandMode::Immediate(X86RegisterClass::Word), + X86OperandMode::Immediate(X86RegisterClass::Double), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::AbsOffset(Address::Abs(x)) if *x < 65536 => [ + X86OperandMode::AbsAddr(16), + X86OperandMode::AbsAddr(32), + X86OperandMode::Immediate(X86RegisterClass::Word), + X86OperandMode::Immediate(X86RegisterClass::Double), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::AbsOffset(Address::Abs(x)) if *x < 4294967296 => [ + X86OperandMode::AbsAddr(32), + X86OperandMode::Immediate(X86RegisterClass::Double), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + X86Operand::AbsOffset(_) => [ + X86OperandMode::AbsAddr(32), + X86OperandMode::Immediate(X86RegisterClass::Quad), + ] + .iter() + .copied() + .any(accepter), + } + } +} + +mod insn; + +pub use insn::X86CodegenOpcode; + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum X86Prefix { + Lock, + Repnz, + Repz, + Rep, +} + +impl X86Prefix { + pub fn mnemonic(&self) -> &'static str { + match self { + Self::Lock => "lock", + Self::Repnz => "repnz", + Self::Repz => "repz", + Self::Rep => "rep", + } + } + + pub fn opcode(&self) -> u8 { + match self { + Self::Lock => 0xF0, + Self::Repnz => 0xF2, + Self::Repz => 0xF3, + Self::Rep => 0xF3, + } + } +} + +impl core::fmt::Display for X86Prefix { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.write_str(self.mnemonic()) + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct X86Instruction { + prefix: Option, + opc: X86CodegenOpcode, + oprs: Vec, + mode_override: Option, +} + +macro_rules! nop_instructions{ + [ + $($mnemonic:ident),* $(,)? + ] => { + paste::paste!{ + #[allow(non_upper_case_globals)] + impl X86Instruction{ + $(pub const [<$mnemonic:camel>]: Self = Self::new(X86CodegenOpcode::[<$mnemonic:camel>],vec![]);)* + } + } + } +} + +nop_instructions![ + ud2, int3, into, iret, iretq, ret, retf, leave, pushf, pushfd, pushfq, lahf, sahf, nop, +]; + +impl X86Instruction { + pub const fn new(opc: X86CodegenOpcode, oprs: Vec) -> Self { + Self { + prefix: None, + opc, + oprs, + mode_override: None, + } + } + pub const fn with_prefix(mut self, prefix: X86Prefix) -> Self { + self.prefix = Some(prefix); + self + } + pub const fn with_mode(mut self, mode: X86Mode) -> Self { + self.mode_override = Some(mode); + self + } + + pub const fn opcode(&self) -> X86CodegenOpcode { + self.opc + } + + pub fn operands(&self) -> &[X86Operand] { + &self.oprs + } + + pub const fn prefix(&self) -> Option { + self.prefix + } + + pub const fn mode_override(&self) -> Option { + self.mode_override + } +} + +impl core::fmt::Display for X86Instruction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(prefix) = &self.prefix { + prefix.fmt(f)?; + f.write_str(" ")?; + } + + self.opc.fmt(f)?; + + let mut sep = " "; + + for opr in &self.oprs { + f.write_str(sep)?; + sep = ", "; + opr.fmt(f)?; + } + + if let Some(x) = self.mode_override { + f.write_fmt(format_args!("; in mode {:?}", x))?; + } + + Ok(()) + } +} + +pub struct X86Encoder { + writer: W, + mode: X86Mode, +} + +impl X86Encoder { + pub const fn new(writer: W, mode: X86Mode) -> Self { + Self { writer, mode } + } + + pub fn into_inner(self) -> W { + self.writer + } + + pub fn inner(&self) -> &W { + &self.writer + } + + pub fn inner_mut(&mut self) -> &mut W { + &mut self.writer + } + pub fn mode(&self) -> X86Mode { + self.mode + } + pub fn set_mode(&mut self, mode: X86Mode) { + self.mode = mode; + } +} + +impl io::Write for X86Encoder { + fn write(&mut self, buf: &[u8]) -> io::Result { + eprintln!("write {:x?}", buf); + self.writer.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.writer.flush() + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + eprintln!("write_all {:#02x?}", buf); + self.writer.write_all(buf) + } +} + +impl InsnWrite for X86Encoder { + fn offset(&self) -> usize { + self.writer.offset() + } + fn write_addr(&mut self, size: usize, addr: Address, rel: bool) -> std::io::Result<()> { + self.writer.write_addr(size, addr, rel) + } + fn write_reloc(&mut self, reloc: crate::traits::Reloc) -> std::io::Result<()> { + self.writer.write_reloc(reloc) + } + fn write_zeroes(&mut self, count: usize) -> std::io::Result<()> { + self.writer.write_zeroes(count) + } +} + +pub struct NoRM; +pub struct NoReg; +pub struct NoReg3; +pub struct NoMask; + +pub struct NoOpcode; + +pub struct RM; + +pub struct Reg; + +pub struct Reg3; + +pub struct Mask; + +pub struct Opcode; + +pub struct ModRMBuilder { + use_nf: bool, + sib_byte: Option, + /// [0]=>b,[1]=>x,[2]=>r + reg_top_bits: [u8; 3], + modrm_byte: u8, + disp: Option
, + size: Option, + reg3: Option, + mode: X86Mode, + group: Option, + sse_prefix: Option, + mask: Option, + rel_addr: bool, + addr_size: Option, + sreg: Option, + _status: PhantomData<(HasRM, HasReg, HasReg3, HasMask, HasOpcode)>, +} + +impl ModRMBuilder { + pub const fn new(mode: X86Mode) -> Self { + Self { + use_nf: false, + sib_byte: None, + reg_top_bits: [0u8; 3], + modrm_byte: 0, + disp: None, + size: None, + reg3: None, + mode, + group: None, + sse_prefix: None, + mask: None, + rel_addr: false, + addr_size: None, + sreg: None, + _status: PhantomData, + } + } +} + +impl + ModRMBuilder +{ + pub fn prefix_allowed(&self, prefix: &X86EncodingPrefix, opts: ModRMOptions) -> bool { + match prefix { + X86EncodingPrefix::NoPrefix => { + match self.reg_top_bits { + [0, 0, 0] => {} + _ => return false, + } + + if self.use_nf { + return false; // Need EVEX + } + + match self.size { + Some(X86RegisterClass::ByteRex) => return false, // always need a REX prefix + Some(X86RegisterClass::Quad) if !opts.no_rex_w => return false, // need REX.W + Some(X86RegisterClass::Ymm) => return false, // Need VEX + Some(X86RegisterClass::Zmm) => return false, // Need EVEX + _ => {} + } + + if self.reg3.is_some() { + return false; // need VEX or EVEX + } + + if self.mask.is_some() { + return false; // need EVEX + } + + true + } + X86EncodingPrefix::Rex => { + if self.mode != X86Mode::Long { + return false; + } + + if self.use_nf { + return false; // Need EVEX + } + + match self.reg_top_bits { + [0 | 1, 0 | 1, 0 | 1] => {} + _ => return false, // 2 top bits needs REX2 or EVEX + } + + match self.size { + Some(X86RegisterClass::Ymm) => return false, // Need VEX + Some(X86RegisterClass::Zmm) => return false, // Need EVEX + _ => {} // Note No prefix is generated when a non-REX compatible byte register is used, no need to filter here + } + + if self.reg3.is_some() { + return false; // need VEX or EVEX + } + + if self.mask.is_some() { + return false; // need EVEX + } + + true + } + X86EncodingPrefix::Rex2 => { + if self.mode != X86Mode::Long { + return false; + } + + if self.use_nf { + return false; // Need EVEX + } + + match self.size { + Some(X86RegisterClass::Ymm) => return false, // Need VEX + Some(X86RegisterClass::Zmm) => return false, // Need EVEX + _ => {} // Note No prefix is generated when a non-REX compatible byte register is used, no need to filter here + } + + if self.reg3.is_some() { + return false; // need VEX or EVEX + } + + self.mask.is_none() + } + X86EncodingPrefix::Vex => { + if self.use_nf { + return false; // Need EVEX + } + + match self.reg_top_bits { + [0 | 1, 0 | 1, 0 | 1] => {} + _ => return false, // 2 top bits needs REX2 or EVEX + } + + match self.size { + Some(X86RegisterClass::Zmm) => return false, // Need EVEX + _ => {} + } + + match self.reg3 { + Some(0..=15) => {} + Some(_) => return false, + None => {} + } + + if self.mask.is_some() { + return false; // need EVEX + } + + true // Legacy instructions cannot be enoded using VEX + } + X86EncodingPrefix::Evex => { + self.mode != X86Mode::Real && self.mode != X86Mode::Virtual8086 + } + prefix => panic!("Unknown prefix: {:?}", prefix), + } + } +} + +impl ModRMBuilder { + pub fn with_group_and_sse_prefix( + self, + group: X86InstructionMap, + sse_prefix: Option, + ) -> io::Result> { + let Self { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group: _, + sse_prefix: _, + mask, + rel_addr, + addr_size, + sreg, + _status, + } = self; + Ok(ModRMBuilder { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group: Some(group), + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status: PhantomData, + }) + } +} + +impl ModRMBuilder { + pub fn encode_modrm(&self, mut writer: W) -> io::Result<()> { + writer.write_all(core::slice::from_ref(&self.modrm_byte))?; + + if let Some(sib) = &self.sib_byte { + writer.write_all(core::slice::from_ref(sib))?; + } + + if let Some(addr) = &self.disp { + let size = if (self.modrm_byte & 0xC0) == 0x80 { + 8 + } else if self.mode == X86Mode::Real || self.mode == X86Mode::Virtual8086 { + 16 + } else { + 32 + }; + + writer.write_addr(size, addr.clone(), self.rel_addr)?; + } + + Ok(()) + } + pub fn encode_prefix( + &self, + mut writer: W, + prefix: X86EncodingPrefix, + opts: ModRMOptions, + ) -> io::Result<()> { + let escape_size = match self.mode { + X86Mode::Virtual8086 | X86Mode::Real => X86RegisterClass::Double, + _ => X86RegisterClass::Word, + }; + + match (self.addr_size, self.mode) { + (None, _) + | (Some(X86RegisterClass::Word), X86Mode::Real | X86Mode::Virtual8086) + | (Some(X86RegisterClass::Double), X86Mode::Protected | X86Mode::Compatibility) + | (Some(X86RegisterClass::Quad), X86Mode::Long) => {} + ( + Some(X86RegisterClass::Double), + X86Mode::Real | X86Mode::Virtual8086 | X86Mode::Long, + ) + | (Some(X86RegisterClass::Word), X86Mode::Protected | X86Mode::Compatibility) => { + writer.write_all(&[0x67])? + } + (Some(size), mode) => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("{:?} addresses are not supported in {:?}", size, mode), + )) + } + } + + let size_escape = &core::slice::from_ref(&0x66u8) + [(opts.no_escape || self.size != Some(escape_size)) as usize..]; + + writer.write_all(size_escape)?; + + #[allow(unsafe_code)] + writer.write_all( + self.sreg + .as_ref() + .map(|s| unsafe { &*(s as *const Segment as *const u8) }) + .map(core::slice::from_ref) + .unwrap_or(&[]), + )?; + + match prefix { + X86EncodingPrefix::NoPrefix => { + writer.write_all( + self.sse_prefix + .as_ref() + .map(|s| s.encoding()) + .unwrap_or(&[]), + )?; + + writer.write_all(self.group.unwrap().escape_prefix_bytes()) + } + X86EncodingPrefix::Rex => { + let mut rex_byte = 0x40u8; + + for (b, n) in self.reg_top_bits.iter().zip(0u8..) { + rex_byte |= *b << n + } + + if !opts.no_rex_w && self.size == Some(X86RegisterClass::Quad) { + rex_byte |= 8; + } + + writer.write_all( + self.sse_prefix + .as_ref() + .map(|s| s.encoding()) + .unwrap_or(&[]), + )?; + + writer.write_all(core::slice::from_ref(&rex_byte))?; + + writer.write_all(self.group.unwrap().escape_prefix_bytes()) + } + X86EncodingPrefix::Rex2 => { + let mut rex2_bytes = [0xD5, 0x00]; + + for (b, n) in self.reg_top_bits.iter().zip(0u8..) { + rex2_bytes[1] |= (*b & 0x1) << n; + rex2_bytes[1] |= (*b & 0x2) << (n + 3); + } + + if !opts.no_rex_w && self.size == Some(X86RegisterClass::Quad) { + rex2_bytes[1] |= 8; + } + + match self.group.unwrap() { + X86InstructionMap::Legacy => {} + X86InstructionMap::Extended => rex2_bytes[1] |= 0x80, + _ => panic!("Improper prefix for instruction"), + } + + writer.write_all( + self.sse_prefix + .as_ref() + .map(|s| s.encoding()) + .unwrap_or(&[]), + )?; + + writer.write_all(&rex2_bytes) + } + X86EncodingPrefix::Vex => todo!(), + X86EncodingPrefix::Evex => todo!(), + _ => panic!("bad prefix"), + } + } +} + +impl ModRMBuilder { + // Sets the r/m field and related fields + /// Note: Unexpected results may occur if called multiple times with different operands + pub fn with_rm( + mut self, + op: X86Operand, + ) -> io::Result> { + match op { + X86Operand::Mask(_) => panic!("Cannot specify a mask specifier in modr/m"), + X86Operand::Register(r) => { + self.size = Some(r.class()); + let rnum = r.regnum(); + + self.modrm_byte = (self.modrm_byte & 0o070) | 0o300 | (rnum & 0o007); + self.reg_top_bits[0] = rnum >> 3; + self.addr_size = None; // Not an address + } + X86Operand::Memory(size, sreg, op) => { + self.size = Some(size); + self.sreg = sreg; + + match op { + X86MemoryOperand::Indirect { reg, disp } => { + let class = reg.class(); + self.addr_size = Some(class); + if class == X86RegisterClass::Word { + let (mode, addr) = match (reg, disp) { + (X86Register::Bp, None) => (0o100, Some(Address::Abs(0))), + (_, None) => (0o000, None), + (_, Some(Address::Abs(x))) if x < 256 => { + (0o100, Some(Address::Abs(x))) + } + (_, Some(addr)) => (0o200, Some(addr)), + }; + + let rm = match reg { + X86Register::Si => 0o4, + X86Register::Di => 0o5, + X86Register::Bp => 0o6, + X86Register::Bx => 0o7, + reg => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("Indirect to register {} cannot be encoded", reg), + )) + } + }; + + self.modrm_byte = (self.modrm_byte & 0o070) | mode | rm; + self.disp = addr; + } else { + let regno = reg.regnum(); + let rm = regno & 0o7; + self.reg_top_bits[0] = regno >> 3; + let (mode, addr) = match (rm, disp) { + (0o005, None) => (0o100, Some(Address::Abs(0))), + (_, None) => (0o000, None), + (_, Some(Address::Abs(x))) if x < 256 => { + (0o100, Some(Address::Abs(x))) + } + (_, Some(addr)) => (0o200, Some(addr)), + }; + + if rm == 0o004 { + self.sib_byte = Some(0o044); + } + + self.modrm_byte = (self.modrm_byte & 0o070) | mode | rm; + self.disp = addr; + } + } + X86MemoryOperand::IndirectSib { + scale, + index, + base, + disp, + } => { + let addr_size = index.class(); + + if addr_size == X86RegisterClass::Word { + if scale != 1 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("16-bit modr/m cannot encode scale {}", scale), + )); + } + + let (mode, disp) = match (index, base, disp) { + (X86Register::Bp, None, None) => (0o100, Some(Address::Abs(0))), + (_, _, None) => (0o000, None), + (_, _, Some(Address::Abs(x))) if x < 256 => { + (0o100, Some(Address::Abs(x))) + } + (_, _, Some(addr)) => (0o200, Some(addr)), + }; + + let rm = match (index, base) { + (X86Register::Si, Some(X86Register::Bx)) => 0o000, + (X86Register::Di, Some(X86Register::Bx)) => 0o001, + (X86Register::Si, Some(X86Register::Bp)) => 0o002, + (X86Register::Di, Some(X86Register::Bp)) => 0o003, + (X86Register::Bx, Some(X86Register::Si)) => 0o000, + (X86Register::Bx, Some(X86Register::Di)) => 0o001, + (X86Register::Bp, Some(X86Register::Si)) => 0o002, + (X86Register::Bp, Some(X86Register::Di)) => 0o003, + (X86Register::Si, None) => 0o004, + (X86Register::Di, None) => 0o005, + (X86Register::Bp, None) => 0o006, + (X86Register::Bx, None) => 0o007, + (reg, None) => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("16-bit modr/m cannot encode register {}", reg), + )) + } + (reg, Some(base)) => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("16-bit modr/m cannot encode {}+{}", reg, base), + )) + } + }; + + self.modrm_byte = (self.modrm_byte & 0o070) | mode | rm; + self.disp = disp; + } else { + let scale = match scale { + 1 => 0o000, + 2 => 0o100, + 4 => 0o200, + 8 => 0o300, + scale => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("SIB cannot encode scale {}", scale), + )) + } + }; + + let idx = index.regnum() & 0o007 << 3; + if idx == 4 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("Cannot index by {}", idx), + )); + } + self.reg_top_bits[1] = idx >> 3; + self.reg_top_bits[0] = base.map(X86Register::regnum).unwrap_or(0) >> 3; + let base = base.map(X86Register::regnum).unwrap_or(0o005) & 0o007; + + let (mode, disp) = match (base, disp) { + (5, None) => (0o100, Some(Address::Abs(0))), + (_, None) => (0o000, None), + (_, Some(Address::Abs(x))) if x < 256 => { + (0o100, Some(Address::Abs(x))) + } + (_, Some(addr)) => (0o0200, Some(addr)), + }; + + let rm = 0o004; + + self.sib_byte = Some(scale | idx | base); + self.modrm_byte = (self.modrm_byte & 0o070) | mode | rm; + self.disp = disp; + } + } + X86MemoryOperand::RelAddr(addr) => { + self.rel_addr = true; + self.modrm_byte = (self.modrm_byte & 0o070) | 0o005; + self.disp = Some(addr); + } + X86MemoryOperand::AbsAddr(_) => todo!(), + } + } + op => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("Cannot encode {} in a modr/m byte", op), + )) + } + } + let Self { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status, + } = self; + Ok(ModRMBuilder { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status: PhantomData, + }) + } + pub fn with_op_size( + self, + size: X86RegisterClass, + ) -> io::Result> { + let Self { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size: _, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status, + } = self; + Ok(ModRMBuilder { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size: Some(size), + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status: PhantomData, + }) + } +} + +impl ModRMBuilder { + pub fn with_reg( + mut self, + op: X86Operand, + ) -> io::Result> { + match op { + X86Operand::Register(reg) => { + let regno = reg.regnum(); + self.size = Some(reg.class()); + self.modrm_byte = (self.modrm_byte & 0o307) | (regno & 0o007) << 3; + self.reg_top_bits[2] = regno >> 3; + let Self { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status, + } = self; + Ok(ModRMBuilder { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status: PhantomData, + }) + } + op => Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("Cannot encode {} in /r field", op), + )), + } + } + + pub fn with_control_bits( + mut self, + ctrl: u8, + ) -> io::Result> { + self.modrm_byte = (self.modrm_byte & 0o307) | (ctrl & 0o007) << 3; + let Self { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status, + } = self; + Ok(ModRMBuilder { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status: PhantomData, + }) + } +} + +impl ModRMBuilder { + pub fn with_reg3( + mut self, + op: X86Operand, + ) -> io::Result> { + match op { + X86Operand::Register(reg) => { + self.reg3 = Some(reg.regnum()); + let Self { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status, + } = self; + Ok(ModRMBuilder { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status: PhantomData, + }) + } + op => Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("Cannot encode {} in /r field", op), + )), + } + } +} + +impl ModRMBuilder { + pub fn with_mask( + mut self, + mask: X86Operand, + ) -> io::Result> { + match mask { + X86Operand::Mask(m) => { + self.mask = Some(m); + let Self { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status, + } = self; + Ok(ModRMBuilder { + use_nf, + sib_byte, + reg_top_bits, + modrm_byte, + disp, + size, + reg3, + mode, + group, + sse_prefix, + mask, + rel_addr, + addr_size, + sreg, + _status: PhantomData, + }) + } + op => Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("Cannot encode {} in mask", op), + )), + } + } +} + +impl X86Encoder { + pub fn write_single(&mut self, b: u8) -> io::Result<()> { + self.write_all(core::slice::from_ref(&b)) + } + #[allow(unused_variables)] + pub fn write_insn(&mut self, insn: X86Instruction) -> io::Result<()> { + let mode = insn.mode_override().unwrap_or(self.mode); + + let opc = insn.opcode(); + let oprs = insn.operands(); + + let mut prefix_space = [MaybeUninit::uninit(); X86EncodingPrefix::PREFIX_COUNT]; + let valid_prefixes = opc.allowed_prefixes(oprs, &mut prefix_space); + + let encoding = opc.encoding_info(oprs).ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("Cannot encode instruction {}", insn), + ) + })?; + + if let Some(prefix) = insn.prefix() { + self.write_single(prefix.opcode())?; + } + + match encoding.mode { + X86EncodingMode::Prefix(_) => todo!("prefix"), + X86EncodingMode::OpcodeOnly => { + let builder = ModRMBuilder::new(mode) + .with_group_and_sse_prefix(encoding.map, encoding.sse_prefix)?; + let option = ModRMOptions::NONE; + for prefix in valid_prefixes { + if builder.prefix_allowed(prefix, option) { + builder.encode_prefix(&mut *self, *prefix, option)?; + self.write_single(encoding.base_opcode)?; + return Ok(()); + } + } + Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!( + "Cannot encode instruction {} (no valid encoding prefix found)", + insn + ), + )) + } + X86EncodingMode::ModRM(option) => match oprs { + [op1 @ (X86Operand::Register(_) | X86Operand::Memory(_, _, _)), op2 @ X86Operand::Register(_)] => + { + let builder = ModRMBuilder::new(mode) + .with_group_and_sse_prefix(encoding.map, encoding.sse_prefix)? + .with_reg(op2.clone())? + .with_rm(op1.clone())?; + for prefix in valid_prefixes { + if builder.prefix_allowed(prefix, option) { + builder.encode_prefix(&mut *self, *prefix, option)?; + self.write_single(encoding.base_opcode)?; + builder.encode_modrm(&mut *self)?; + return Ok(()); + } + } + Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!( + "Cannot encode instruction {} (no valid encoding prefix found)", + insn + ), + )) + } + [op2 @ X86Operand::Register(_), op1 @ X86Operand::Memory(_, _, _)] => { + let builder = ModRMBuilder::new(mode) + .with_group_and_sse_prefix(encoding.map, encoding.sse_prefix)? + .with_reg(op2.clone())? + .with_rm(op1.clone())?; + for prefix in valid_prefixes { + if builder.prefix_allowed(prefix, option) { + builder.encode_prefix(&mut *self, *prefix, option)?; + self.write_single(encoding.base_opcode)?; + builder.encode_modrm(&mut *self)?; + return Ok(()); + } + } + Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!( + "Cannot encode instruction {} (no valid encoding prefix found)", + insn + ), + )) + } + oprs => todo!("modr/m /r {:?}", oprs), + }, + X86EncodingMode::ModRMControl(bits, option) => match oprs { + [op @ (X86Operand::Register(_) | X86Operand::Memory(_, _, _)), X86Operand::Immediate(imm)] => + { + let imm_bytes = imm.to_le_bytes(); + let builder = ModRMBuilder::new(mode) + .with_group_and_sse_prefix(encoding.map, encoding.sse_prefix)? + .with_control_bits(bits)? + .with_rm(op.clone())?; + for prefix in valid_prefixes { + if builder.prefix_allowed(prefix, option) { + builder.encode_prefix(&mut *self, *prefix, option)?; + self.write_single(encoding.base_opcode)?; + builder.encode_modrm(&mut *self)?; + return self.write_all(&imm_bytes[..encoding.imm_size]); + } + } + Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!( + "Cannot encode instruction {} (no valid encoding prefix found)", + insn + ), + )) + } + [oprs @ ..] => todo!("{:?}", oprs), + }, + X86EncodingMode::ModRMOp2(op) => todo!("{}", op), + X86EncodingMode::OpReg(_) => todo!("+r"), + X86EncodingMode::AvxModRM(_) => todo!("avx modr/m"), + X86EncodingMode::EvexModRM(_) => todo!("evex modr/m"), + X86EncodingMode::LegacyEvexModRM(_) => todo!("APX modr/m"), + X86EncodingMode::OpcodeWithSize(_) => todo!("opcode with size"), + X86EncodingMode::OffsetImm(bits) => { + let size = match bits { + 8 => X86RegisterClass::Byte, + 16 => X86RegisterClass::Word, + 32 => X86RegisterClass::Double, + 64 => X86RegisterClass::Quad, + x => panic!("Invalid immediate size {}", x), + }; + + let builder = ModRMBuilder::new(mode) + .with_group_and_sse_prefix(encoding.map, encoding.sse_prefix)? + .with_op_size(size)?; + let option = ModRMOptions::NONE; + for prefix in valid_prefixes { + if builder.prefix_allowed(prefix, option) { + builder.encode_prefix(&mut *self, *prefix, option)?; + self.write_single(encoding.base_opcode)?; + match oprs { + [X86Operand::Immediate(n)] => { + let imm_bytes = n.to_le_bytes(); + + self.write_all(&imm_bytes[..(bits / 8)])?; + } + [X86Operand::RelOffset(off)] => { + self.write_addr(bits, off.clone(), true)?; + } + [X86Operand::AbsOffset(off)] => { + self.write_addr(bits, off.clone(), false)?; + } + _ => panic!("Invalid operand "), + } + return Ok(()); + } + } + Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!( + "Cannot encode instruction {} (no valid encoding prefix found)", + insn + ), + )) + } + } + } +} diff --git a/arch-ops/src/x86/codegen/insn.rs b/arch-ops/src/x86/codegen/insn.rs new file mode 100644 index 0000000..4b2f480 --- /dev/null +++ b/arch-ops/src/x86/codegen/insn.rs @@ -0,0 +1,485 @@ +use super::{ + ModRMOptions, OpRegOptions, SsePrefix, X86Encoding, X86EncodingMode, X86EncodingPrefix, + X86InstructionMap, X86Operand, X86OperandMode, +}; +use crate::x86::{X86Mode, X86Register, X86RegisterClass}; + +use core::mem::MaybeUninit; + +macro_rules! expand_opt { + () => { + None + }; + ($expr:expr) => { + Some($expr) + }; +} + +macro_rules! expand_or_zero { + () => { + 0 + }; + ($val:literal) => { + $val + }; +} + +macro_rules! x86_codegen_instructions { + { + $(insn $mnemonic:ident { + $([$($oprs:pat),*] $(in $($modes:ident)|*)? => $($encoding_prefix:ident)|+ $opcode:literal $(imm $immsize:literal)? @ $encoding_mode:expr;)* + })* + } => { + paste::paste!{ + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] + pub enum X86CodegenOpcode{ + $([<$mnemonic:camel>]),* + } + + impl X86CodegenOpcode{ + pub const fn mnenomic(&self) -> &'static str{ + match self{ + $(Self::[<$mnemonic:camel>] => ::core::stringify!($mnemonic)),* + } + } + + #[allow(unsafe_code, unused_assignment, unused_mut)] + pub fn allowed_prefixes<'a>(&self, oprs: &[X86Operand], buffer: &'a mut [MaybeUninit]) -> &'a mut [X86EncodingPrefix]{ + let mut allowed_prefixes = [None;X86EncodingPrefix::PREFIX_COUNT]; + + use X86OperandMode::*; + use X86Register::*; + use X86RegisterClass::*; + use X86RegisterClass::{Cr,Dr}; + match self{ + $(Self::[<$mnemonic:camel>] => { + $({ + if { + let mut idx = 0; + $(({ + (oprs.get({let i = idx; idx += 1; i}).map(|opr| opr.matches_mode(|md| matches!(md,$oprs))).unwrap_or(false)) + })&&)* (oprs.len()==idx) + } { + for prefix in [$(X86EncodingPrefix::$encoding_prefix),*]{ + allowed_prefixes[prefix as usize] = Some(prefix); + } + } + })* + }),* + } + + + let mut offset = 0; + for prefix in allowed_prefixes{ + if let Some(prefix) = prefix{ + buffer[offset] = MaybeUninit::new(prefix); + offset += 1; + } + } + + unsafe{&mut *(&mut buffer[..offset] as *mut [MaybeUninit] as *mut [X86EncodingPrefix])} + } + + #[allow(dead_code, unused_mut)] + pub fn encoding_info(&self, oprs: &[X86Operand]) -> Option{ + use X86EncodingMode::*; + use X86OperandMode::*; + use X86Register::*; + use X86RegisterClass::*; + use X86RegisterClass::{Cr,Dr}; + match self{ + $(Self::[<$mnemonic:camel>] => { + match oprs{ + $(oprs if { + let mut idx = 0; + $(({ + (oprs.get({let i = idx; idx += 1; i}).map(|opr| opr.matches_mode(|md| matches!(md,$oprs))).unwrap_or(false)) + })&&)* (oprs.len()==idx) + } =>Some(X86Encoding{ + map: X86InstructionMap::from_opcode($opcode).unwrap(), + base_opcode: ($opcode&0xFF) as u8, + mode: $encoding_mode, + allowed_modes: expand_opt!($(&[$(X86Mode::$modes),*])?), + sse_prefix: SsePrefix::from_opcode($opcode), + imm_size: expand_or_zero!($($immsize)?) + }),)* + _ => None + } + })* + } + } + } + + impl core::fmt::Display for X86CodegenOpcode{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result{ + match self{ + $(Self::[<$mnemonic:camel>] => f.write_str(::core::stringify!($mnemonic))),* + } + } + } + } + }; +} + +x86_codegen_instructions! { + insn add{ + [TargetRegister(Al), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x04 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Ax), MemOrReg(Word)] => NoPrefix | Rex | Rex2 0x05 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Eax), MemOrReg(Double)] => NoPrefix | Rex | Rex2 0x05 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Rax), MemOrReg(Double)] => Rex | Rex2 0x05 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Reg(Byte)] => NoPrefix | Rex | Rex2 0x00 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x01 @ ModRM(ModRMOptions::NONE); + [Reg(Byte), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x02 @ ModRM(ModRMOptions::NONE); + [Reg(Word | Double | Quad), MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x03 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x80 imm 1 @ ModRMControl(0,ModRMOptions::NONE); + [MemOrReg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0x81 imm 2 @ ModRMControl(0,ModRMOptions::NONE); + [MemOrReg(Double | Quad), Immediate(Double)] => NoPrefix | Rex | Rex2 0x81 imm 4 @ ModRMControl(0,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x83 imm 1 @ ModRMControl(0,ModRMOptions::NONE); + } + insn or{ + [TargetRegister(Al), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x0C @ ModRM(ModRMOptions::NONE); + [TargetRegister(Ax), MemOrReg(Word)] => NoPrefix | Rex | Rex2 0x0D @ ModRM(ModRMOptions::NONE); + [TargetRegister(Eax), MemOrReg(Double)] => NoPrefix | Rex | Rex2 0x0D @ ModRM(ModRMOptions::NONE); + [TargetRegister(Rax), MemOrReg(Double)] => Rex | Rex2 0x0D @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Reg(Byte)] => NoPrefix | Rex | Rex2 0x08 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x09 @ ModRM(ModRMOptions::NONE); + [Reg(Byte), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x0A @ ModRM(ModRMOptions::NONE); + [Reg(Word | Double | Quad), MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x0B @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x80 imm 1 @ ModRMControl(1,ModRMOptions::NONE); + [MemOrReg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0x81 imm 2 @ ModRMControl(1,ModRMOptions::NONE); + [MemOrReg(Double | Quad), Immediate(Double)] => NoPrefix | Rex | Rex2 0x81 imm 4 @ ModRMControl(1,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x83 imm 1 @ ModRMControl(1,ModRMOptions::NONE); + } + insn adc{ + [TargetRegister(Al), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x14 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Ax), MemOrReg(Word)] => NoPrefix | Rex | Rex2 0x15 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Eax), MemOrReg(Double)] => NoPrefix | Rex | Rex2 0x15 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Rax), MemOrReg(Double)] => Rex | Rex2 0x15 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Reg(Byte)] => NoPrefix | Rex | Rex2 0x10 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x11 @ ModRM(ModRMOptions::NONE); + [Reg(Byte), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x12 @ ModRM(ModRMOptions::NONE); + [Reg(Word | Double | Quad), MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x13 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x80 imm 1 @ ModRMControl(2,ModRMOptions::NONE); + [MemOrReg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0x81 imm 2 @ ModRMControl(2,ModRMOptions::NONE); + [MemOrReg(Double | Quad), Immediate(Double)] => NoPrefix | Rex | Rex2 0x81 imm 4 @ ModRMControl(2,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x83 imm 1 @ ModRMControl(2,ModRMOptions::NONE); + } + insn sbb{ + [TargetRegister(Al), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x1C @ ModRM(ModRMOptions::NONE); + [TargetRegister(Ax), MemOrReg(Word)] => NoPrefix | Rex | Rex2 0x1D @ ModRM(ModRMOptions::NONE); + [TargetRegister(Eax), MemOrReg(Double)] => NoPrefix | Rex | Rex2 0x1D @ ModRM(ModRMOptions::NONE); + [TargetRegister(Rax), MemOrReg(Double)] => Rex | Rex2 0x1D @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Reg(Byte)] => NoPrefix | Rex | Rex2 0x18 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x19 @ ModRM(ModRMOptions::NONE); + [Reg(Byte), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x1A @ ModRM(ModRMOptions::NONE); + [Reg(Word | Double | Quad), MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x1B @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x80 imm 1 @ ModRMControl(3,ModRMOptions::NONE); + [MemOrReg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0x81 imm 2 @ ModRMControl(3,ModRMOptions::NONE); + [MemOrReg(Double | Quad), Immediate(Double)] => NoPrefix | Rex | Rex2 0x81 imm 4 @ ModRMControl(3,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x83 imm 1 @ ModRMControl(3,ModRMOptions::NONE); + } + insn and{ + [TargetRegister(Al), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x24 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Ax), MemOrReg(Word)] => NoPrefix | Rex | Rex2 0x25 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Eax), MemOrReg(Double)] => NoPrefix | Rex | Rex2 0x25 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Rax), MemOrReg(Double)] => Rex | Rex2 0x25 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Reg(Byte)] => NoPrefix | Rex | Rex2 0x20 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x21 @ ModRM(ModRMOptions::NONE); + [Reg(Byte), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x22 @ ModRM(ModRMOptions::NONE); + [Reg(Word | Double | Quad), MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x23 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x80 imm 1 @ ModRMControl(4,ModRMOptions::NONE); + [MemOrReg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0x81 imm 2 @ ModRMControl(4,ModRMOptions::NONE); + [MemOrReg(Double | Quad), Immediate(Double)] => NoPrefix | Rex | Rex2 0x81 imm 4 @ ModRMControl(4,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x83 imm 1 @ ModRMControl(4,ModRMOptions::NONE); + + } + insn sub{ + [TargetRegister(Al), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x2C @ ModRM(ModRMOptions::NONE); + [TargetRegister(Ax), MemOrReg(Word)] => NoPrefix | Rex | Rex2 0x2D @ ModRM(ModRMOptions::NONE); + [TargetRegister(Eax), MemOrReg(Double)] => NoPrefix | Rex | Rex2 0x2D @ ModRM(ModRMOptions::NONE); + [TargetRegister(Rax), MemOrReg(Double)] => Rex | Rex2 0x2D @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Reg(Byte)] => NoPrefix | Rex | Rex2 0x28 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x29 @ ModRM(ModRMOptions::NONE); + [Reg(Byte), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x2A @ ModRM(ModRMOptions::NONE); + [Reg(Word | Double | Quad), MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x2B @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x80 imm 1 @ ModRMControl(5,ModRMOptions::NONE); + [MemOrReg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0x81 imm 2 @ ModRMControl(5,ModRMOptions::NONE); + [MemOrReg(Double | Quad), Immediate(Double)] => NoPrefix | Rex | Rex2 0x81 imm 4 @ ModRMControl(5,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x83 imm 1 @ ModRMControl(5,ModRMOptions::NONE); + } + insn xor{ + [TargetRegister(Al), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x34 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Ax), MemOrReg(Word)] => NoPrefix | Rex | Rex2 0x35 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Eax), MemOrReg(Double)] => NoPrefix | Rex | Rex2 0x35 @ ModRM(ModRMOptions::NONE); + [TargetRegister(Rax), MemOrReg(Double)] => Rex | Rex2 0x35 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Reg(Byte)] => NoPrefix | Rex | Rex2 0x30 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x31 @ ModRM(ModRMOptions::NONE); + [Reg(Byte), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x32 @ ModRM(ModRMOptions::NONE); + [Reg(Word | Double | Quad), MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x33 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x80 imm 1 @ ModRMControl(6,ModRMOptions::NONE); + [MemOrReg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0x81 imm 2 @ ModRMControl(6,ModRMOptions::NONE); + [MemOrReg(Double | Quad), Immediate(Double)] => NoPrefix | Rex | Rex2 0x81 imm 4 @ ModRMControl(6,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x83 imm 1 @ ModRMControl(6,ModRMOptions::NONE); + + } + insn cmp{ + [TargetRegister(Al), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x3C @ ModRM(ModRMOptions::NONE); + [TargetRegister(Ax), MemOrReg(Word)] => NoPrefix | Rex | Rex2 0x3D @ ModRM(ModRMOptions::NONE); + [TargetRegister(Eax), MemOrReg(Double)] => NoPrefix | Rex | Rex2 0x3D @ ModRM(ModRMOptions::NONE); + [TargetRegister(Rax), MemOrReg(Double)] => Rex | Rex2 0x3D @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Reg(Byte)] => NoPrefix | Rex | Rex2 0x38 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x39 @ ModRM(ModRMOptions::NONE); + [Reg(Byte), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x3A @ ModRM(ModRMOptions::NONE); + [Reg(Word | Double | Quad), MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x3B @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x80 imm 1 @ ModRMControl(7,ModRMOptions::NONE); + [MemOrReg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0x81 imm 2 @ ModRMControl(7,ModRMOptions::NONE); + [MemOrReg(Double | Quad), Immediate(Double)] => NoPrefix | Rex | Rex2 0x81 imm 4 @ ModRMControl(7,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x83 imm 1 @ ModRMControl(7,ModRMOptions::NONE); + } + insn push{ + [TargetRegister(Es)] in Real | Protected => NoPrefix 0x06 @ OpcodeOnly; + [TargetRegister(Cs)] in Real | Protected => NoPrefix 0x0E @ OpcodeOnly; + [TargetRegister(Ss)] in Real | Protected => NoPrefix 0x16 @ OpcodeOnly; + [TargetRegister(Ds)] in Real | Protected => NoPrefix 0x1E @ OpcodeOnly; + [TargetRegister(Fs)] => NoPrefix 0x0FA0 @ OpcodeOnly; + [TargetRegister(Gs)] => NoPrefix 0x0FA8 @ OpcodeOnly; + [Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x50 @ OpReg(OpRegOptions::NO_REX_W); + [Immediate(Word | Double)] => NoPrefix 0x68 @ OpcodeOnly; + [Immediate(Byte)] => NoPrefix 0x6A @ OpcodeOnly; + [MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0xFF @ ModRMControl(6,ModRMOptions::NO_REX_W); + } + insn pop{ + [TargetRegister(Es)] in Real | Protected => NoPrefix 0x07 @ OpcodeOnly; + [TargetRegister(Ss)] in Real | Protected => NoPrefix 0x17 @ OpcodeOnly; + [TargetRegister(Ds)] in Real | Protected => NoPrefix 0x1F @ OpcodeOnly; + [TargetRegister(Fs)] => NoPrefix 0x0FA1 @ OpcodeOnly; + [TargetRegister(Gs)] => NoPrefix 0x0FA9 @ OpcodeOnly; + [Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x58 @ OpReg(OpRegOptions::NO_REX_W); + } + insn pusha{ + [] in Real | Protected => NoPrefix 0x60 @ OpcodeOnly; + } + insn popa{ + [] in Real | Protected => NoPrefix 0x61 @ OpcodeOnly; + } + insn bound{ + [Reg(Word | Double), MemoryOnly(Word | Double)] in Real | Protected => NoPrefix 0x62 @ ModRM(ModRMOptions::NONE); + } + insn arpl{ + [MemOrReg(Word), Reg(Word)] in Real | Protected => NoPrefix 0x63 @ ModRM(ModRMOptions::NO_ESCAPE); + } + insn movsx{ + [Reg(Double | Quad), MemOrReg(Double)] in Long => NoPrefix 0x63 @ ModRM(ModRMOptions::IGNORE_SIZE_MISMATCH); + } + insn daa{ + [TargetRegister(Al)] in Real | Protected => NoPrefix 0x27 @ OpcodeOnly; + [] in Real | Protected => NoPrefix 0x27 @ OpcodeOnly; + } + insn das{ + [TargetRegister(Al)] in Real | Protected => NoPrefix 0x2F @ OpcodeOnly; + [] in Real | Protected => NoPrefix 0x2F @ OpcodeOnly; + } + insn aaa{ + [TargetRegister(Al)] in Real | Protected => NoPrefix 0x37 @ OpcodeOnly; + [] in Real | Protected => NoPrefix 0x37 @ OpcodeOnly; + } + insn aas{ + [TargetRegister(Al)] in Real | Protected => NoPrefix 0x3F @ OpcodeOnly; + [] in Real | Protected => NoPrefix 0x3F @ OpcodeOnly; + } + insn inc{ + [Reg(Word | Double)] in Real | Protected => NoPrefix 0x40 @ OpReg(OpRegOptions::NONE); + [MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0xFE @ ModRMControl(0,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0xFF @ ModRMControl(0, ModRMOptions::NONE); + } + insn dec{ + [Reg(Word | Double)] in Real | Protected => NoPrefix 0x48 @ OpReg(OpRegOptions::NONE); + [MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0xFE @ ModRMControl(1,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0xFF @ ModRMControl(1, ModRMOptions::NONE); + } + insn imul{ + [MemOrReg(Word), Reg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0x69 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Double | Quad), Reg(Double | Quad), Immediate(Double) ] => NoPrefix | Rex | Rex2 0x69 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad), Immediate(Byte)] => NoPrefix | Rex | Rex2 0x6B @ ModRM(ModRMOptions::NONE); + [MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0xF6 @ ModRMControl(5,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0xF7 @ ModRMControl(5,ModRMOptions::NONE); + } + insn ins{ + [StringAddr(Byte, Di | Edi | Rdi)] => NoPrefix 0x6C @ OpcodeOnly; + [StringAddr(Word | Double, Di | Edi | Rdi)] => NoPrefix | Rex | Rex2 0x6D @ OpcodeOnly; + } + insn outs{ + [StringAddr(Byte, Si | Esi | Edi)] => NoPrefix 0x6E @ OpcodeOnly; + [StringAddr(Word | Double, Si | Esi | Edi)] => NoPrefix | Rex | Rex2 0x6F @ OpcodeOnly; + } + insn jo { + [RelAddr(8)] => NoPrefix 0x70 @ OpcodeOnly; + } + insn jno{ + [RelAddr(8)] => NoPrefix 0x71 @ OpcodeOnly; + } + insn jb{ + [RelAddr(8)] => NoPrefix 0x72 @ OpcodeOnly; + } + insn jnb{ + [RelAddr(8)] => NoPrefix 0x73 @ OpcodeOnly; + } + insn jz { + [RelAddr(8)] => NoPrefix 0x74 @ OpcodeOnly; + } + insn jnz{ + [RelAddr(8)] => NoPrefix 0x75 @ OpcodeOnly; + } + insn jbe{ + [RelAddr(8)] => NoPrefix 0x76 @ OpcodeOnly; + } + insn jnbe{ + [RelAddr(8)] => NoPrefix 0x77 @ OpcodeOnly; + } + insn js{ + [RelAddr(8)] => NoPrefix 0x78 @ OpcodeOnly; + } + insn jns{ + [RelAddr(8)] => NoPrefix 0x79 @ OpcodeOnly; + } + insn jp{ + [RelAddr(8)] => NoPrefix 0x7A @ OpcodeOnly; + } + insn jnp{ + [RelAddr(8)] => NoPrefix 0x7B @ OpcodeOnly; + } + insn jl{ + [RelAddr(8)] => NoPrefix 0x7C @ OpcodeOnly; + } + insn jnl{ + [RelAddr(8)] => NoPrefix 0x7D @ OpcodeOnly; + } + insn jle{ + [RelAddr(8)] => NoPrefix 0x7E @ OpcodeOnly; + } + insn jnle{ + [RelAddr(8)] => NoPrefix 0x7F @ OpcodeOnly; + } + insn test{ + [MemOrReg(Byte),Reg(Byte)] => NoPrefix | Rex | Rex2 0x84 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x85 @ ModRM(ModRMOptions::NONE); + } + insn nop{ + [] => NoPrefix | Rex | Rex2 0x90 @ OpcodeOnly; + } + insn xchg{ + [Reg(Byte), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x86 @ ModRM(ModRMOptions::NONE); + [Reg(Word), TargetRegister(Ax)] => NoPrefix | Rex | Rex2 0x90 @ OpReg(OpRegOptions::NONE); + [Reg(Double), TargetRegister(Eax)] => NoPrefix | Rex | Rex2 0x90 @ OpReg(OpRegOptions::NONE); + [Reg(Quad), TargetRegister(Rax)] => Rex | Rex2 0x90 @ OpReg(OpRegOptions::NONE); + } + insn fwait{ + [] => NoPrefix 0x9B @ OpcodeOnly; + } + insn pushf{ + [] => NoPrefix 0x9C @ OpcodeWithSize(Word); + } + insn pushfd{ + [] in Real | Protected => NoPrefix 0x9C @ OpcodeWithSize(Double); + } + insn pushfq{ + [] in Long => NoPrefix 0x9C @ OpcodeOnly; + } + insn popf{ + [] => NoPrefix 0x9D @ OpcodeWithSize(Word); + } + insn popfd{ + [] in Real | Protected => NoPrefix 0x9D @ OpcodeWithSize(Double); + } + insn popfq{ + [] in Long => NoPrefix 0x9D @ OpcodeOnly; + } + insn sahf{ + [] => NoPrefix 0x9E @ OpcodeOnly; + } + insn lahf{ + [] => NoPrefix 0x9F @ OpcodeOnly; + } + insn mov{ + [TargetRegister(Al), MemOffset(Byte)] => NoPrefix 0xA0 @ OffsetImm(32); + [TargetRegister(Ax), MemOffset(Word)] => NoPrefix 0xA1 @ OffsetImm(32); + [TargetRegister(Eax), MemOffset(Double)] => NoPrefix 0xA1 @ OffsetImm(32); + [TargetRegister(Rax), MemOffset(Quad)] => Rex | Rex2 0xA1 @ OffsetImm(32); + [MemOffset(Byte), TargetRegister(Al)] => NoPrefix 0xA2 @ OffsetImm(32); + [MemOffset(Word), TargetRegister(Ax)] => NoPrefix 0xA3 @ OffsetImm(32); + [MemOffset(Double), TargetRegister(Eax)] => NoPrefix 0xA3 @ OffsetImm(32); + [MemOffset(Quad), TargetRegister(Rax)] => Rex | Rex2 0xA3 @ OffsetImm(32); + [MemOrReg(Byte), Reg(Byte)] => NoPrefix | Rex | Rex2 0x88 @ ModRM(ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Reg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x89 @ ModRM(ModRMOptions::NONE); + [Reg(Byte), MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0x8A @ ModRM(ModRMOptions::NONE); + [Reg(Word | Double | Quad), MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x8B @ ModRM(ModRMOptions::NONE); + [Reg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0xB0 imm 1 @ OpReg(OpRegOptions::NONE); + [Reg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0xB8 imm 2 @ OpReg(OpRegOptions::NONE); + [Reg(Double), Immediate(Double)] => NoPrefix | Rex | Rex2 0xB8 imm 4 @ OpReg(OpRegOptions::NONE); + [MemOrReg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0xC6 imm 1 @ ModRMControl(0,ModRMOptions::NONE); + [MemOrReg(Word), Immediate(Word)] => NoPrefix | Rex | Rex2 0xC7 imm 2 @ ModRMControl(0,ModRMOptions::NONE); + [MemOrReg(Double | Quad), Immediate(Double)] => NoPrefix | Rex | Rex2 0xC7 imm 4 @ ModRMControl(0,ModRMOptions::NONE); + [Reg(Quad), Immediate(Quad)] => NoPrefix | Rex | Rex2 0xB8 @ OpReg(OpRegOptions::NONE); + [RegRM(Word | Double | Quad), Reg(Sreg)] => NoPrefix | Rex | Rex2 0x8C @ ModRM(ModRMOptions::NONE); + [MemoryOnly(Word), Reg(Sreg)] => NoPrefix | Rex | Rex2 0x8C @ ModRM(ModRMOptions::NO_ESCAPE); + [Reg(Sreg), RegRM(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0x8E @ ModRM(ModRMOptions::NONE); + [Reg(Sreg), MemoryOnly(Word)] => NoPrefix | Rex | Rex2 0x8E @ ModRM(ModRMOptions::NO_ESCAPE); + [RegRM(Double | Quad), Reg(Cr)] => NoPrefix | Rex | Rex2 0x0F20 @ ModRM(ModRMOptions::NO_REX_W); + [RegRM(Double | Quad), Reg(Dr)] => NoPrefix | Rex | Rex2 0x0F21 @ ModRM(ModRMOptions::NO_REX_W); + [Reg(Cr), RegRM(Double | Quad)] => NoPrefix | Rex | Rex2 0x0F22 @ ModRM(ModRMOptions::NO_REX_W); + [Reg(Dr), RegRM(Double | Quad)] => NoPrefix | Rex | Rex2 0x0F23 @ ModRM(ModRMOptions::NO_REX_W); + } + insn enter{ + [Immediate(Word), Immediate(Byte)] => NoPrefix 0xC8 @ OpcodeOnly; + } + insn leave{ + [] => NoPrefix 0xC9 @ OpcodeOnly; + } + insn ret{ + [Immediate(Word)] => NoPrefix 0xC2 imm 2 @ OpcodeOnly; + [] => NoPrefix 0xC3 @ OpcodeOnly; + } + insn retf{ + [Immediate(Word)] => NoPrefix 0xCA @ OpcodeOnly; + [] => NoPrefix 0xCB @ OpcodeOnly; + } + insn int3{ + [] => NoPrefix 0xCC @ OpcodeOnly; + } + insn int{ + [Immediate(Byte)] => NoPrefix 0xCD @ OpcodeOnly; + } + insn into{ + [] => NoPrefix 0xCE @ OpcodeOnly; + } + insn iret{ + [] => NoPrefix 0xCF @ OpcodeOnly; + } + insn iretq{ + [] => Rex | Rex2 0xCF @ OpcodeWithSize(Quad); + } + insn ud2{ + [] => NoPrefix 0x0F0B @ OpcodeOnly; + } + insn rol{ + [MemOrReg(Byte)] => NoPrefix | Rex | Rex2 0xD0 @ ModRMControl(0,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0xD1 @ ModRMControl(0,ModRMOptions::NONE); + [MemOrReg(Byte), Immediate(Byte)] => NoPrefix | Rex | Rex2 0xC0 @ ModRMControl(0,ModRMOptions::NONE); + [MemOrReg(Word | Double | Quad), Immediate(Byte)] => NoPrefix | Rex | Rex2 0xC1 @ ModRMControl(1,ModRMOptions::NONE); + + } + insn call{ + [RelAddr(16)] => NoPrefix 0xE8 @ OffsetImm(16); + [RelAddr(32)] => NoPrefix 0xE8 @ OffsetImm(32); + [MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0xFF @ ModRMControl(2,ModRMOptions::NONE); + } + insn lea{ + [Reg(Word | Double | Quad), MemoryOnly(_)] => NoPrefix | Rex | Rex2 0x8D @ ModRM(ModRMOptions::IGNORE_SIZE_MISMATCH); + } + insn jmp{ + [RelAddr(8)] => NoPrefix 0xEA @ OffsetImm(8); + [RelAddr(16)] => NoPrefix 0xE9 @ OffsetImm(16); + [RelAddr(32)] => NoPrefix 0xE9 @ OffsetImm(32); + [MemOrReg(Word | Double | Quad)] => NoPrefix | Rex | Rex2 0xFF @ ModRMControl(4,ModRMOptions::NONE); + } +} diff --git a/arch-ops/src/x86/features.rs b/arch-ops/src/x86/features.rs index 6bf11a6..8560d0c 100644 --- a/arch-ops/src/x86/features.rs +++ b/arch-ops/src/x86/features.rs @@ -142,5 +142,10 @@ define_x86_features! { (Sahf, "sahf"), (MovBe, "movbe"), (Adcx, "adcx"), - (PrefecthW, "prefetchw") + (PrefecthW, "prefetchw"), + (Apx, "apx"), + (Avx10, "avx10"), + (Avx10_128, "avx10-128"), + (Avx10_256, "avx10-256"), + (Avx10_512, "avx10-512"), } diff --git a/arch-ops/src/x86/insn.rs b/arch-ops/src/x86/insn.rs index ad23fe2..ffe7efe 100644 --- a/arch-ops/src/x86/insn.rs +++ b/arch-ops/src/x86/insn.rs @@ -12,49 +12,7 @@ pub enum Prefix { Repz, } -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] - -pub enum X86Mode { - Real, - Protected, - Virtual8086, - Compatibility, - Long, -} - -impl X86Mode { - pub fn default_mode_for(target: &Target) -> Option { - match target.arch() { - target_tuples::Architecture::I86 => Some(X86Mode::Real), - target_tuples::Architecture::I8086 => Some(X86Mode::Real), - target_tuples::Architecture::I086 => Some(X86Mode::Real), - target_tuples::Architecture::I186 => Some(X86Mode::Real), - target_tuples::Architecture::I286 => Some(X86Mode::Real), - target_tuples::Architecture::I386 => Some(X86Mode::Protected), - target_tuples::Architecture::I486 => Some(X86Mode::Protected), - target_tuples::Architecture::I586 => Some(X86Mode::Protected), - target_tuples::Architecture::I686 => Some(X86Mode::Protected), - target_tuples::Architecture::X86_64 => Some(X86Mode::Long), - _ => None, - } - } - - pub fn largest_gpr(&self) -> X86RegisterClass { - match self { - Self::Real | Self::Virtual8086 => X86RegisterClass::Word, - Self::Protected | Self::Compatibility => X86RegisterClass::Double, - Self::Long => X86RegisterClass::Quad, - } - } - - pub fn width(&self) -> u16 { - match self { - Self::Real | Self::Virtual8086 => 16, - Self::Protected | Self::Compatibility => 32, - Self::Long => 64, - } - } -} +pub use super::X86Mode; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub enum X86OperandType { @@ -234,7 +192,6 @@ macro_rules! define_x86_instructions { } } -use target_tuples::Target; use X86OperandType::*; use X86RegisterClass::*;