diff --git a/; b/; new file mode 100644 index 0000000..cecfcde --- /dev/null +++ b/; @@ -0,0 +1,68 @@ +mod setup; +use setup::*; + +#[test] +fn mov() { + let c = &mut TestCtx::new("mov"); + + for dst in regs() { + for src in regs() { + eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); + } + } + + for dst in regs() { + for src in mems() { + eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); + } + } + + for dst in regs() { + for src in imms() { + eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); + } + } + + for dst in mems() { + for src in regs() { + eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); + } + } + + for dst in mems() { + for src in imms() { + eq(c, format!("mov {dst}, {src}"), |c| c.mov(dst, src)); + } + } +} + +#[test] +fn add_sub() { + let c = &mut TestCtx::new("add_sub"); + + // add + for dst in regs() { + for src in imms() { + eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src)) + } + } + + for dst in regs() { + for src in regs() { + eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src)) + } + } + + for dst in mems() { + for src in imms() { + eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src)) + } + } + + // sub + for dst in regs() { + for src in imms() { + eq(c, format!("sub {dst}, {src}"), |c| c.sub(dst, src)) + } + } +} diff --git a/src/arch/x86_64/encode.rs b/src/arch/x86_64/encode.rs index 6612100..aa4e002 100644 --- a/src/arch/x86_64/encode.rs +++ b/src/arch/x86_64/encode.rs @@ -19,11 +19,8 @@ impl Code { if dst.width() != src.width() { return Err("src and dst are not same width".into()); } - if dst.incompatible(&src) { - return Err("incompatible registers due to rex".into()); - } self.prefix16(dst); - self.rex(dst, src, 0, dst); + self.rex(dst, src, 0, dst)?; self.bytes.push(0x88 | dst.not8()); self.modrm(src, dst); } @@ -42,7 +39,7 @@ impl Code { if src_width <= Width::B32 { dst = dst.lower64(); } - self.rex(dst, 0, 0, dst); + self.rex(dst, 0, 0, dst)?; self.bytes.push(0xb0 | (dst.not8() << 3) | dst.base()); self.imm(src, dst.width()); } @@ -51,12 +48,9 @@ impl Code { if src.width != dst.width() { return Err("register & memory sizes don't match".into()); } - if dst.high() && src.reg.gt8() { - return Err("registers incompatible (REX)".into()); - } self.prefix32(src)?; self.prefix16(dst); - self.rex(dst, dst, 0, src); + self.rex(dst, dst, 0, src)?; self.bytes.push(0x8a | dst.not8()); self.modrm(dst, src); } @@ -66,12 +60,9 @@ impl Code { if src.width() != dst.width { return Err("register & memory sizes don't match".into()); } - if src.high() && dst.reg.gt8() { - return Err("registers incompatible (REX)".into()); - } self.prefix32(dst)?; self.prefix16(src); - self.rex(dst, src, 0, dst); + self.rex(dst, src, 0, dst)?; self.bytes.push(0x88 | src.not8()); self.modrm(src, dst); } @@ -90,7 +81,7 @@ impl Code { } self.prefix32(dst)?; self.prefix16(encode_width); - self.rex(dst, 0, 0, dst); + self.rex(dst, 0, 0, dst)?; self.bytes.push(0xc6 | encode_width.not8()); self.modrm(0, dst); self.imm(src, encode_width); @@ -142,10 +133,11 @@ impl Code { Ok(()) } - pub fn lea(&mut self, dst: Reg, sym: Symbol) { - self.rex(1, dst, 0, 0); + pub fn lea(&mut self, dst: Reg, sym: Symbol) -> ERes { + self.rex(1, dst, 0, 0)?; self.bytes.push(0x8d); self.modrm(dst, sym); + Ok(()) } pub fn int(&mut self, code: u8) { @@ -170,42 +162,68 @@ impl Code { self.bytes.push(0xc3); } - fn add_sub(&mut self, dst: impl RegMem, src: impl Into, ext: u8) -> ERes { - let mut src = src.into(); - let mut imm_width = src.width_signed()?; - let dst_width = dst.width().min(Width::B32); - - if imm_width > dst_width { - imm_width = src.width_unsigned()?; - if dst.width() == Width::B64 || imm_width > dst_width { - return Err("immediate overflow".into()); + fn add_sub(&mut self, dst: impl RegMem, src: impl Into, ext: u8) -> ERes { + match src.into() { + RegMemImm::Reg(src) => { + if src.width() != dst.width() { + return Err("incompatible widths".into()); + } + self.prefix32(dst)?; + self.prefix16(src); + self.rex(dst, src, 0, dst)?; + self.bytes.push(src.not8()); + self.modrm(src, dst); } - src = src.reinterpret(dst_width); - imm_width = src.width_signed()?; - } - let code = if dst.width() == Width::B8 { - 0x80 - } else if imm_width == Width::B8 { - 0x83 - } else { - imm_width = dst_width; - 0x81 - }; + RegMemImm::Imm(mut src) => { + let mut imm_width = src.width_signed()?; + let dst_width = dst.width().min(Width::B32); - self.prefix32(dst)?; - self.prefix16(dst_width); - self.rex(dst, 0, 0, dst); - self.bytes.push(code); - self.modrm(ext, dst); - self.imm(src, imm_width); + if imm_width > dst_width { + imm_width = src.width_unsigned()?; + if dst.width() == Width::B64 || imm_width > dst_width { + return Err("immediate overflow".into()); + } + src = src.reinterpret(dst_width); + imm_width = src.width_signed()?; + } + let code = if dst.width() == Width::B8 { + 0x80 + } else if imm_width == Width::B8 { + 0x83 + } else { + imm_width = dst_width; + 0x81 + }; + + self.prefix32(dst)?; + self.prefix16(dst_width); + self.rex(dst, 0, 0, dst)?; + self.bytes.push(code); + self.modrm(ext, dst); + self.imm(src, imm_width); + } + RegMemImm::Mem(src) => { + let RegMemKind::Reg(dst) = dst.kind() else { + return Err("cannot add memory to memory".into()); + }; + if src.width() != dst.width() { + return Err("incompatible widths".into()); + } + self.prefix32(src)?; + self.prefix16(dst); + self.rex(dst, dst, 0, src)?; + self.bytes.push(0x2 | dst.not8()); + self.modrm(dst, src); + } + } Ok(()) } - pub fn add(&mut self, dst: impl RegMem, src: impl Into) -> ERes { + pub fn add(&mut self, dst: impl RegMem, src: impl Into) -> ERes { self.add_sub(dst, src, 0) } - pub fn sub(&mut self, dst: impl RegMem, src: impl Into) -> ERes { + pub fn sub(&mut self, dst: impl RegMem, src: impl Into) -> ERes { self.add_sub(dst, src, 5) } @@ -227,10 +245,14 @@ impl Code { Ok(()) } - fn rex(&mut self, w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) { - if w.rexw() || r.rex() || x.rex() || b.rex() | r.req() | b.req() { + fn rex(&mut self, w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> ERes { + if r.req() && b.req_no() || r.req_no() && b.req() { + return Err("registers incompatible (REX)".into()); + } + if w.rexw() || r.rex() || x.rex() || b.rex() || r.req() || b.req() { self.bytes.push(rex(w, r, x, b)); } + Ok(()) } fn modrm(&mut self, reg: impl ModRMReg, rm: impl ModRMRM) { diff --git a/src/arch/x86_64/test/asm/mod.rs b/src/arch/x86_64/test/asm/mod.rs index 21217a3..1b3fbe5 100644 --- a/src/arch/x86_64/test/asm/mod.rs +++ b/src/arch/x86_64/test/asm/mod.rs @@ -38,20 +38,40 @@ fn mov() { #[test] fn add_sub() { - let c = &mut TestCtx::new("mov"); + let c = &mut TestCtx::new("add_sub"); + // add for dst in regs() { for src in imms() { eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src)) } } + for dst in regs() { + for src in regs() { + eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src)) + } + } + + for dst in regs() { + for src in mems() { + eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src)) + } + } + for dst in mems() { for src in imms() { eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src)) } } + for dst in mems() { + for src in regs() { + eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src)) + } + } + + // sub for dst in regs() { for src in imms() { eq(c, format!("sub {dst}, {src}"), |c| c.sub(dst, src)) diff --git a/src/arch/x86_64/test/asm/setup.rs b/src/arch/x86_64/test/asm/setup.rs index 3050a40..16b3b33 100644 --- a/src/arch/x86_64/test/asm/setup.rs +++ b/src/arch/x86_64/test/asm/setup.rs @@ -59,6 +59,7 @@ pub struct TestCtx { changed: bool, } +#[track_caller] pub fn eq( ctx: &mut TestCtx, asm: impl AsRef, @@ -102,7 +103,7 @@ fn nasm(input: &str) -> Result, String> { let fout = "/tmp/69420nasm_out.o"; let input = "result:".to_string() + input; write(fin, input.as_bytes()); - run(["nasm", "-w+error", "-felf64", fin, &format!("-o{fout}")])?; + run(["nasm", "-O1", "-w+error", "-felf64", fin, &format!("-o{fout}")])?; let output = run(["objdump", "--no-addresses", "-dw", "-Mintel", fout])?; let mut iter = output.lines().skip_while(|l| !l.contains("result")).skip(1); let res_line = iter.next().unwrap().trim(); diff --git a/src/arch/x86_64/types/arg.rs b/src/arch/x86_64/types/arg.rs index 2c466b9..c7ff75b 100644 --- a/src/arch/x86_64/types/arg.rs +++ b/src/arch/x86_64/types/arg.rs @@ -196,6 +196,9 @@ pub trait RexBit: Sized { fn req(&self) -> bool { false } + fn req_no(&self) -> bool { + false + } } impl RexBit for u8 { @@ -211,12 +214,18 @@ impl RexBit for Reg { fn req(&self) -> bool { self.gt4() && (self.width() == Width::B8) && !self.high() } + fn req_no(&self) -> bool { + self.high() + } } impl RexBit for Mem { fn rex(&self) -> bool { self.reg.rex() } + fn req(&self) -> bool { + self.reg.gt8() + } } pub trait RexW { diff --git a/test/nasm_cache/add_sub b/test/nasm_cache/add_sub new file mode 100644 index 0000000..139242f Binary files /dev/null and b/test/nasm_cache/add_sub differ diff --git a/test/nasm_cache/mov b/test/nasm_cache/mov index ed0bb4f..d7f6e9d 100644 Binary files a/test/nasm_cache/mov and b/test/nasm_cache/mov differ