This commit is contained in:
nora 2023-12-29 17:18:20 +01:00
parent 46e5662aab
commit ce2ad5a957
6 changed files with 124 additions and 110 deletions

View file

@ -138,15 +138,15 @@ pub enum StatementKind {
ptr: Operand,
value: Operand,
/// Amount of bytes to store.
size: Operand,
align: Operand,
size: u64,
align: u64,
},
Load {
result: Register,
ptr: Operand,
/// Amount of bytes to load.
size: Operand,
align: Operand,
size: u64,
align: u64,
},
BinOp {
result: Register,

View file

@ -112,11 +112,9 @@ impl<W: Write> PrettyPrinter<W> {
align,
} => writeln!(
self.out,
" store {}, {}, size={}, align={}",
" store {}, {}, size={size}, align={align}",
print_op(ptr_reg),
print_op(value),
print_op(size),
print_op(align)
),
StatementKind::Load {
result,
@ -125,11 +123,9 @@ impl<W: Write> PrettyPrinter<W> {
align,
} => writeln!(
self.out,
" {} = load {}, size={}, align={}",
" {} = load {}, size={size}, align={align}",
print_reg(result),
print_op(ptr_reg),
print_op(size),
print_op(align)
),
StatementKind::BinOp {
kind,

View file

@ -40,24 +40,20 @@ pub trait Visitor {
StatementKind::Store {
ptr,
value,
size,
align,
size: _,
align: _,
} => {
self.visit_operand(ptr);
self.visit_operand(value);
self.visit_operand(size);
self.visit_operand(align);
}
StatementKind::Load {
result,
ptr,
size,
align,
size: _,
align: _,
} => {
self.visit_reg(result);
self.visit_operand(ptr);
self.visit_operand(size);
self.visit_operand(align);
}
StatementKind::BinOp {
kind: _,

View file

@ -118,8 +118,8 @@ impl<'a, 'cx> FuncBuilder<'a, 'cx> {
let stmt = StatementKind::Load {
result: reg,
ptr,
size: Operand::const_u64(tyl.layout.size),
align: Operand::const_u64(tyl.layout.align),
size: tyl.layout.size,
align: tyl.layout.align,
};
self.cur_bb_mut()
.statements
@ -131,8 +131,8 @@ impl<'a, 'cx> FuncBuilder<'a, 'cx> {
let stmt = StatementKind::Store {
ptr,
value: rhs,
size: Operand::const_u64(layout.size),
align: Operand::const_u64(layout.align),
size: layout.size,
align: layout.align,
};
self.cur_bb_mut()
.statements

View file

@ -11,7 +11,7 @@ use object::{
};
use parser::Error;
type Result<T, E = Error> = std::result::Result<T, E>;
type Result<T = (), E = Error> = std::result::Result<T, E>;
pub fn generate<'cx>(lcx: &'cx LoweringCx<'cx>, ir: &Ir<'cx>) -> Result<()> {
let mut obj = Object::new(

View file

@ -24,15 +24,12 @@
//! | %r11 | temporary register | No |
//! | %r12-r14 | callee-saved registers | Yes |
//! | %r15 | callee-saved register; optionally used as GOT base pointer | Yes |
#![allow(unused_variables, dead_code)]
use analysis::{
ir::{self, BbIdx, Branch, Func, Location, Operand, Register, Statement, StatementKind},
LoweringCx,
};
use iced_x86::{
code_asm::{self as x, CodeAssembler},
code_asm::{self as x, AsmRegister64, CodeAssembler},
Formatter, IcedError, NasmFormatter,
};
use parser::{Error, Span};
@ -45,19 +42,20 @@ use crate::{
trait IcedErrExt {
type T;
fn sp(self, span: Span) -> Result<Self::T, Error>;
fn sp(self, cx: &AsmCtxt<'_, '_>) -> Result<Self::T, Error>;
}
impl<T> IcedErrExt for Result<T, IcedError> {
type T = T;
fn sp(self, span: Span) -> Result<Self::T, Error> {
self.map_err(|e| Error::new(e.to_string(), span))
fn sp(self, cx: &AsmCtxt<'_, '_>) -> Result<Self::T, Error> {
self.map_err(|e| Error::new(e.to_string(), cx.current_span))
}
}
struct AsmCtxt<'cx> {
struct AsmCtxt<'f, 'cx> {
lcx: &'cx LoweringCx<'cx>,
func: &'f Func<'cx>,
a: CodeAssembler,
reg_map: FxHashMap<Register, RegValue>,
reg_occupancy: Vec<Option<Register>>,
@ -65,21 +63,18 @@ struct AsmCtxt<'cx> {
stack_layout: stack::StackLayout,
current_span: Span,
// caches
last_register_uses: Vec<Option<Location>>,
}
impl<'cx> AsmCtxt<'cx> {
fn allocate_result_ssa_reg(
&mut self,
f: &Func<'_>,
reg: Register,
location: Location,
) -> RegValue {
impl<'cx> AsmCtxt<'_, 'cx> {
fn allocate_result_ssa_reg(&mut self, reg: Register, location: Location) -> RegValue {
for (i, opt_reg) in self.reg_occupancy.iter_mut().enumerate() {
if let Some(reg) = opt_reg.as_mut() {
if let Some(last_use) = self.last_register_uses[reg.as_usize()] {
if ir::info::dominates_location(f, last_use, location) {
if ir::info::dominates_location(self.func, last_use, location) {
// The last use dominates our location - the SSA reg is dead now.
*opt_reg = None;
}
@ -88,28 +83,94 @@ impl<'cx> AsmCtxt<'cx> {
if opt_reg.is_none() {
*opt_reg = Some(reg);
return RegValue::MachineReg(MachineReg(i));
let value = RegValue::MachineReg(MachineReg(i));
self.reg_map.insert(reg, value);
return value;
}
}
todo!("spill.")
}
fn generate_func(&mut self, func: &Func<'cx>) -> Result<()> {
fn gen_store(&mut self, ptr: Operand, value: Operand, size: u64, _align: u64) -> Result<()> {
if size != 8 {
todo!("stores of less or more than 8 bytes: {size}");
}
match ptr {
Operand::Const(_) => todo!("const stores not implemented"),
Operand::Reg(reg) => {
let ptr_value = self.reg_map[&reg];
match (ptr_value, value) {
(RegValue::StackRelativePtr { offset }, Operand::Const(c)) => {
self.a
.mov(x::qword_ptr(x::rsp + offset), c.as_i32())
.sp(self)?;
}
(RegValue::StackRelativePtr { offset: offset_ptr }, Operand::Reg(value)) => {
let value = self.reg_map[&value];
match value {
RegValue::StackRelativePtr {
offset: offset_value,
} => {
self.a.mov(x::rax, x::rsp + offset_value).sp(self)?;
self.a.mov(x::rsp + offset_ptr, x::rax).sp(self)?;
}
RegValue::MachineReg(reg) => {
self.a.mov(x::rsp + offset_ptr, machine_reg_to_reg(reg)).sp(self)?;
}
RegValue::Spilled { .. } => todo!("spills"),
}
}
(RegValue::Spilled { .. }, _) => todo!("spilled"),
(RegValue::MachineReg(_), _) => todo!("machine reg"),
};
}
}
Ok(())
}
fn gen_load(
&mut self,
loc: Location,
result: Register,
ptr: Operand,
size: u64,
_align: u64,
) -> Result {
assert_eq!(size, 8);
let result = self.allocate_result_ssa_reg(result, loc);
match (result, ptr) {
(RegValue::MachineReg(num), Operand::Reg(ptr)) => {
let into = machine_reg_to_reg(num);
match self.reg_map[&ptr] {
RegValue::StackRelativePtr { offset } => {
self.a.mov(into, x::qword_ptr(x::rsp + offset)).sp(self)?;
}
_ => {}
}
}
_ => todo!("loading into a not-reg or from a not-reg"),
}
Ok(())
}
fn generate_func(&mut self, func: &Func<'cx>) -> Result {
// Prologue: Save rbp and save rsp in rbp.
self.a.push(x::rbp).sp(func.def_span)?;
self.a.mov(x::rbp, x::rsp).sp(func.def_span)?;
self.a.push(x::rbp).sp(self)?;
self.a.mov(x::rbp, x::rsp).sp(self)?;
self.a
.sub(x::rsp, self.stack_layout.total_size as i32)
.sp(func.def_span)?;
.sp(self)?;
loop {
let bb = &func.bbs[self.bb_idx.as_usize()];
for stmt in &bb.statements {
let Statement {
span: st_sp,
ref kind,
} = *stmt;
for (idx, stmt) in bb.statements.iter().enumerate() {
let loc = Location::stmt(self.bb_idx, idx);
let Statement { span, ref kind } = *stmt;
self.current_span = span;
match *kind {
StatementKind::Alloca { result, .. } => {
@ -130,78 +191,32 @@ impl<'cx> AsmCtxt<'cx> {
size,
align,
} => {
let Operand::Const(size) = size else {
todo!("non const size");
};
if size.as_i32() != 8 {
todo!("stores of less or more than 8 bytes: {size}");
}
match ptr {
Operand::Const(_) => todo!("const stores not implemented"),
Operand::Reg(reg) => {
let ptr_value = self.reg_map[&reg];
match (ptr_value, value) {
(RegValue::StackRelativePtr { offset }, Operand::Const(c)) => {
self.a
.mov(x::qword_ptr(x::rsp + offset), c.as_i32())
.sp(st_sp)?;
}
(
RegValue::StackRelativePtr { offset },
Operand::Reg(value),
) => {
todo!("stack relative ptr + reg value")
}
(RegValue::Spilled { .. }, _) => todo!("spilled"),
(RegValue::MachineReg(_), _) => todo!("machine reg"),
};
//let rhs = match value {
// Operand::Const(c) => {}
// Operand::Reg(reg) => {}
//};
// mov [rsp + OFFSET], RHS
//self.a.add_instruction(Instruction::with2(Code::Mov, op0, op1))
// self.a.mov(x::ptr(x::rax), x::rbx).sp(st_sp);
}
}
self.gen_store(ptr, value, size, align)?;
}
StatementKind::Load {
result,
ptr,
size,
align,
} => todo!("loads."),
StatementKind::BinOp {
kind,
lhs,
rhs,
result,
} => todo!("binary operations"),
StatementKind::UnaryOperation { rhs, kind, result } => {
} => {
self.gen_load(loc, result, ptr, size, align)?;
}
StatementKind::BinOp { .. } => todo!("binary operations"),
StatementKind::UnaryOperation { .. } => {
todo!("unary operations")
}
StatementKind::PtrOffset {
result,
ptr: reg,
amount,
} => todo!("pointer offset :D"),
StatementKind::Call {
result,
func,
ref args,
} => todo!("function calls 💀"),
StatementKind::PtrOffset { .. } => todo!("pointer offset :D"),
StatementKind::Call { .. } => todo!("function calls 💀"),
}
}
match bb.term {
Branch::Ret(_) => {
// Epilogue: Restore rsp, rbp and return.
self.a.mov(x::rsp, x::rbp).sp(func.def_span)?;
self.a.pop(x::rbp).sp(func.def_span)?;
self.a.mov(x::rax, 0_u64).sp(func.def_span)?;
self.a.ret().sp(func.def_span)?;
self.a.mov(x::rsp, x::rbp).sp(self)?;
self.a.pop(x::rbp).sp(self)?;
self.a.mov(x::rax, 0_u64).sp(self)?;
self.a.ret().sp(self)?;
break;
}
Branch::Switch { .. } => todo!("switch"),
@ -220,7 +235,7 @@ pub fn generate_func<'cx>(lcx: &'cx LoweringCx<'cx>, func: &Func<'cx>) -> Result
crate::registers::debug_layout(func, &layout);
let fn_sp = func.def_span;
let a = CodeAssembler::new(64).sp(fn_sp)?;
let a = CodeAssembler::new(64).unwrap();
let stack_layout = stack::allocate_stack_space(8, func);
dbg!(&stack_layout);
@ -228,16 +243,18 @@ pub fn generate_func<'cx>(lcx: &'cx LoweringCx<'cx>, func: &Func<'cx>) -> Result
let mut cx = AsmCtxt {
lcx,
a,
func,
reg_map: FxHashMap::default(),
reg_occupancy: vec![None; 8],
bb_idx: BbIdx(0),
stack_layout,
current_span: fn_sp,
last_register_uses: ir::info::last_register_uses(func),
};
cx.generate_func(func)?;
let code = cx.a.assemble(0x4000).sp(fn_sp)?;
let code = cx.a.assemble(0x4000).sp(&cx)?;
print!("{}:\n---", func.name);
let mut output = String::new();
@ -250,3 +267,8 @@ pub fn generate_func<'cx>(lcx: &'cx LoweringCx<'cx>, func: &Func<'cx>) -> Result
Ok(code)
}
fn machine_reg_to_reg(reg: MachineReg) -> AsmRegister64 {
use x::*;
[rcx, rdx, rsi, rdi, r8, r9, r11][reg.0]
}