diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/analysis/src/ir.rs b/analysis/src/ir.rs index 164a449..8a5d80c 100644 --- a/analysis/src/ir.rs +++ b/analysis/src/ir.rs @@ -131,8 +131,8 @@ pub struct Statement { pub enum StatementKind { Alloca { result: Register, - size: Operand, - align: Operand, + size: u64, + align: u64, }, Store { ptr: Operand, @@ -234,6 +234,8 @@ impl Func<'_> { } impl BbIdx { + pub const ZERO: Self = Self(0); + pub fn from_usize(n: usize) -> Self { Self(n.try_into().unwrap()) } diff --git a/analysis/src/ir/info.rs b/analysis/src/ir/info.rs index 61cabf2..66d3f6e 100644 --- a/analysis/src/ir/info.rs +++ b/analysis/src/ir/info.rs @@ -4,7 +4,7 @@ use super::{BbIdx, Branch, Func, Location, Operand}; use crate::ir::visit::Visitor; pub fn traverse_postorder(func: &Func<'_>) -> Vec { - // the final traversial, backwards. + // the final traversal, backwards. // the starting bb has to be visited last. let mut traversal = vec![BbIdx(0)]; let mut i = 0; diff --git a/analysis/src/ir/pretty.rs b/analysis/src/ir/pretty.rs index 1ea52c8..b294f71 100644 --- a/analysis/src/ir/pretty.rs +++ b/analysis/src/ir/pretty.rs @@ -33,7 +33,7 @@ impl<'a> Customizer<'a> for DefaultCustomizer<'a> { self.0.set(Some(func)); } - fn fmt_reg(&self, reg: Register, f: &mut fmt::Formatter<'_>, loc: Location) -> fmt::Result { + fn fmt_reg(&self, reg: Register, f: &mut fmt::Formatter<'_>, _loc: Location) -> fmt::Result { match self.0.get().unwrap().regs[reg.0 as usize].name { None => write!(f, "%{}", reg.0), Some(name) => write!(f, "%{name}"), @@ -101,10 +101,8 @@ impl PrettyPrinter { } => { writeln!( self.out, - " {} = alloca, size={}, align={}", + " {} = alloca, size={size}, align={align}", print_reg(reg), - print_op(size), - print_op(align) ) } StatementKind::Store { diff --git a/analysis/src/ir/validate.rs b/analysis/src/ir/validate.rs index abb1aff..a28798a 100644 --- a/analysis/src/ir/validate.rs +++ b/analysis/src/ir/validate.rs @@ -1,6 +1,6 @@ use rustc_hash::FxHashSet; -use super::{visit::Visitor, Branch, Func, Register}; +use super::{visit::Visitor, Branch, Func, Register, StatementKind}; use crate::ir::BbIdx; pub fn validate(func: &Func<'_>) { @@ -14,6 +14,16 @@ pub fn validate(func: &Func<'_>) { } } + for (i, bb) in func.bbs.iter().enumerate().skip(1) { + if bb + .statements + .iter() + .any(|stmt| matches!(stmt.kind, StatementKind::Alloca { .. })) + { + panic!("alloca is only allowed in first block, found in block {i}") + } + } + let mut reg_names = FxHashSet::default(); for reg in &func.regs { if let Some(name) = reg.name { diff --git a/analysis/src/ir/visit.rs b/analysis/src/ir/visit.rs index 882dfd5..4c8cce3 100644 --- a/analysis/src/ir/visit.rs +++ b/analysis/src/ir/visit.rs @@ -32,12 +32,10 @@ pub trait Visitor { match stmt.kind { StatementKind::Alloca { result, - size, - align, + size: _, + align: _, } => { self.visit_reg(result); - self.visit_operand(size); - self.visit_operand(align); } StatementKind::Store { ptr, diff --git a/analysis/src/lower.rs b/analysis/src/lower.rs index faaf4a7..ee73ef2 100644 --- a/analysis/src/lower.rs +++ b/analysis/src/lower.rs @@ -116,7 +116,7 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { for (var, def_span) in &decl.init_declarators { let tyl = self.lcx.layout_of(ty); let (name, name_span) = var.declarator.decl.name(); - let ptr_to = self.build.alloca(tyl.layout, Some(name), span); + let ptr_to = self.build.reserve_local(tyl.layout, name, span); let variable_info = VariableInfo { def_span: *def_span, @@ -478,7 +478,7 @@ fn lower_func<'cx>( let span = param.declarator.1; let alloca_name = Symbol::intern(&format!("{}.local", name)); - let ptr_to = cx.build.alloca(tyl.layout, Some(alloca_name), span); + let ptr_to = cx.build.reserve_local(tyl.layout, alloca_name, span); let variable_info = VariableInfo { def_span: span, diff --git a/analysis/src/lower/builder.rs b/analysis/src/lower/builder.rs index b5f8ae4..fa2dd3b 100644 --- a/analysis/src/lower/builder.rs +++ b/analysis/src/lower/builder.rs @@ -3,8 +3,8 @@ use parser::{Span, Symbol}; use super::LoweringCx; use crate::{ ir::{ - self, BasicBlock, BbIdx, BinKind, Branch, ConstValue, Func, Layout, Operand, Register, - RegisterData, Statement, StatementKind, TyLayout, UnaryKind, + self, BasicBlock, BbIdx, BinKind, Branch, Func, Layout, Operand, Register, RegisterData, + Statement, StatementKind, TyLayout, UnaryKind, }, ty::{Ty, TyKind}, }; @@ -47,6 +47,15 @@ impl<'a, 'cx> FuncBuilder<'a, 'cx> { reg } + pub fn reserve_local(&mut self, layout: &Layout, name: Symbol, span: Span) -> Register { + // Every local is a singleton. + let prev = self.current_bb; + self.current_bb = BbIdx(0); + let reg = self.alloca(layout, Some(name), span); + self.current_bb = prev; + reg + } + pub fn alloca(&mut self, layout: &Layout, name: Option, span: Span) -> Register { let void_ptr = self .lcx @@ -56,8 +65,8 @@ impl<'a, 'cx> FuncBuilder<'a, 'cx> { span, kind: StatementKind::Alloca { result: reg, - size: Operand::Const(ConstValue::u64(layout.size)), - align: Operand::Const(ConstValue::u64(layout.align)), + size: layout.size, + align: layout.align, }, }; self.cur_bb_mut().statements.push(stmt); diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index d92d668..a82d069 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -1,4 +1,5 @@ mod registers; +mod stack; mod x86_64; use std::process::Stdio; diff --git a/codegen/src/registers.rs b/codegen/src/registers.rs index dc2d6c4..e57604a 100644 --- a/codegen/src/registers.rs +++ b/codegen/src/registers.rs @@ -53,7 +53,7 @@ pub struct MachineReg(pub usize); pub enum RegValue { /// The SSA register contains an address on the stack. /// The offset is the offset from the start of the function. - StackRelative { offset: u64 }, + StackRelativePtr { offset: u64 }, /// The SSA register resides on the stack as it has been spilled. /// This should be rather rare in practice. Spilled { offset: u64 }, @@ -65,15 +65,13 @@ pub enum RegValue { pub struct FunctionLayout { /// Where a register comes from at a particular usage of a register. register_uses: FxHashMap<(Location, Register), RegValue>, - total_stack_space: u64, } -pub fn compute_layout(f: &Func) -> FunctionLayout { +pub fn compute_layout(_f: &Func) -> FunctionLayout { let register_uses = FxHashMap::default(); FunctionLayout { register_uses, - total_stack_space: 0, } } @@ -102,7 +100,7 @@ impl<'a> ir::pretty::Customizer<'a> for LayoutPrinter<'a> { match layout { Some(RegValue::MachineReg(mach)) => write!(f, "reg-{}", mach.0)?, Some(RegValue::Spilled { offset }) => write!(f, "spill-{offset}")?, - Some(RegValue::StackRelative { offset }) => { + Some(RegValue::StackRelativePtr { offset }) => { write!(f, "i-forgot-what-this-meant-{offset}")? } None => write!(f, "")?, diff --git a/codegen/src/stack.rs b/codegen/src/stack.rs new file mode 100644 index 0000000..16da9b2 --- /dev/null +++ b/codegen/src/stack.rs @@ -0,0 +1,43 @@ +use std::collections::HashMap; + +use analysis::ir::{BbIdx, Func, Register, StatementKind}; + +#[derive(Debug)] +pub struct StackLayout { + pub allocas: HashMap, + pub total_size: u64, +} + +/// Based on the alloca's in the initial block, this calculcates stack pointer offsets for every allocation. +pub fn allocate_stack_space<'cx>(start_align: u64, func: &Func<'cx>) -> StackLayout { + assert_eq!(start_align, 8); + // REMEMBER: The stack grows down (on x86-64). + + let mut temp_layout = HashMap::new(); + let mut offset_backwards = 0; + + for stmt in &func.bb(BbIdx::ZERO).statements { + if let StatementKind::Alloca { + result, + size, + align, + } = stmt.kind + { + if size != 8 || align != 8 { + todo!("non 8 integer {size} {align}") + } + offset_backwards += 8; + temp_layout.insert(result, offset_backwards); + } + } + + let total_size = offset_backwards; + + StackLayout { + allocas: temp_layout + .into_iter() + .map(|(reg, offset_back)| (reg, total_size - offset_back)) + .collect(), + total_size, + } +} diff --git a/codegen/src/x86_64.rs b/codegen/src/x86_64.rs index 6899be9..1d48ab4 100644 --- a/codegen/src/x86_64.rs +++ b/codegen/src/x86_64.rs @@ -40,7 +40,7 @@ use rustc_hash::FxHashMap; use crate::{ registers::{MachineReg, RegValue}, - Result, + stack, Result, }; trait IcedErrExt { @@ -61,9 +61,10 @@ struct AsmCtxt<'cx> { a: CodeAssembler, reg_map: FxHashMap, reg_occupancy: Vec>, - current_stack_offset: u64, bb_idx: BbIdx, + stack_layout: stack::StackLayout, + // caches last_register_uses: Vec>, } @@ -98,6 +99,9 @@ impl<'cx> AsmCtxt<'cx> { // 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 + .sub(x::rsp, self.stack_layout.total_size as i32) + .sp(func.def_span)?; loop { let bb = &func.bbs[self.bb_idx.as_usize()]; @@ -108,27 +112,15 @@ impl<'cx> AsmCtxt<'cx> { } = *stmt; match *kind { - StatementKind::Alloca { - result: reg, - size, - align: _, - } => { - // For alloca, we allocate some space on the stack by subtracting from RSP. - // 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. get a better computer") - } - }; + StatementKind::Alloca { result, .. } => { self.reg_map.insert( - reg, - RegValue::StackRelative { - offset: self.current_stack_offset, + result, + RegValue::StackRelativePtr { + offset: *self + .stack_layout + .allocas + .get(&result) + .expect("no stack layout slot present for alloc register"), }, ); } @@ -149,14 +141,15 @@ impl<'cx> AsmCtxt<'cx> { Operand::Reg(reg) => { let ptr_value = self.reg_map[®]; match (ptr_value, value) { - (RegValue::StackRelative { offset }, Operand::Const(c)) => { - let offset_from_cur = self.current_stack_offset - offset; - + (RegValue::StackRelativePtr { offset }, Operand::Const(c)) => { self.a - .mov(x::qword_ptr(x::rsp + offset_from_cur), c.as_i32()) + .mov(x::qword_ptr(x::rsp + offset), c.as_i32()) .sp(st_sp)?; } - (RegValue::StackRelative { offset }, Operand::Reg(value)) => { + ( + RegValue::StackRelativePtr { offset }, + Operand::Reg(value), + ) => { todo!("stack relative ptr + reg value") } (RegValue::Spilled { .. }, _) => todo!("spilled"), @@ -229,13 +222,16 @@ pub fn generate_func<'cx>(lcx: &'cx LoweringCx<'cx>, func: &Func<'cx>) -> Result let fn_sp = func.def_span; let a = CodeAssembler::new(64).sp(fn_sp)?; + let stack_layout = stack::allocate_stack_space(8, func); + dbg!(&stack_layout); + let mut cx = AsmCtxt { lcx, a, reg_map: FxHashMap::default(), reg_occupancy: vec![None; 8], - current_stack_offset: 0, bb_idx: BbIdx(0), + stack_layout, last_register_uses: ir::info::last_register_uses(func), }; diff --git a/flake.lock b/flake.lock deleted file mode 100644 index f121b62..0000000 --- a/flake.lock +++ /dev/null @@ -1,26 +0,0 @@ -{ - "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1685199731, - "narHash": "sha256-t8I2oEFetISWpY4NUydCNcDJLSBGGyMCeozzVborlZA=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "3580ac6c65ec97ebccb02c166af8d19045d4d291", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index b4c7407..0000000 --- a/flake.nix +++ /dev/null @@ -1,40 +0,0 @@ -{ - description = "the uwucc c compiler"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs"; # also valid: "nixpkgs" - }; - - outputs = { self, nixpkgs }: - let - allSystems = [ - "x86_64-linux" # 64-bit Intel/AMD Linux - "aarch64-linux" # 64-bit ARM Linux - "x86_64-darwin" # 64-bit Intel macOS - "aarch64-darwin" # 64-bit ARM macOS - ]; - - # Helper to provide system-specific attributes - forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f { - pkgs = import nixpkgs { inherit system; }; - }); - in - { - devShells = forAllSystems ({ pkgs }: { - default = pkgs.mkShell { - buildInputs = with pkgs; [ - clang - llvmPackages_16.bintools - rustup - cargo-insta - gdb - ]; - - shellHook = '' - export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin - export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/ - ''; - }; - }); - }; -} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..583e343 --- /dev/null +++ b/shell.nix @@ -0,0 +1,15 @@ +{ pkgs ? import { } }: pkgs.mkShell +{ + buildInputs = with pkgs; [ + clang + llvmPackages_16.bintools + rustup + cargo-insta + gdb + ]; + + shellHook = '' + export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin + export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/ + ''; +}