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))
}
}
}
+68 -46
View File
@@ -19,11 +19,8 @@ impl Code {
if dst.width() != src.width() { if dst.width() != src.width() {
return Err("src and dst are not same width".into()); 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.prefix16(dst);
self.rex(dst, src, 0, dst); self.rex(dst, src, 0, dst)?;
self.bytes.push(0x88 | dst.not8()); self.bytes.push(0x88 | dst.not8());
self.modrm(src, dst); self.modrm(src, dst);
} }
@@ -42,7 +39,7 @@ impl Code {
if src_width <= Width::B32 { if src_width <= Width::B32 {
dst = dst.lower64(); 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.bytes.push(0xb0 | (dst.not8() << 3) | dst.base());
self.imm(src, dst.width()); self.imm(src, dst.width());
} }
@@ -51,12 +48,9 @@ impl Code {
if src.width != dst.width() { if src.width != dst.width() {
return Err("register & memory sizes don't match".into()); 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.prefix32(src)?;
self.prefix16(dst); self.prefix16(dst);
self.rex(dst, dst, 0, src); self.rex(dst, dst, 0, src)?;
self.bytes.push(0x8a | dst.not8()); self.bytes.push(0x8a | dst.not8());
self.modrm(dst, src); self.modrm(dst, src);
} }
@@ -66,12 +60,9 @@ impl Code {
if src.width() != dst.width { if src.width() != dst.width {
return Err("register & memory sizes don't match".into()); 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.prefix32(dst)?;
self.prefix16(src); self.prefix16(src);
self.rex(dst, src, 0, dst); self.rex(dst, src, 0, dst)?;
self.bytes.push(0x88 | src.not8()); self.bytes.push(0x88 | src.not8());
self.modrm(src, dst); self.modrm(src, dst);
} }
@@ -90,7 +81,7 @@ impl Code {
} }
self.prefix32(dst)?; self.prefix32(dst)?;
self.prefix16(encode_width); self.prefix16(encode_width);
self.rex(dst, 0, 0, dst); self.rex(dst, 0, 0, dst)?;
self.bytes.push(0xc6 | encode_width.not8()); self.bytes.push(0xc6 | encode_width.not8());
self.modrm(0, dst); self.modrm(0, dst);
self.imm(src, encode_width); self.imm(src, encode_width);
@@ -142,10 +133,11 @@ impl Code {
Ok(()) Ok(())
} }
pub fn lea(&mut self, dst: Reg, sym: Symbol) { pub fn lea(&mut self, dst: Reg, sym: Symbol) -> ERes {
self.rex(1, dst, 0, 0); self.rex(1, dst, 0, 0)?;
self.bytes.push(0x8d); self.bytes.push(0x8d);
self.modrm(dst, sym); self.modrm(dst, sym);
Ok(())
} }
pub fn int(&mut self, code: u8) { pub fn int(&mut self, code: u8) {
@@ -170,42 +162,68 @@ impl Code {
self.bytes.push(0xc3); self.bytes.push(0xc3);
} }
fn add_sub(&mut self, dst: impl RegMem, src: impl Into<Imm>, ext: u8) -> ERes { fn add_sub(&mut self, dst: impl RegMem, src: impl Into<RegMemImm>, ext: u8) -> ERes {
let mut src = src.into(); match src.into() {
let mut imm_width = src.width_signed()?; RegMemImm::Reg(src) => {
let dst_width = dst.width().min(Width::B32); if src.width() != dst.width() {
return Err("incompatible widths".into());
if imm_width > dst_width { }
imm_width = src.width_unsigned()?; self.prefix32(dst)?;
if dst.width() == Width::B64 || imm_width > dst_width { self.prefix16(src);
return Err("immediate overflow".into()); self.rex(dst, src, 0, dst)?;
self.bytes.push(src.not8());
self.modrm(src, dst);
} }
src = src.reinterpret(dst_width); RegMemImm::Imm(mut src) => {
imm_width = src.width_signed()?; let mut imm_width = src.width_signed()?;
} let dst_width = dst.width().min(Width::B32);
let code = if dst.width() == Width::B8 {
0x80
} else if imm_width == Width::B8 {
0x83
} else {
imm_width = dst_width;
0x81
};
self.prefix32(dst)?; if imm_width > dst_width {
self.prefix16(dst_width); imm_width = src.width_unsigned()?;
self.rex(dst, 0, 0, dst); if dst.width() == Width::B64 || imm_width > dst_width {
self.bytes.push(code); return Err("immediate overflow".into());
self.modrm(ext, dst); }
self.imm(src, imm_width); 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(()) 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) 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) self.add_sub(dst, src, 5)
} }
@@ -227,10 +245,14 @@ impl Code {
Ok(()) Ok(())
} }
fn rex(&mut self, w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) { fn rex(&mut self, w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> ERes {
if w.rexw() || r.rex() || x.rex() || b.rex() | r.req() | b.req() { 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)); self.bytes.push(rex(w, r, x, b));
} }
Ok(())
} }
fn modrm(&mut self, reg: impl ModRMReg, rm: impl ModRMRM) { fn modrm(&mut self, reg: impl ModRMReg, rm: impl ModRMRM) {
+21 -1
View File
@@ -38,20 +38,40 @@ fn mov() {
#[test] #[test]
fn add_sub() { fn add_sub() {
let c = &mut TestCtx::new("mov"); let c = &mut TestCtx::new("add_sub");
// add
for dst in regs() { for dst in regs() {
for src in imms() { for src in imms() {
eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src)) 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 dst in mems() {
for src in imms() { for src in imms() {
eq(c, format!("add {dst}, {src}"), |c| c.add(dst, src)) 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 dst in regs() {
for src in imms() { for src in imms() {
eq(c, format!("sub {dst}, {src}"), |c| c.sub(dst, src)) eq(c, format!("sub {dst}, {src}"), |c| c.sub(dst, src))
+2 -1
View File
@@ -59,6 +59,7 @@ pub struct TestCtx {
changed: bool, changed: bool,
} }
#[track_caller]
pub fn eq( pub fn eq(
ctx: &mut TestCtx, ctx: &mut TestCtx,
asm: impl AsRef<str>, asm: impl AsRef<str>,
@@ -102,7 +103,7 @@ fn nasm(input: &str) -> Result<Vec<u8>, String> {
let fout = "/tmp/69420nasm_out.o"; let fout = "/tmp/69420nasm_out.o";
let input = "result:".to_string() + input; let input = "result:".to_string() + input;
write(fin, input.as_bytes()); 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 output = run(["objdump", "--no-addresses", "-dw", "-Mintel", fout])?;
let mut iter = output.lines().skip_while(|l| !l.contains("result")).skip(1); let mut iter = output.lines().skip_while(|l| !l.contains("result")).skip(1);
let res_line = iter.next().unwrap().trim(); let res_line = iter.next().unwrap().trim();
+9
View File
@@ -196,6 +196,9 @@ pub trait RexBit: Sized {
fn req(&self) -> bool { fn req(&self) -> bool {
false false
} }
fn req_no(&self) -> bool {
false
}
} }
impl RexBit for u8 { impl RexBit for u8 {
@@ -211,12 +214,18 @@ impl RexBit for Reg {
fn req(&self) -> bool { fn req(&self) -> bool {
self.gt4() && (self.width() == Width::B8) && !self.high() self.gt4() && (self.width() == Width::B8) && !self.high()
} }
fn req_no(&self) -> bool {
self.high()
}
} }
impl RexBit for Mem { impl RexBit for Mem {
fn rex(&self) -> bool { fn rex(&self) -> bool {
self.reg.rex() self.reg.rex()
} }
fn req(&self) -> bool {
self.reg.gt8()
}
} }
pub trait RexW { pub trait RexW {
Binary file not shown.
Binary file not shown.