Compare commits
14
Commits
84e184518f
...
parser3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
780336fbe4 | ||
|
|
e4d2dcfe15 | ||
|
|
759af9a3d7 | ||
|
|
9f7d95a67e | ||
|
|
1944683dc5 | ||
|
|
3cbd50e619 | ||
|
|
c062993130 | ||
|
|
6cc81d7a5c | ||
|
|
85eacd783d | ||
|
|
4fe4b50c8b | ||
|
|
026aec8565 | ||
|
|
113f3d4d9c | ||
|
|
d66f8f02b7 | ||
|
|
4e06e474ea |
@@ -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
@@ -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 ® 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!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
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
@@ -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
@@ -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],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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))
|
||||||
|
|||||||
@@ -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 ® in Reg::IMPORTANT {
|
for ® 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> {
|
||||||
|
|||||||
@@ -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 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)
|
||||||
@@ -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,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;
|
||||||
|
|||||||
@@ -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 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
Reference in New Issue
Block a user