Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
780336fbe4 | ||
|
|
e4d2dcfe15 | ||
|
|
759af9a3d7 | ||
|
|
9f7d95a67e | ||
|
|
1944683dc5 | ||
|
|
3cbd50e619 | ||
|
|
c062993130 | ||
|
|
6cc81d7a5c | ||
|
|
85eacd783d | ||
|
|
4fe4b50c8b | ||
|
|
026aec8565 | ||
|
|
113f3d4d9c |
@@ -9,5 +9,6 @@ pub trait Arch: Sized {
|
||||
const NAME: &str;
|
||||
type Asm;
|
||||
type Addr: Addr;
|
||||
type CallConv;
|
||||
fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg>;
|
||||
}
|
||||
|
||||
+236
-13
@@ -1,14 +1,24 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use super::*;
|
||||
use crate::backend::{LibImport, LinkedProgram, SymImport, SymTable, Symbol};
|
||||
use util::*;
|
||||
use crate::backend::{Body, Func, LibImport, LinkedProgram, SymImport, SymTable, Symbol, VarId};
|
||||
|
||||
pub struct Encoder<'a> {
|
||||
pub code: Code,
|
||||
pub sym_tab: SymTable<u64>,
|
||||
pub sym_refs: HashMap<Symbol, Vec<usize>>,
|
||||
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> {
|
||||
@@ -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);
|
||||
|
||||
for f in &p.funcs {
|
||||
let addr = encoder.code.bytes.len();
|
||||
encoder.sym_tab.insert(f.sym, addr as u64);
|
||||
for instr in &f.instrs {
|
||||
encoder.compile_instr(instr)?;
|
||||
}
|
||||
encoder.func(f);
|
||||
}
|
||||
|
||||
for (pos, sym) in encoder.code.missing.drain(..) {
|
||||
@@ -60,14 +66,228 @@ 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>;
|
||||
impl<'a> Encoder<'a> {
|
||||
fn compile_instr(&mut self, instr: &BInstr) -> Result<(), CompilerMsg> {
|
||||
match instr {
|
||||
BInstr::Asm(asm) => {
|
||||
self.code.extend(asm);
|
||||
fn func(&mut self, f: &Func<X86_64>) -> ERes {
|
||||
let addr = self.code.bytes.len();
|
||||
self.sym_tab.insert(f.sym, addr as u64);
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
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 ® 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(®) = 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(®);
|
||||
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!(),
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -77,6 +297,9 @@ impl<'a> Encoder<'a> {
|
||||
code: Code::default(),
|
||||
sym_tab: SymTable::new(program.sym_count()),
|
||||
sym_refs: Default::default(),
|
||||
seg: 0,
|
||||
segs: Default::default(),
|
||||
active: Default::default(),
|
||||
program,
|
||||
}
|
||||
}
|
||||
|
||||
+91
-66
@@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
use crate::backend::Symbol;
|
||||
|
||||
type ERes = Result<(), CompilerMsg>;
|
||||
pub type ERes = Result<(), CompilerMsg>;
|
||||
|
||||
/// machine code
|
||||
#[derive(Default)]
|
||||
@@ -11,25 +11,21 @@ pub struct 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 src = src.into();
|
||||
match dst {
|
||||
RegMem::Reg(mut dst) => match src {
|
||||
RegImmMem::Reg(src) => {
|
||||
RegMemImm::Reg(src) => {
|
||||
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());
|
||||
}
|
||||
let width = dst.width();
|
||||
self.prefix16(width);
|
||||
self.rex(width, src, 0, dst);
|
||||
self.bytes.push(0x88 | width.not8());
|
||||
self.prefix16(dst);
|
||||
self.rex(dst, src, 0, dst)?;
|
||||
self.bytes.push(0x88 | dst.not8());
|
||||
self.modrm(src, dst);
|
||||
}
|
||||
RegImmMem::Imm(src) => {
|
||||
RegMemImm::Imm(src) => {
|
||||
let src_width = src.width_unsigned()?;
|
||||
if src_width > dst.width() {
|
||||
return Err("immediate cannot fit in register".into());
|
||||
@@ -44,40 +40,34 @@ 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());
|
||||
}
|
||||
}
|
||||
RegImmMem::Mem(src) => {
|
||||
RegMemImm::Mem(src) => {
|
||||
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.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);
|
||||
}
|
||||
},
|
||||
RegMem::Mem(dst) => match src {
|
||||
RegImmMem::Reg(src) => {
|
||||
RegMemImm::Reg(src) => {
|
||||
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.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);
|
||||
}
|
||||
RegImmMem::Imm(src) => {
|
||||
RegMemImm::Imm(src) => {
|
||||
let encode_width = dst.width.min(Width::B32);
|
||||
let src_width = if dst.width == Width::B64 {
|
||||
src.width_signed()
|
||||
@@ -90,32 +80,32 @@ impl Code {
|
||||
if src_width > dst.width {
|
||||
return Err("source cannot fit in destination".into());
|
||||
}
|
||||
self.prefix32(&dst)?;
|
||||
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);
|
||||
}
|
||||
RegImmMem::Mem(_) => return Err("cannot move memory to memory".into()),
|
||||
RegMemImm::Mem(_) => return Err("cannot move memory to memory".into()),
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn push(&mut self, reg: impl Into<RegImmMem>) -> ERes {
|
||||
pub fn push(&mut self, reg: impl Into<RegMemImm>) -> ERes {
|
||||
match reg.into() {
|
||||
RegImmMem::Reg(reg) => match reg.width() {
|
||||
RegMemImm::Reg(reg) => match reg.width() {
|
||||
Width::B64 => {
|
||||
if reg.gt8() {
|
||||
self.bytes.push(0x41);
|
||||
}
|
||||
self.bytes.push(0x50 | reg.base());
|
||||
}
|
||||
Width::B16 => {}
|
||||
Width::B16 => todo!(),
|
||||
_ => 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 => {
|
||||
self.bytes.push(0x6a);
|
||||
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()),
|
||||
},
|
||||
RegImmMem::Mem(mem) => todo!(),
|
||||
RegMemImm::Mem(mem) => todo!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pop(&mut self, reg: Reg) -> ERes {
|
||||
pub fn pop(&mut self, reg: RegW) -> ERes {
|
||||
match reg.width() {
|
||||
Width::B64 | Width::B16 => (),
|
||||
_ => return Err("register must be 64 or 16 bit".into()),
|
||||
@@ -144,10 +134,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: RegW, 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) {
|
||||
@@ -172,42 +163,69 @@ impl Code {
|
||||
self.bytes.push(0xc3);
|
||||
}
|
||||
|
||||
fn add_sub(&mut self, dst: Reg, src: impl Into<Imm>, ext: u8) -> ERes {
|
||||
let mut src = src.into();
|
||||
let mut width = src.width_signed()?;
|
||||
let dst_width = dst.width().min(Width::B32);
|
||||
|
||||
self.prefix16(dst_width);
|
||||
self.rex(dst, 0, 0, dst);
|
||||
|
||||
if width > dst_width {
|
||||
width = src.width_unsigned()?;
|
||||
if dst.width() == Width::B64 || width > dst_width {
|
||||
return Err("immediate overflow".into());
|
||||
fn add_sub(&mut self, dst: impl Into<RegMem>, src: impl Into<RegMemImm>, ext: u8) -> ERes {
|
||||
let dst = dst.into();
|
||||
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);
|
||||
width = src.width_signed()?;
|
||||
}
|
||||
RegMemImm::Imm(mut src) => {
|
||||
let mut imm_width = src.width_signed()?;
|
||||
let dst_width = dst.width().min(Width::B32);
|
||||
|
||||
if dst.width() == Width::B8 {
|
||||
self.bytes.push(0x80);
|
||||
} else if width == Width::B8 {
|
||||
self.bytes.push(0x83);
|
||||
} else {
|
||||
self.bytes.push(0x81);
|
||||
width = dst_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.modrm(ext, dst);
|
||||
self.imm(src, width);
|
||||
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(())
|
||||
}
|
||||
|
||||
pub fn add(&mut self, dst: Reg, src: impl Into<Imm>) -> ERes {
|
||||
pub fn add(&mut self, dst: impl Into<RegMem>, src: impl Into<RegMemImm>) -> ERes {
|
||||
self.add_sub(dst, src, 0)
|
||||
}
|
||||
|
||||
pub fn sub(&mut self, dst: Reg, src: impl Into<Imm>) -> ERes {
|
||||
pub fn sub(&mut self, dst: impl Into<RegMem>, src: impl Into<RegMemImm>) -> ERes {
|
||||
self.add_sub(dst, src, 5)
|
||||
}
|
||||
|
||||
@@ -217,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() {
|
||||
Width::B8 | Width::B16 => return Err("invalid register width".into()),
|
||||
Width::B32 => self.bytes.push(0x67),
|
||||
@@ -226,10 +247,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) {
|
||||
|
||||
+27
-3
@@ -1,6 +1,5 @@
|
||||
mod compile;
|
||||
mod encode;
|
||||
mod reg;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod types;
|
||||
@@ -14,7 +13,6 @@ use crate::{
|
||||
|
||||
pub use compile::*;
|
||||
pub use encode::*;
|
||||
pub use reg::*;
|
||||
pub use types::*;
|
||||
use util::*;
|
||||
|
||||
@@ -22,9 +20,35 @@ pub struct X86_64;
|
||||
|
||||
impl Arch for X86_64 {
|
||||
const NAME: &str = "x86_64";
|
||||
type Asm = Code;
|
||||
type Asm = Asm;
|
||||
type Addr = u64;
|
||||
type CallConv = CallConv;
|
||||
fn compile(p: &Program<Self>) -> Result<LinkedProgram<Self::Addr>, CompilerMsg> {
|
||||
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],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -38,14 +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))
|
||||
|
||||
@@ -36,13 +36,13 @@ pub fn imms() -> impl Iterator<Item = i128> {
|
||||
IMMS.iter().cloned()
|
||||
}
|
||||
|
||||
pub fn regs() -> impl Iterator<Item = Reg> {
|
||||
Reg::IMPORTANT.iter().cloned()
|
||||
pub fn regs() -> impl Iterator<Item = RegW> {
|
||||
RegW::IMPORTANT.iter().cloned()
|
||||
}
|
||||
|
||||
pub fn mems() -> impl Iterator<Item = Mem> {
|
||||
gen move {
|
||||
for ® in Reg::IMPORTANT {
|
||||
for ® in RegW::IMPORTANT {
|
||||
for &disp in DISPS {
|
||||
for &width in WIDTHS {
|
||||
yield mem(reg, disp, width);
|
||||
@@ -59,42 +59,62 @@ pub struct TestCtx {
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn eq(
|
||||
ctx: &mut TestCtx,
|
||||
asm: impl AsRef<str>,
|
||||
instr: impl FnOnce(&mut Code) -> Result<(), CompilerMsg>,
|
||||
instr: impl Fn(&mut Code) -> Result<(), CompilerMsg>,
|
||||
) {
|
||||
let asm = asm.as_ref();
|
||||
let expected = if let Some(val) = ctx.cache.get(asm) {
|
||||
val
|
||||
let (mut res, cache) = eq_(ctx, asm, &instr);
|
||||
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 {
|
||||
ctx.changed = true;
|
||||
let res = nasm(asm);
|
||||
ctx.cache.insert(asm.to_string(), res);
|
||||
ctx.cache.get(asm).unwrap()
|
||||
(ctx.cache.get(asm).unwrap(), false)
|
||||
};
|
||||
let code = &mut ctx.code;
|
||||
let res = instr(code);
|
||||
match (expected, res) {
|
||||
(Ok(expected), Err(e)) => {
|
||||
panic!(
|
||||
"{asm}: failed to compile: {}\nexpected: {expected:x?}",
|
||||
e.msg
|
||||
);
|
||||
}
|
||||
let res = match (expected, res) {
|
||||
(Ok(expected), Err(e)) => Err(format!(
|
||||
"{asm}: failed to compile: {}\nexpected: {expected:x?}",
|
||||
e.msg
|
||||
)),
|
||||
(Err(e), Ok(_)) => {
|
||||
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(_)) => {
|
||||
let res = &code.bytes[..];
|
||||
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> {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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 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)]
|
||||
pub struct Imm(pub i128);
|
||||
|
||||
pub fn mem(reg: Reg, disp: i32, width: Width) -> Mem {
|
||||
Mem { reg, disp, width }
|
||||
}
|
||||
|
||||
impl Imm {
|
||||
pub fn overflow_msg() -> CompilerMsg {
|
||||
"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 {
|
||||
fn from(value: u64) -> Self {
|
||||
Self(value as i128)
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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::*;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,152 +1,3 @@
|
||||
use crate::backend::Symbol;
|
||||
|
||||
use super::*;
|
||||
|
||||
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 Reg {
|
||||
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 Reg {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
pub fn addr_offset(pos: usize, addr: u64) -> [u8; 4] {
|
||||
let pos = (pos + 4) as i32;
|
||||
|
||||
@@ -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
@@ -1,6 +1,8 @@
|
||||
mod addr;
|
||||
mod id;
|
||||
mod symbol;
|
||||
pub use addr::*;
|
||||
pub use id::*;
|
||||
pub use symbol::*;
|
||||
|
||||
use crate::{arch::Arch, backend::LinkedProgram, io::CompilerMsg};
|
||||
@@ -10,6 +12,7 @@ pub struct Program<A: Arch> {
|
||||
pub funcs: Vec<Func<A>>,
|
||||
pub entry: Option<Symbol>,
|
||||
pub external: Vec<External>,
|
||||
pub call_convs: Vec<A::CallConv>,
|
||||
|
||||
sym_info: Vec<SymInfo>,
|
||||
sym_count: usize,
|
||||
@@ -21,8 +24,10 @@ pub struct Data {
|
||||
}
|
||||
|
||||
pub struct Func<A: Arch> {
|
||||
pub instrs: Vec<Instr<A>>,
|
||||
pub params: Vec<VarId>,
|
||||
pub body: Body<A>,
|
||||
pub sym: Symbol,
|
||||
pub conv: usize,
|
||||
}
|
||||
|
||||
pub struct External {
|
||||
@@ -35,16 +40,38 @@ pub struct SymInfo {
|
||||
pub external: bool,
|
||||
}
|
||||
|
||||
pub type Body<A> = Vec<Instr<A>>;
|
||||
|
||||
pub enum Instr<A: Arch> {
|
||||
Set { dst: VarId, src: Vec<u8> },
|
||||
Call { dst: FnId, args: Vec<VarId> },
|
||||
Copy { dst: VarId, src: VarId },
|
||||
Set {
|
||||
dst: 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),
|
||||
}
|
||||
|
||||
pub type VarId = usize;
|
||||
pub type FnId = usize;
|
||||
|
||||
impl<A: Arch> Program<A> {
|
||||
pub fn encode_data(&self, data: &mut Vec<u8>, sym_tab: &mut SymTable<A::Addr>) {
|
||||
for d in &self.ro_data {
|
||||
@@ -70,7 +97,7 @@ impl<A: Arch> Program<A> {
|
||||
name: name.into(),
|
||||
external: false,
|
||||
});
|
||||
self.funcs.push(Func { instrs, sym });
|
||||
self.funcs.push(Func { body: instrs, sym });
|
||||
sym
|
||||
}
|
||||
|
||||
|
||||
@@ -64,21 +64,21 @@ pub fn parse_imm(mut s: &str, span: Span) -> Result<Imm, CompilerMsg> {
|
||||
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 err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register or immediate");
|
||||
Ok(match &next {
|
||||
Token::Ident(ident) => RegImmMem::Reg(Reg::parse(ident).ok_or_else(err)?),
|
||||
Token::Lit(LitTy::Number(num)) => RegImmMem::Imm(parse_imm(num, ctx.span)?),
|
||||
Token::Ident(ident) => RegMemImm::Reg(RegW::parse(ident).ok_or_else(err)?),
|
||||
Token::Lit(LitTy::Number(num)) => RegMemImm::Imm(parse_imm(num, ctx.span)?),
|
||||
_ => 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 err = || CompilerMsg::unexpected_token(&next, ctx.span, "a register");
|
||||
let Token::Ident(next) = &next else {
|
||||
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.
Reference in New Issue
Block a user