Compare commits

..
14 Commits
Author SHA1 Message Date
iris 780336fbe4 work 2026-07-21 16:22:45 -04:00
iris e4d2dcfe15 work 2026-07-19 21:07:21 -04:00
iris 759af9a3d7 oop 2026-07-19 20:58:51 -04:00
iris 9f7d95a67e work 2026-07-19 20:43:49 -04:00
iris 1944683dc5 stuffé 2026-07-19 02:59:17 -04:00
iris 3cbd50e619 work 2026-07-18 16:39:35 -04:00
iris c062993130 work 2026-07-18 13:33:59 -04:00
iris 6cc81d7a5c fix tests 2026-06-17 01:57:41 -04:00
iris 85eacd783d add more adds 2026-06-17 01:34:25 -04:00
iris 4fe4b50c8b idek 2026-06-17 00:53:26 -04:00
iris 026aec8565 stuff 2026-06-17 00:22:10 -04:00
iris 113f3d4d9c refactor 2026-06-16 23:55:47 -04:00
iris d66f8f02b7 modrm 2026-06-16 21:03:30 -04:00
iris 4e06e474ea add 2026-06-16 02:29:56 -04:00
20 changed files with 1206 additions and 503 deletions
+1
View File
@@ -9,5 +9,6 @@ pub trait Arch: Sized {
const NAME: &str; const NAME: &str;
type Asm; type Asm;
type Addr: Addr; type Addr: Addr;
type CallConv;
fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg>; fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg>;
} }
+235 -12
View File
@@ -1,14 +1,24 @@
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use super::*; use super::*;
use crate::backend::{LibImport, LinkedProgram, SymImport, SymTable, Symbol}; use crate::backend::{Body, Func, LibImport, LinkedProgram, SymImport, SymTable, Symbol, VarId};
use util::*;
pub struct Encoder<'a> { pub struct Encoder<'a> {
pub code: Code, pub code: Code,
pub sym_tab: SymTable<u64>, pub sym_tab: SymTable<u64>,
pub sym_refs: HashMap<Symbol, Vec<usize>>, pub sym_refs: HashMap<Symbol, Vec<usize>>,
pub program: &'a Program<X86_64>, pub program: &'a Program<X86_64>,
pub active: FnData,
}
#[derive(Default)]
struct FnData {
pub segs: Vec<SegUses>,
pub seg: usize,
pub i: usize,
pub var: HashMap<VarId, RegW>,
pub reg: [Option<VarId>; 16],
pub reg_next: [Option<RegUse>; 16],
} }
pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> { pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
@@ -17,11 +27,7 @@ pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
p.encode_data(&mut encoder.code.bytes, &mut encoder.sym_tab); p.encode_data(&mut encoder.code.bytes, &mut encoder.sym_tab);
for f in &p.funcs { for f in &p.funcs {
let addr = encoder.code.bytes.len(); encoder.func(f);
encoder.sym_tab.insert(f.sym, addr as u64);
for instr in &f.instrs {
encoder.compile_instr(instr)?;
}
} }
for (pos, sym) in encoder.code.missing.drain(..) { for (pos, sym) in encoder.code.missing.drain(..) {
@@ -60,15 +66,229 @@ pub fn compile(p: &Program<X86_64>) -> Result<LinkedProgram<u64>, CompilerMsg> {
}) })
} }
#[derive(Default)]
pub struct SegUses {
var: HashMap<VarId, Vec<usize>>,
reg: [Vec<RegUse>; 16],
}
#[derive(Clone, Copy)]
pub struct RegUse {
pos: usize,
// required var, if any
var: Option<VarId>,
}
type BInstr = crate::backend::Instr<X86_64>; type BInstr = crate::backend::Instr<X86_64>;
impl<'a> Encoder<'a> { impl<'a> Encoder<'a> {
fn compile_instr(&mut self, instr: &BInstr) -> Result<(), CompilerMsg> { fn func(&mut self, f: &Func<X86_64>) -> ERes {
match instr { let addr = self.code.bytes.len();
BInstr::Asm(asm) => { self.sym_tab.insert(f.sym, addr as u64);
self.code.extend(asm); self.active = Default::default();
self.calc_uses(&f.body);
for (i, instr) in f.body.iter().enumerate() {
self.active.i = i;
let seg = &self.active.segs[self.active.seg];
for r in 0..self.active.reg_next.len() {
let u = self.active.reg_next[r];
let Some(u) = u else {
continue;
};
if u.pos != r {
continue;
} }
// if there's a var currently in the reg
if let Some(var) = self.active.reg[r] {
// done if it's what's needed
if u.var.is_some_and(|v| var == v) {
continue;
}
// otherwise save it
self.save(var);
}
if let Some(var) = u.var {
if let Some(cur) = self.active.var.get(&var) {}
}
}
match instr {
BInstr::Asm(asm) => self.asm(asm)?,
_ => todo!(), _ => todo!(),
} }
}
Ok(())
}
fn save(&mut self, var: VarId) {
todo!()
}
fn calc_uses(&mut self, body: &Body<X86_64>) -> (usize, usize) {
let mut pos = 0;
let seg_i = self.active.segs.len();
self.active.segs.push(Default::default());
let mut uses = SegUses::default();
macro_rules! push {
($var:ident) => {
push!($var, 0)
};
($var:ident, $pos:expr) => {
uses.var.entry($var.clone()).or_default().push(pos + $pos)
};
}
for instr in body {
match instr {
BInstr::Set { dst, src: _ } => push!(dst),
BInstr::Call { dst, f, args } => {
let conv = &self.program.call_convs[self.program.funcs[f].conv];
push!(dst);
for &reg in conv.scratch() {
uses.reg[reg as usize].push(RegUse { pos, var: None });
}
for (i, &arg) in args.iter().enumerate() {
push!(arg);
if let Some(&reg) = conv.param().get(i) {
uses.reg[reg as usize].last_mut().unwrap().var = Some(arg);
}
}
}
BInstr::Copy { dst, src } => {
push!(dst);
push!(src);
}
BInstr::Add { dst, src1, src2 } => {
push!(dst);
push!(src1);
push!(src2);
}
BInstr::If { cond, then, else_ } => {
push!(cond);
pos += 1;
let (seg_i1, len1) = self.calc_uses(then);
let (seg_i2, len2) = self.calc_uses(else_);
// insert closest usages
for (var, poss1) in &self.active.segs[seg_i1].var {
if let Some(poss2) = self.active.segs[seg_i2].var.get(var) {
push!(var, poss1[0].min(poss2[0]));
} else {
push!(var, poss1[0]);
}
}
for (var, poss2) in &self.active.segs[seg_i2].var {
if !self.active.segs[seg_i1].var.contains_key(var) {
push!(var, poss2[0]);
}
}
pos += len1.max(len2);
continue;
}
BInstr::Loop(instrs) => {
let (seg_i2, len) = self.calc_uses(instrs);
// insert closest usages
for (var, poss) in &self.active.segs[seg_i2].var {
push!(var, poss[0]);
}
// during register allocation, want to insert the first use of each var
// at len + use, because if it jumps back up, that will be the next
// usage rather than if the loop exits; pad ensures there is room so
// the insertions are always before the rest of the usages (after loop)
let pad = (len * 2).saturating_sub(pos);
pos += len + pad;
continue;
}
BInstr::Break(var) => {
if let Some(var) = var {
push!(var);
}
}
BInstr::Return(var) => {
if let Some(var) = var {
push!(var);
}
}
BInstr::Asm(asm) => {
let mut used = HashSet::new();
for i in &asm.instrs {
if let Some(reg) = i.dst_reg() {
used.insert(reg.reg());
}
}
for &(reg, var) in &asm.args {
push!(var);
used.remove(&reg);
uses.reg[reg as usize].push(RegUse {
pos,
var: Some(var),
});
}
for reg in used {
uses.reg[reg as usize].push(RegUse { pos, var: None });
}
}
}
pos += 1;
}
self.active.segs[seg_i] = uses;
return (seg_i, pos);
}
pub fn prep_var(&mut self, var: VarId) -> RegW {
let seg = &mut self.active.segs[self.active.seg];
if let Some(reg) = self.active.var.get(&var) {
*reg
} else {
}
}
pub fn rvm(&mut self, input: Rvm) -> RegMem {
match input {
Rvm::Reg(reg) => reg.into(),
Rvm::Var(var) => self.prep_var(var).into(),
Rvm::Mem(mem) => mem.into(),
}
}
pub fn rvmi(&mut self, input: Rvmi) -> RegMemImm {
match input {
Rvmi::Reg(reg) => reg.into(),
Rvmi::Var(var) => self.prep_var(var).into(),
Rvmi::Mem(mem) => mem.into(),
Rvmi::Imm(imm) => imm.into(),
}
}
pub fn asm(&mut self, asm: &Asm) -> ERes {
let mut used = HashSet::default();
let mut vars = HashSet::default();
for &instr in &asm.instrs {
if let Some(regw) = instr.dst_reg() {
used.insert(regw.reg());
}
for var in instr.vars() {
vars.insert(var);
}
}
let overlap = used.intersection(&self.active.var.values());
for var in &vars {
if let Some(reg) = self.active.var.get(var)
&& used.contains(reg)
{}
}
for &instr in &asm.instrs {
match instr {
Instr::Mov { dst, src } => self.code.mov(self.rvm(dst), self.rvmi(src))?,
Instr::Push(rvmi) => todo!(),
Instr::Pop(rvm) => todo!(),
Instr::Lea { dst, src } => todo!(),
Instr::Int(code) => self.code.int(code),
Instr::Syscall => self.code.syscall(),
Instr::Call(sym) => self.code.call(sym),
Instr::CallAt(sym) => self.code.call_mem(sym),
Instr::Ret => self.code.ret(),
Instr::Add { dst, src } => todo!(),
Instr::Sub { dst, src } => todo!(),
}
}
Ok(()) Ok(())
} }
@@ -77,6 +297,9 @@ impl<'a> Encoder<'a> {
code: Code::default(), code: Code::default(),
sym_tab: SymTable::new(program.sym_count()), sym_tab: SymTable::new(program.sym_count()),
sym_refs: Default::default(), sym_refs: Default::default(),
seg: 0,
segs: Default::default(),
active: Default::default(),
program, program,
} }
} }
+113 -80
View File
@@ -1,7 +1,7 @@
use super::*; use super::*;
use crate::backend::Symbol; use crate::backend::Symbol;
type ERes = Result<(), CompilerMsg>; pub type ERes = Result<(), CompilerMsg>;
/// machine code /// machine code
#[derive(Default)] #[derive(Default)]
@@ -11,25 +11,21 @@ pub struct Code {
} }
impl Code { impl Code {
pub fn mov(&mut self, dst: impl Into<RegMem>, src: impl Into<RegImmMem>) -> ERes { pub fn mov(&mut self, dst: impl Into<RegMem>, src: impl Into<RegMemImm>) -> ERes {
let dst = dst.into(); let dst = dst.into();
let src = src.into(); let src = src.into();
match dst { match dst {
RegMem::Reg(mut dst) => match src { RegMem::Reg(mut dst) => match src {
RegImmMem::Reg(src) => { RegMemImm::Reg(src) => {
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) { self.prefix16(dst);
return Err("incompatible registers due to rex".into()); self.rex(dst, src, 0, dst)?;
self.bytes.push(0x88 | dst.not8());
self.modrm(src, dst);
} }
let width = dst.width(); RegMemImm::Imm(src) => {
self.prefix16(width);
self.rex(width, src, 0, dst);
self.bytes.push(0x88 | width.not8());
self.bytes.push(modrm_regs(src, dst));
}
RegImmMem::Imm(src) => {
let src_width = src.width_unsigned()?; let src_width = src.width_unsigned()?;
if src_width > dst.width() { if src_width > dst.width() {
return Err("immediate cannot fit in register".into()); return Err("immediate cannot fit in register".into());
@@ -44,40 +40,34 @@ 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());
} }
} }
RegImmMem::Mem(src) => { RegMemImm::Mem(src) => {
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() { self.prefix32(src)?;
return Err("registers incompatible (REX)".into());
}
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_regdisp(dst, src); self.modrm(dst, src);
} }
}, },
RegMem::Mem(dst) => match src { RegMem::Mem(dst) => match src {
RegImmMem::Reg(src) => { RegMemImm::Reg(src) => {
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() { self.prefix32(dst)?;
return Err("registers incompatible (REX)".into());
}
self.prefix32(&dst)?;
self.prefix16(src); self.prefix16(src);
self.rex(src, src, 0, dst); self.rex(dst, src, 0, dst)?;
self.bytes.push(0x88 | src.not8()); self.bytes.push(0x88 | src.not8());
self.modrm_regdisp(src, dst); self.modrm(src, dst);
} }
RegImmMem::Imm(src) => { RegMemImm::Imm(src) => {
let encode_width = dst.width.min(Width::B32); let encode_width = dst.width.min(Width::B32);
let src_width = if dst.width == Width::B64 { let src_width = if dst.width == Width::B64 {
src.width_signed() src.width_signed()
@@ -90,32 +80,32 @@ impl Code {
if src_width > dst.width { if src_width > dst.width {
return Err("source cannot fit in destination".into()); return Err("source cannot fit in destination".into());
} }
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_regdisp(None, dst); self.modrm(0, dst);
self.imm(src, encode_width); self.imm(src, encode_width);
} }
RegImmMem::Mem(_) => return Err("cannot move memory to memory".into()), RegMemImm::Mem(_) => return Err("cannot move memory to memory".into()),
}, },
} }
Ok(()) Ok(())
} }
pub fn push(&mut self, reg: impl Into<RegImmMem>) -> ERes { pub fn push(&mut self, reg: impl Into<RegMemImm>) -> ERes {
match reg.into() { match reg.into() {
RegImmMem::Reg(reg) => match reg.width() { RegMemImm::Reg(reg) => match reg.width() {
Width::B64 => { Width::B64 => {
if reg.gt8() { if reg.gt8() {
self.bytes.push(0x41); self.bytes.push(0x41);
} }
self.bytes.push(0x50 | reg.base()); self.bytes.push(0x50 | reg.base());
} }
Width::B16 => {} Width::B16 => todo!(),
_ => return Err("register must be 64 or 16 bit".into()), _ => return Err("register must be 64 or 16 bit".into()),
}, },
RegImmMem::Imm(imm) => match imm.width_unsigned()? { RegMemImm::Imm(imm) => match imm.width_unsigned()? {
Width::B8 => { Width::B8 => {
self.bytes.push(0x6a); self.bytes.push(0x6a);
self.bytes.push(imm.0 as u8); self.bytes.push(imm.0 as u8);
@@ -126,12 +116,12 @@ impl Code {
} }
Width::B64 => return Err("immediate must be 32 bit or less".into()), Width::B64 => return Err("immediate must be 32 bit or less".into()),
}, },
RegImmMem::Mem(mem) => todo!(), RegMemImm::Mem(mem) => todo!(),
} }
Ok(()) Ok(())
} }
pub fn pop(&mut self, reg: Reg) -> ERes { pub fn pop(&mut self, reg: RegW) -> ERes {
match reg.width() { match reg.width() {
Width::B64 | Width::B16 => (), Width::B64 | Width::B16 => (),
_ => return Err("register must be 64 or 16 bit".into()), _ => return Err("register must be 64 or 16 bit".into()),
@@ -144,10 +134,11 @@ impl Code {
Ok(()) Ok(())
} }
pub fn lea(&mut self, dst: Reg, sym: Symbol) { pub fn lea(&mut self, dst: RegW, sym: Symbol) -> ERes {
self.bytes self.rex(1, dst, 0, 0)?;
.extend([rex(1, dst, 0, 0), 0x8d, modrm_disp32(dst)]); self.bytes.push(0x8d);
self.sym_offset4(sym); self.modrm(dst, sym);
Ok(())
} }
pub fn int(&mut self, code: u8) { pub fn int(&mut self, code: u8) {
@@ -172,35 +163,70 @@ impl Code {
self.bytes.push(0xc3); self.bytes.push(0xc3);
} }
pub fn sub(&mut self, dst: Reg, src: impl Into<Imm>) -> ERes { fn add_sub(&mut self, dst: impl Into<RegMem>, src: impl Into<RegMemImm>, ext: u8) -> ERes {
let mut src = src.into(); let dst = dst.into();
let mut width = src.width_signed()?; 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); let dst_width = dst.width().min(Width::B32);
self.prefix16(dst_width); if imm_width > dst_width {
self.rex(dst, 0, 0, dst); imm_width = src.width_unsigned()?;
if dst.width() == Width::B64 || imm_width > dst_width {
if width > dst_width {
width = src.width_unsigned()?;
if dst.width() == Width::B64 || width > dst_width {
return Err("immediate overflow".into()); return Err("immediate overflow".into());
} }
src = src.reinterpret(dst_width); src = src.reinterpret(dst_width);
width = src.width_signed()?; imm_width = src.width_signed()?;
} }
let code = if dst.width() == Width::B8 {
if dst.width() == Width::B8 { 0x80
self.bytes.push(0x80); } else if imm_width == Width::B8 {
} else if width == Width::B8 { 0x83
self.bytes.push(0x83);
} else { } else {
self.bytes.push(0x81); imm_width = dst_width;
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 RegMem::Reg(dst) = dst 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(())
} }
self.bytes.push(modrm(0b11, 0b101, dst.base())); pub fn add(&mut self, dst: impl Into<RegMem>, src: impl Into<RegMemImm>) -> ERes {
self.imm(src, width); self.add_sub(dst, src, 0)
Ok(()) }
pub fn sub(&mut self, dst: impl Into<RegMem>, src: impl Into<RegMemImm>) -> ERes {
self.add_sub(dst, src, 5)
} }
fn prefix16(&mut self, width: impl Into<Width>) { fn prefix16(&mut self, width: impl Into<Width>) {
@@ -209,7 +235,10 @@ impl Code {
} }
} }
fn prefix32(&mut self, mem: &Mem) -> Result<(), CompilerMsg> { fn prefix32(&mut self, mem: impl MaybeMem) -> Result<(), CompilerMsg> {
let Some(mem) = mem.mem() else {
return Ok(());
};
match mem.reg.width() { match mem.reg.width() {
Width::B8 | Width::B16 => return Err("invalid register width".into()), Width::B8 | Width::B16 => return Err("invalid register width".into()),
Width::B32 => self.bytes.push(0x67), Width::B32 => self.bytes.push(0x67),
@@ -218,31 +247,35 @@ 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_regdisp(&mut self, reg: impl Into<Option<Reg>>, mem: Mem) { fn modrm(&mut self, reg: impl ModRMReg, rm: impl ModRMRM) {
const I8_MIN: i32 = i8::MIN as i32; let addr = rm.addr();
const I8_MAX: i32 = i8::MAX as i32; let mod_ = match addr {
let mod_ = match mem.disp { EffAddr::Mem0 | EffAddr::Sym(_) => 0b00,
0 => 0b00, EffAddr::Mem8(_) => 0b01,
I8_MIN..=I8_MAX => 0b01, EffAddr::Mem32(_) => 0b10,
_ => 0b10, EffAddr::None => 0b11,
}; };
let r = reg.into().map(|r| Reg::base(&r)).unwrap_or(0); self.bytes
self.bytes.push(modrm(mod_, r, mem.reg.base())); .push(((mod_ as u8) << 6) | (reg.val() << 3) | rm.rm());
if mem.reg.base() == rsp.base() { if !matches!(addr, EffAddr::None) && rm.rm() == 0b100 {
// SIB // SIB
self.bytes.push(0x24); self.bytes.push(0x24);
} }
match mod_ { match addr {
0b00 => (), EffAddr::Mem8(disp) => self.bytes.push(disp as u8),
0b01 => self.bytes.push(mem.disp as u8), EffAddr::Mem32(disp) => self.bytes.extend(disp.to_le_bytes()),
0b10 => self.bytes.extend(mem.disp.to_le_bytes()), EffAddr::Sym(sym) => self.sym_offset4(sym),
_ => unreachable!(), _ => (),
} }
} }
+27 -3
View File
@@ -1,6 +1,5 @@
mod compile; mod compile;
mod encode; mod encode;
mod reg;
#[cfg(test)] #[cfg(test)]
mod test; mod test;
mod types; mod types;
@@ -14,7 +13,6 @@ use crate::{
pub use compile::*; pub use compile::*;
pub use encode::*; pub use encode::*;
pub use reg::*;
pub use types::*; pub use types::*;
use util::*; use util::*;
@@ -22,9 +20,35 @@ pub struct X86_64;
impl Arch for X86_64 { impl Arch for X86_64 {
const NAME: &str = "x86_64"; const NAME: &str = "x86_64";
type Asm = Code; type Asm = Asm;
type Addr = u64; type Addr = u64;
type CallConv = CallConv;
fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg> { fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg> {
compile(p) compile(p)
} }
} }
pub enum CallConv {
SystemV,
}
impl CallConv {
pub fn param(&self) -> &[Reg] {
use regs::*;
match self {
Self::SystemV => &[rdi, rsi, rdx, rcx, r8, r9],
}
}
pub fn scratch(&self) -> &[Reg] {
use regs::*;
match self {
Self::SystemV => &[rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11],
}
}
pub fn ret(&self) -> &[Reg] {
use regs::*;
match self {
Self::SystemV => &[rax, rdx],
}
}
}
-197
View File
@@ -1,197 +0,0 @@
#[derive(Clone, Copy, PartialEq)]
pub struct Reg {
val: u8,
high: bool,
width: Width,
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum Width {
B8 = 0,
B16 = 1,
B32 = 2,
B64 = 3,
}
def_regs! {
0b0000 : rax eax ax al,
0b0001 : rcx ecx cx cl !_,
0b0010 : rdx edx dx dl,
0b0011 : rbx ebx bx bl,
0b0100 : rsp esp sp spl norex=ah !_,
0b0101 : rbp ebp bp bpl norex=ch,
0b0110 : rsi esi si sil norex=dh !_,
0b0111 : rdi edi di dil norex=bh,
0b1000 : r8 r8d r8w r8b,
0b1001 : r9 r9d r9w r9b !_,
0b1010 : r10 r10d r10w r10b,
0b1011 : r11 r11d r11w r11b,
0b1100 : r12 r12d r12w r12b !_,
0b1101 : r13 r13d r13w r13b,
0b1110 : r14 r14d r14w r14b,
0b1111 : r15 r15d r15w r15b,
}
impl Reg {
pub fn base(&self) -> u8 {
self.val & 0b111
}
/// checks if register is not one of the first 8 (0-7)
pub fn gt8(&self) -> bool {
self.val >= 0b1000
}
pub fn gt4(&self) -> bool {
self.val >= 0b0100
}
pub fn width(&self) -> Width {
self.width
}
pub fn not8(&self) -> u8 {
self.width.not8()
}
pub fn high(&self) -> bool {
self.high
}
/// if self has 64 bit width, changes width to 32 bit
pub fn lower64(&self) -> Self {
let mut new = *self;
new.width = new.width.min(Width::B32);
new
}
pub fn requires_rex(&self) -> bool {
self.gt8()
|| self.width == Width::B64
|| (self.gt4() && self.width == Width::B8 && !self.high)
}
pub fn incompatible(&self, other: &Reg) -> bool {
(self.requires_rex() && other.high) || (self.high && other.requires_rex())
}
const fn new(val: u8, width: Width, high: bool) -> Self {
Self { val, high, width }
}
}
impl Width {
pub const fn max_val(&self) -> u64 {
match self {
Self::B64 => u64::MAX,
Self::B32 => u32::MAX as u64,
Self::B16 => u16::MAX as u64,
Self::B8 { .. } => u8::MAX as u64,
}
}
pub fn min(self, other: Self) -> Self {
if self <= other { self } else { other }
}
pub const fn bytes(&self) -> usize {
match self {
Self::B64 => 8,
Self::B32 => 4,
Self::B16 => 2,
Self::B8 { .. } => 1,
}
}
/// greater than 8 bits
pub const fn not8(&self) -> u8 {
!matches!(self, Self::B8) as u8
}
}
macro_rules! filter {
($($filtered:ident)*; ! $_:tt $($item:ident)*; $($rest:tt)*) => {
filter!($($filtered)* $($item)*; $($rest)*)
};
($($filtered:ident)*; $($item:ident)*; $($rest:tt)*) => {
filter!($($filtered)*; $($rest)*)
};
($($filtered:ident)*;) => {
[$($filtered, )*]
};
}
use filter;
macro_rules! def_regs {
($($val:literal : $B64:ident $B32:ident $B16:ident $B8:ident $(norex=$B8H:ident)? $(!$imp:tt)?,)*) => {
$(
#[allow(non_upper_case_globals)]
pub const $B64: Reg = Reg::new($val, Width::B64, false);
#[allow(non_upper_case_globals)]
pub const $B32: Reg = Reg::new($val, Width::B32, false);
#[allow(non_upper_case_globals)]
pub const $B16: Reg = Reg::new($val, Width::B16, false);
#[allow(non_upper_case_globals)]
pub const $B8 : Reg = Reg::new($val, Width::B8 , false);
$(
#[allow(non_upper_case_globals)]
pub const $B8H: Reg = Reg::new($val, Width::B8, true);
)?
)*
impl Reg {
// #[cfg(test)]
// pub const ALL: &[Reg] = &[
// $( $B64, $B32, $B16, $B8, $($B8H,)? )*
// ];
#[cfg(test)]
pub const IMPORTANT: &[Reg] = &
filter!(; $($(!$imp)? $B64 $B32 $B16 $B8 $($B8H)?; )* )
;
pub fn parse(s: &str) -> Option<Self> {
Some(match s.to_lowercase().as_str() {
$(
stringify!($B64) => $B64,
stringify!($B32) => $B32,
stringify!($B16) => $B16,
stringify!($B8 ) => $B8,
$(
stringify!($B8H) => $B8H,
)?
)*
_ => return None,
})
}
}
impl std::fmt::Display for Reg {
#[allow(non_upper_case_globals)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match *self {
$(
$B64 => stringify!($B64),
$B32 => stringify!($B32),
$B16 => stringify!($B16),
$B8 => stringify!($B8),
$(
$B8H => stringify!($B8H),
)?
)*
_ => "UNKNOWN",
})
}
}
};
}
use def_regs;
use crate::arch::x86_64::Imm;
impl From<Reg> for Width {
fn from(value: Reg) -> Self {
value.width
}
}
+34 -2
View File
@@ -37,9 +37,41 @@ fn mov() {
} }
#[test] #[test]
fn sub() { 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 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))
+37 -17
View File
@@ -36,13 +36,13 @@ pub fn imms() -> impl Iterator<Item = i128> {
IMMS.iter().cloned() IMMS.iter().cloned()
} }
pub fn regs() -> impl Iterator<Item = Reg> { pub fn regs() -> impl Iterator<Item = RegW> {
Reg::IMPORTANT.iter().cloned() RegW::IMPORTANT.iter().cloned()
} }
pub fn mems() -> impl Iterator<Item = Mem> { pub fn mems() -> impl Iterator<Item = Mem> {
gen move { gen move {
for &reg in Reg::IMPORTANT { for &reg in RegW::IMPORTANT {
for &disp in DISPS { for &disp in DISPS {
for &width in WIDTHS { for &width in WIDTHS {
yield mem(reg, disp, width); yield mem(reg, disp, width);
@@ -59,42 +59,62 @@ 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>,
instr: impl FnOnce(&mut Code) -> Result<(), CompilerMsg>, instr: impl Fn(&mut Code) -> Result<(), CompilerMsg>,
) { ) {
let asm = asm.as_ref(); let asm = asm.as_ref();
let expected = if let Some(val) = ctx.cache.get(asm) { let (mut res, cache) = eq_(ctx, asm, &instr);
val if res.is_err() && cache {
ctx.cache.remove(asm);
res = eq_(ctx, asm, &instr).0;
}
if let Err(err) = res {
panic!("{err}");
}
}
#[track_caller]
pub fn eq_(
ctx: &mut TestCtx,
asm: &str,
instr: impl FnOnce(&mut Code) -> Result<(), CompilerMsg>,
) -> (Result<(), String>, bool) {
let (expected, cache) = if let Some(val) = ctx.cache.get(asm) {
(val, true)
} else { } else {
ctx.changed = true; ctx.changed = true;
let res = nasm(asm); let res = nasm(asm);
ctx.cache.insert(asm.to_string(), res); ctx.cache.insert(asm.to_string(), res);
ctx.cache.get(asm).unwrap() (ctx.cache.get(asm).unwrap(), false)
}; };
let code = &mut ctx.code; let code = &mut ctx.code;
let res = instr(code); let res = instr(code);
match (expected, res) { let res = match (expected, res) {
(Ok(expected), Err(e)) => { (Ok(expected), Err(e)) => Err(format!(
panic!(
"{asm}: failed to compile: {}\nexpected: {expected:x?}", "{asm}: failed to compile: {}\nexpected: {expected:x?}",
e.msg e.msg
); )),
}
(Err(e), Ok(_)) => { (Err(e), Ok(_)) => {
let res = &code.bytes[..]; let res = &code.bytes[..];
panic!("{asm}: should not have compiled:\n{e}\ngot: {res:x?}"); Err(format!(
"{asm}: should not have compiled:\n{e}\ngot: {res:x?}"
))
} }
(Err(_), Err(_)) => (), (Err(_), Err(_)) => Ok(()),
(Ok(expected), Ok(_)) => { (Ok(expected), Ok(_)) => {
let res = &code.bytes[..]; let res = &code.bytes[..];
if expected != res { if expected != res {
panic!("{asm}: expected {expected:x?}, got {res:x?}") Err(format!("{asm}: expected {expected:x?}, got {res:x?}"))
} else {
Ok(())
} }
} }
} };
code.bytes.clear(); ctx.code.bytes.clear();
(res, cache)
} }
fn nasm(input: &str) -> Result<Vec<u8>, String> { fn nasm(input: &str) -> Result<Vec<u8>, String> {
+292
View File
@@ -0,0 +1,292 @@
use super::*;
use crate::backend::Symbol;
#[derive(Clone, Copy)]
pub enum RegMem {
Reg(RegW),
Mem(Mem),
}
#[derive(Clone, Copy)]
pub enum RegMemImm {
Reg(RegW),
Imm(Imm),
Mem(Mem),
}
pub trait MaybeMem {
fn mem(&self) -> Option<Mem>;
}
impl RegMem {
pub fn width(&self) -> Width {
match self {
RegMem::Reg(reg_w) => reg_w.width(),
RegMem::Mem(mem) => mem.width,
}
}
}
impl MaybeMem for RegW {
fn mem(&self) -> Option<Mem> {
None
}
}
impl MaybeMem for Mem {
fn mem(&self) -> Option<Mem> {
Some(*self)
}
}
impl MaybeMem for RegMem {
fn mem(&self) -> Option<Mem> {
match self {
RegMem::Reg(reg_w) => None,
RegMem::Mem(mem) => Some(*mem),
}
}
}
impl RexW for RegMem {
fn rexw(&self) -> bool {
match self {
RegMem::Reg(reg_w) => reg_w.rexw(),
RegMem::Mem(mem) => mem.rexw(),
}
}
}
impl RexBit for RegMem {
fn rex(&self) -> bool {
match self {
RegMem::Reg(reg_w) => reg_w.rex(),
RegMem::Mem(mem) => mem.rex(),
}
}
}
impl ModRMRM for RegMem {
fn rm(&self) -> u8 {
match self {
RegMem::Reg(reg_w) => reg_w.rm(),
RegMem::Mem(mem) => mem.rm(),
}
}
fn addr(&self) -> EffAddr {
match self {
RegMem::Reg(reg_w) => reg_w.addr(),
RegMem::Mem(mem) => mem.addr(),
}
}
}
// fromrot
impl From<RegW> for RegMemImm {
fn from(value: RegW) -> Self {
Self::Reg(value)
}
}
impl From<RegW> for RegMem {
fn from(value: RegW) -> Self {
Self::Reg(value)
}
}
impl From<Mem> for RegMemImm {
fn from(value: Mem) -> Self {
Self::Mem(value)
}
}
impl From<Mem> for RegMem {
fn from(value: Mem) -> Self {
Self::Mem(value)
}
}
impl From<u64> for RegMemImm {
fn from(value: u64) -> Self {
Self::Imm(value.into())
}
}
impl From<i64> for RegMemImm {
fn from(value: i64) -> Self {
Self::Imm(value.into())
}
}
impl From<i32> for RegMemImm {
fn from(value: i32) -> Self {
Self::Imm(value.into())
}
}
impl From<i128> for RegMemImm {
fn from(value: i128) -> Self {
Self::Imm(value.into())
}
}
impl From<Imm> for RegMemImm {
fn from(value: Imm) -> Self {
Self::Imm(value)
}
}
pub trait ModRMRM {
fn rm(&self) -> u8;
fn addr(&self) -> EffAddr;
}
pub enum EffAddr {
Mem0,
Mem8(i8),
Mem32(i32),
Sym(Symbol),
None,
}
impl ModRMRM for RegW {
fn rm(&self) -> u8 {
self.base()
}
fn addr(&self) -> EffAddr {
EffAddr::None
}
}
impl ModRMRM for Mem {
fn rm(&self) -> u8 {
self.reg.base()
}
fn addr(&self) -> EffAddr {
const I8_MIN: i32 = i8::MIN as i32;
const I8_MAX: i32 = i8::MAX as i32;
let disp = self.disp;
match disp {
0 => {
if self.reg.base() == 0b101 {
EffAddr::Mem8(0)
} else {
EffAddr::Mem0
}
}
I8_MIN..=I8_MAX => EffAddr::Mem8(disp as i8),
_ => EffAddr::Mem32(disp),
}
}
}
impl ModRMRM for i32 {
fn rm(&self) -> u8 {
0b101
}
fn addr(&self) -> EffAddr {
EffAddr::Mem32(*self)
}
}
impl ModRMRM for Symbol {
fn rm(&self) -> u8 {
0b101
}
fn addr(&self) -> EffAddr {
EffAddr::Sym(*self)
}
}
impl ModRMReg for u8 {
fn val(&self) -> u8 {
*self
}
}
impl ModRMReg for RegW {
fn val(&self) -> u8 {
self.base()
}
}
pub trait ModRMReg {
fn val(&self) -> u8;
}
#[inline(always)]
pub fn rex(w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> u8 {
0b0100_0000 | bit(w.rexw(), 3) | bit(r.rex(), 2) | bit(x.rex(), 1) | bit(b.rex(), 0)
}
#[inline(always)]
fn bit(val: bool, pos: u8) -> u8 {
(val as u8) << pos
}
pub trait RexBit: Sized {
fn rex(&self) -> bool;
fn req(&self) -> bool {
false
}
fn req_no(&self) -> bool {
false
}
}
impl RexBit for u8 {
fn rex(&self) -> bool {
*self != 0
}
}
impl RexBit for RegW {
fn rex(&self) -> bool {
self.gt8()
}
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 {
fn rexw(&self) -> bool;
}
impl RexW for Width {
fn rexw(&self) -> bool {
*self == Width::B64
}
}
impl RexW for RegW {
fn rexw(&self) -> bool {
self.width().rexw()
}
}
impl RexW for u8 {
fn rexw(&self) -> bool {
*self == 1
}
}
impl RexW for Mem {
fn rexw(&self) -> bool {
self.width.rexw()
}
}
+128
View File
@@ -0,0 +1,128 @@
use super::*;
use crate::backend::{Symbol, VarId};
pub struct Asm {
pub args: Vec<(Reg, VarId)>,
pub instrs: Vec<Instr>,
}
pub enum Instr {
Mov { dst: Rvm, src: Rvmi },
Push(Rvmi),
Pop(Rvm),
Lea { dst: Rv, src: Symbol },
Int(u8),
Syscall,
Call(Symbol),
CallAt(Symbol),
Ret,
Add { dst: Rvm, src: Rvmi },
Sub { dst: Rvm, src: Rvmi },
}
impl Instr {
pub fn dst_reg(&self) -> Option<RegW> {
match self {
Instr::Mov { dst, src: _ } => dst.reg(),
Instr::Push(_) => None,
Instr::Pop(dst) => dst.reg(),
Instr::Lea { dst, src: _ } => dst.reg(),
Instr::Int(_) => None,
Instr::Syscall => None,
Instr::Call(_) => None,
Instr::CallAt(_) => None,
Instr::Ret => None,
Instr::Add { dst, src: _ } => dst.reg(),
Instr::Sub { dst, src: _ } => dst.reg(),
}
}
pub fn vars(&self) -> impl Iterator<Item = VarId> {
let mut vars = (None, None);
match self {
Instr::Mov { dst, src } => vars = (dst.var(), src.var()),
Instr::Push(src) => vars.0 = src.var(),
Instr::Pop(dst) => vars.0 = dst.var(),
Instr::Lea { dst, .. } => vars.0 = dst.var(),
Instr::Int(_) => (),
Instr::Syscall => (),
Instr::Call(_) => (),
Instr::CallAt(_) => (),
Instr::Ret => (),
Instr::Add { dst, src } => vars = (dst.var(), src.var()),
Instr::Sub { dst, src } => vars = (dst.var(), src.var()),
}
vars.0.into_iter().chain(vars.1)
}
}
#[derive(Clone, Copy)]
pub enum Rv {
Reg(RegW),
Var(VarId),
}
#[derive(Clone, Copy)]
pub enum Rvm {
Reg(RegW),
Var(VarId),
Mem(Mem),
}
#[derive(Clone, Copy)]
pub enum Rvmi {
Reg(RegW),
Var(VarId),
Mem(Mem),
Imm(Imm),
}
impl Rv {
pub fn reg(&self) -> Option<RegW> {
if let &Self::Reg(v) = self {
Some(v)
} else {
None
}
}
pub fn var(&self) -> Option<VarId> {
if let &Self::Var(v) = self {
Some(v)
} else {
None
}
}
}
impl Rvm {
pub fn reg(&self) -> Option<RegW> {
if let &Self::Reg(v) = self {
Some(v)
} else {
None
}
}
pub fn var(&self) -> Option<VarId> {
if let &Self::Var(v) = self {
Some(v)
} else {
None
}
}
}
impl Rvmi {
pub fn reg(&self) -> Option<RegW> {
if let &Self::Reg(v) = self {
Some(v)
} else {
None
}
}
pub fn var(&self) -> Option<VarId> {
if let &Self::Var(v) = self {
Some(v)
} else {
None
}
}
}
@@ -1,34 +1,10 @@
use super::Width;
use crate::io::CompilerMsg;
use std::num::TryFromIntError; use std::num::TryFromIntError;
use super::*;
#[derive(Clone, Copy)]
pub struct Mem {
pub reg: Reg,
pub disp: i32,
pub width: Width,
}
#[derive(Clone, Copy)]
pub enum RegImmMem {
Reg(Reg),
Imm(Imm),
Mem(Mem),
}
#[derive(Clone, Copy)]
pub enum RegMem {
Reg(Reg),
Mem(Mem),
}
#[derive(Clone, Copy, PartialEq, PartialOrd)] #[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct Imm(pub i128); pub struct Imm(pub i128);
pub fn mem(reg: Reg, disp: i32, width: Width) -> Mem {
Mem { reg, disp, width }
}
impl Imm { impl Imm {
pub fn overflow_msg() -> CompilerMsg { pub fn overflow_msg() -> CompilerMsg {
"immediate overflow".into() "immediate overflow".into()
@@ -72,68 +48,6 @@ impl TryFrom<Imm> for u8 {
} }
} }
impl std::fmt::Display for Mem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Mem { reg, disp, width } = *self;
let size = match width {
Width::B8 => "BYTE",
Width::B16 => "WORD",
Width::B32 => "DWORD",
Width::B64 => "QWORD",
};
write!(f, "{size} [{reg} {}]", signed_hex(disp as i128, true))
}
}
// fromrot
impl From<Reg> for RegImmMem {
fn from(value: Reg) -> Self {
Self::Reg(value)
}
}
impl From<Reg> for RegMem {
fn from(value: Reg) -> Self {
Self::Reg(value)
}
}
impl From<Mem> for RegImmMem {
fn from(value: Mem) -> Self {
Self::Mem(value)
}
}
impl From<Mem> for RegMem {
fn from(value: Mem) -> Self {
Self::Mem(value)
}
}
impl From<u64> for RegImmMem {
fn from(value: u64) -> Self {
Self::Imm(value.into())
}
}
impl From<i64> for RegImmMem {
fn from(value: i64) -> Self {
Self::Imm(value.into())
}
}
impl From<i32> for RegImmMem {
fn from(value: i32) -> Self {
Self::Imm(value.into())
}
}
impl From<i128> for RegImmMem {
fn from(value: i128) -> Self {
Self::Imm(value.into())
}
}
impl From<u64> for Imm { impl From<u64> for Imm {
fn from(value: u64) -> Self { fn from(value: u64) -> Self {
Self(value as i128) Self(value as i128)
+27
View File
@@ -0,0 +1,27 @@
use crate::arch::x86_64::util::signed_hex;
use super::*;
#[derive(Clone, Copy)]
pub struct Mem {
pub reg: RegW,
pub disp: i32,
pub width: Width,
}
pub fn mem(reg: RegW, disp: i32, width: Width) -> Mem {
Mem { reg, disp, width }
}
impl std::fmt::Display for Mem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Mem { reg, disp, width } = *self;
let size = match width {
Width::B8 => "BYTE",
Width::B16 => "WORD",
Width::B32 => "DWORD",
Width::B64 => "QWORD",
};
write!(f, "{size} [{reg} {}]", signed_hex(disp as i128, true))
}
}
+13
View File
@@ -0,0 +1,13 @@
mod arg;
mod asm;
mod imm;
mod mem;
mod reg;
mod width;
pub use arg::*;
pub use asm::*;
pub use imm::*;
pub use mem::*;
pub use reg::*;
pub use width::*;
+196
View File
@@ -0,0 +1,196 @@
use super::Width;
#[derive(Clone, Copy, PartialEq)]
pub struct RegW {
reg: Reg,
high: bool,
width: Width,
}
pub type Reg = u8;
def_regs! { RegW;
0b0000 : rax eax ax al,
0b0001 : rcx ecx cx cl !_,
0b0010 : rdx edx dx dl,
0b0011 : rbx ebx bx bl,
0b0100 : rsp esp sp spl norex=ah !_,
0b0101 : rbp ebp bp bpl norex=ch !_,
0b0110 : rsi esi si sil norex=dh !_,
0b0111 : rdi edi di dil norex=bh,
0b1000 : r8 r8d r8w r8b,
0b1001 : r9 r9d r9w r9b !_,
0b1010 : r10 r10d r10w r10b,
0b1011 : r11 r11d r11w r11b,
0b1100 : r12 r12d r12w r12b !_,
0b1101 : r13 r13d r13w r13b,
0b1110 : r14 r14d r14w r14b,
0b1111 : r15 r15d r15w r15b,
}
impl RegW {
pub fn reg(&self) -> u8 {
self.reg
}
pub fn base(&self) -> u8 {
self.reg & 0b111
}
/// checks if register is not one of the first 8 (0-7)
pub fn gt8(&self) -> bool {
self.reg >= 0b1000
}
pub fn gt4(&self) -> bool {
self.reg >= 0b0100
}
pub fn width(&self) -> Width {
self.width
}
pub fn not8(&self) -> u8 {
self.width.not8()
}
pub fn high(&self) -> bool {
self.high
}
/// if self has 64 bit width, changes width to 32 bit
pub fn lower64(&self) -> Self {
let mut new = *self;
new.width = new.width.min(Width::B32);
new
}
pub fn requires_rex(&self) -> bool {
self.gt8()
|| self.width == Width::B64
|| (self.gt4() && self.width == Width::B8 && !self.high)
}
pub fn incompatible(&self, other: &RegW) -> bool {
(self.requires_rex() && other.high) || (self.high && other.requires_rex())
}
const fn new(reg: u8, width: Width, high: bool) -> Self {
Self { reg, high, width }
}
}
impl Width {
pub const fn max_val(&self) -> u64 {
match self {
Self::B64 => u64::MAX,
Self::B32 => u32::MAX as u64,
Self::B16 => u16::MAX as u64,
Self::B8 { .. } => u8::MAX as u64,
}
}
pub fn min(self, other: Self) -> Self {
if self <= other { self } else { other }
}
pub const fn bytes(&self) -> usize {
match self {
Self::B64 => 8,
Self::B32 => 4,
Self::B16 => 2,
Self::B8 { .. } => 1,
}
}
/// greater than 8 bits
pub const fn not8(&self) -> u8 {
!matches!(self, Self::B8) as u8
}
}
macro_rules! filter {
($($filtered:ident)*; ! $_:tt $($item:ident)*; $($rest:tt)*) => {
filter!($($filtered)* $($item)*; $($rest)*)
};
($($filtered:ident)*; $($item:ident)*; $($rest:tt)*) => {
filter!($($filtered)*; $($rest)*)
};
($($filtered:ident)*;) => {
[$($filtered, )*]
};
}
use filter;
macro_rules! def_regs {
($reg:ident; $($val:literal : $B64:ident $B32:ident $B16:ident $B8:ident $(norex=$B8H:ident)? $(!$imp:tt)?,)*) => {
pub mod regs {
$(
#[allow(non_upper_case_globals)]
pub const $B64: u8 = $val;
)*
}
pub mod regws {
use super::*;
$(
#[allow(non_upper_case_globals)]
pub const $B64: $reg = $reg::new($val, Width::B64, false);
#[allow(non_upper_case_globals)]
pub const $B32: $reg = $reg::new($val, Width::B32, false);
#[allow(non_upper_case_globals)]
pub const $B16: $reg = $reg::new($val, Width::B16, false);
#[allow(non_upper_case_globals)]
pub const $B8 : $reg = $reg::new($val, Width::B8 , false);
$(
#[allow(non_upper_case_globals)]
pub const $B8H: $reg = $reg::new($val, Width::B8, true);
)?
)*
impl $reg {
// #[cfg(test)]
// pub const ALL: &[$reg] = &[
// $( $B64, $B32, $B16, $B8, $($B8H,)? )*
// ];
#[cfg(test)]
pub const IMPORTANT: &[$reg] = &
filter!(; $($(!$imp)? $B64 $B32 $B16 $B8 $($B8H)?; )* )
;
pub fn parse(s: &str) -> Option<Self> {
Some(match s.to_lowercase().as_str() {
$(
stringify!($B64) => $B64,
stringify!($B32) => $B32,
stringify!($B16) => $B16,
stringify!($B8 ) => $B8,
$(
stringify!($B8H) => $B8H,
)?
)*
_ => return None,
})
}
}
impl std::fmt::Display for $reg {
#[allow(non_upper_case_globals)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match *self {
$(
$B64 => stringify!($B64),
$B32 => stringify!($B32),
$B16 => stringify!($B16),
$B8 => stringify!($B8),
$(
$B8H => stringify!($B8H),
)?
)*
_ => "UNKNOWN",
})
}
}
}
};
}
use def_regs;
+22
View File
@@ -0,0 +1,22 @@
use super::*;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum Width {
B8 = 0,
B16 = 1,
B32 = 2,
B64 = 3,
}
impl From<RegW> for Width {
fn from(value: RegW) -> Self {
value.width()
}
}
impl From<Mem> for Width {
fn from(value: Mem) -> Self {
value.width
}
}
-83
View File
@@ -1,86 +1,3 @@
use super::*;
#[inline(always)]
pub fn modrm_regs(reg: impl Into<Reg>, reg_rm: impl Into<Reg>) -> u8 {
modrm(0b11, reg.into().base(), reg_rm.into().base())
}
#[inline(always)]
pub fn modrm_disp32(reg: impl Into<Reg>) -> u8 {
modrm(0b00, reg.into().base(), 0b101)
}
#[inline(always)]
pub fn modrm(mod_: u8, reg: u8, rm: u8) -> u8 {
(mod_ << 6) | (reg << 3) | rm
}
#[inline(always)]
pub fn rex(w: impl RexW, r: impl RexBit, x: u8, b: impl RexBit) -> u8 {
0b0100_0000 | bit(w.rexw(), 3) | bit(r.rex(), 2) | bit(x.rex(), 1) | bit(b.rex(), 0)
}
#[inline(always)]
fn bit(val: bool, pos: u8) -> u8 {
(val as u8) << pos
}
pub trait RexBit: Sized {
fn rex(&self) -> bool;
fn req(&self) -> bool {
false
}
}
impl RexBit for u8 {
fn rex(&self) -> bool {
*self != 0
}
}
impl RexBit for Reg {
fn rex(&self) -> bool {
self.gt8()
}
fn req(&self) -> bool {
self.gt4() && (self.width() == Width::B8) && !self.high()
}
}
impl RexBit for Mem {
fn rex(&self) -> bool {
self.reg.rex()
}
}
pub trait RexW {
fn rexw(&self) -> bool;
}
impl RexW for Width {
fn rexw(&self) -> bool {
*self == Width::B64
}
}
impl RexW for Reg {
fn rexw(&self) -> bool {
self.width().rexw()
}
}
impl RexW for u8 {
fn rexw(&self) -> bool {
*self == 1
}
}
impl RexW for Mem {
fn rexw(&self) -> bool {
self.width.rexw()
}
}
/// assumes the next instruction is directly after /// assumes the next instruction is directly after
pub fn addr_offset(pos: usize, addr: u64) -> [u8; 4] { pub fn addr_offset(pos: usize, addr: u64) -> [u8; 4] {
let pos = (pos + 4) as i32; let pos = (pos + 4) as i32;
+31
View File
@@ -0,0 +1,31 @@
ids!(VarId FnId);
impl<A: Arch> Index<FnId> for Vec<Func<A>> {
type Output = Func<A>;
fn index(&self, index: FnId) -> &Self::Output {
&self[index.0]
}
}
impl<A: Arch> Index<&FnId> for Vec<Func<A>> {
type Output = Func<A>;
fn index(&self, index: &FnId) -> &Self::Output {
&self[index.0]
}
}
macro_rules! ids {
($($name:ident)*) => {
$(
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct $name(usize);
)*
};
}
use std::ops::Index;
use ids;
use crate::{arch::Arch, backend::Func};
+35 -8
View File
@@ -1,6 +1,8 @@
mod addr; mod addr;
mod id;
mod symbol; mod symbol;
pub use addr::*; pub use addr::*;
pub use id::*;
pub use symbol::*; pub use symbol::*;
use crate::{arch::Arch, backend::LinkedProgram, io::CompilerMsg}; use crate::{arch::Arch, backend::LinkedProgram, io::CompilerMsg};
@@ -10,6 +12,7 @@ pub struct Program<A: Arch> {
pub funcs: Vec<Func<A>>, pub funcs: Vec<Func<A>>,
pub entry: Option<Symbol>, pub entry: Option<Symbol>,
pub external: Vec<External>, pub external: Vec<External>,
pub call_convs: Vec<A::CallConv>,
sym_info: Vec<SymInfo>, sym_info: Vec<SymInfo>,
sym_count: usize, sym_count: usize,
@@ -21,8 +24,10 @@ pub struct Data {
} }
pub struct Func<A: Arch> { pub struct Func<A: Arch> {
pub instrs: Vec<Instr<A>>, pub params: Vec<VarId>,
pub body: Body<A>,
pub sym: Symbol, pub sym: Symbol,
pub conv: usize,
} }
pub struct External { pub struct External {
@@ -35,16 +40,38 @@ pub struct SymInfo {
pub external: bool, pub external: bool,
} }
pub type Body<A> = Vec<Instr<A>>;
pub enum Instr<A: Arch> { pub enum Instr<A: Arch> {
Set { dst: VarId, src: Vec<u8> }, Set {
Call { dst: FnId, args: Vec<VarId> }, dst: VarId,
Copy { dst: VarId, src: VarId }, src: Vec<u8>,
},
Call {
dst: VarId,
f: FnId,
args: Vec<VarId>,
},
Copy {
dst: VarId,
src: VarId,
},
Add {
dst: VarId,
src1: VarId,
src2: VarId,
},
If {
cond: VarId,
then: Body<A>,
else_: Body<A>,
},
Loop(Body<A>),
Return(Option<VarId>),
Break(Option<VarId>),
Asm(A::Asm), Asm(A::Asm),
} }
pub type VarId = usize;
pub type FnId = usize;
impl<A: Arch> Program<A> { impl<A: Arch> Program<A> {
pub fn encode_data(&self, data: &mut Vec<u8>, sym_tab: &mut SymTable<A::Addr>) { pub fn encode_data(&self, data: &mut Vec<u8>, sym_tab: &mut SymTable<A::Addr>) {
for d in &self.ro_data { for d in &self.ro_data {
@@ -70,7 +97,7 @@ impl<A: Arch> Program<A> {
name: name.into(), name: name.into(),
external: false, external: false,
}); });
self.funcs.push(Func { instrs, sym }); self.funcs.push(Func { body: instrs, sym });
sym sym
} }
+5 -5
View File
@@ -64,21 +64,21 @@ pub fn parse_imm(mut s: &str, span: Span) -> Result<Imm, CompilerMsg> {
Ok(Imm(val)) Ok(Imm(val))
} }
pub fn parse_rmi(ctx: &mut crate::parser::ParseCtx) -> Result<RegImmMem, CompilerMsg> { pub fn parse_rmi(ctx: &mut crate::parser::ParseCtx) -> Result<RegMemImm, CompilerMsg> {
let next = ctx.expect_next()?; let next = ctx.expect_next()?;
let err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register or immediate"); let err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register or immediate");
Ok(match &next { Ok(match &next {
Token::Ident(ident) => RegImmMem::Reg(Reg::parse(ident).ok_or_else(err)?), Token::Ident(ident) => RegMemImm::Reg(RegW::parse(ident).ok_or_else(err)?),
Token::Lit(LitTy::Number(num)) => RegImmMem::Imm(parse_imm(num, ctx.span)?), Token::Lit(LitTy::Number(num)) => RegMemImm::Imm(parse_imm(num, ctx.span)?),
_ => return Err(err()), _ => return Err(err()),
}) })
} }
pub fn parse_reg(ctx: &mut crate::parser::ParseCtx) -> Result<Reg, CompilerMsg> { pub fn parse_reg(ctx: &mut crate::parser::ParseCtx) -> Result<RegW, CompilerMsg> {
let next = ctx.expect_next()?; let next = ctx.expect_next()?;
let err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register"); let err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register");
let Token::Ident(next) = &next else { let Token::Ident(next) = &next else {
return Err(err()); return Err(err());
}; };
Reg::parse(next).ok_or_else(err) RegW::parse(next).ok_or_else(err)
} }
Binary file not shown.
Binary file not shown.