From cabd924ac08180e8fe9238da345b5c2d76bad975 Mon Sep 17 00:00:00 2001 From: Julian Hartl Date: Sun, 7 Jul 2024 16:51:15 +0200 Subject: [PATCH] Fix build for backend --- ir/crates/back/Cargo.toml | 1 - .../src/codegen/machine/function/builder.rs | 12 +- .../back/src/codegen/machine/function/mod.rs | 50 +++--- ir/crates/back/src/codegen/machine/instr.rs | 24 ++- ir/crates/back/src/codegen/machine/mod.rs | 3 +- .../back/src/codegen/machine/module/asm.rs | 2 +- .../src/codegen/machine/module/builder.rs | 2 +- .../back/src/codegen/machine/module/mod.rs | 8 +- ir/crates/back/src/codegen/machine/reg.rs | 53 ++++-- .../codegen/register_allocator/linear_scan.rs | 24 +-- .../src/codegen/register_allocator/mod.rs | 145 +++++++++------- .../back/src/codegen/selection_dag/builder.rs | 155 +++++++++++------- .../back/src/codegen/selection_dag/mod.rs | 25 ++- ir/crates/middle/src/cfg/mod.rs | 26 +-- ir/crates/middle/src/lib.rs | 4 +- .../cfg_simplify/unreachable_bb_elim.rs | 2 +- 16 files changed, 318 insertions(+), 218 deletions(-) diff --git a/ir/crates/back/Cargo.toml b/ir/crates/back/Cargo.toml index fe3777d..2257990 100644 --- a/ir/crates/back/Cargo.toml +++ b/ir/crates/back/Cargo.toml @@ -17,7 +17,6 @@ daggy = "0.8.0" log = "0.4.20" natrix_middle = { path = "../middle" } unicorn-engine = "2.0.1" -cranelift-entity = "0.105.2" slotmap = "1.0.7" anyhow = "1.0.81" diff --git a/ir/crates/back/src/codegen/machine/function/builder.rs b/ir/crates/back/src/codegen/machine/function/builder.rs index 6c517bd..ce55568 100644 --- a/ir/crates/back/src/codegen/machine/function/builder.rs +++ b/ir/crates/back/src/codegen/machine/function/builder.rs @@ -68,11 +68,17 @@ impl FunctionBuilder { .find(|(_, mbb)| **mbb == mbb_id) .unwrap() .0; - debug!("Building machine basic block for basic block {}", bb); + debug!( + "Building machine basic block for basic block {}", + bb.display(&function.cfg) + ); let dag = sel_dag.get_bb_dag(bb); - dag.save_graphviz("out").unwrap(); + dag.save_graphviz("out", &function.cfg).unwrap(); let mut node_list = Vec::with_capacity(dag.node_count()); - debug!("Determining traversal order for basic block {}", bb); + debug!( + "Determining traversal order for basic block {}", + bb.display(&function.cfg) + ); let bfs = Bfs::new(dag.graph(), dag.term_node()); for n in bfs.iter(dag.graph()) { node_list.push(n); diff --git a/ir/crates/back/src/codegen/machine/function/mod.rs b/ir/crates/back/src/codegen/machine/function/mod.rs index 292693d..87b8811 100644 --- a/ir/crates/back/src/codegen/machine/function/mod.rs +++ b/ir/crates/back/src/codegen/machine/function/mod.rs @@ -8,13 +8,13 @@ pub use cfg::{ BasicBlockId, Cfg, }; -use cranelift_entity::{ - entity_impl, - PrimaryMap, -}; use daggy::Walker; use index_vec::IndexVec; use iter_tools::Itertools; +use slotmap::{ + new_key_type, + SlotMap, +}; use smallvec::{ smallvec, SmallVec, @@ -33,30 +33,29 @@ use crate::codegen::machine::{ PseudoInstr, }, isa::PhysicalRegister, - reg::VRegInfo, + reg::VReg, Instr, InstrId, MachInstr, Register, Size, TargetMachine, - VReg, + VRegRef, }; pub mod builder; pub mod cfg; -#[derive(Copy, Clone, Eq, PartialEq)] -pub struct FunctionId(u32); - -entity_impl!(FunctionId, "fun"); +new_key_type! { + pub struct FunctionId; +} #[derive(Debug, Clone)] pub struct Function { pub name: String, pub basic_blocks: IndexVec>, - pub(crate) vregs: PrimaryMap>, - pub(crate) params: SmallVec<[VReg; 2]>, + pub(crate) vregs: SlotMap>, + pub(crate) params: SmallVec<[VRegRef; 2]>, pub(crate) return_ty_size: Size, cfg: Option, } @@ -66,31 +65,32 @@ impl Function { Self { name, basic_blocks: IndexVec::default(), - vregs: PrimaryMap::new(), + vregs: SlotMap::with_key(), cfg: None, params: SmallVec::new(), return_ty_size: Size::Byte, } } - pub fn alloc_vreg(&mut self, size: Size) -> VReg { - self.vregs.push(VRegInfo { + pub fn alloc_vreg(&mut self, size: Size, symbol: String) -> VRegRef { + self.vregs.insert(VReg { size, tied_to: None, fixed: None, + symbol, }) } - pub fn get_vreg(&self, vreg: VReg) -> &VRegInfo { + pub fn get_vreg(&self, vreg: VRegRef) -> &VReg { &self.vregs[vreg] } - pub fn tie_vreg(&mut self, vreg: VReg, to: VReg) { - debug!("Tying {vreg} to {to}"); + pub fn tie_vreg(&mut self, vreg: VRegRef, to: VRegRef) { + // debug!("Tying {vreg} to {to}"); self.vregs[vreg].tied_to = Some(to); } - pub fn fix_vreg(&mut self, vreg: VReg, to: TM::Reg) { - debug!("Fixing {vreg} to {}", to.name()); + pub fn fix_vreg(&mut self, vreg: VRegRef, to: TM::Reg) { + // debug!("Fixing {vreg} to {}", to.name()); self.vregs[vreg].fixed = Some(to); } @@ -240,9 +240,9 @@ impl Display for Function { let bb = &self.basic_blocks[bb_id]; writeln!(f, "{bb_id}: ")?; for (dest, operands) in &bb.phis { - write!(f, " {dest} = phi ")?; + write!(f, " {} = phi ", dest.display(self))?; for (i, (reg, bb)) in operands.iter().enumerate() { - write!(f, "{reg}:{bb}")?; + write!(f, "{}:{bb}", reg.display(self))?; if i < operands.len() - 1 { write!(f, ", ")?; } @@ -252,13 +252,13 @@ impl Display for Function { for instr in &bb.instructions { write!(f, " ")?; if let Some(out) = instr.writes() { - write!(f, "{out} = ")?; + write!(f, "{} = ", out.display(self))?; } write!(f, "{}", instr.name())?; let operands = instr.operands(); let operands_len = operands.len(); for (i, operand) in operands.into_iter().enumerate() { - write!(f, " {operand}")?; + write!(f, " {}", operand.display(self))?; if i < operands_len - 1 { write!(f, ",")?; } @@ -268,7 +268,7 @@ impl Display for Function { if !reads_impl.is_empty() { write!(f, " {{implicit reads: ")?; for (i, reg) in reads_impl.into_iter().enumerate() { - write!(f, "{reg}")?; + write!(f, "{}", reg.display(self))?; if i < reads_impl_len - 1 { write!(f, ", ")?; } diff --git a/ir/crates/back/src/codegen/machine/instr.rs b/ir/crates/back/src/codegen/machine/instr.rs index a64599e..cf5d8b4 100644 --- a/ir/crates/back/src/codegen/machine/instr.rs +++ b/ir/crates/back/src/codegen/machine/instr.rs @@ -12,6 +12,7 @@ use crate::codegen::{ machine::{ function::BasicBlockId, isa::MachInstr as MInstr, + Function, Register, TargetMachine, }, @@ -108,6 +109,14 @@ pub enum InstrOperand { Label(BasicBlockId), } +impl InstrOperand { + pub fn display<'func>(&self, func: &'func Function) -> InstrOperandDisplay<'func, '_, TM> { + InstrOperandDisplay { + operand: self, + func, + } + } +} #[derive(Debug)] pub enum InstrOperandMut<'a, TM: TargetMachine> { Reg(&'a mut Register), @@ -115,12 +124,17 @@ pub enum InstrOperandMut<'a, TM: TargetMachine> { Label(&'a mut BasicBlockId), } -impl Display for InstrOperand { +pub struct InstrOperandDisplay<'func, 'op, TM: TargetMachine> { + operand: &'op InstrOperand, + func: &'func Function, +} + +impl Display for InstrOperandDisplay<'_, '_, TM> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Self::Reg(reg) => write!(f, "{}", reg), - Self::Imm(imm) => write!(f, "{}", imm), - Self::Label(label) => write!(f, "{}", label), + match self.operand { + InstrOperand::Reg(reg) => write!(f, "{}", reg.display(self.func)), + InstrOperand::Imm(imm) => write!(f, "{}", imm), + InstrOperand::Label(label) => write!(f, "{}", label), } } } diff --git a/ir/crates/back/src/codegen/machine/mod.rs b/ir/crates/back/src/codegen/machine/mod.rs index aab0a81..b5c0d10 100644 --- a/ir/crates/back/src/codegen/machine/mod.rs +++ b/ir/crates/back/src/codegen/machine/mod.rs @@ -1,7 +1,6 @@ use std::fmt::Debug; pub use backend::Backend; -pub use cranelift_entity::EntityRef; pub use function::{ Function, FunctionId, @@ -18,7 +17,7 @@ pub use module::Module; use natrix_middle::ty::Type; pub use reg::{ Register, - VReg, + VRegRef, }; use crate::codegen::machine::{ diff --git a/ir/crates/back/src/codegen/machine/module/asm.rs b/ir/crates/back/src/codegen/machine/module/asm.rs index 958fab6..d704efb 100644 --- a/ir/crates/back/src/codegen/machine/module/asm.rs +++ b/ir/crates/back/src/codegen/machine/module/asm.rs @@ -1,6 +1,6 @@ use std::ops::Range; -use cranelift_entity::SecondaryMap; +use slotmap::SecondaryMap; use crate::codegen::machine::{ FunctionId, diff --git a/ir/crates/back/src/codegen/machine/module/builder.rs b/ir/crates/back/src/codegen/machine/module/builder.rs index a89067d..5bafaa5 100644 --- a/ir/crates/back/src/codegen/machine/module/builder.rs +++ b/ir/crates/back/src/codegen/machine/module/builder.rs @@ -22,7 +22,7 @@ impl<'module, TM: TargetMachine> Builder<'module, TM> { pub fn build(mut self) -> Module { for (_, function) in &mut self.module.functions { let builder = FunctionBuilder::::new(); - self.mtbb.functions.push(builder.build(function)); + self.mtbb.functions.insert(builder.build(function)); } self.mtbb } diff --git a/ir/crates/back/src/codegen/machine/module/mod.rs b/ir/crates/back/src/codegen/machine/module/mod.rs index 039f363..2f170a7 100644 --- a/ir/crates/back/src/codegen/machine/module/mod.rs +++ b/ir/crates/back/src/codegen/machine/module/mod.rs @@ -4,7 +4,7 @@ use asm::{ FunctionSymbolTableEntry, }; pub use builder::Builder; -use cranelift_entity::PrimaryMap; +use slotmap::SlotMap; use tracing::{ debug, info, @@ -28,20 +28,20 @@ mod builder; #[derive(Debug, Clone)] pub struct Module { - pub(crate) functions: PrimaryMap>, + pub(crate) functions: SlotMap>, } impl Default for Module { fn default() -> Self { Self { - functions: PrimaryMap::new(), + functions: SlotMap::with_key(), } } } impl Module { pub fn add_function(&mut self, function: Function) -> FunctionId { - self.functions.push(function) + self.functions.insert(function) } pub fn functions(&self) -> impl ExactSizeIterator)> { diff --git a/ir/crates/back/src/codegen/machine/reg.rs b/ir/crates/back/src/codegen/machine/reg.rs index 0cb0124..83af9bd 100644 --- a/ir/crates/back/src/codegen/machine/reg.rs +++ b/ir/crates/back/src/codegen/machine/reg.rs @@ -3,7 +3,7 @@ use std::fmt::{ Formatter, }; -use cranelift_entity::entity_impl; +use slotmap::new_key_type; use crate::codegen::machine::{ function::Function, @@ -14,25 +14,25 @@ use crate::codegen::machine::{ #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Register { - Virtual(VReg), + Virtual(VRegRef), Physical(TM::Reg), } -impl From for Register { - fn from(vreg: VReg) -> Self { +impl From for Register { + fn from(vreg: VRegRef) -> Self { Self::Virtual(vreg) } } impl Register { - pub fn try_as_virtual(&self) -> Option { + pub fn try_as_virtual(&self) -> Option { match self { Register::Virtual(virt_reg) => Some(*virt_reg), Register::Physical(_) => None, } } - pub fn try_as_virtual_mut(&mut self) -> Option<&mut VReg> { + pub fn try_as_virtual_mut(&mut self) -> Option<&mut VRegRef> { match self { Register::Virtual(virt_reg) => Some(virt_reg), Register::Physical(_) => None, @@ -52,35 +52,52 @@ impl Register { Register::Physical(phys_reg) => phys_reg.size(), } } + + pub fn display<'func>(&self, func: &'func Function) -> RegisterDisplay<'func, TM> { + RegisterDisplay { func, reg: *self } + } } -// impl Copy for Register {} +pub struct RegisterDisplay<'func, TM: TargetMachine> { + func: &'func Function, + reg: Register, +} -impl Display for Register { +impl Display for RegisterDisplay<'_, TM> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Register::Virtual(virt_reg) => write!(f, "{}", virt_reg), + match self.reg { + Register::Virtual(virt_reg) => write!(f, "{}", self.func.vregs[virt_reg]), Register::Physical(phys_reg) => write!(f, "${}", phys_reg.name()), } } } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct VReg(u32); +new_key_type! { + pub struct VRegRef; +} -impl VReg { +impl VRegRef { pub fn size(self, func: &Function) -> Size { func.get_vreg(self).size } -} -entity_impl!(VReg, "v"); + pub fn display(self, func: &Function) -> String { + func.get_vreg(self).symbol.clone() + } +} -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct VRegInfo { +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct VReg { pub size: Size, /// If set, the vreg will be placed in the same location as tied_to - pub tied_to: Option, + pub tied_to: Option, /// If set, the vreg is ensured to be placed in the same location as fixed pub fixed: Option, + pub symbol: String, +} + +impl Display for VReg { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.symbol) + } } diff --git a/ir/crates/back/src/codegen/register_allocator/linear_scan.rs b/ir/crates/back/src/codegen/register_allocator/linear_scan.rs index 2771138..5eef4de 100644 --- a/ir/crates/back/src/codegen/register_allocator/linear_scan.rs +++ b/ir/crates/back/src/codegen/register_allocator/linear_scan.rs @@ -6,7 +6,7 @@ use crate::codegen::{ isa::PhysicalRegister, Size, TargetMachine, - VReg, + VRegRef, }, register_allocator::{ LiveRange, @@ -21,15 +21,15 @@ use crate::codegen::{ #[derive(Debug)] pub struct RegAlloc<'liveness, TM: TargetMachine> { /// Liveness representation. - liveness_repr: &'liveness LivenessRepr, + liveness_repr: &'liveness LivenessRepr, /// List of free registers. free_regs: Vec, - active: Vec<(VReg, TM::Reg)>, - inactive: Vec<(VReg, TM::Reg)>, + active: Vec<(VRegRef, TM::Reg)>, + inactive: Vec<(VRegRef, TM::Reg)>, } impl<'liveness, TM: TargetMachine> RegAllocAlgorithm<'liveness, TM> for RegAlloc<'liveness, TM> { - fn new(liveness_repr: &'liveness LivenessRepr) -> Self { + fn new(liveness_repr: &'liveness LivenessRepr) -> Self { let free_regs = TM::Reg::all() .iter() .copied() @@ -92,7 +92,7 @@ impl RegAlloc<'_, TM> { } /// Inserts a new live interval into the active list. - fn insert_active(&mut self, vreg: VReg, reg: TM::Reg) { + fn insert_active(&mut self, vreg: VRegRef, reg: TM::Reg) { self.free_regs.retain(|r| { if reg.interferes_with(*r) { debug!("Removing {} from free list", r.name()); @@ -110,9 +110,9 @@ impl RegAlloc<'_, TM> { self.active.push((vreg, reg)); } - fn remove_active(&mut self, i: usize) -> (VReg, TM::Reg) { + fn remove_active(&mut self, i: usize) -> (VRegRef, TM::Reg) { let (vreg, reg) = self.active.remove(i); - debug!("Removing active interval: {}", vreg); + // debug!("Removing active interval: {}"); for reg in reg.regclass().filter(|r| reg.interferes_with(*r)) { self.free_reg(reg); } @@ -124,14 +124,14 @@ impl RegAlloc<'_, TM> { self.free_regs.push(reg); } - fn insert_inactive(&mut self, vreg: VReg, reg: TM::Reg) { - debug!("Inserting inactive vreg: {}", vreg); + fn insert_inactive(&mut self, vreg: VRegRef, reg: TM::Reg) { + // debug!("Inserting inactive vreg: {}", vreg); self.inactive.push((vreg, reg)); } - fn remove_inactive(&mut self, i: usize) -> (VReg, TM::Reg) { + fn remove_inactive(&mut self, i: usize) -> (VRegRef, TM::Reg) { let (vreg, reg) = self.inactive.remove(i); - debug!("Removing inactive interval: {}", vreg); + // debug!("Removing inactive interval: {}", vreg); (vreg, reg) } diff --git a/ir/crates/back/src/codegen/register_allocator/mod.rs b/ir/crates/back/src/codegen/register_allocator/mod.rs index 7701842..550a156 100644 --- a/ir/crates/back/src/codegen/register_allocator/mod.rs +++ b/ir/crates/back/src/codegen/register_allocator/mod.rs @@ -8,13 +8,13 @@ use std::{ }; pub use coalescer::Coalescer; -pub use cranelift_entity::SecondaryMap; pub use daggy::Walker; pub use iter_tools::Itertools; use rustc_hash::{ FxHashMap, FxHashSet, }; +use slotmap::SecondaryMap; use smallvec::{ smallvec, SmallVec, @@ -37,7 +37,7 @@ use crate::codegen::machine::{ isa::PhysicalRegister, reg::{ Register, - VReg, + VRegRef, }, InstrId, Size, @@ -477,17 +477,17 @@ impl Iterator for InstrNumberingIter<'_> { } #[derive(Default, Debug)] -pub struct LiveSets(FxHashMap>); +pub struct LiveSets(FxHashMap>); impl LiveSets { - fn insert(&mut self, bb: BasicBlockId, reg: VReg) { + fn insert(&mut self, bb: BasicBlockId, reg: VRegRef) { self.0.entry(bb).or_default().insert(reg); } - fn remove(&mut self, bb: BasicBlockId, reg: VReg) { + fn remove(&mut self, bb: BasicBlockId, reg: VRegRef) { self.0.entry(bb).or_default().remove(®); } - fn get(&self, bb: BasicBlockId) -> impl Iterator + '_ { + fn get(&self, bb: BasicBlockId) -> impl Iterator + '_ { self.0 .get(&bb) .map(|regs| regs.iter().copied()) @@ -497,29 +497,46 @@ impl LiveSets { } #[derive(Debug)] -pub struct LivenessRepr { +pub struct LivenessRepr { pub instr_numbering: InstrNumbering, - pub lifetimes: SecondaryMap, + pub lifetimes: SecondaryMap, + _phantom: std::marker::PhantomData, } -impl LivenessRepr { - pub fn new(func: &Function) -> Self { +impl LivenessRepr { + pub fn new(func: &Function) -> Self { debug!("Creating liveness representation"); Self { instr_numbering: InstrNumbering::new(func), lifetimes: SecondaryMap::default(), + _phantom: std::marker::PhantomData, } } - pub fn add_range(&mut self, vreg: VReg, range: LiveRange) { + pub fn add_range(&mut self, vreg: VRegRef, range: LiveRange) { self.lifetimes[vreg].add_range(range); } + + pub fn display<'func>( + &'func self, + func: &'func Function, + ) -> LivenessReprDisplay<'func, '_, TM> { + LivenessReprDisplay { + liveness_repr: self, + func, + } + } +} + +pub struct LivenessReprDisplay<'func, 'liveness, TM: TargetMachine> { + liveness_repr: &'liveness LivenessRepr, + func: &'func Function, } -impl Display for LivenessRepr { +impl Display for LivenessReprDisplay<'_, '_, TM> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - for (vreg, lifetime) in self.lifetimes.iter() { - writeln!(f, "{}: {}", vreg, lifetime)?; + for (vreg, lifetime) in self.liveness_repr.lifetimes.iter() { + writeln!(f, "{}: {}", vreg.display(self.func), lifetime)?; } Ok(()) } @@ -529,20 +546,20 @@ pub type RegAllocHints = SmallVec<[::Reg; 2]>; #[derive(Debug, Clone)] pub struct RegAllocVReg { - pub id: VReg, + pub id: VRegRef, pub size: Size, } -pub trait RegAllocAlgorithm<'liveness, A: TargetMachine> { - fn new(liveness_repr: &'liveness LivenessRepr) -> Self; - fn allocate_arbitrary(&mut self, vreg: &RegAllocVReg, hints: RegAllocHints) -> A::Reg; +pub trait RegAllocAlgorithm<'liveness, TM: TargetMachine> { + fn new(liveness_repr: &'liveness LivenessRepr) -> Self; + fn allocate_arbitrary(&mut self, vreg: &RegAllocVReg, hints: RegAllocHints) -> TM::Reg; - fn try_allocate_fixed(&mut self, vreg: &RegAllocVReg, reg: A::Reg) -> bool; + fn try_allocate_fixed(&mut self, vreg: &RegAllocVReg, reg: TM::Reg) -> bool; - fn try_evict(&mut self, reg: A::Reg) -> bool; + fn try_evict(&mut self, reg: TM::Reg) -> bool; /// Returns true if a register was evicted - fn allocate_fixed_or_evict(&mut self, vreg: &RegAllocVReg, reg: A::Reg) -> bool { + fn allocate_fixed_or_evict(&mut self, vreg: &RegAllocVReg, reg: TM::Reg) -> bool { if self.try_allocate_fixed(vreg, reg) { return false; } @@ -560,24 +577,24 @@ pub trait RegAllocAlgorithm<'liveness, A: TargetMachine> { } } -struct VRegAllocations<'liveness, A: TargetMachine> { - map: FxHashMap, - liveness_repr: &'liveness LivenessRepr, +struct VRegAllocations<'liveness, TM: TargetMachine> { + map: FxHashMap, + liveness_repr: &'liveness LivenessRepr, } -impl<'liveness, A: TargetMachine> VRegAllocations<'liveness, A> { - pub fn new(liveness_repr: &'liveness LivenessRepr) -> Self { +impl<'liveness, TM: TargetMachine> VRegAllocations<'liveness, TM> { + pub fn new(liveness_repr: &'liveness LivenessRepr) -> Self { Self { map: FxHashMap::default(), liveness_repr, } } - pub fn get_allocated_reg(&self, vreg: VReg) -> Option { + pub fn get_allocated_reg(&self, vreg: VRegRef) -> Option { self.map.get(&vreg).copied() } - pub fn start_allocation(&mut self, vreg: VReg, reg: A::Reg) { + pub fn start_allocation(&mut self, vreg: VRegRef, reg: TM::Reg) { self.map.insert(vreg, reg); } } @@ -585,20 +602,20 @@ impl<'liveness, A: TargetMachine> VRegAllocations<'liveness, A> { pub struct RegisterAllocator< 'liveness, 'func, - A: TargetMachine, - RegAlloc: RegAllocAlgorithm<'liveness, A>, + TM: TargetMachine, + RegAlloc: RegAllocAlgorithm<'liveness, TM>, > { algo: RegAlloc, - func: &'func mut Function, - marker: std::marker::PhantomData, - allocations: VRegAllocations<'liveness, A>, - liveness_repr: &'liveness LivenessRepr, + func: &'func mut Function, + marker: std::marker::PhantomData, + allocations: VRegAllocations<'liveness, TM>, + liveness_repr: &'liveness LivenessRepr, } impl<'liveness, 'func, TM: TargetMachine, RegAlloc: RegAllocAlgorithm<'liveness, TM>> RegisterAllocator<'liveness, 'func, TM, RegAlloc> { - pub fn new(func: &'func mut Function, liveness_repr: &'liveness LivenessRepr) -> Self { + pub fn new(func: &'func mut Function, liveness_repr: &'liveness LivenessRepr) -> Self { Self { func, algo: RegAlloc::new(liveness_repr), @@ -658,33 +675,39 @@ impl<'liveness, 'func, TM: TargetMachine, RegAlloc: RegAllocAlgorithm<'liveness, None => match vreg_info.tied_to { None => { debug!( - "Allocating {vreg} at {:?} with hints: {:?} and size {size}", - instr_uid, hints + "Allocating {} at {:?} with hints: {:?} and size {size}", + vreg.display(self.func), + instr_uid, + hints ); Some(self.algo.allocate_arbitrary(&alloc_vreg, hints)) } Some(tied_to) => { debug!( - "{vreg} is tied to {tied_to}. Trying to put it in the same register" + "{} is tied to {}. Trying to put it in the same register", + vreg.display(self.func), + tied_to.display(self.func) ); assert!( !lifetime.overlaps_with(&self.liveness_repr.lifetimes[tied_to]), - "Tied register {tied_to} overlaps with {vreg}" + "Tied register {} overlaps with {}", + tied_to.display(self.func), + vreg.display(self.func) ); let allocated_reg = self.allocations.get_allocated_reg(tied_to); match allocated_reg { None => { - debug!("Tied register {tied_to} is not allocated yet."); + // debug!("Tied register {tied_to} is not allocated yet."); None } Some(allocated_reg) => { - debug!("Tied register {tied_to} is allocated at {}. Trying to allocate {vreg} there", allocated_reg.name()); + // debug!("Tied register {tied_to} is allocated at {}. Trying to allocate {vreg} there", allocated_reg.name()); if self .algo .allocate_fixed_or_evict(&alloc_vreg, allocated_reg) { - debug!("Evicted {} to allocate {vreg}", allocated_reg.name()); + // debug!("Evicted {} to allocate {vreg}", allocated_reg.name()); } Some(allocated_reg) } @@ -692,21 +715,21 @@ impl<'liveness, 'func, TM: TargetMachine, RegAlloc: RegAllocAlgorithm<'liveness, } }, Some(fixed) => { - debug!( - "Allocating {vreg} at {:?} in fixed register {}", - instr_uid, - fixed.name() - ); + // debug!( + // "Allocating {vreg} at {:?} in fixed register {}", + // instr_uid, + // fixed.name() + // ); self.algo.allocate_fixed_or_evict(&alloc_vreg, fixed); Some(fixed) } }; if let Some(phys_reg) = phys_reg { assert_eq!(phys_reg.size(), size, "Register size mismatch"); - debug!("Allocated {} for {vreg}", phys_reg.name()); + // debug!("Allocated {} for {vreg}", phys_reg.name()); self.allocations.start_allocation(vreg, phys_reg); } else { - debug!("No register available for {vreg}. Retrying later"); + // debug!("No register available for {vreg}. Retrying later"); worklist.push_back((vreg, instr_uid)); } } @@ -715,27 +738,27 @@ impl<'liveness, 'func, TM: TargetMachine, RegAlloc: RegAllocAlgorithm<'liveness, let instr = &mut self.func.basic_blocks[instr_uid.bb].instructions[instr_uid.instr]; for reg in instr.read_regs_mut() { if let Some(vreg) = reg.try_as_virtual() { - debug!( - "Replacing {} with its physical register at {}", - vreg, instr_uid - ); + // debug!( + // "Replacing {} with its physical register at {}", + // vreg, instr_uid + // ); let phys_reg = self .allocations .get_allocated_reg(vreg) - .unwrap_or_else(|| panic!("{vreg} was not allocated")); + .unwrap_or_else(|| panic!("{:?} was not allocated", vreg)); *reg = Register::Physical(phys_reg); } } for reg in instr.written_regs_mut() { if let Some(vreg) = reg.try_as_virtual() { - debug!( - "Replacing {} with its physical register at {}", - vreg, instr_uid - ); + // debug!( + // "Replacing {} with its physical register at {}", + // vreg, instr_uid + // ); let phys_reg = self .allocations .get_allocated_reg(vreg) - .unwrap_or_else(|| panic!("{vreg} was not allocated")); + .unwrap_or_else(|| panic!("{:?} was not allocated", vreg)); *reg = Register::Physical(phys_reg); } } @@ -762,7 +785,7 @@ impl<'liveness, 'func, TM: TargetMachine, RegAlloc: RegAllocAlgorithm<'liveness, } impl Function { - pub fn liveness_repr(&mut self) -> LivenessRepr { + pub fn liveness_repr(&mut self) -> LivenessRepr { #[derive(Default, Clone)] struct IncompleteLiveRange { start: Option, @@ -842,7 +865,7 @@ impl Function { debug!("{:?}", live_sets); - debug!("{}", repr); + debug!("{}", repr.display(self)); repr } } diff --git a/ir/crates/back/src/codegen/selection_dag/builder.rs b/ir/crates/back/src/codegen/selection_dag/builder.rs index 23d2e8f..4a3b685 100644 --- a/ir/crates/back/src/codegen/selection_dag/builder.rs +++ b/ir/crates/back/src/codegen/selection_dag/builder.rs @@ -1,8 +1,4 @@ use codegen::selection_dag; -use cranelift_entity::{ - EntityRef, - SecondaryMap, -}; use daggy::{ petgraph::visit::IntoNodeIdentifiers, NodeIndex, @@ -11,20 +7,25 @@ use daggy::{ use iter_tools::Itertools; use natrix_middle::{ cfg::{ + BBArgRef, BasicBlockRef, BranchTerm, + InstrRef, JumpTarget, Terminator, TerminatorKind, }, instruction::{ Const, - VRegData, + OpInstr, }, ty::Type, + InstrKind, + Value, }; use rustc_hash::FxHashMap; use selection_dag::SelectionDAG; +use slotmap::SecondaryMap; use tracing::debug; use crate::{ @@ -34,7 +35,7 @@ use crate::{ function::Function, reg::{ Register, - VReg, + VRegRef, }, TargetMachine, }, @@ -48,12 +49,12 @@ use crate::{ }, }; -#[derive(Debug)] pub struct Builder<'func, TM: TargetMachine> { function: &'func mut Function, sel_dag: SelectionDAG, - reg_mapping: SecondaryMap>, - defining_nodes: FxHashMap<(VReg, BasicBlockRef), NodeIndex>, + reg_mapping: SecondaryMap>, + bb_arg_reg_mapping: SecondaryMap>, + defining_nodes: FxHashMap<(VRegRef, BasicBlockRef), NodeIndex>, } impl<'func, TM: TargetMachine> Builder<'func, TM> { @@ -61,6 +62,7 @@ impl<'func, TM: TargetMachine> Builder<'func, TM> { Self { function, reg_mapping: SecondaryMap::new(), + bb_arg_reg_mapping: SecondaryMap::new(), sel_dag: SelectionDAG::default(), defining_nodes: FxHashMap::default(), } @@ -70,25 +72,23 @@ impl<'func, TM: TargetMachine> Builder<'func, TM> { debug!("Building SelectionDAGs for function {}", func.name); let basic_blocks = func .cfg - .basic_block_ids() + .basic_blocks + .keys() .filter(|bb_id| *bb_id != func.cfg.entry_block_ref()) - .collect::>(); + .collect_vec(); for bb_id in basic_blocks { + let bb = &func.cfg.basic_blocks[bb_id]; // Eliminate basic block arguments - debug!("Eliminating basic block arguments for {}", bb_id); - let bb_args = func - .cfg - .basic_block(bb_id) + debug!("Eliminating basic block arguments for {}", bb); + let bb_args = bb .arguments() - .map(|arg| (arg, func.cfg.vreg_ty_cloned(arg))) + .map(|arg| (arg, func.cfg.bb_args[arg].ty.clone())) .collect_vec(); let mut temp_regs = FxHashMap::<_, Vec<_>>::default(); let preds = func.cfg.predecessors(bb_id).collect::>(); for pred_id in preds { - let args = func - .cfg - .basic_block(pred_id) + let args = func.cfg.basic_blocks[pred_id] .terminator() .branch_args(bb_id) .unwrap() @@ -100,21 +100,24 @@ impl<'func, TM: TargetMachine> Builder<'func, TM> { let succ_len = func.cfg.successors(pred_id).count(); let copy_instr_bb = if succ_len == 1 { // No need to split the critical edge - func.cfg.basic_block_mut(pred_id).update_terminator(|term| { + func.cfg.basic_blocks[pred_id].update_terminator(|term| { term.clear_args(bb_id); }); pred_id } else { // Create new basic block for copying the argument ops to temp regs - let critical_edge_split_bb = func.cfg.new_basic_block(); - func.cfg - .basic_block_mut(critical_edge_split_bb) - .set_terminator(Terminator::new( - TerminatorKind::Branch(BranchTerm::new(JumpTarget::no_args(bb_id))), - critical_edge_split_bb, - )); + let split_crit_bb_symbol = format!( + "$split_crit:{}->{}", + bb_id.display(&func.cfg), + pred_id.display(&func.cfg) + ); + let critical_edge_split_bb = func.cfg.new_basic_block(split_crit_bb_symbol); + func.cfg.basic_blocks[critical_edge_split_bb].set_terminator(Terminator::new( + TerminatorKind::Branch(BranchTerm::new(JumpTarget::no_args(bb_id))), + critical_edge_split_bb, + )); func.cfg.recompute_successors(critical_edge_split_bb); - func.cfg.basic_block_mut(pred_id).update_terminator(|term| { + func.cfg.basic_blocks[pred_id].update_terminator(|term| { term.clear_args(bb_id); term.update_references_to_bb(bb_id, critical_edge_split_bb); }); @@ -123,49 +126,58 @@ impl<'func, TM: TargetMachine> Builder<'func, TM> { critical_edge_split_bb }; - for (arg, (bb_arg, ty)) in args.into_iter().zip(bb_args.iter()) { + for (arg, (bb_arg_ref, ty)) in args.into_iter().zip(bb_args.iter()) { // Create a temp reg and copy the jump arg to it - let temp_reg = func.cfg.new_vreg(VRegData { - defined_in: pred_id, - ty: ty.clone(), - }); - temp_regs.entry(*bb_arg).or_default().push(temp_reg); - let instr = func.cfg.copy_op_instr(temp_reg, arg); - func.cfg.add_instruction(copy_instr_bb, ty.clone(), instr); + + let bb_arg = &func.cfg.bb_args[*bb_arg_ref]; + let instr_ref = func.cfg.add_instruction( + copy_instr_bb, + ty.clone(), + InstrKind::Op(OpInstr { op: arg }), + // Use $ to create a unique name, because $ can't be used to create a symbol + format!("${}", bb_arg.symbol), + ); + temp_regs.entry(*bb_arg_ref).or_default().push(instr_ref); } } - let _ = func.cfg.basic_block_mut(bb_id).clear_arguments(); + let _ = func.cfg.basic_blocks[bb_id].clear_arguments(); for (arg, _) in bb_args { - let mapped_arg = self.map_vreg(arg, func); + let mapped_arg = self.map_value(arg.into(), func); let operands = temp_regs .remove(&arg) .unwrap() .into_iter() - .map(|reg| { - let mapped = self.map_vreg(reg, func); + .map(|instr_ref| { + let mapped = self.map_value(instr_ref.into(), func); // Ensure that the temp reg and arg reg will be placed in the same location => we can trivially remove the phi instruction later on self.function.tie_vreg(mapped, mapped_arg); // Using defined_in here is fine, because the reg that we use here is always defined in the immediate pred of bb_id - (Register::Virtual(mapped), func.cfg.vreg(reg).defined_in) + ( + Register::Virtual(mapped), + func.cfg.instructions[instr_ref].defined_in, + ) }) .collect_vec(); let pseudo_op = PseudoOp::Phi(Register::Virtual(mapped_arg), operands); - debug!("Adding phi to {bb_id}: {:?}", pseudo_op); + let bb = &func.cfg.basic_blocks[bb_id]; + debug!("Adding phi to {bb}: {:?}", pseudo_op); let phi_op = Op::Pseudo(pseudo_op); self.define_node(bb_id, phi_op); } - debug!("Eliminated basic block arguments for {}", bb_id); + let bb = &func.cfg.basic_blocks[bb_id]; + debug!("Eliminated basic block arguments for {}", bb); } - for (bb_id, bb) in func.cfg.basic_blocks() { - debug!("Building SelectionDAG for basic block {}", bb_id); + for (bb_id, bb) in &func.cfg.basic_blocks { + debug!("Building SelectionDAG for basic block {}", bb); if bb_id == func.cfg.entry_block_ref() { for arg in bb.arguments() { - let mapped_reg = self.map_vreg(arg, func); + let mapped_reg = self.map_value(arg.into(), func); self.function.params.push(mapped_reg); self.define_node(bb_id, Op::Pseudo(PseudoOp::Def(mapped_reg))); } } - for instr in bb.instructions() { + for instr_ref in bb.instructions() { + let instr = &func.cfg.instructions[instr_ref]; debug!("Add instruction to SelectionDAG: {:?}", instr); match &instr.kind { @@ -173,7 +185,7 @@ impl<'func, TM: TargetMachine> Builder<'func, TM> { natrix_middle::InstrKind::Store(_) => unimplemented!(), natrix_middle::InstrKind::Load(_) => unimplemented!(), natrix_middle::InstrKind::Op(op_instr) => { - let out_reg = self.map_vreg(op_instr.value, func); + let out_reg = self.map_value(instr.id.into(), func); let op = match self.map_op(&op_instr.op, func) { Operand::Reg(reg) => { Op::Pseudo(PseudoOp::Copy(Register::Virtual(out_reg), reg)) @@ -186,7 +198,7 @@ impl<'func, TM: TargetMachine> Builder<'func, TM> { self.define_node(bb_id, op); } natrix_middle::InstrKind::Sub(sub_instr) => { - let out_reg = self.map_vreg(sub_instr.value, func); + let out_reg = self.map_value(instr.id.into(), func); let lhs = self.map_op(&sub_instr.lhs, func); let rhs = self.map_op(&sub_instr.rhs, func); self.define_node( @@ -195,7 +207,7 @@ impl<'func, TM: TargetMachine> Builder<'func, TM> { ); } natrix_middle::InstrKind::Add(add_instr) => { - let out_reg = self.map_vreg(add_instr.value, func); + let out_reg = self.map_value(instr.id.into(), func); let lhs = self.map_op(&add_instr.lhs, func); let rhs = self.map_op(&add_instr.rhs, func); self.define_node( @@ -204,7 +216,7 @@ impl<'func, TM: TargetMachine> Builder<'func, TM> { ); } natrix_middle::InstrKind::Cmp(cmp_instr) => { - let out_reg = self.map_vreg(cmp_instr.value, func); + let out_reg = self.map_value(instr.id.into(), func); let lhs = self.map_op(&cmp_instr.lhs, func); let rhs = self.map_op(&cmp_instr.rhs, func); self.define_node( @@ -254,7 +266,7 @@ impl<'func, TM: TargetMachine> Builder<'func, TM> { ) -> Operand { match op { natrix_middle::instruction::Op::Value(vreg) => { - Operand::Reg(Register::Virtual(self.map_vreg(*vreg, func))) + Operand::Reg(Register::Virtual(self.map_value(*vreg, func))) } natrix_middle::instruction::Op::Const(constant) => Operand::Imm(match constant { Const::Int(ty, value) => { @@ -328,24 +340,43 @@ impl<'func, TM: TargetMachine> Builder<'func, TM> { term_node } - fn map_vreg(&mut self, vreg: natrix_middle::VReg, func: &natrix_middle::Function) -> VReg { - let mapped = self.reg_mapping[vreg]; - match mapped { - Some(reg) => reg, - None => { - let vreg_ty = func.cfg.vreg_ty(vreg); - let mapped_vreg = self.function.alloc_vreg(vreg_ty.into()); - self.reg_mapping[vreg] = Some(mapped_vreg); + fn map_value(&mut self, value: Value, func: &natrix_middle::Function) -> VRegRef { + match value { + Value::Instr(instr_ref) => { + if let Some(reg) = self.reg_mapping[instr_ref] { + return reg; + } + } + Value::BBArg(bb_arg_ref) => { + if let Some(reg) = self.bb_arg_reg_mapping[bb_arg_ref] { + return reg; + } + } + }; + match value { + Value::Instr(instr_ref) => { + let instr = &func.cfg.instructions[instr_ref]; + let ty = &instr.ty; + let mapped_vreg = self.function.alloc_vreg(ty.into(), instr.symbol.clone()); + self.reg_mapping[instr_ref] = Some(mapped_vreg); + mapped_vreg + } + + Value::BBArg(bb_arg_ref) => { + let bb_arg = &func.cfg.bb_args[bb_arg_ref]; + let ty = &bb_arg.ty; + let mapped_vreg = self.function.alloc_vreg(ty.into(), bb_arg.symbol.clone()); + self.bb_arg_reg_mapping[bb_arg_ref] = Some(mapped_vreg); mapped_vreg } } } - fn define_out_val(&mut self, node: NodeIndex, reg: VReg, bb_id: BasicBlockRef) { + fn define_out_val(&mut self, node: NodeIndex, reg: VRegRef, bb_id: BasicBlockRef) { self.defining_nodes.insert((reg, bb_id), node); } - fn get_defining_node(&self, vreg: VReg, bb_id: BasicBlockRef) -> Option { + fn get_defining_node(&self, vreg: VRegRef, bb_id: BasicBlockRef) -> Option { self.defining_nodes.get(&(vreg, bb_id)).copied() } } diff --git a/ir/crates/back/src/codegen/selection_dag/mod.rs b/ir/crates/back/src/codegen/selection_dag/mod.rs index a1de3b3..7c5236d 100644 --- a/ir/crates/back/src/codegen/selection_dag/mod.rs +++ b/ir/crates/back/src/codegen/selection_dag/mod.rs @@ -12,7 +12,10 @@ use daggy::petgraph::dot::{ Dot, }; use natrix_middle::{ - cfg::BasicBlockRef, + cfg::{ + BasicBlockRef, + Cfg, + }, instruction::CmpOp, }; use rustc_hash::FxHashMap; @@ -24,7 +27,7 @@ use smallvec::{ use crate::codegen::machine::{ reg::{ Register, - VReg, + VRegRef, }, Size, TargetMachine, @@ -38,11 +41,11 @@ type Dag = daggy::Dag, Edge>; pub struct BasicBlockDAG { dag: Dag, term_node: Option, - bb: natrix_middle::cfg::BasicBlockRef, + bb: BasicBlockRef, } impl BasicBlockDAG { - pub fn new(bb: natrix_middle::cfg::BasicBlockRef) -> Self { + pub fn new(bb: BasicBlockRef) -> Self { Self { dag: Dag::new(), term_node: None, @@ -61,10 +64,18 @@ impl BasicBlockDAG { format!("{:?}", Dot::with_config(&self.dag, &[Config::EdgeNoLabel])) } - pub fn save_graphviz>(&self, path: P) -> std::io::Result<()> { + pub fn save_graphviz>( + &self, + path: P, + cfg: &Cfg, + ) -> std::io::Result<()> { std::fs::create_dir_all(&path)?; std::fs::write( - format!("{}/{}.dot", path.as_ref().display(), self.bb), + format!( + "{}/{}.dot", + path.as_ref().display(), + cfg.basic_blocks[self.bb] + ), self.graphviz(), ) } @@ -238,7 +249,7 @@ impl Op { #[derive(Debug, Clone, Eq, PartialEq)] pub enum PseudoOp { - Def(VReg), + Def(VRegRef), Copy(Register, Register), Ret(Option>), Phi(Register, Vec<(Register, BasicBlockRef)>), diff --git a/ir/crates/middle/src/cfg/mod.rs b/ir/crates/middle/src/cfg/mod.rs index 98af79e..3713996 100644 --- a/ir/crates/middle/src/cfg/mod.rs +++ b/ir/crates/middle/src/cfg/mod.rs @@ -34,6 +34,12 @@ mod builder; mod domtree; new_key_type! { pub struct BasicBlockRef; } + +impl BasicBlockRef { + pub fn display(self, cfg: &Cfg) -> &String { + &cfg.basic_blocks[self].symbol + } +} #[derive(Debug, Clone, Eq, PartialEq)] struct CFGNode { bb_ref: BasicBlockRef, @@ -45,7 +51,7 @@ pub struct Cfg { graph: Graph, pub basic_blocks: SlotMap, pub instructions: SlotMap, - pub basic_block_args: SlotMap, + pub bb_args: SlotMap, entry_block: Option, } @@ -172,9 +178,7 @@ impl Cfg { } pub fn add_bb_argument(&mut self, bb_id: BasicBlockRef, ty: Type, symbol: String) -> BBArgRef { - let arg_ref = self - .basic_block_args - .insert_with_key(|id| BBArg { id, ty, symbol }); + let arg_ref = self.bb_args.insert_with_key(|id| BBArg { id, ty, symbol }); self.basic_blocks[bb_id].arguments.insert(arg_ref); arg_ref } @@ -183,11 +187,7 @@ impl Cfg { self.instructions .keys() .map(move |instr_id| Value::Instr(instr_id)) - .chain( - self.basic_block_args - .keys() - .map(move |arg_id| Value::BBArg(arg_id)), - ) + .chain(self.bb_args.keys().map(move |arg_id| Value::BBArg(arg_id))) } } @@ -199,8 +199,8 @@ impl Display for Cfg { if !bb.arguments.is_empty() { write!(f, "(")?; for (index, arg_ref) in bb.arguments.iter().copied().enumerate() { - let arg = &self.basic_block_args[arg_ref]; - let arg_ty = &self.basic_block_args[arg_ref].ty; + let arg = &self.bb_args[arg_ref]; + let arg_ty = &self.bb_args[arg_ref].ty; write!(f, "{arg_ty} {arg}")?; if index < bb.arguments.len() - 1 { write!(f, ", ")?; @@ -244,8 +244,8 @@ new_key_type! { pub struct BBArgRef; } #[derive(Debug, Clone, Eq, PartialEq)] pub struct BBArg { - pub(crate) id: BBArgRef, - pub(crate) ty: Type, + pub id: BBArgRef, + pub ty: Type, pub symbol: String, } diff --git a/ir/crates/middle/src/lib.rs b/ir/crates/middle/src/lib.rs index fc1467b..6ac6af4 100644 --- a/ir/crates/middle/src/lib.rs +++ b/ir/crates/middle/src/lib.rs @@ -11,7 +11,7 @@ pub use instruction::{ Instr, InstrKind, }; -use module::Module; +pub use module::Module; pub use ty::Type; use crate::cfg::{ @@ -41,7 +41,7 @@ impl<'cfg> std::fmt::Display for ValueDisplay<'cfg> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.1 { Value::Instr(instr) => write!(f, "{}", self.0.instructions[instr]), - Value::BBArg(arg) => write!(f, "{}", self.0.basic_block_args[arg]), + Value::BBArg(arg) => write!(f, "{}", self.0.bb_args[arg]), } } } diff --git a/ir/crates/middle/src/optimization/function_pass/cfg_simplify/unreachable_bb_elim.rs b/ir/crates/middle/src/optimization/function_pass/cfg_simplify/unreachable_bb_elim.rs index 6e17464..bb248e0 100644 --- a/ir/crates/middle/src/optimization/function_pass/cfg_simplify/unreachable_bb_elim.rs +++ b/ir/crates/middle/src/optimization/function_pass/cfg_simplify/unreachable_bb_elim.rs @@ -130,7 +130,7 @@ impl FunctionPass for Pass { .clear_arguments() .collect_vec(); for (argument, op) in successors_args.into_iter().zip(branch_args) { - let ty = function.cfg.basic_block_args[argument].ty.clone(); + let ty = function.cfg.bb_args[argument].ty.clone(); // To keep the program consistent, we need to insert move instruction for each argument // They look like this: // =