add more adds

This commit is contained in:
2026-06-17 01:34:25 -04:00
parent 4fe4b50c8b
commit 85eacd783d
7 changed files with 168 additions and 48 deletions
+68
View File
@@ -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))
}
}
}
+45 -23
View File
@@ -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,8 +162,19 @@ impl Code {
self.bytes.push(0xc3);
}
fn add_sub(&mut self, dst: impl RegMem, src: impl Into<Imm>, ext: u8) -> ERes {
let mut src = src.into();
fn add_sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>, 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);
}
RegMemImm::Imm(mut src) => {
let mut imm_width = src.width_signed()?;
let dst_width = dst.width().min(Width::B32);
@@ -194,18 +197,33 @@ impl Code {
self.prefix32(dst)?;
self.prefix16(dst_width);
self.rex(dst, 0, 0, dst);
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<Imm>) -> ERes {
pub fn add(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> ERes {
self.add_sub(dst, src, 0)
}
pub fn sub(&mut self, dst: impl RegMem, src: impl Into<Imm>) -> ERes {
pub fn sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>) -> 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) {
+21 -1
View File
@@ -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))
+2 -1
View File
@@ -59,6 +59,7 @@ pub struct TestCtx {
changed: bool,
}
#[track_caller]
pub fn eq(
ctx: &mut TestCtx,
asm: impl AsRef<str>,
@@ -102,7 +103,7 @@ fn nasm(input: &str) -> Result<Vec<u8>, 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();
+9
View File
@@ -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 {
Binary file not shown.
Binary file not shown.