From db219d3d74c4a3291a9ede1efb60ffae70f572b2 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 24 May 2023 21:49:56 +0200 Subject: [PATCH] more codegen --- Cargo.lock | 1 + analysis/src/ir.rs | 10 ++- analysis/src/ir/pretty.rs | 2 + codegen/Cargo.toml | 1 + codegen/src/lib.rs | 12 ++-- codegen/src/x86.rs | 134 +++++++++++++++++++++++++++++++++++--- notes.md | 69 ++++++++++++++++++++ 7 files changed, 214 insertions(+), 15 deletions(-) create mode 100644 notes.md diff --git a/Cargo.lock b/Cargo.lock index 01ceab9..f3f53ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,7 @@ dependencies = [ "iced-x86", "object", "parser", + "rustc-hash", ] [[package]] diff --git a/analysis/src/ir.rs b/analysis/src/ir.rs index af00e12..87e96d0 100644 --- a/analysis/src/ir.rs +++ b/analysis/src/ir.rs @@ -104,7 +104,7 @@ pub struct RegisterData<'cx> { pub name: Option, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Register(pub u32); #[derive(Debug, Clone)] @@ -245,6 +245,14 @@ impl ConstValue { pub fn u64(int: u64) -> Self { Self::Int(int.into()) } + + pub fn as_i32(self) -> i32 { + match self { + Self::StaticPtr(_) => panic!("StaticPtr cannot be converted to integer"), + Self::Void => panic!("Void cannot be converted to integer"), + Self::Int(int) => int.try_into().unwrap(), + } + } } impl Operand { diff --git a/analysis/src/ir/pretty.rs b/analysis/src/ir/pretty.rs index 4bcac66..65371d1 100644 --- a/analysis/src/ir/pretty.rs +++ b/analysis/src/ir/pretty.rs @@ -184,6 +184,8 @@ impl PrettyPrinter { } } + writeln!(self.out, "}}")?; + Ok(()) } } diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index df34498..ac83b86 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -18,3 +18,4 @@ iced-x86 = { version = "1.18.0", default-features = false, features = [ ] } object = { version = "0.31.1", features = ["write"] } parser = { path = "../parser" } +rustc-hash = "1.1.0" diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 461b3bc..0525aa4 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -10,7 +10,7 @@ use object::{ type Result = std::result::Result; -pub fn generate<'cx>(lcx: &LoweringCx<'cx>, ir: &Ir<'cx>) -> Result<()> { +pub fn generate<'cx>(lcx: &'cx LoweringCx<'cx>, ir: &Ir<'cx>) -> Result<()> { let mut obj = Object::new( object::BinaryFormat::Elf, object::Architecture::X86_64, @@ -61,11 +61,11 @@ pub fn generate<'cx>(lcx: &LoweringCx<'cx>, ir: &Ir<'cx>) -> Result<()> { "linking with `cc` failed" ))); } else { - std::fs::remove_file("main.o").map_err(|err| { - analysis::Error::new_without_span(format!( - "failed to remove temporary file main.o: {err}" - )) - })?; + // std::fs::remove_file("main.o").map_err(|err| { + // analysis::Error::new_without_span(format!( + // "failed to remove temporary file main.o: {err}" + // )) + // })?; } Ok(()) diff --git a/codegen/src/x86.rs b/codegen/src/x86.rs index 025380e..fea78b2 100644 --- a/codegen/src/x86.rs +++ b/codegen/src/x86.rs @@ -1,6 +1,13 @@ -use analysis::{ir::Func, LoweringCx}; -use iced_x86::{code_asm as x, IcedError}; +use analysis::{ + ir::{BbIdx, ConstValue, Func, Operand, Register, Statement, StatementKind}, + LoweringCx, +}; +use iced_x86::{ + code_asm::{self as x, CodeAssembler}, + IcedError, Instruction, +}; use parser::Span; +use rustc_hash::FxHashMap; use crate::Result; @@ -17,16 +24,127 @@ impl IcedErrExt for Result { } } -pub fn generate_func<'cx>(_lcx: &LoweringCx<'cx>, func: &Func<'cx>) -> Result> { +#[derive(Debug, Clone, Copy)] +enum RegValue { + Stack { offset: u64 }, +} + +struct AsmCtxt<'cx> { + lcx: &'cx LoweringCx<'cx>, + a: CodeAssembler, + reg_map: FxHashMap, + current_stack_offset: u64, + bb_idx: BbIdx, +} + +impl<'cx> AsmCtxt<'cx> { + fn generate_func(&mut self, func: &Func<'cx>) -> Result<()> { + // TODO: Prologue + + loop { + let bb = &func.bbs[self.bb_idx.as_usize()]; + for stmt in &bb.statements { + let Statement { + span: st_sp, + ref kind, + } = *stmt; + + match *kind { + StatementKind::Alloca { + reg, + size, + align: _, + } => { + // TODO: Align + match size { + Operand::Const(c) => { + let offset = c.as_i32(); + self.a.sub(x::rsp, offset).sp(st_sp)?; + self.current_stack_offset += offset as u64; + } + Operand::Reg(_) => todo!("dynamic alloca is not supported"), + }; + self.reg_map.insert( + reg, + RegValue::Stack { + offset: self.current_stack_offset, + }, + ); + } + StatementKind::Store { + ptr, + value, + size, + align, + } => match ptr { + Operand::Const(_) => todo!("const stores not implemented"), + Operand::Reg(reg) => { + let value = self.reg_map[®]; + let stack_offset = match value { + RegValue::Stack { offset } => offset, + }; + //let rhs = match value { + // Operand::Const(c) => {} + // Operand::Reg(reg) => {} + //}; + + // mov [rbp + OFFSET], RHS + + //self.a.add_instruction(Instruction::with2(Code::Mov, op0, op1)) + + self.a.mov(x::ptr(x::rax), x::rbx); + } + }, + StatementKind::Load { + result, + ptr, + size, + align, + } => todo!(), + StatementKind::BinOp { + kind, + lhs, + rhs, + result, + } => todo!(), + StatementKind::UnaryOperation { rhs, kind, result } => todo!(), + StatementKind::PtrOffset { + result, + reg, + amount, + } => todo!(), + StatementKind::Call { + result, + func, + ref args, + } => todo!(), + } + } + + todo!("next bbs"); + } + + Ok(()) + } +} + +pub fn generate_func<'cx>(lcx: &'cx LoweringCx<'cx>, func: &Func<'cx>) -> Result> { assert_eq!(func.arity, 0, "arguments??? in MY uwucc????"); - let sp = func.def_span; - let mut a = x::CodeAssembler::new(64).sp(sp)?; + let fn_sp = func.def_span; + let mut a = CodeAssembler::new(64).sp(fn_sp)?; - a.xor(x::rax, x::rax).sp(sp)?; - a.ret().sp(sp)?; + let mut cx = AsmCtxt { + lcx, + a, + reg_map: FxHashMap::default(), + current_stack_offset: 0, + bb_idx: BbIdx(0), + }; - let code = a.assemble(0x4000).sp(sp)?; + cx.generate_func(func)?; + + let code = cx.a.assemble(0x4000).sp(fn_sp)?; Ok(code) } diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..ddfdea1 --- /dev/null +++ b/notes.md @@ -0,0 +1,69 @@ +# lowering + +This C program + +```c +long main() { + long a = 0; + int b = 0; + return 0; +} +``` + +should lower into the following unoptimized IR + +```llvmir +def main() { + bb0: + %a = alloca, size=8, align=8 + store %a, 0, size=8, align=8 + %b = alloca, size=4, align=4 + store %b, 0, size=4, align=4 + ret 0 +} +``` + +this IR can then be lowered to very sane machine code ignoring stack alignment which also like matters + +```x86asm +sub rbp, 8 ; a +mov [rbp], 0 +sub rbp, 4 ; b +xor rax, rax +ret +``` + +--- + +```c +int main() +{ + int a = 1 + 3 * 4; + return 0; +} +``` + +```llvmir +def main() { + bb0: + %a = alloca, size=4, align=4 + %1 = mul 3, 4 + %2 = add 1, %1 + store %a, %2, size=4, align=4 + ret 0 +} +``` + +```x86asm +sub rbp, 4 ; a +sub rbp, 4 ; %1 +mov rbx, 3 +mul rbx, 4 +mov dword ptr [rbp], rbx +sub rbp, 4 ; %2 +mov rbx, 1 +add rbx, dword ptr [rbp + 4] +mov dword ptr [rbp + 8], dword ptr [rbp] +xor rax, rax +ret +``` \ No newline at end of file