#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Reg {
RAX = 0,
RCX = 1,
RDX = 2,
RBX = 3,
RSP = 4,
RBP = 5,
RSI = 6,
RDI = 7,
R8 = 8,
R9 = 9,
R10 = 10,
R11 = 11,
R12 = 12,
R13 = 13,
R14 = 14,
R15 = 15,
}
impl Reg {
fn lo(self) -> u8 {
(self as u8) & 7
}
fn hi(self) -> u8 {
(self as u8) >> 3
}
fn needs_rex(self) -> bool {
(self as u8) >= 8
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Cc {
O = 0,
NO = 1,
B = 2, AE = 3, E = 4, NE = 5, BE = 6, A = 7, S = 8, NS = 9,
P = 10,
NP = 11,
L = 12, GE = 13, LE = 14, G = 15, }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Label(pub u32);
#[derive(Clone, Copy)]
struct Fixup {
offset: usize,
label: Label,
}
pub struct Assembler {
pub code: Vec<u8>,
labels: Vec<usize>,
fixups: Vec<Fixup>,
}
const LABEL_UNBOUND: usize = usize::MAX;
impl Assembler {
pub fn new() -> Self {
Self {
code: Vec::with_capacity(4096),
labels: Vec::new(),
fixups: Vec::new(),
}
}
pub fn with_capacity(code_capacity: usize, label_capacity: usize) -> Self {
Self {
code: Vec::with_capacity(code_capacity),
labels: Vec::with_capacity(label_capacity),
fixups: Vec::with_capacity(label_capacity),
}
}
pub fn new_label(&mut self) -> Label {
let id = self.labels.len() as u32;
self.labels.push(LABEL_UNBOUND);
Label(id)
}
pub fn bind_label(&mut self, label: Label) {
self.labels[label.0 as usize] = self.code.len();
}
pub fn offset(&self) -> usize {
self.code.len()
}
pub fn patch_i32(&mut self, offset: usize, value: i32) {
self.code[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
}
#[inline(always)]
fn emit(&mut self, b: u8) {
self.code.push(b);
}
#[inline(always)]
fn emit2(&mut self, a: u8, b: u8) {
self.code.extend_from_slice(&[a, b]);
}
#[inline(always)]
fn emit3(&mut self, a: u8, b: u8, c: u8) {
self.code.extend_from_slice(&[a, b, c]);
}
fn emit_u32(&mut self, v: u32) {
self.code.extend_from_slice(&v.to_le_bytes());
}
fn emit_u64(&mut self, v: u64) {
self.code.extend_from_slice(&v.to_le_bytes());
}
fn emit_i32(&mut self, v: i32) {
self.code.extend_from_slice(&v.to_le_bytes());
}
fn emit_label_fixup(&mut self, label: Label) {
let offset = self.code.len();
self.fixups.push(Fixup { offset, label });
self.emit_u32(0); }
fn rex_w(&mut self, reg: Reg, rm: Reg) {
self.emit(0x48 | (reg.hi() << 2) | rm.hi());
}
fn rex_w_b(&mut self, rm: Reg) {
self.emit(0x48 | rm.hi());
}
fn rex_opt(&mut self, reg: Reg, rm: Reg) {
let r = reg.hi();
let b = rm.hi();
if r != 0 || b != 0 {
self.emit(0x40 | (r << 2) | b);
}
}
fn rex_opt_b(&mut self, rm: Reg) {
if rm.needs_rex() {
self.emit(0x40 | rm.hi());
}
}
fn modrm_rr(&mut self, reg: Reg, rm: Reg) {
self.emit(0xC0 | (reg.lo() << 3) | rm.lo());
}
fn modrm_disp32(&mut self, reg: u8, base: Reg) {
if base.lo() == 4 {
self.emit(0x80 | (reg << 3) | 4); self.emit(0x24); } else {
self.emit(0x80 | (reg << 3) | base.lo());
}
}
fn modrm_mem_disp32(&mut self, reg: Reg, base: Reg) {
self.modrm_disp32(reg.lo(), base);
}
pub fn mov_rr(&mut self, dst: Reg, src: Reg) {
if dst == src { return; }
self.emit3(
0x48 | (src.hi() << 2) | dst.hi(),
0x89,
0xC0 | (src.lo() << 3) | dst.lo(),
);
}
pub fn mov_ri64(&mut self, dst: Reg, imm: u64) {
if imm == 0 {
self.rex_opt(dst, dst);
self.emit(0x31);
self.modrm_rr(dst, dst);
} else if imm <= u32::MAX as u64 {
self.rex_opt_b(dst);
self.emit(0xB8 + dst.lo());
self.emit_u32(imm as u32);
} else if imm as i64 >= i32::MIN as i64 && imm as i64 <= i32::MAX as i64 {
self.rex_w_b(dst);
self.emit(0xC7);
self.emit(0xC0 | dst.lo());
self.emit_i32(imm as i32);
} else {
self.rex_w_b(dst);
self.emit(0xB8 + dst.lo());
self.emit_u64(imm);
}
}
pub fn mov_ri32(&mut self, dst: Reg, imm: u32) {
self.rex_opt_b(dst);
self.emit(0xB8 + dst.lo());
self.emit_u32(imm);
}
pub fn mov_load32(&mut self, dst: Reg, base: Reg, disp: i32) {
self.rex_opt(dst, base);
self.emit(0x8B);
self.modrm_mem_disp32(dst, base);
self.emit_i32(disp);
}
pub fn mov_load64(&mut self, dst: Reg, base: Reg, disp: i32) {
self.rex_w(dst, base);
self.emit(0x8B);
self.modrm_mem_disp32(dst, base);
self.emit_i32(disp);
}
pub fn movsxd_load_sib4(&mut self, dst: Reg, base: Reg, index: Reg) {
self.emit(0x48 | (dst.hi() << 2) | (index.hi() << 1) | base.hi());
self.emit(0x63); self.emit((dst.lo() << 3) | 4);
self.emit(0x80 | (index.lo() << 3) | base.lo());
}
pub fn mov_store32(&mut self, base: Reg, disp: i32, src: Reg) {
self.rex_opt(src, base);
self.emit(0x89);
self.modrm_mem_disp32(src, base);
self.emit_i32(disp);
}
pub fn mov_store64(&mut self, base: Reg, disp: i32, src: Reg) {
self.rex_w(src, base);
self.emit(0x89);
self.modrm_mem_disp32(src, base);
self.emit_i32(disp);
}
pub fn mov_store32_imm(&mut self, base: Reg, disp: i32, imm: i32) {
self.rex_opt_b(base);
self.emit(0xC7);
self.modrm_disp32(0, base);
self.emit_i32(disp);
self.emit_i32(imm);
}
pub fn mov_store64_imm(&mut self, base: Reg, disp: i32, imm: i32) {
self.rex_w_b(base);
self.emit(0xC7);
self.modrm_disp32(0, base);
self.emit_i32(disp);
self.emit_i32(imm);
}
fn modrm_sib_base_index(&mut self, reg: u8, base: Reg, index: Reg) {
if base.lo() == 5 {
self.emit(0x44 | (reg << 3)); self.emit((index.lo() << 3) | base.lo()); self.emit(0); } else {
self.emit((reg << 3) | 4); self.emit((index.lo() << 3) | base.lo()); }
}
pub fn movzx_load8_sib(&mut self, dst: Reg, base: Reg, index: Reg) {
self.emit(0x48 | (dst.hi() << 2) | (index.hi() << 1) | base.hi());
self.emit(0x0F);
self.emit(0xB6);
self.modrm_sib_base_index(dst.lo(), base, index);
}
pub fn movzx_load16_sib(&mut self, dst: Reg, base: Reg, index: Reg) {
let rex = 0x40 | (dst.hi() << 2) | (index.hi() << 1) | base.hi();
if rex != 0x40 { self.emit(rex); }
self.emit(0x0F);
self.emit(0xB7);
self.modrm_sib_base_index(dst.lo(), base, index);
}
pub fn mov_load32_sib(&mut self, dst: Reg, base: Reg, index: Reg) {
let rex = 0x40 | (dst.hi() << 2) | (index.hi() << 1) | base.hi();
if rex != 0x40 { self.emit(rex); }
self.emit(0x8B);
self.modrm_sib_base_index(dst.lo(), base, index);
}
pub fn mov_load64_sib(&mut self, dst: Reg, base: Reg, index: Reg) {
self.emit(0x48 | (dst.hi() << 2) | (index.hi() << 1) | base.hi());
self.emit(0x8B);
self.modrm_sib_base_index(dst.lo(), base, index);
}
pub fn mov_store8_sib(&mut self, base: Reg, index: Reg, src: Reg) {
self.emit(0x40 | (src.hi() << 2) | (index.hi() << 1) | base.hi());
self.emit(0x88);
self.modrm_sib_base_index(src.lo(), base, index);
}
pub fn mov_store16_sib(&mut self, base: Reg, index: Reg, src: Reg) {
self.emit(0x66); let rex = 0x40 | (src.hi() << 2) | (index.hi() << 1) | base.hi();
if rex != 0x40 { self.emit(rex); }
self.emit(0x89);
self.modrm_sib_base_index(src.lo(), base, index);
}
pub fn mov_store32_sib(&mut self, base: Reg, index: Reg, src: Reg) {
let rex = 0x40 | (src.hi() << 2) | (index.hi() << 1) | base.hi();
if rex != 0x40 { self.emit(rex); }
self.emit(0x89);
self.modrm_sib_base_index(src.lo(), base, index);
}
pub fn mov_store64_sib(&mut self, base: Reg, index: Reg, src: Reg) {
self.emit(0x48 | (src.hi() << 2) | (index.hi() << 1) | base.hi());
self.emit(0x89);
self.modrm_sib_base_index(src.lo(), base, index);
}
pub fn add_r64_mem(&mut self, dst: Reg, base: Reg, disp: i32) {
self.rex_w(dst, base);
self.emit(0x03);
self.modrm_mem_disp32(dst, base);
self.emit_i32(disp);
}
pub fn movzx_load8_deref(&mut self, dst: Reg, base: Reg) {
self.emit(0x48 | (dst.hi() << 2) | base.hi());
self.emit(0x0F);
self.emit(0xB6);
if base.lo() == 5 {
self.emit((dst.lo() << 3) | base.lo() | 0x40);
self.emit(0);
} else if base.lo() == 4 {
self.emit((dst.lo() << 3) | 4);
self.emit(0x24);
} else {
self.emit((dst.lo() << 3) | base.lo());
}
}
pub fn cmp_byte_sib_disp32(&mut self, base: Reg, index: Reg, disp: i32, imm: u8) {
let rex = 0x40 | (index.hi() << 1) | base.hi();
if rex != 0x40 { self.emit(rex); }
self.emit(0x80); self.emit(0xBC); self.emit((index.lo() << 3) | base.lo());
self.emit_i32(disp);
self.emit(imm);
}
pub fn cmp_byte_deref_imm(&mut self, base: Reg, imm: u8) {
if base.needs_rex() {
self.emit(0x41 | base.hi());
}
self.emit(0x80); if base.lo() == 5 {
self.emit(0x78 | base.lo()); self.emit(0); } else if base.lo() == 4 {
self.emit(0x38 | 4); self.emit(0x24); } else {
self.emit(0x38 | base.lo()); }
self.emit(imm);
}
fn alu_rr64(&mut self, op: u8, dst: Reg, src: Reg) {
self.rex_w(src, dst);
self.emit(op);
self.modrm_rr(src, dst);
}
fn alu_rr32(&mut self, op: u8, dst: Reg, src: Reg) {
self.rex_opt(src, dst);
self.emit(op);
self.modrm_rr(src, dst);
}
pub fn add_rr(&mut self, dst: Reg, src: Reg) { self.alu_rr64(0x01, dst, src); }
pub fn sub_rr(&mut self, dst: Reg, src: Reg) { self.alu_rr64(0x29, dst, src); }
pub fn and_rr(&mut self, dst: Reg, src: Reg) { self.alu_rr64(0x21, dst, src); }
pub fn or_rr(&mut self, dst: Reg, src: Reg) { self.alu_rr64(0x09, dst, src); }
pub fn xor_rr(&mut self, dst: Reg, src: Reg) { self.alu_rr64(0x31, dst, src); }
pub fn cmp_rr(&mut self, a: Reg, b: Reg) { self.alu_rr64(0x39, a, b); }
pub fn test_rr(&mut self, a: Reg, b: Reg) { self.alu_rr64(0x85, a, b); }
pub fn add_rr32(&mut self, dst: Reg, src: Reg) { self.alu_rr32(0x01, dst, src); }
pub fn sub_rr32(&mut self, dst: Reg, src: Reg) { self.alu_rr32(0x29, dst, src); }
fn alu_ri64(&mut self, ext: u8, dst: Reg, imm: i32) {
self.rex_w_b(dst);
self.emit(0x81);
self.emit(0xC0 | (ext << 3) | dst.lo());
self.emit_i32(imm);
}
fn alu_ri32(&mut self, ext: u8, dst: Reg, imm: i32) {
self.rex_opt_b(dst);
self.emit(0x81);
self.emit(0xC0 | (ext << 3) | dst.lo());
self.emit_i32(imm);
}
pub fn add_ri(&mut self, dst: Reg, imm: i32) { self.alu_ri64(0, dst, imm); }
pub fn sub_ri(&mut self, dst: Reg, imm: i32) { self.alu_ri64(5, dst, imm); }
pub fn and_ri(&mut self, dst: Reg, imm: i32) { self.alu_ri64(4, dst, imm); }
pub fn or_ri(&mut self, dst: Reg, imm: i32) { self.alu_ri64(1, dst, imm); }
pub fn xor_ri(&mut self, dst: Reg, imm: i32) { self.alu_ri64(6, dst, imm); }
pub fn cmp_ri(&mut self, a: Reg, imm: i32) { self.alu_ri64(7, a, imm); }
pub fn add_ri32(&mut self, dst: Reg, imm: i32) { self.alu_ri32(0, dst, imm); }
pub fn sub_ri32(&mut self, dst: Reg, imm: i32) { self.alu_ri32(5, dst, imm); }
pub fn cmp_ri32(&mut self, a: Reg, imm: i32) { self.alu_ri32(7, a, imm); }
pub fn cmp_mem32_imm(&mut self, base: Reg, disp: i32, imm: i32) {
if base.hi() != 0 {
self.emit(0x41); }
self.emit(0x81); self.modrm_disp32(7, base); self.emit_i32(disp);
self.emit_i32(imm);
}
pub fn cmp_mem32_r(&mut self, base: Reg, disp: i32, src: Reg) {
if base.hi() != 0 || src.hi() != 0 {
self.emit(0x40 | src.hi() << 2 | base.hi()); }
self.emit(0x39); self.modrm_disp32(src.lo(), base);
self.emit_i32(disp);
}
pub fn sub_mem64_imm32(&mut self, base: Reg, disp: i32, imm: i32) {
self.rex_w_b(base); self.emit(0x81); self.modrm_disp32(5, base); self.emit_i32(disp);
self.emit_i32(imm);
}
pub fn add_mem64_imm32(&mut self, base: Reg, disp: i32, imm: i32) {
self.rex_w_b(base); self.emit(0x81); self.modrm_disp32(0, base); self.emit_i32(disp);
self.emit_i32(imm);
}
pub fn imul_rr(&mut self, dst: Reg, src: Reg) {
self.rex_w(dst, src);
self.emit(0x0F);
self.emit(0xAF);
self.modrm_rr(dst, src);
}
pub fn imul_rr32(&mut self, dst: Reg, src: Reg) {
self.rex_opt(dst, src);
self.emit(0x0F);
self.emit(0xAF);
self.modrm_rr(dst, src);
}
pub fn imul_rri(&mut self, dst: Reg, src: Reg, imm: i32) {
self.rex_w(dst, src);
self.emit(0x69);
self.modrm_rr(dst, src);
self.emit_i32(imm);
}
pub fn imul_rri32(&mut self, dst: Reg, src: Reg, imm: i32) {
self.rex_opt(dst, src);
self.emit(0x69);
self.modrm_rr(dst, src);
self.emit_i32(imm);
}
pub fn mul_rdx_rax(&mut self, src: Reg) {
self.rex_w_b(src);
self.emit(0xF7);
self.emit(0xE0 | src.lo()); }
pub fn imul_rdx_rax(&mut self, src: Reg) {
self.rex_w_b(src);
self.emit(0xF7);
self.emit(0xE8 | src.lo()); }
pub fn div64(&mut self, src: Reg) {
self.rex_w_b(src);
self.emit(0xF7);
self.emit(0xF0 | src.lo()); }
pub fn idiv64(&mut self, src: Reg) {
self.rex_w_b(src);
self.emit(0xF7);
self.emit(0xF8 | src.lo()); }
pub fn div32(&mut self, src: Reg) {
self.rex_opt_b(src);
self.emit(0xF7);
self.emit(0xF0 | src.lo());
}
pub fn idiv32(&mut self, src: Reg) {
self.rex_opt_b(src);
self.emit(0xF7);
self.emit(0xF8 | src.lo());
}
pub fn cqo(&mut self) {
self.emit(0x48);
self.emit(0x99);
}
pub fn cdq(&mut self) {
self.emit(0x99);
}
pub fn inc64(&mut self, dst: Reg) {
self.rex_w_b(dst);
self.emit(0xFF);
self.emit(0xC0 | dst.lo()); }
pub fn dec64(&mut self, dst: Reg) {
self.rex_w_b(dst);
self.emit(0xFF);
self.emit(0xC8 | dst.lo()); }
pub fn neg64(&mut self, dst: Reg) {
self.rex_w_b(dst);
self.emit(0xF7);
self.emit(0xD8 | dst.lo()); }
pub fn not64(&mut self, dst: Reg) {
self.rex_w_b(dst);
self.emit(0xF7);
self.emit(0xD0 | dst.lo()); }
fn shift_ri64(&mut self, ext: u8, dst: Reg, imm: u8) {
self.rex_w_b(dst);
self.emit(0xC1);
self.emit(0xC0 | (ext << 3) | dst.lo());
self.emit(imm);
}
pub fn shift_cl64(&mut self, ext: u8, dst: Reg) {
self.rex_w_b(dst);
self.emit(0xD3);
self.emit(0xC0 | (ext << 3) | dst.lo());
}
fn shift_ri32(&mut self, ext: u8, dst: Reg, imm: u8) {
self.rex_opt_b(dst);
self.emit(0xC1);
self.emit(0xC0 | (ext << 3) | dst.lo());
self.emit(imm);
}
pub fn shift_cl32(&mut self, ext: u8, dst: Reg) {
self.rex_opt_b(dst);
self.emit(0xD3);
self.emit(0xC0 | (ext << 3) | dst.lo());
}
pub fn shl_ri64(&mut self, dst: Reg, imm: u8) { self.shift_ri64(4, dst, imm); }
pub fn shr_ri64(&mut self, dst: Reg, imm: u8) { self.shift_ri64(5, dst, imm); }
pub fn sar_ri64(&mut self, dst: Reg, imm: u8) { self.shift_ri64(7, dst, imm); }
pub fn shl_cl64(&mut self, dst: Reg) { self.shift_cl64(4, dst); }
pub fn shr_cl64(&mut self, dst: Reg) { self.shift_cl64(5, dst); }
pub fn sar_cl64(&mut self, dst: Reg) { self.shift_cl64(7, dst); }
pub fn rol_cl64(&mut self, dst: Reg) { self.shift_cl64(0, dst); }
pub fn ror_cl64(&mut self, dst: Reg) { self.shift_cl64(1, dst); }
pub fn rol_ri64(&mut self, dst: Reg, imm: u8) { self.shift_ri64(0, dst, imm); }
pub fn ror_ri64(&mut self, dst: Reg, imm: u8) { self.shift_ri64(1, dst, imm); }
pub fn shl_ri32(&mut self, dst: Reg, imm: u8) { self.shift_ri32(4, dst, imm); }
pub fn shr_ri32(&mut self, dst: Reg, imm: u8) { self.shift_ri32(5, dst, imm); }
pub fn sar_ri32(&mut self, dst: Reg, imm: u8) { self.shift_ri32(7, dst, imm); }
pub fn shl_cl32(&mut self, dst: Reg) { self.shift_cl32(4, dst); }
pub fn shr_cl32(&mut self, dst: Reg) { self.shift_cl32(5, dst); }
pub fn sar_cl32(&mut self, dst: Reg) { self.shift_cl32(7, dst); }
pub fn rol_cl32(&mut self, dst: Reg) { self.shift_cl32(0, dst); }
pub fn ror_cl32(&mut self, dst: Reg) { self.shift_cl32(1, dst); }
pub fn rol_ri32(&mut self, dst: Reg, imm: u8) { self.shift_ri32(0, dst, imm); }
pub fn ror_ri32(&mut self, dst: Reg, imm: u8) { self.shift_ri32(1, dst, imm); }
pub fn movsxd(&mut self, dst: Reg, src: Reg) {
self.rex_w(dst, src);
self.emit(0x63);
self.modrm_rr(dst, src);
}
pub fn movsx_8_64(&mut self, dst: Reg, src: Reg) {
self.rex_w(dst, src);
self.emit(0x0F);
self.emit(0xBE);
self.modrm_rr(dst, src);
}
pub fn movsx_16_64(&mut self, dst: Reg, src: Reg) {
self.rex_w(dst, src);
self.emit(0x0F);
self.emit(0xBF);
self.modrm_rr(dst, src);
}
pub fn movzx_8_64(&mut self, dst: Reg, src: Reg) {
self.rex_w(dst, src);
self.emit(0x0F);
self.emit(0xB6);
self.modrm_rr(dst, src);
}
pub fn movzx_16_64(&mut self, dst: Reg, src: Reg) {
self.rex_opt(dst, src);
self.emit(0x0F);
self.emit(0xB7);
self.modrm_rr(dst, src);
}
pub fn movzx_32_64(&mut self, dst: Reg, src: Reg) {
self.rex_opt(src, dst);
self.emit(0x89);
self.modrm_rr(src, dst);
}
pub fn setcc(&mut self, cc: Cc, dst: Reg) {
if dst.needs_rex() || matches!(dst, Reg::RSP | Reg::RBP | Reg::RSI | Reg::RDI) {
self.emit(0x40 | dst.hi());
}
self.emit(0x0F);
self.emit(0x90 + cc as u8);
self.emit(0xC0 | dst.lo());
}
pub fn cmovcc(&mut self, cc: Cc, dst: Reg, src: Reg) {
self.rex_w(dst, src);
self.emit(0x0F);
self.emit(0x40 + cc as u8);
self.modrm_rr(dst, src);
}
pub fn popcnt64(&mut self, dst: Reg, src: Reg) {
self.emit(0xF3);
self.rex_w(dst, src);
self.emit(0x0F);
self.emit(0xB8);
self.modrm_rr(dst, src);
}
pub fn lzcnt64(&mut self, dst: Reg, src: Reg) {
self.emit(0xF3);
self.rex_w(dst, src);
self.emit(0x0F);
self.emit(0xBD);
self.modrm_rr(dst, src);
}
pub fn tzcnt64(&mut self, dst: Reg, src: Reg) {
self.emit(0xF3);
self.rex_w(dst, src);
self.emit(0x0F);
self.emit(0xBC);
self.modrm_rr(dst, src);
}
pub fn bswap64(&mut self, dst: Reg) {
self.rex_w_b(dst);
self.emit(0x0F);
self.emit(0xC8 + dst.lo());
}
pub fn push(&mut self, reg: Reg) {
self.rex_opt_b(reg);
self.emit(0x50 + reg.lo());
}
pub fn pop(&mut self, reg: Reg) {
self.rex_opt_b(reg);
self.emit(0x58 + reg.lo());
}
pub fn jmp_label(&mut self, label: Label) {
let bound = self.labels[label.0 as usize];
if bound != LABEL_UNBOUND {
let target = bound;
let rel = target as isize - (self.code.len() as isize + 2);
if rel >= i8::MIN as isize && rel <= i8::MAX as isize {
self.emit(0xEB);
self.emit(rel as u8);
return;
}
}
self.emit(0xE9);
self.emit_label_fixup(label);
}
pub fn jcc_label(&mut self, cc: Cc, label: Label) {
let bound = self.labels[label.0 as usize];
if bound != LABEL_UNBOUND {
let target = bound;
let rel = target as isize - (self.code.len() as isize + 2);
if rel >= i8::MIN as isize && rel <= i8::MAX as isize {
self.emit(0x70 + cc as u8);
self.emit(rel as u8);
return;
}
}
self.emit(0x0F);
self.emit(0x80 + cc as u8);
self.emit_label_fixup(label);
}
pub fn jmp_reg(&mut self, reg: Reg) {
self.rex_opt_b(reg);
self.emit(0xFF);
self.emit(0xE0 | reg.lo()); }
pub fn call_reg(&mut self, reg: Reg) {
self.rex_opt_b(reg);
self.emit(0xFF);
self.emit(0xD0 | reg.lo()); }
pub fn call_label(&mut self, label: Label) {
self.emit(0xE8);
self.emit_label_fixup(label);
}
pub fn ret(&mut self) {
self.emit(0xC3);
}
pub fn lea(&mut self, dst: Reg, base: Reg, disp: i32) {
self.rex_w(dst, base);
self.emit(0x8D);
self.modrm_mem_disp32(dst, base);
self.emit_i32(disp);
}
pub fn lea_sib_scaled_32(&mut self, dst: Reg, base: Reg, index: Reg, scale_log2: u8) {
debug_assert!(scale_log2 <= 3);
let rex = 0x40 | (dst.hi() << 2) | (index.hi() << 1) | base.hi();
if rex != 0x40 { self.emit(rex); }
self.emit(0x8D); let scale_bits = scale_log2 << 6;
if base.lo() == 5 {
self.emit(0x44 | (dst.lo() << 3)); self.emit(scale_bits | (index.lo() << 3) | base.lo());
self.emit(0x00); } else {
self.emit((dst.lo() << 3) | 0x04); self.emit(scale_bits | (index.lo() << 3) | base.lo());
}
}
pub fn ud2(&mut self) {
self.emit(0x0F);
self.emit(0x0B);
}
pub fn nop(&mut self) {
self.emit(0x90);
}
pub fn int3(&mut self) {
self.emit(0xCC);
}
pub fn label_offset(&self, label: Label) -> Option<usize> {
let off = self.labels[label.0 as usize];
if off == LABEL_UNBOUND { None } else { Some(off) }
}
pub fn finalize(mut self) -> Vec<u8> {
for fixup in &self.fixups {
let target = self.labels[fixup.label.0 as usize];
assert!(target != LABEL_UNBOUND, "unbound label {:?}", fixup.label);
let rel = (target as i64) - (fixup.offset as i64 + 4);
let rel32 = rel as i32;
self.code[fixup.offset..fixup.offset + 4]
.copy_from_slice(&rel32.to_le_bytes());
}
self.code
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mov_ri64_zero() {
let mut asm = Assembler::new();
asm.mov_ri64(Reg::RAX, 0);
assert_eq!(&asm.code, &[0x31, 0xC0]);
}
#[test]
fn test_mov_ri64_small() {
let mut asm = Assembler::new();
asm.mov_ri64(Reg::RAX, 42);
assert_eq!(&asm.code, &[0xB8, 0x2A, 0x00, 0x00, 0x00]);
}
#[test]
fn test_label_resolution() {
let mut asm = Assembler::new();
let lbl = asm.new_label();
asm.jmp_label(lbl); asm.nop(); asm.bind_label(lbl); let code = asm.finalize();
assert_eq!(code[0], 0xE9);
let rel = i32::from_le_bytes([code[1], code[2], code[3], code[4]]);
assert_eq!(rel, 1); }
#[test]
fn test_push_pop_r15() {
let mut asm = Assembler::new();
asm.push(Reg::R15);
asm.pop(Reg::R15);
assert_eq!(&asm.code, &[0x41, 0x57, 0x41, 0x5F]);
}
}