From 5dfe58d66b043f1b27ca88815ed68c999b7948f0 Mon Sep 17 00:00:00 2001 From: Julian Hartl <90799563+julian-hartl@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:59:22 +0100 Subject: [PATCH] Restructure backend (#14) --- ir/crates/back/Cargo.toml | 2 +- ir/crates/back/src/codegen/isa/mod.rs | 23 - .../codegen/machine/abi/calling_convention.rs | 12 +- ir/crates/back/src/codegen/machine/abi/mod.rs | 23 +- ir/crates/back/src/codegen/machine/asm.rs | 17 +- ir/crates/back/src/codegen/machine/backend.rs | 49 + .../src/codegen/machine/function/builder.rs | 395 ++++++ .../back/src/codegen/machine/function/cfg.rs | 190 +++ .../back/src/codegen/machine/function/mod.rs | 274 ++++ ir/crates/back/src/codegen/machine/instr.rs | 244 ++++ ir/crates/back/src/codegen/machine/isa.rs | 80 ++ ir/crates/back/src/codegen/machine/mod.rs | 1262 +---------------- .../back/src/codegen/machine/module/asm.rs | 58 + .../src/codegen/machine/module/builder.rs | 11 +- .../back/src/codegen/machine/module/mod.rs | 99 +- ir/crates/back/src/codegen/machine/reg.rs | 81 ++ ir/crates/back/src/codegen/mod.rs | 4 +- .../codegen/register_allocator/coalescer.rs | 48 +- .../codegen/register_allocator/linear_scan.rs | 87 +- .../src/codegen/register_allocator/mod.rs | 358 +++-- .../back/src/codegen/selection_dag/builder.rs | 265 ++-- .../back/src/codegen/selection_dag/mod.rs | 191 +-- .../codegen/targets/calling_convention/mod.rs | 1 + .../targets/calling_convention/systemv.rs | 77 + ir/crates/back/src/codegen/targets/mod.rs | 2 + .../codegen/{isa => targets}/x86_64/asm.rs | 250 ++-- .../codegen/{isa => targets}/x86_64/mod.rs | 638 ++++----- ir/crates/back/src/emu.rs | 108 +- ir/crates/back/src/lib.rs | 15 +- ir/crates/compiler/src/main.rs | 4 +- ir/rustfmt.toml | 4 + 31 files changed, 2640 insertions(+), 2232 deletions(-) delete mode 100644 ir/crates/back/src/codegen/isa/mod.rs create mode 100644 ir/crates/back/src/codegen/machine/backend.rs create mode 100644 ir/crates/back/src/codegen/machine/function/builder.rs create mode 100644 ir/crates/back/src/codegen/machine/function/cfg.rs create mode 100644 ir/crates/back/src/codegen/machine/function/mod.rs create mode 100644 ir/crates/back/src/codegen/machine/instr.rs create mode 100644 ir/crates/back/src/codegen/machine/isa.rs create mode 100644 ir/crates/back/src/codegen/machine/module/asm.rs create mode 100644 ir/crates/back/src/codegen/machine/reg.rs create mode 100644 ir/crates/back/src/codegen/targets/calling_convention/mod.rs create mode 100644 ir/crates/back/src/codegen/targets/calling_convention/systemv.rs create mode 100644 ir/crates/back/src/codegen/targets/mod.rs rename ir/crates/back/src/codegen/{isa => targets}/x86_64/asm.rs (53%) rename ir/crates/back/src/codegen/{isa => targets}/x86_64/mod.rs (52%) create mode 100644 ir/rustfmt.toml diff --git a/ir/crates/back/Cargo.toml b/ir/crates/back/Cargo.toml index d7aa08c..fe3777d 100644 --- a/ir/crates/back/Cargo.toml +++ b/ir/crates/back/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" index_vec = "0.1.3" tracing = "0.1.40" rustc-hash = "1.1.0" -strum = { version = "0.26.1",features = ["derive"]} +strum = { version = "0.26.1", features = ["derive"] } strum_macros = "0.26.1" tracing-test = "0.2.4" smallvec = "1.13.1" diff --git a/ir/crates/back/src/codegen/isa/mod.rs b/ir/crates/back/src/codegen/isa/mod.rs deleted file mode 100644 index 345b2a4..0000000 --- a/ir/crates/back/src/codegen/isa/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::codegen::machine::{Abi, PhysicalRegister}; - -pub mod x86_64; - -pub enum Endianness { - Little, - Big -} - -pub enum Architecture { - X86_64, -} -// -// pub trait Architecture { -// fn get_calling_convention(&self) -> &dyn CallingConvention; -// } - -// pub trait CallingConvention { -// -// type Abi: Abi; -// -// fn get_return_register(&self, size: Size) -> Self::Abi::Reg; -// } diff --git a/ir/crates/back/src/codegen/machine/abi/calling_convention.rs b/ir/crates/back/src/codegen/machine/abi/calling_convention.rs index eef243b..690b371 100644 --- a/ir/crates/back/src/codegen/machine/abi/calling_convention.rs +++ b/ir/crates/back/src/codegen/machine/abi/calling_convention.rs @@ -1,16 +1,18 @@ -use natrix_middle::Type; -use crate::codegen::machine; -use crate::codegen::machine::Size; +use crate::codegen::{ + machine, + machine::Size, +}; pub trait CallingConvention { type Reg: machine::PhysicalRegister; - fn parameter_slots(params: impl Iterator) -> impl Iterator>; + fn parameter_slots(params: impl Iterator) + -> impl Iterator>; fn return_slot(size: Size) -> Slot; } pub enum Slot { Register(R), - Stack + Stack, } diff --git a/ir/crates/back/src/codegen/machine/abi/mod.rs b/ir/crates/back/src/codegen/machine/abi/mod.rs index 310699e..658f7be 100644 --- a/ir/crates/back/src/codegen/machine/abi/mod.rs +++ b/ir/crates/back/src/codegen/machine/abi/mod.rs @@ -1,24 +1,3 @@ -use std::fmt::Debug; -use std::hash::Hash; - pub use calling_convention::CallingConvention; -use crate::codegen::machine; -use crate::codegen::machine::asm::Assembler; - -pub mod calling_convention; - -pub trait Abi: Debug + Default + Clone + PartialEq + Eq + Hash { - type I: machine::MachineInstr; - - type REG: machine::PhysicalRegister + 'static + Hash + Copy; - - type ASSEMBLER: Assembler; - - type CallingConvention: CallingConvention; - - fn get_assembler(base_addr: u64) -> Self::ASSEMBLER { - Self::ASSEMBLER::new(base_addr) - } -} - +pub mod calling_convention; \ No newline at end of file diff --git a/ir/crates/back/src/codegen/machine/asm.rs b/ir/crates/back/src/codegen/machine/asm.rs index 1b997c2..4146a16 100644 --- a/ir/crates/back/src/codegen/machine/asm.rs +++ b/ir/crates/back/src/codegen/machine/asm.rs @@ -1,13 +1,16 @@ -use crate::codegen::machine::{Abi, BasicBlockId}; +use crate::codegen::machine::{ + function::cfg::BasicBlockId, + TargetMachine, +}; + +pub trait Assembler { + type TM: TargetMachine; + + fn new(base_addr: u64) -> Self; -pub trait Assembler { - fn new( - base_addr: u64, - ) -> Self; - fn begin_basic_block(&mut self, bb_id: BasicBlockId); - fn assemble(&mut self, instr: &A::I); + fn assemble(&mut self, instr: &::Instr); fn finish(self) -> Vec; diff --git a/ir/crates/back/src/codegen/machine/backend.rs b/ir/crates/back/src/codegen/machine/backend.rs new file mode 100644 index 0000000..48f08b4 --- /dev/null +++ b/ir/crates/back/src/codegen/machine/backend.rs @@ -0,0 +1,49 @@ +use std::fmt::Debug; + +use smallvec::SmallVec; + +use crate::codegen::{ + machine::{ + function::{ + builder::{ + MatchedPattern, + PatternIn, + }, + Function, + }, + TargetMachine, + }, + selection_dag::Immediate, +}; +use crate::codegen::machine::Instr; + +type Reg = <::TM as TargetMachine>::Reg; +type BackInstr = <::TM as TargetMachine>::Instr; + +pub trait Backend { + type TM: TargetMachine; + + type P: Pattern; + + fn patterns() -> &'static [Self::P]; + + fn mov(dest: Reg, src: Reg) -> BackInstr; + + fn mov_imm(dest: Reg, imm: Immediate) -> BackInstr; + + fn ret() -> BackInstr; + + fn new() -> Self; +} + +pub trait Pattern: Sized + Debug + Clone + PartialEq + Eq + 'static { + type TM: TargetMachine; + + fn in_(&self) -> PatternIn; + + fn into_instr( + self, + function: &mut Function, + matched: MatchedPattern, + ) -> SmallVec<[Instr; 2]>; +} diff --git a/ir/crates/back/src/codegen/machine/function/builder.rs b/ir/crates/back/src/codegen/machine/function/builder.rs new file mode 100644 index 0000000..0bcc768 --- /dev/null +++ b/ir/crates/back/src/codegen/machine/function/builder.rs @@ -0,0 +1,395 @@ +use daggy::{ + petgraph::prelude::Bfs, + Walker, +}; +use rustc_hash::FxHashMap; +use tracing::debug; + +use natrix_middle::instruction::CmpOp; + +use crate::codegen::{ + machine::{ + backend::{ + Backend, + Pattern, + }, + function::{ + cfg::BasicBlockId, + Function, + }, + instr::{ + InstrOperand, + PseudoInstr, + }, + MachInstr, + Register, + Size, + TargetMachine, + }, + selection_dag, + selection_dag::{ + Immediate, + MachineOp, + Op, + Operand, + PseudoOp, + }, +}; +use crate::codegen::machine::Instr; + +#[derive(Debug)] +pub struct FunctionBuilder { + function: Function, + backend: TM::Backend, + bb_mapping: FxHashMap, +} + +impl FunctionBuilder { + pub fn new() -> Self { + Self { + function: Function::new(Default::default()), + backend: Backend::new(), + bb_mapping: FxHashMap::default(), + } + } + + pub fn build(mut self, function: &mut natrix_middle::Function) -> Function { + self.function.name = function.name.clone(); + self.function.return_ty_size = Size::from_ty(&function.ret_ty); + debug!("Building machine function for function {}", function.name); + let sel_dag_builder = selection_dag::Builder::new(&mut self.function); + let mut sel_dag = sel_dag_builder.build(function); + for bb in function.cfg.basic_block_ids_ordered() { + self.create_bb(bb); + } + for mbb_id in self.function.basic_blocks.indices() { + let bb = *self + .bb_mapping + .iter() + .find(|(_, mbb)| **mbb == mbb_id) + .unwrap() + .0; + debug!("Building machine basic block for basic block {}", bb); + let dag = sel_dag.get_bb_dag(bb); + dag.save_graphviz("out").unwrap(); + let mut node_list = Vec::with_capacity(dag.node_count()); + debug!("Determining traversal order for basic block {}", bb); + let bfs = Bfs::new(dag.graph(), dag.term_node()); + for n in bfs.iter(dag.graph()) { + node_list.push(n); + } + debug!("Traversal order: {:?}", node_list); + let mut instructions = Vec::new(); + while let Some(node_id) = node_list.pop() { + let op = &dag[node_id]; + match op { + Op::Pseudo(op) => { + debug!("Found pseudo op {:?}", op); + match op { + PseudoOp::Copy(dest, src) => { + instructions.push(Instr::Pseudo(PseudoInstr::Copy( + dest.clone(), + src.clone(), + ))); + } + PseudoOp::Ret(operand) => { + instructions.push(Instr::Pseudo(PseudoInstr::Ret( + operand.as_ref().cloned().map(|operand| match operand { + Operand::Reg(reg) => InstrOperand::Reg(reg), + Operand::Imm(imm) => InstrOperand::Imm(imm), + }), + ))); + } + PseudoOp::Phi(dest, operands) => { + instructions.push(Instr::Pseudo(PseudoInstr::Phi( + dest.clone(), + operands.clone(), + ))); + } + PseudoOp::Def(reg) => { + instructions + .push(Instr::Pseudo(PseudoInstr::Def(Register::Virtual(*reg)))); + } + } + } + Op::Machine(op) => { + let dag_node_pattern = match op { + MachineOp::Mov(dest, src) => PatternIn::Mov( + PatternInOutput::Reg(dest.size(&self.function)), + self.operand_to_pattern(src), + ), + MachineOp::Sub(dest, lhs, rhs) => PatternIn::Sub( + PatternInOutput::Reg(dest.size(&self.function)), + self.operand_to_pattern(lhs), + self.operand_to_pattern(rhs), + ), + MachineOp::Add(dest, lhs, rhs) => PatternIn::Add( + PatternInOutput::Reg(dest.size(&self.function)), + self.operand_to_pattern(lhs), + self.operand_to_pattern(rhs), + ), + MachineOp::Br(bb_id) => PatternIn::Br, + MachineOp::Cmp(dest, cmp_op, lhs, rhs) => PatternIn::Cmp( + PatternInOutput::Reg(dest.size(&self.function)), + *cmp_op, + self.operand_to_pattern(lhs), + self.operand_to_pattern(rhs), + ), + MachineOp::CondBr(cond, true_target, false_target) => { + PatternIn::CondBr(self.operand_to_pattern(cond)) + } + }; + let mut matching_pattern = None; + debug!("Matching patterns for node {:?}", op); + + for pattern in TM::Backend::patterns() { + let pattern_in = pattern.in_(); + debug!("Checking {:?}", pattern_in); + debug!("Matching with {:?}", dag_node_pattern); + if pattern_in != dag_node_pattern { + debug!("Pattern does not match"); + continue; + } + debug!("Pattern matches"); + matching_pattern = Some(pattern.clone()); + break; + } + match matching_pattern { + None => { + panic!("No pattern matched for node {:?}", op); + } + Some(pattern) => { + let matched = match op { + MachineOp::Mov(dest, src) => { + MatchedPattern::Mov(MatchedMovPattern { + dest: MatchedPatternOutput::Reg(*dest), + src: self.operand_to_matched_pattern_operand(src), + }) + } + MachineOp::Sub(dest, lhs, rhs) => { + MatchedPattern::Sub(MatchedSubPattern { + dest: MatchedPatternOutput::Reg(*dest), + lhs: self.operand_to_matched_pattern_operand(lhs), + rhs: self.operand_to_matched_pattern_operand(rhs), + }) + } + MachineOp::Add(dest, lhs, rhs) => { + MatchedPattern::Add(MatchedAddPattern { + dest: MatchedPatternOutput::Reg(*dest), + lhs: self.operand_to_matched_pattern_operand(lhs), + rhs: self.operand_to_matched_pattern_operand(rhs), + }) + } + MachineOp::Br(target) => MatchedPattern::Br(MatchedBrPattern { + target: self.bb_mapping[target], + }), + MachineOp::Cmp(dest, cmp_op, lhs, rhs) => { + MatchedPattern::Cmp(MatchedCmpPattern { + dest: MatchedPatternOutput::Reg(*dest), + cmp_op: *cmp_op, + lhs: self.operand_to_matched_pattern_operand(lhs), + rhs: self.operand_to_matched_pattern_operand(rhs), + }) + } + MachineOp::CondBr(cond, true_target, false_target) => { + MatchedPattern::CondBr(MatchedCondBrPattern { + cond: self.operand_to_matched_pattern_operand(cond), + true_target: self.bb_mapping[true_target], + false_target: self.bb_mapping[false_target], + }) + } + }; + let generated_instructions = + pattern.into_instr(&mut self.function, matched); + debug!("Generated instructions {:?}", generated_instructions); + instructions.extend(generated_instructions.into_iter()); + } + } + } + } + } + for instr in instructions { + self.function.basic_blocks[mbb_id].instructions.push(instr); + } + } + debug!( + "Finished building machine function for function {}", + function.name + ); + debug!("{}", self.function); + self.function + } + + fn create_bb(&mut self, bb: natrix_middle::cfg::BasicBlockId) -> BasicBlockId { + let mbb = self.function.create_bb(); + self.bb_mapping.insert(bb, mbb); + mbb + } + + fn operand_to_matched_pattern_operand( + &self, + src: &Operand, + ) -> MatchedPatternOperand { + match src { + Operand::Reg(reg) => MatchedPatternOperand::Reg(*reg), + Operand::Imm(imm) => MatchedPatternOperand::Imm(imm.clone()), + } + } + + fn operand_to_pattern(&self, src: &Operand) -> PatternInOperand { + match src { + Operand::Reg(reg) => PatternInOperand::Reg(reg.size(&self.function)), + Operand::Imm(imm) => PatternInOperand::Imm(imm.size), + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum PatternIn { + Mov(PatternInOutput, PatternInOperand), + Sub(PatternInOutput, PatternInOperand, PatternInOperand), + Add(PatternInOutput, PatternInOperand, PatternInOperand), + Cmp(PatternInOutput, CmpOp, PatternInOperand, PatternInOperand), + Br, + CondBr(PatternInOperand), +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum PatternInOutput { + Reg(Size), +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum PatternInOperand { + Reg(Size), + Imm(Size), +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MatchedMovPattern { + pub dest: MatchedPatternOutput, + pub src: MatchedPatternOperand, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MatchedSubPattern { + pub dest: MatchedPatternOutput, + pub lhs: MatchedPatternOperand, + pub rhs: MatchedPatternOperand, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MatchedAddPattern { + pub dest: MatchedPatternOutput, + pub lhs: MatchedPatternOperand, + pub rhs: MatchedPatternOperand, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MatchedBrPattern { + pub target: BasicBlockId, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MatchedCmpPattern { + pub dest: MatchedPatternOutput, + pub cmp_op: CmpOp, + pub lhs: MatchedPatternOperand, + pub rhs: MatchedPatternOperand, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MatchedCondBrPattern { + pub cond: MatchedPatternOperand, + pub true_target: BasicBlockId, + pub false_target: BasicBlockId, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum MatchedPattern { + Mov(MatchedMovPattern), + Sub(MatchedSubPattern), + Add(MatchedAddPattern), + Cmp(MatchedCmpPattern), + CondBr(MatchedCondBrPattern), + Br(MatchedBrPattern), +} + +impl MatchedPattern { + pub fn try_as_mov(&self) -> Option<&MatchedMovPattern> { + match self { + MatchedPattern::Mov(mov) => Some(mov), + _ => None, + } + } + + pub fn try_as_sub(&self) -> Option<&MatchedSubPattern> { + match self { + MatchedPattern::Sub(sub) => Some(sub), + _ => None, + } + } + + pub fn try_as_add(&self) -> Option<&MatchedAddPattern> { + match self { + MatchedPattern::Add(add) => Some(add), + _ => None, + } + } + + pub fn try_as_br(&self) -> Option<&MatchedBrPattern> { + match self { + MatchedPattern::Br(br) => Some(br), + _ => None, + } + } + + pub fn try_as_cmp(&self) -> Option<&MatchedCmpPattern> { + match self { + MatchedPattern::Cmp(cmp) => Some(cmp), + _ => None, + } + } + + pub fn try_as_cond_br(&self) -> Option<&MatchedCondBrPattern> { + match self { + MatchedPattern::CondBr(cond_br) => Some(cond_br), + _ => None, + } + } +} + +impl MatchedPatternOutput { + pub fn try_as_reg(&self) -> Option<&Register> { + match self { + MatchedPatternOutput::Reg(reg) => Some(reg), + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum MatchedPatternOperand { + Reg(Register), + Imm(Immediate), +} + +impl MatchedPatternOperand { + pub fn try_as_reg(&self) -> Option<&Register> { + match self { + MatchedPatternOperand::Reg(reg) => Some(reg), + _ => None, + } + } + + pub fn try_as_imm(&self) -> Option<&Immediate> { + match self { + MatchedPatternOperand::Imm(imm) => Some(imm), + _ => None, + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum MatchedPatternOutput { + Reg(Register), +} diff --git a/ir/crates/back/src/codegen/machine/function/cfg.rs b/ir/crates/back/src/codegen/machine/function/cfg.rs new file mode 100644 index 0000000..f3ae92d --- /dev/null +++ b/ir/crates/back/src/codegen/machine/function/cfg.rs @@ -0,0 +1,190 @@ +use daggy::{ + NodeIndex, + petgraph::{ + Directed, + Direction, + prelude::{ + Bfs, + DfsPostOrder, + StableGraph, + }, + }, + Walker, +}; +use index_vec::IndexVec; +use iter_tools::Itertools; +use rustc_hash::FxHashMap; + +use crate::codegen::{ + machine::{ + instr::InstrOperand, + InstrId, + MachInstr, + }, + register_allocator::{ + InstrNumbering, + InstrUid, + LivenessRepr, + ProgPoint, + }, +}; +use crate::codegen::machine::{Instr, TargetMachine}; + +index_vec::define_index_type! { + pub struct BasicBlockId = u32; + + DISPLAY_FORMAT = "bb{}"; +} + +#[derive(Debug, Clone)] +pub struct Cfg { + entry_block: BasicBlockId, + graph: StableGraph<(), (), Directed>, + node_to_block_map: FxHashMap, + block_to_node_map: FxHashMap, +} + +impl Cfg { + pub fn build(bbs: &IndexVec>) -> Self { + let mut cfg = Self::new(BasicBlockId::new(0)); + for bb_id in bbs.indices() { + let node = cfg.graph.add_node(()); + cfg.node_to_block_map.insert(node, bb_id); + cfg.block_to_node_map.insert(bb_id, node); + } + for (bb_id, bb) in bbs.iter_enumerated() { + for instr in &bb.instructions { + let ins = instr.operands(); + for operand in ins { + if let InstrOperand::Label(successor_id) = operand { + cfg.graph.add_edge( + *cfg.block_to_node_map + .get(&bb_id) + .expect("Block not found in block_to_node_map"), + *cfg.block_to_node_map + .get(&successor_id) + .expect("Block not found in block_to_node_map"), + (), + ); + } + } + } + } + cfg + } + + pub fn new(entry_block: BasicBlockId) -> Self { + Self { + entry_block, + graph: StableGraph::new(), + node_to_block_map: FxHashMap::default(), + block_to_node_map: FxHashMap::default(), + } + } + + /// Traverses the cfg using a post order depth first traversal + pub fn dfs_postorder(&self) -> impl Iterator + '_ { + DfsPostOrder::new(&self.graph, self.entry_node()) + .iter(&self.graph) + .map(|node| self.node_to_block_map[&node]) + } + + pub fn bfs(&self) -> impl Iterator + '_ { + Bfs::new(&self.graph, self.entry_node()) + .iter(&self.graph) + .map(|node| self.node_to_block_map[&node]) + } + + pub fn predecessors(&self, bb: BasicBlockId) -> impl Iterator + '_ { + self.graph + .neighbors_directed(self.block_to_node_map[&bb], Direction::Incoming) + .map(|node| self.node_to_block_map[&node]) + } + + pub fn successors(&self, bb: BasicBlockId) -> impl Iterator + '_ { + self.graph + .neighbors(self.block_to_node_map[&bb]) + .map(|node| self.node_to_block_map[&node]) + } + + fn entry_node(&self) -> NodeIndex { + self.node_to_block_map + .iter() + .find_map(|(node, bb)| { + if *bb == self.entry_block { + return Some(*node); + } + None + }) + .expect("Did not find matching entry in node_to_block_map for entry block") + } + + /// Returns an ordering of basic block with the following guarantees: + /// 1. All predecessors of a basic block are visited before the basic block itself (except if the bb is a predecessor of itself) + pub fn ordered(&self) -> Vec { + // let mut visited = FxHashSet::default(); + let order = self.bfs().collect_vec(); + // let mut stack = VecDeque::new(); + // stack.push_back(self.entry_block); + // while let Some(bb) = stack.pop_front() { + // debug!("Visiting basic block {}:{:?}", bb,visited); + // if visited.contains(&bb) { + // continue; + // } + // let mut all_preds_visited = true; + // for pred in self.predecessors(bb) { + // if !(pred == bb || visited.contains(&pred)) { + // debug!("Pred {} of {} has not been visited yet", pred, bb); + // stack.push_back(pred); + // all_preds_visited = false; + // } + // } + // if all_preds_visited { + // visited.insert(bb); + // order.push(bb); + // for succ in self.successors(bb) { + // stack.push_back(succ); + // } + // } else { + // stack.push_back(bb); + // } + // } + order + } +} + +#[derive(Debug, Clone)] +pub struct BasicBlock { + pub id: BasicBlockId, + pub instructions: IndexVec>, +} + +impl BasicBlock { + pub fn new(id: BasicBlockId) -> Self { + Self { + id, + instructions: IndexVec::default(), + } + } + + pub fn entry_pp(&self, liveness_repr: &LivenessRepr) -> ProgPoint { + let instr_nr = liveness_repr + .instr_numbering + .get_instr_nr(InstrUid { + bb: self.id, + instr: 0.into(), + }) + .unwrap(); + ProgPoint::Read(instr_nr) + } + + pub fn exit_pp(&self, instr_numbering: &InstrNumbering) -> ProgPoint { + let instr_nr = instr_numbering + .get_instr_nr(InstrUid { + bb: self.id, + instr: self.instructions.len_idx() - 1, + }) + .unwrap(); + ProgPoint::Write(instr_nr) + } +} diff --git a/ir/crates/back/src/codegen/machine/function/mod.rs b/ir/crates/back/src/codegen/machine/function/mod.rs new file mode 100644 index 0000000..120b7e5 --- /dev/null +++ b/ir/crates/back/src/codegen/machine/function/mod.rs @@ -0,0 +1,274 @@ +use std::fmt::{ + Display, + Formatter, +}; + +use cranelift_entity::{ + entity_impl, + PrimaryMap, +}; +use daggy::Walker; +use index_vec::IndexVec; +use smallvec::{ + smallvec, + SmallVec, +}; +use tracing::debug; + +pub use cfg::{BasicBlock, BasicBlockId, Cfg}; + +use crate::codegen::machine::{ + abi::{ + calling_convention::Slot, + CallingConvention, + }, + backend::Backend, + instr::{ + InstrOperand, + PseudoInstr, + }, + InstrId, + isa::PhysicalRegister, + MachInstr, + reg::VRegInfo, + Size, + TargetMachine, + VReg, +}; +use crate::codegen::machine::asm::Assembler; +use crate::codegen::machine::Instr; + +pub mod builder; +pub mod cfg; + +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct FunctionId(u32); + +entity_impl!(FunctionId, "fun"); + +#[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) return_ty_size: Size, + cfg: Option, +} + +impl Function { + pub fn new(name: String) -> Self { + Self { + name, + basic_blocks: IndexVec::default(), + vregs: PrimaryMap::new(), + cfg: None, + params: SmallVec::new(), + return_ty_size: Size::Byte, + } + } + + pub fn alloc_vreg(&mut self, size: Size) -> VReg { + self.vregs.push(VRegInfo { + size, + tied_to: None, + fixed: None, + }) + } + + pub fn get_vreg(&self, vreg: VReg) -> &VRegInfo { + &self.vregs[vreg] + } + pub fn tie_vreg(&mut self, vreg: VReg, to: VReg) { + 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()); + self.vregs[vreg].fixed = Some(to); + } + + pub fn create_bb(&mut self) -> BasicBlockId { + let id = self.basic_blocks.next_idx(); + self.basic_blocks.push(BasicBlock::new(id)) + } + + pub fn assemble(&self, base_addr: u64) -> Vec { + debug!("Assembling function {}", self.name); + let mut asm = TM::Assembler::new(base_addr); + for bb_id in self.cfg().ordered() { + let bb = &self.basic_blocks[bb_id]; + debug!("Assembling basic block {}", bb_id); + asm.begin_basic_block(bb_id); + for instr in &bb.instructions { + debug!("Assembling instruction {:?}", instr); + asm.assemble(instr.try_as_machine().unwrap_or_else(|| { + panic!("Pseudo instructions should have been expanded: {:?}", instr) + })); + } + } + debug!("Finishing assembly for function {}", self.name); + debug!("{}", asm.format()); + asm.finish() + } + + pub fn expand_pseudo_instructions(&mut self) + where + B: Backend, + { + debug!("Expanding pseudo instructions for function {}", self.name); + for bb in &mut self.basic_blocks { + if bb.instructions.is_empty() { + continue; + } + let mut instr_id = InstrId::new(0); + while instr_id <= bb.instructions.last_idx() { + let instr = &mut bb.instructions[instr_id]; + if let Instr::Pseudo(pseudo_instr) = instr { + let expanded: SmallVec<[_; 2]> = match pseudo_instr { + PseudoInstr::Copy(dest, src) => { + let instr = B::mov( + dest.try_as_physical().unwrap(), + src.try_as_physical().unwrap(), + ); + smallvec![instr] + } + PseudoInstr::Ret(value) => match value.as_mut() { + None => { + smallvec![B::ret()] + } + Some(value) => { + let return_slot = + TM::CallingConvention::return_slot(match value { + InstrOperand::Reg(reg) => { + reg.try_as_physical().unwrap().size() + } + InstrOperand::Imm(imm) => imm.size, + InstrOperand::Label(_) => unreachable!(), + }); + match return_slot { + Slot::Register(dest) => { + let instr = match value { + InstrOperand::Reg(reg) => { + let reg = reg.try_as_physical().unwrap(); + if reg == dest { + None + } else { + Some(B::mov(dest, reg)) + } + } + InstrOperand::Imm(imm) => Some(B::mov_imm(dest, *imm)), + InstrOperand::Label(_) => unreachable!(), + }; + if let Some(instr) = instr { + smallvec![instr, B::ret()] + } else { + smallvec![B::ret()] + } + } + Slot::Stack => unimplemented!(), + } + } + }, + PseudoInstr::Phi(_, _) => { + unreachable!("Phi should have been coalesced away by now") + } + PseudoInstr::Def(reg) => { + assert!( + reg.try_as_physical().is_some(), + "Def pseudo instruction should have a physical register" + ); + smallvec![] + } + }; + debug!( + "Expanded pseudo instruction {:?} to {:?}", + pseudo_instr, expanded + ); + bb.instructions.remove(instr_id); + let expanded_len = expanded.len(); + if expanded_len == 0 { + continue; + } + for (offset, instr) in expanded.into_iter().enumerate() { + bb.instructions + .insert(instr_id + offset, Instr::Machine(instr)); + } + instr_id += expanded_len - 1; + } + instr_id += 1; + } + } + } + + pub fn remove_fallthrough_jumps(&mut self) { + let ordered = self.cfg().ordered(); + for (i, bb_id) in ordered.iter().copied().enumerate() { + let bb = &mut self.basic_blocks[bb_id]; + let Some(next_bb_id) = ordered.get(i + 1).copied() else { + continue; + }; + if let Some(last_instr) = bb.instructions.last() { + if let Some(InstrOperand::Label(label)) = last_instr.operands().last() { + if *label == next_bb_id { + debug!("Removing fallthrough jump from {} to {}", bb_id, next_bb_id); + bb.instructions.pop(); + } + } + } + } + } + + pub fn build_cfg(&mut self) { + let cfg = Cfg::build(&self.basic_blocks); + self.cfg = Some(cfg); + } + + pub fn cfg(&self) -> &Cfg { + self.cfg.as_ref().expect("Cfg has not been built yey") + } +} + +impl Display for Function { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "function {}:", self.name)?; + let bbs: Box> = match &self.cfg { + Some(cfg) => Box::new(cfg.ordered().into_iter()), + None => Box::new(self.basic_blocks.indices().into_iter()), + }; + for bb_id in bbs { + let bb = &self.basic_blocks[bb_id]; + writeln!(f, "{bb_id}: ")?; + for instr in &bb.instructions { + write!(f, " ")?; + if let Some(out) = instr.writes() { + write!(f, "{out} = ")?; + } + write!(f, "{}", instr.name())?; + let operands = instr.operands(); + let operands_len = operands.len(); + for (i, operand) in operands.into_iter().enumerate() { + write!(f, " {operand}")?; + if i < operands_len - 1 { + write!(f, ",")?; + } + } + let reads_impl = instr.reads_implicitly(); + let reads_impl_len = reads_impl.len(); + if !reads_impl.is_empty() { + write!(f, " {{implicit reads: ")?; + for (i, reg) in reads_impl.into_iter().enumerate() { + write!(f, "{reg}")?; + if i < reads_impl_len - 1 { + write!(f, ", ")?; + } + } + write!(f, "}}")?; + } + writeln!(f)?; + } + } + Ok(()) + } +} diff --git a/ir/crates/back/src/codegen/machine/instr.rs b/ir/crates/back/src/codegen/machine/instr.rs new file mode 100644 index 0000000..2806352 --- /dev/null +++ b/ir/crates/back/src/codegen/machine/instr.rs @@ -0,0 +1,244 @@ +use std::fmt::{ + Display, + Formatter, +}; + +use smallvec::{ + smallvec, + SmallVec, +}; + +use crate::codegen::{ + machine::Register, + selection_dag::Immediate, +}; +use crate::codegen::machine::function::BasicBlockId; +use crate::codegen::machine::isa::MachInstr as MInstr; +use crate::codegen::machine::TargetMachine; + +index_vec::define_index_type! { + pub struct InstrId = u32; + + DISPLAY_FORMAT = "instr{}"; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Instr { + Pseudo(PseudoInstr), + Machine(TM::Instr), +} + +impl Instr { + pub fn name(&self) -> &'static str { + match self { + Instr::Pseudo(pseudo) => pseudo.name(), + Instr::Machine(machine) => machine.name(), + } + } + + pub fn reads(&self) -> SmallVec<[Register; 2]> { + match self { + Instr::Pseudo(pseudo) => pseudo.reads(), + Instr::Machine(machine) => machine.reads(), + } + } + + pub fn reads_implicitly(&self) -> SmallVec<[Register; 2]> { + let writes = self.writes(); + let reg_operands = self + .operands() + .iter() + .filter_map(|operand| { + if let InstrOperand::Reg(reg) = operand { + Some(*reg) + } else { + None + } + }) + .collect::>(); + let mut implicit_reads = SmallVec::new(); + for read in self.reads() { + if !reg_operands.contains(&read) && writes.map_or(true, |writes| writes != read) { + implicit_reads.push(read); + } + } + implicit_reads + } + + pub fn writes(&self) -> Option> { + match self { + Instr::Pseudo(pseudo) => pseudo.writes(), + Instr::Machine(machine) => machine.writes().map(|reg| reg.into()), + } + } + + pub fn operands(&self) -> SmallVec<[InstrOperand; 3]> { + match self { + Instr::Pseudo(pseudo) => pseudo.operands(), + Instr::Machine(machine) => machine.operands(), + } + } + + pub fn written_regs_mut(&mut self) -> SmallVec<[&mut Register; 1]> { + match self { + Instr::Pseudo(pseudo) => pseudo.written_regs_mut(), + Instr::Machine(machine) => machine.written_regs_mut(), + } + } + pub fn read_regs_mut(&mut self) -> SmallVec<[&mut Register; 2]> { + match self { + Instr::Pseudo(pseudo) => pseudo.read_regs_mut(), + Instr::Machine(machine) => machine.read_regs_mut(), + } + } + pub fn try_as_machine(&self) -> Option<&TM::Instr> { + match self { + Instr::Pseudo(_) => None, + Instr::Machine(machine) => Some(machine), + } + } + + pub fn is_phi(&self) -> bool { + match self { + Instr::Pseudo(PseudoInstr::Phi(_, _)) => true, + _ => false, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum InstrOperand { + Reg(Register), + Imm(Immediate), + Label(BasicBlockId), +} + +#[derive(Debug)] +pub enum InstrOperandMut<'a, TM: TargetMachine> { + Reg(&'a mut Register), + Imm(&'a mut Immediate), + Label(&'a mut BasicBlockId), +} + +impl Display for InstrOperand { + 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), + } + } +} + +impl<'op, TM: TargetMachine> From<&'op mut InstrOperand> for InstrOperandMut<'op, TM> { + fn from(value: &'op mut InstrOperand) -> Self { + match value { + InstrOperand::Reg(reg) => InstrOperandMut::Reg(reg), + InstrOperand::Imm(imm) => InstrOperandMut::Imm(imm), + InstrOperand::Label(label) => InstrOperandMut::Label(label), + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum PseudoInstr { + Copy(Register, Register), + Ret(Option>), + Phi(Register, Vec>), + Def(Register), +} + +impl PseudoInstr { + pub fn name(&self) -> &'static str { + match self { + Self::Copy(_, _) => "COPY", + Self::Ret(_) => "RET", + Self::Phi(_, _) => "PHI", + Self::Def(_) => "DEF", + } + } + + pub fn reads(&self) -> SmallVec<[Register; 2]> { + match self { + Self::Copy(_, to) => { + smallvec![*to,] + } + Self::Ret(value) => { + let mut reads = smallvec![]; + if let Some(InstrOperand::Reg(reg)) = value { + reads.push(*reg) + } + reads + } + Self::Phi(_, operands) => operands.clone().into(), + Self::Def(_) => { + smallvec![] + } + } + } + + pub fn operands(&self) -> SmallVec<[InstrOperand; 3]> { + match self { + Self::Copy(dest, src) => { + smallvec![InstrOperand::Reg(*dest), InstrOperand::Reg(*src),] + } + Self::Ret(value) => match value { + None => smallvec![], + Some(value) => smallvec![value.clone()], + }, + Self::Phi(dest, operands) => { + let mut ops = smallvec![InstrOperand::Reg(*dest),]; + ops.extend(operands.iter().map(|reg| InstrOperand::Reg(*reg))); + ops + } + Self::Def(reg) => { + smallvec![InstrOperand::Reg(*reg),] + } + } + } + + pub fn written_regs_mut(&mut self) -> SmallVec<[&mut Register; 1]> { + match self { + Self::Copy(dest, _) => { + smallvec![dest,] + } + Self::Ret(_) => { + smallvec![] + } + Self::Phi(dest, _) => { + smallvec![dest,] + } + Self::Def(reg) => { + smallvec![reg,] + } + } + } + + pub fn read_regs_mut(&mut self) -> SmallVec<[&mut Register; 2]> { + match self { + Self::Copy(_, src) => { + smallvec![src,] + } + Self::Ret(value) => { + let mut reads = smallvec![]; + if let Some(InstrOperand::Reg(reg)) = value { + reads.push(reg) + } + reads + } + Self::Phi(_, operands) => operands.iter_mut().collect(), + Self::Def(_) => { + smallvec![] + } + } + } + + pub fn writes(&self) -> Option> { + match self { + Self::Copy(dest, _) => Some(*dest), + Self::Ret(_) => None, + Self::Phi(dest, _) => Some(*dest), + Self::Def(dest) => Some(*dest), + } + } +} diff --git a/ir/crates/back/src/codegen/machine/isa.rs b/ir/crates/back/src/codegen/machine/isa.rs new file mode 100644 index 0000000..3a8ce12 --- /dev/null +++ b/ir/crates/back/src/codegen/machine/isa.rs @@ -0,0 +1,80 @@ +use std::{ + fmt::Debug, + hash::Hash, +}; + +use smallvec::SmallVec; + +use crate::codegen::machine::{ + instr::InstrOperand, + reg::Register, + Size, +}; +use crate::codegen::machine::TargetMachine; + +pub trait PhysicalRegister: Debug + Clone + Copy + PartialEq + Eq + Sized + Hash + 'static { + fn name(&self) -> &'static str; + + fn all() -> &'static [Self]; + + fn is_gp(&self) -> bool; + + fn size(&self) -> Size; + + fn into_unicorn_emu_reg(self) -> impl Into; + + /// Returns the sub registers of this register. + /// + /// E.g. on x86-64, the sub registers of RAX are EAX, AX, AH and AL. + fn subregs(&self) -> Option<&'static [Self]>; + + fn superregs(&self) -> Option<&'static [Self]>; + + fn regclass(&self) -> impl Iterator + where + Self: 'static, + { + self.subregs() + .into_iter() + .flatten() + .copied() + .chain(self.superregs().into_iter().flatten().copied()) + .chain(std::iter::once(*self)) + } + + fn has_subreg(&self, other: Self) -> bool + where + Self: 'static, + { + self.subregs() + .map_or(false, |subregs| subregs.contains(&other)) + } + + fn interferes_with(self, other: Self) -> bool + where + Self: 'static, + { + if self == other { + return true; + } + if self.has_subreg(other) || other.has_subreg(self) { + return true; + } + false + } +} + +pub trait MachInstr: Debug + PartialEq + Eq + Clone { + type TM: TargetMachine; + fn name(&self) -> &'static str; + + fn writes(&self) -> Option>; + + fn reads(&self) -> SmallVec<[Register; 2]>; + + fn operands(&self) -> SmallVec<[InstrOperand; 3]>; + + fn written_regs_mut(&mut self) -> SmallVec<[&mut Register; 1]>; + + fn read_regs_mut(&mut self) -> SmallVec<[&mut Register; 2]>; +} diff --git a/ir/crates/back/src/codegen/machine/mod.rs b/ir/crates/back/src/codegen/machine/mod.rs index 8d66eca..7ef81b1 100644 --- a/ir/crates/back/src/codegen/machine/mod.rs +++ b/ir/crates/back/src/codegen/machine/mod.rs @@ -1,598 +1,40 @@ -use std::collections::VecDeque; -use std::fmt::{Debug, Display, Formatter}; -use std::hash::Hash; -use std::ops::Range; - -use cranelift_entity::{entity_impl, EntityRef, PrimaryMap}; -use daggy::{NodeIndex, Walker}; -use daggy::petgraph::{algo, Directed, Direction}; -use daggy::petgraph::prelude::StableGraph; -use daggy::petgraph::visit::{Bfs, DfsPostOrder, IntoEdges}; -use iced_x86::Instruction; -use index_vec::{index_vec, IndexVec}; -use iter_tools::Itertools; -use rustc_hash::{FxHashMap, FxHashSet}; -use slotmap::{HopSlotMap, new_key_type}; -use smallvec::{SmallVec, smallvec}; -use tracing::debug; - -pub use abi::Abi; -use natrix_middle::instruction::CmpOp; -use natrix_middle::ty::Type; +use std::fmt::Debug; + +pub use cranelift_entity::EntityRef; + +pub use backend::Backend; +pub use function::{ + Function, + FunctionId, +}; +pub use instr::{ + Instr, + InstrId, +}; +pub use isa::{ + MachInstr, + PhysicalRegister, +}; pub use module::Module; +use natrix_middle::ty::Type; +pub use reg::{ + Register, + VReg, +}; -use crate::codegen::{machine, selection_dag}; -use crate::codegen::isa::{Architecture, Endianness}; -use crate::codegen::machine::abi::calling_convention::Slot; use crate::codegen::machine::abi::CallingConvention; use crate::codegen::machine::asm::Assembler; -use crate::codegen::register_allocator::{InstrNumbering, InstrUid, LivenessRepr, ProgPoint}; -use crate::codegen::selection_dag::{Immediate, MachineOp, Op, Operand, PseudoOp}; pub mod abi; pub mod asm; +pub mod isa; pub mod module; +pub mod reg; -pub trait TargetMachine { - type Abi: Abi; - - type Backend: Backend; - - fn backend() -> Self::Backend { - Self::Backend::new() - } - - fn endianness() -> Endianness; - - fn arch() -> Architecture; -} - -// #[derive(Debug, Clone)] -// pub struct SubReg((P, Range)); - -pub trait PhysicalRegister: Debug + Clone + Copy + PartialEq + Eq + Sized { - fn name(&self) -> &'static str; - - fn all() -> &'static [Self]; - - fn is_gp(&self) -> bool; - - fn size(&self) -> Size; - - fn into_unicorn_emu_reg(self) -> impl Into; - - /// Returns the sub registers of this register. - /// - /// E.g. on x86-64, the sub registers of RAX are EAX, AX, AH and AL. - fn subregs(&self) -> Option<&'static [Self]>; - - fn superregs(&self) -> Option<&'static [Self]>; - - fn regclass(&self) -> impl Iterator where Self: 'static { - self.subregs().into_iter().flatten().copied() - .chain( - self.superregs().into_iter().flatten().copied() - ) - .chain( - std::iter::once(*self) - ) - } - - fn has_subreg(&self, other: Self) -> bool where Self: 'static { - self.subregs().map_or(false, |subregs| subregs.contains(&other)) - } - - fn interferes_with(self, other: Self) -> bool where Self: 'static { - if self == other { - return true; - } - if self.has_subreg(other) || other.has_subreg(self) { - return true; - } - false - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct VReg(u32); - -entity_impl!(VReg, "v"); - -impl VReg { - pub fn size(self, func: &Function) -> Size where A: Abi { - func.get_vreg(self).size - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Register { - Virtual(VReg), - Physical(A::REG), -} - -impl From for Register { - fn from(vreg: VReg) -> Self { - Self::Virtual(vreg) - } -} - -impl Register { - 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> { - match self { - Register::Virtual(virt_reg) => Some(virt_reg), - Register::Physical(_) => None, - } - } - - pub fn try_as_physical(&self) -> Option { - match self { - Register::Virtual(_) => None, - Register::Physical(phys_reg) => Some(*phys_reg), - } - } - - pub fn size(&self, func: &Function) -> Size { - match self { - Register::Virtual(vreg) => vreg.size(func), - Register::Physical(phys_reg) => phys_reg.size(), - } - } -} - -impl std::marker::Copy for Register {} - -impl Display for Register { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - Register::Virtual(virt_reg) => write!(f, "{}", virt_reg), - Register::Physical(phys_reg) => write!(f, "${}", phys_reg.name()), - } - } -} - -index_vec::define_index_type! { - pub struct InstrId = u32; - - DISPLAY_FORMAT = "instr{}"; -} -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Instr { - Pseudo(PseudoInstr), - Machine(A::I), -} - -impl Instr { - pub fn name(&self) -> &'static str { - match self { - Instr::Pseudo(pseudo) => pseudo.name(), - Instr::Machine(machine) => machine.name(), - } - } - - pub fn reads(&self) -> SmallVec<[Register; 2]> { - match self { - Instr::Pseudo(pseudo) => pseudo.reads(), - Instr::Machine(machine) => machine.reads(), - } - } - - pub fn reads_implicitly(&self) -> SmallVec<[Register; 2]> { - let writes = self.writes(); - let reg_operands = self.operands().iter().filter_map(|operand| { - if let InstrOperand::Reg(reg) = operand { - Some(*reg) - } else { - None - } - }).collect::>(); - let mut implicit_reads = SmallVec::new(); - for read in self.reads() { - if !reg_operands.contains(&read) && writes.map_or(true, |writes| writes != read) { - implicit_reads.push(read); - } - } - implicit_reads - } - - pub fn writes(&self) -> Option> { - match self { - Instr::Pseudo(pseudo) => pseudo.writes(), - Instr::Machine(machine) => machine.writes().map(|reg| reg.into()), - } - } - - pub fn operands(&self) -> SmallVec<[InstrOperand; 3]> { - match self { - Instr::Pseudo(pseudo) => pseudo.operands(), - Instr::Machine(machine) => machine.operands(), - } - } - - pub fn written_regs_mut(&mut self) -> SmallVec<[&mut Register; 1]> { - match self { - Instr::Pseudo(pseudo) => pseudo.written_regs_mut(), - Instr::Machine(machine) => machine.written_regs_mut(), - } - } - pub fn read_regs_mut(&mut self) -> SmallVec<[&mut Register; 2]> { - match self { - Instr::Pseudo(pseudo) => pseudo.read_regs_mut(), - Instr::Machine(machine) => machine.read_regs_mut(), - } - } - pub fn try_as_machine(&self) -> Option<&A::I> { - match self { - Instr::Pseudo(_) => None, - Instr::Machine(machine) => Some(machine), - } - } - - pub fn is_phi(&self) -> bool { - match self { - Instr::Pseudo(PseudoInstr::Phi(_, _)) => true, - _ => false, - } - } -} - -pub trait MachineInstr: Debug + PartialEq + Eq + Clone { - type Abi: Abi; - - fn name(&self) -> &'static str; - - fn writes(&self) -> Option>; - - fn reads(&self) -> SmallVec<[Register; 2]>; - - fn operands(&self) -> SmallVec<[InstrOperand; 3]>; - - fn written_regs_mut(&mut self) -> SmallVec<[&mut Register; 1]>; - - fn read_regs_mut(&mut self) -> SmallVec<[&mut Register; 2]>; -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum InstrOperand { - Reg(Register), - Imm(Immediate), - Label(BasicBlockId), -} - -#[derive(Debug)] -pub enum InstrOperandMut<'a, A: Abi> { - Reg(&'a mut Register), - Imm(&'a mut Immediate), - Label(&'a mut BasicBlockId), -} - -impl Display for InstrOperand { - 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), - } - } -} - -impl<'op, A: Abi> From<&'op mut InstrOperand> for InstrOperandMut<'op, A> { - fn from(value: &'op mut InstrOperand) -> Self { - match value { - InstrOperand::Reg(reg) => InstrOperandMut::Reg(reg), - InstrOperand::Imm(imm) => InstrOperandMut::Imm(imm), - InstrOperand::Label(label) => InstrOperandMut::Label(label), - } - } -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum PseudoInstr { - Copy(Register, Register), - Ret(Option>), - Phi(Register, Vec>), - Def(Register), -} - -impl PseudoInstr { - pub fn name(&self) -> &'static str { - match self { - Self::Copy(_, _) => "COPY", - Self::Ret(_) => "RET", - Self::Phi(_, _) => "PHI", - Self::Def(_) => "DEF", - } - } - - pub fn reads(&self) -> SmallVec<[Register; 2]> { - match self { - Self::Copy(_, to) => { - smallvec![ - *to, - ] - } - Self::Ret(value) => { - let mut reads = smallvec![]; - if let Some(InstrOperand::Reg(reg)) = value { - reads.push(*reg) - } - reads - } - Self::Phi(_, operands) => { - operands.clone().into() - } - Self::Def(_) => { - smallvec![] - } - } - } - - pub fn operands(&self) -> SmallVec<[InstrOperand; 3]> { - match self { - Self::Copy(dest, src) => { - smallvec![ - InstrOperand::Reg(*dest), - InstrOperand::Reg(*src), - ] - } - Self::Ret(value) => { - match value { - None => smallvec![], - Some(value) => smallvec![value.clone()], - } - } - Self::Phi(dest, operands) => { - let mut ops = smallvec![ - InstrOperand::Reg(*dest), - ]; - ops.extend(operands.iter().map(|reg| InstrOperand::Reg(*reg))); - ops - } - Self::Def(reg) => { - smallvec![ - InstrOperand::Reg(*reg), - ] - } - } - } - - pub fn written_regs_mut(&mut self) -> SmallVec<[&mut Register; 1]> { - match self { - Self::Copy(dest, _) => { - smallvec![ - dest, - ] - } - Self::Ret(_) => { - smallvec![] - } - Self::Phi(dest, _) => { - smallvec![ - dest, - ] - } - Self::Def(reg) => { - smallvec![ - reg, - ] - } - } - } - - pub fn read_regs_mut(&mut self) -> SmallVec<[&mut Register; 2]> { - match self { - Self::Copy(_, src) => { - smallvec![ - src, - ] - } - Self::Ret(value) => { - let mut reads = smallvec![]; - if let Some(InstrOperand::Reg(reg)) = value { - reads.push(reg) - } - reads - } - Self::Phi(_, operands) => { - operands.iter_mut().collect() - } - Self::Def(_) => { - smallvec![] - } - } - } - - pub fn writes(&self) -> Option> { - match self { - Self::Copy(dest, _) => Some(*dest), - Self::Ret(_) => None, - Self::Phi(dest, _) => Some(*dest), - Self::Def(dest) => Some(*dest), - } - } -} - -pub trait Backend { - type ABI: Abi; - - type P: Pattern; - - fn patterns() -> &'static [Self::P]; - - fn mov(dest: ::REG, src: ::REG) -> ::I; - - fn mov_imm(dest: ::REG, imm: Immediate) -> ::I; - - fn ret() -> ::I; - - fn new() -> Self; -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum PatternIn { - Mov(PatternInOutput, PatternInOperand), - Sub(PatternInOutput, PatternInOperand, PatternInOperand), - Add(PatternInOutput, PatternInOperand, PatternInOperand), - Cmp(PatternInOutput, CmpOp, PatternInOperand, PatternInOperand), - Br, - CondBr(PatternInOperand), -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum PatternInOutput { - Reg(Size) -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum PatternInOperand { - Reg(Size), - Imm(Size), -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct MatchedMovPattern { - pub dest: MatchedPatternOutput, - pub src: MatchedPatternOperand, -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct MatchedSubPattern { - pub dest: MatchedPatternOutput, - pub lhs: MatchedPatternOperand, - pub rhs: MatchedPatternOperand, -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct MatchedAddPattern { - pub dest: MatchedPatternOutput, - pub lhs: MatchedPatternOperand, - pub rhs: MatchedPatternOperand, -} - - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct MatchedBrPattern { - pub target: BasicBlockId, -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct MatchedCmpPattern { - pub dest: MatchedPatternOutput, - pub cmp_op: CmpOp, - pub lhs: MatchedPatternOperand, - pub rhs: MatchedPatternOperand, -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct MatchedCondBrPattern { - pub cond: MatchedPatternOperand, - pub true_target: BasicBlockId, - pub false_target: BasicBlockId, -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum MatchedPattern { - Mov(MatchedMovPattern), - Sub(MatchedSubPattern), - Add(MatchedAddPattern), - Cmp(MatchedCmpPattern), - CondBr(MatchedCondBrPattern), - Br(MatchedBrPattern), -} - -impl MatchedPattern { - pub fn try_as_mov(&self) -> Option<&MatchedMovPattern> { - match self { - MatchedPattern::Mov(mov) => Some(mov), - _ => None, - } - } - - pub fn try_as_sub(&self) -> Option<&MatchedSubPattern> { - match self { - MatchedPattern::Sub(sub) => Some(sub), - _ => None, - } - } - - pub fn try_as_add(&self) -> Option<&MatchedAddPattern> { - match self { - MatchedPattern::Add(add) => Some(add), - _ => None, - } - } - - pub fn try_as_br(&self) -> Option<&MatchedBrPattern> { - match self { - MatchedPattern::Br(br) => Some(br), - _ => None, - } - } - - pub fn try_as_cmp(&self) -> Option<&MatchedCmpPattern> { - match self { - MatchedPattern::Cmp(cmp) => Some(cmp), - _ => None, - } - } - - pub fn try_as_cond_br(&self) -> Option<&MatchedCondBrPattern> { - match self { - MatchedPattern::CondBr(cond_br) => Some(cond_br), - _ => None, - } - } -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum MatchedPatternOutput { - Reg(Register) -} - -impl MatchedPatternOutput { - pub fn try_as_reg(&self) -> Option<&Register> { - match self { - MatchedPatternOutput::Reg(reg) => Some(reg), - } - } -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum MatchedPatternOperand { - Reg(Register), - Imm(Immediate), -} - -impl MatchedPatternOperand { - pub fn try_as_reg(&self) -> Option<&Register> { - match self { - MatchedPatternOperand::Reg(reg) => Some(reg), - _ => None, - } - } - - pub fn try_as_imm(&self) -> Option<&Immediate> { - match self { - MatchedPatternOperand::Imm(imm) => Some(imm), - _ => None, - } - } -} - -pub trait Pattern: Sized + Debug + Clone + PartialEq + Eq + 'static { - type ABI: Abi; - - fn in_(&self) -> PatternIn; - - fn into_instr(self, function: &mut Function, matched: MatchedPattern) -> SmallVec<[Instr<::ABI>; 2]>; -} +pub mod function; +pub mod instr; +pub mod backend; #[derive(Debug, Copy, Clone, PartialEq, Eq, Display)] pub enum Size { @@ -644,651 +86,27 @@ impl From<&Type> for Size { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct VRegInfo { - pub size: Size, - /// If set, the vreg will be placed in the same location as tied_to - pub tied_to: Option, - /// If set, the vreg is ensured to be placed in the same location as fixed - pub fixed: Option, -} - -#[derive(Copy, Clone, Eq, PartialEq)] -pub struct FunctionId(u32); - -entity_impl!(FunctionId, "fun"); - -#[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) return_ty_size: Size, - cfg: Option, -} - -impl Function { - pub fn new( - name: String, - ) -> Self { - Self { - name, - basic_blocks: IndexVec::default(), - vregs: PrimaryMap::new(), - cfg: None, - params: SmallVec::new(), - return_ty_size: Size::Byte, - } - } - - pub fn alloc_vreg(&mut self, size: Size) -> VReg { - self.vregs.push(VRegInfo { size, tied_to: None, fixed: None }) - } - - pub fn get_vreg(&self, vreg: VReg) -> &VRegInfo { - &self.vregs[vreg] - } - pub fn tie_vreg(&mut self, vreg: VReg, to: VReg) { - debug!("Tying {vreg} to {to}"); - self.vregs[vreg].tied_to = Some(to); - } - - pub fn fix_vreg(&mut self, vreg: VReg, to: A::REG) { - debug!("Fixing {vreg} to {}",to.name()); - self.vregs[vreg].fixed = Some(to); - } - - pub fn create_bb(&mut self) -> BasicBlockId { - let id = self.basic_blocks.next_idx(); - self.basic_blocks.push(BasicBlock::new( - id - )) - } - - pub fn assemble(&self, base_addr: u64) -> Vec { - debug!("Assembling function {}", self.name); - let mut asm = A::get_assembler(base_addr); - for bb_id in self.cfg().ordered() { - let bb = &self.basic_blocks[bb_id]; - debug!("Assembling basic block {}", bb_id); - asm.begin_basic_block(bb_id); - for instr in &bb.instructions { - debug!("Assembling instruction {:?}", instr); - asm.assemble(instr.try_as_machine().unwrap_or_else(|| panic!("Pseudo instructions should have been expanded: {:?}", instr))); - } - } - debug!("Finishing assembly for function {}", self.name); - debug!("{}", asm.format()); - asm.finish() - } - - - pub fn expand_pseudo_instructions(&mut self) where B: Backend { - debug!("Expanding pseudo instructions for function {}", self.name); - for bb in &mut self.basic_blocks { - if bb.instructions.is_empty() { continue; } - let mut instr_id = InstrId::new(0); - while instr_id <= bb.instructions.last_idx() { - let instr = &mut bb.instructions[instr_id]; - if let Instr::Pseudo(pseudo_instr) = instr { - let expanded: SmallVec<[_; 2]> = match pseudo_instr { - PseudoInstr::Copy(dest, src) => { - let instr = B::mov(dest.try_as_physical().unwrap(), src.try_as_physical().unwrap()); - smallvec![ - instr - ] - } - PseudoInstr::Ret(value) => { - match value.as_mut() { - None => { - smallvec![ - B::ret() - ] - } - Some(value) => { - let return_slot = ::CallingConvention::return_slot( - match value { - InstrOperand::Reg(reg) => { - reg.try_as_physical().unwrap().size() - } - InstrOperand::Imm(imm) => { - imm.size - } - InstrOperand::Label(_) => unreachable!() - } - ); - match return_slot { - Slot::Register(dest) => { - let instr = match value { - InstrOperand::Reg(reg) => { - let reg = reg.try_as_physical().unwrap(); - if reg == dest { - None - } else { - Some(B::mov(dest, reg)) - } - } - InstrOperand::Imm(imm) => { - Some(B::mov_imm(dest, *imm)) - } - InstrOperand::Label(_) => unreachable!() - }; - if let Some(instr) = instr { - smallvec![ - instr, - B::ret() - ] - } else { - smallvec![ - B::ret() - ] - } - } - Slot::Stack => unimplemented!() - } - } - } - } - PseudoInstr::Phi(_, _) => { - unreachable!("Phi should have been coalesced away by now") - } - PseudoInstr::Def(reg) => { - assert!(reg.try_as_physical().is_some(), "Def pseudo instruction should have a physical register"); - smallvec![] - } - }; - debug!("Expanded pseudo instruction {:?} to {:?}", pseudo_instr, expanded); - bb.instructions.remove(instr_id); - let expanded_len = expanded.len(); - if (expanded_len == 0) { - continue; - } - for (offset, instr) in expanded.into_iter().enumerate() { - bb.instructions.insert(instr_id + offset, Instr::Machine(instr)); - } - instr_id += expanded_len - 1; - } - instr_id += 1; - } - } - } - - pub fn remove_fallthrough_jumps(&mut self) { - let ordered = self.cfg().ordered(); - for (i, bb_id) in ordered.iter().copied().enumerate() { - let bb = &mut self.basic_blocks[bb_id]; - let Some(next_bb_id) = ordered.get(i + 1).copied() else { - continue; - }; - if let Some(last_instr) = bb.instructions.last() { - if let Some(InstrOperand::Label(label)) = last_instr.operands().last() { - if *label == next_bb_id { - debug!("Removing fallthrough jump from {} to {}", bb_id, next_bb_id); - bb.instructions.pop(); - } - } - } - } - } - - pub fn build_cfg(&mut self) { - let cfg = Cfg::build(&self.basic_blocks); - self.cfg = Some(cfg); - } - - pub fn cfg(&self) -> &Cfg { - self.cfg.as_ref().expect("Cfg has not been built yey") - } -} - -impl Display for Function { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f, "function {}:", self.name)?; - let bbs: Box> = match &self.cfg { - Some(cfg) => Box::new(cfg.ordered().into_iter()), - None => Box::new(self.basic_blocks.indices().into_iter()), - }; - for bb_id in bbs { - let bb = &self.basic_blocks[bb_id]; - writeln!(f, "{bb_id}: ")?; - for instr in &bb.instructions { - write!(f, " ")?; - if let Some(out) = instr.writes() { - write!(f, "{out} = ")?; - } - write!(f, "{}", instr.name())?; - let operands = instr.operands(); - let operands_len = operands.len(); - for (i, operand) in operands.into_iter().enumerate() { - write!(f, " {operand}")?; - if i < operands_len - 1 { - write!(f, ",")?; - } - } - let reads_impl = instr.reads_implicitly(); - let reads_impl_len = reads_impl.len(); - if !reads_impl.is_empty() { - write!(f, " {{implicit reads: ")?; - for (i, reg) in reads_impl.into_iter().enumerate() { - write!(f, "{reg}")?; - if i < reads_impl_len - 1 { - write!(f, ", ")?; - } - } - write!(f, "}}")?; - } - writeln!(f)?; - } - } - Ok(()) - } +pub enum Endianness { + Little, + Big, } -#[derive(Debug, Clone)] -pub struct Cfg { - entry_block: BasicBlockId, - graph: StableGraph<(), (), Directed>, - node_to_block_map: FxHashMap, - block_to_node_map: FxHashMap, +pub enum Architecture { + X86_64, } -impl Cfg { - pub fn build(bbs: &IndexVec>) -> Self { - let mut cfg = Self::new( - BasicBlockId::new(0), - ); - for bb_id in bbs.indices() { - let node = cfg.graph.add_node(()); - cfg.node_to_block_map.insert(node, bb_id); - cfg.block_to_node_map.insert(bb_id, node); - } - for (bb_id, bb) in bbs.iter_enumerated() { - for instr in &bb.instructions { - let ins = instr.operands(); - for operand in ins { - if let InstrOperand::Label(successor_id) = operand { - cfg.graph.add_edge( - *cfg.block_to_node_map.get(&bb_id).expect("Block not found in block_to_node_map"), - *cfg.block_to_node_map.get(&successor_id).expect("Block not found in block_to_node_map"), - (), - ); - } - } - } - } - cfg - } +pub trait TargetMachine: Debug + Default + Copy + Clone + PartialEq + Eq { + type Reg: PhysicalRegister; - pub fn new(entry_block: BasicBlockId) -> Self { - Self { - entry_block, - graph: StableGraph::new(), - node_to_block_map: FxHashMap::default(), - block_to_node_map: FxHashMap::default(), - } - } + type Instr: MachInstr; - /// Traverses the cfg using a post order depth first traversal - pub fn dfs_postorder(&self) -> impl Iterator + '_ { - DfsPostOrder::new( - &self.graph, - self.entry_node(), - ).iter(&self.graph).map( - |node| - self.node_to_block_map[&node] - ) - } + type CallingConvention: CallingConvention; - pub fn bfs(&self) -> impl Iterator + '_ { - Bfs::new( - &self.graph, - self.entry_node(), - ).iter(&self.graph).map( - |node| - self.node_to_block_map[&node] - ) - } - - pub fn predecessors(&self, bb: BasicBlockId) -> impl Iterator + '_ { - self.graph.neighbors_directed( - self.block_to_node_map[&bb], - Direction::Incoming, - ).map( - |node| self.node_to_block_map[&node] - ) - } - - pub fn successors(&self, bb: BasicBlockId) -> impl Iterator + '_ { - self.graph.neighbors( - self.block_to_node_map[&bb], - ).map( - |node| self.node_to_block_map[&node] - ) - } + type Backend: Backend; + type Assembler: Assembler; - fn entry_node(&self) -> NodeIndex { - self.node_to_block_map.iter().find_map( - |(node, bb)| { - if *bb == self.entry_block { - return Some(*node); - } - None - } - ).expect("Did not find matching entry in node_to_block_map for entry block") - } - - /// Returns an ordering of basic block with the following guarantees: - /// 1. All predecessors of a basic block are visited before the basic block itself (except if the bb is a predecessor of itself) - pub fn ordered(&self) -> Vec { - // let mut visited = FxHashSet::default(); - let mut order = self.bfs().collect_vec(); - // let mut stack = VecDeque::new(); - // stack.push_back(self.entry_block); - // while let Some(bb) = stack.pop_front() { - // debug!("Visiting basic block {}:{:?}", bb,visited); - // if visited.contains(&bb) { - // continue; - // } - // let mut all_preds_visited = true; - // for pred in self.predecessors(bb) { - // if !(pred == bb || visited.contains(&pred)) { - // debug!("Pred {} of {} has not been visited yet", pred, bb); - // stack.push_back(pred); - // all_preds_visited = false; - // } - // } - // if all_preds_visited { - // visited.insert(bb); - // order.push(bb); - // for succ in self.successors(bb) { - // stack.push_back(succ); - // } - // } else { - // stack.push_back(bb); - // } - // } - order - } -} - -index_vec::define_index_type! { - pub struct BasicBlockId = u32; - - DISPLAY_FORMAT = "bb{}"; -} -#[derive(Debug, Clone)] -pub struct BasicBlock { - pub id: BasicBlockId, - pub instructions: IndexVec>, -} - -impl BasicBlock { - pub fn new(id: BasicBlockId) -> Self { - Self { - id, - instructions: IndexVec::default(), - } - } - - pub fn entry_pp(&self, liveness_repr: &LivenessRepr) -> ProgPoint { - let instr_nr = liveness_repr.instr_numbering.get_instr_nr( - InstrUid { - bb: self.id, - instr: 0.into(), - } - ).unwrap(); - ProgPoint::Read(instr_nr) - } - - pub fn exit_pp(&self, instr_numbering: &InstrNumbering) -> ProgPoint { - let instr_nr = instr_numbering.get_instr_nr( - InstrUid { - bb: self.id, - instr: self.instructions.len_idx() - 1, - } - ).unwrap(); - ProgPoint::Write(instr_nr) - } -} - -#[derive(Debug)] -pub struct FunctionBuilder { - function: Function, - backend: TM::Backend, - bb_mapping: FxHashMap, -} - -impl FunctionBuilder { - pub fn new() -> Self { - Self { - function: Function::new(Default::default()), - backend: TM::backend(), - bb_mapping: FxHashMap::default(), - } - } - - pub fn build(mut self, function: &mut natrix_middle::Function) -> Function { - self.function.name = function.name.clone(); - self.function.return_ty_size = Size::from_ty( - &function.ret_ty - ); - debug!("Building machine function for function {}", function.name); - let mut sel_dag_builder = selection_dag::Builder::new(&mut self.function); - let mut sel_dag = sel_dag_builder.build(function); - for bb in function.cfg.basic_block_ids_ordered() { - self.create_bb(bb); - } - for mbb_id in self.function.basic_blocks.indices() { - let bb = *self.bb_mapping.iter().find(|(_, mbb)| **mbb == mbb_id).unwrap().0; - debug!("Building machine basic block for basic block {}", bb); - let dag = sel_dag.get_bb_dag(bb); - dag.save_graphviz("out").unwrap(); - let mut node_list = Vec::with_capacity(dag.node_count()); - debug!("Determining traversal order for basic block {}", bb); - let bfs = Bfs::new(dag.graph(), dag.term_node()); - for n in bfs.iter(dag.graph()) { - node_list.push(n); - } - debug!("Traversal order: {:?}", node_list); - let mut instructions = Vec::new(); - while let Some(node_id) = node_list.pop() { - let op = &dag[node_id]; - match op { - Op::Pseudo(op) => { - debug!("Found pseudo op {:?}", op); - match op { - PseudoOp::Copy(dest, src) => { - instructions.push( - Instr::Pseudo( - PseudoInstr::Copy( - dest.clone(), - src.clone(), - ) - ) - ); - } - PseudoOp::Ret(operand) => { - instructions.push( - Instr::Pseudo( - PseudoInstr::Ret( - operand.as_ref().cloned().map(|operand| match operand { - Operand::Reg(reg) => InstrOperand::Reg( - reg - ), - Operand::Imm(imm) => InstrOperand::Imm( - imm - ), - }) - ) - ) - ); - } - PseudoOp::Phi(dest, operands) => { - instructions.push( - Instr::Pseudo( - PseudoInstr::Phi( - dest.clone(), - operands.clone(), - ) - ) - ); - } - PseudoOp::Def(reg) => { - instructions.push( - Instr::Pseudo( - PseudoInstr::Def( - Register::Virtual( - *reg - ), - ) - ) - ); - } - } - } - Op::Machine(op) => { - let dag_node_pattern = match op { - MachineOp::Mov(dest, src) => { - PatternIn::Mov( - PatternInOutput::Reg(dest.size(&self.function)), - self.operand_to_pattern(src), - ) - } - MachineOp::Sub(dest, lhs, rhs) => { - PatternIn::Sub( - PatternInOutput::Reg(dest.size(&self.function)), - self.operand_to_pattern(lhs), - self.operand_to_pattern(rhs), - ) - } - MachineOp::Add(dest, lhs, rhs) => { - PatternIn::Add( - PatternInOutput::Reg(dest.size(&self.function)), - self.operand_to_pattern(lhs), - self.operand_to_pattern(rhs), - ) - } - MachineOp::Br(bb_id) => { - PatternIn::Br - } - MachineOp::Cmp(dest, cmp_op, lhs, rhs) => { - PatternIn::Cmp( - PatternInOutput::Reg(dest.size(&self.function)), - *cmp_op, - self.operand_to_pattern(lhs), - self.operand_to_pattern(rhs), - ) - } - MachineOp::CondBr(cond, true_target, false_target) => { - PatternIn::CondBr( - self.operand_to_pattern(cond) - ) - } - }; - let mut matching_pattern = None; - debug!("Matching patterns for node {:?}", op); - - for pattern in TM::Backend::patterns() { - let pattern_in = pattern.in_(); - debug!("Checking {:?}", pattern_in); - debug!("Matching with {:?}", dag_node_pattern); - if pattern_in != dag_node_pattern { - debug!("Pattern does not match"); - continue; - } - debug!("Pattern matches"); - matching_pattern = Some(pattern.clone()); - break; - } - match matching_pattern { - None => { - panic!("No pattern matched for node {:?}", op); - } - Some(pattern) => { - let matched = match op { - MachineOp::Mov(dest, src) => MatchedPattern::Mov( - MatchedMovPattern { - dest: MatchedPatternOutput::Reg(*dest), - src: self.operand_to_matched_pattern_operand(src), - } - ), - MachineOp::Sub(dest, lhs, rhs) => MatchedPattern::Sub( - MatchedSubPattern { - dest: MatchedPatternOutput::Reg(*dest), - lhs: self.operand_to_matched_pattern_operand(lhs), - rhs: self.operand_to_matched_pattern_operand(rhs), - } - ), - MachineOp::Add(dest, lhs, rhs) => MatchedPattern::Add( - MatchedAddPattern { - dest: MatchedPatternOutput::Reg(*dest), - lhs: self.operand_to_matched_pattern_operand(lhs), - rhs: self.operand_to_matched_pattern_operand(rhs), - } - ), - MachineOp::Br(target) => MatchedPattern::Br( - MatchedBrPattern { - target: self.bb_mapping[target], - } - ), - MachineOp::Cmp(dest, cmp_op, lhs, rhs) => MatchedPattern::Cmp( - MatchedCmpPattern { - dest: MatchedPatternOutput::Reg(*dest), - cmp_op: *cmp_op, - lhs: self.operand_to_matched_pattern_operand(lhs), - rhs: self.operand_to_matched_pattern_operand(rhs), - } - ), - MachineOp::CondBr(cond, true_target, false_target) => MatchedPattern::CondBr( - MatchedCondBrPattern { - cond: self.operand_to_matched_pattern_operand(cond), - true_target: self.bb_mapping[true_target], - false_target: self.bb_mapping[false_target], - } - ), - }; - let generated_instructions = pattern.into_instr( - &mut self.function, - matched, - ); - debug!("Generated instructions {:?}", generated_instructions); - instructions.extend(generated_instructions.into_iter()); - } - } - } - } - } - for instr in instructions { - self.function.basic_blocks[mbb_id].instructions.push(instr); - } - } - debug!("Finished building machine function for function {}", function.name); - debug!("{}", self.function); - self.function - } - - fn create_bb(&mut self, bb: natrix_middle::cfg::BasicBlockId) -> BasicBlockId { - let mbb = self.function.create_bb(); - self.bb_mapping.insert(bb, mbb); - mbb - } - - fn operand_to_matched_pattern_operand(&self, src: &Operand) -> MatchedPatternOperand { - match src { - Operand::Reg(reg) => MatchedPatternOperand::Reg(reg.clone()), - Operand::Imm(imm) => MatchedPatternOperand::Imm(imm.clone()), - } - } + fn endianness() -> Endianness; - fn operand_to_pattern(&self, src: &Operand) -> PatternInOperand { - match src { - Operand::Reg(reg) => PatternInOperand::Reg(reg.size(&self.function)), - Operand::Imm(imm) => PatternInOperand::Imm(imm.size), - } - } + fn arch() -> Architecture; } - - - diff --git a/ir/crates/back/src/codegen/machine/module/asm.rs b/ir/crates/back/src/codegen/machine/module/asm.rs new file mode 100644 index 0000000..73a8aa0 --- /dev/null +++ b/ir/crates/back/src/codegen/machine/module/asm.rs @@ -0,0 +1,58 @@ +use std::ops::Range; + +use cranelift_entity::SecondaryMap; + +use crate::codegen::machine::{FunctionId, Module, TargetMachine}; + +#[derive(Debug, Clone, Eq, PartialEq, Default)] +pub struct FunctionSymbolTableEntry { + pub offset: u64, + pub size: u64, +} + +pub type FunctionSymbolTable = SecondaryMap; + +pub struct AsmModule<'module, TM: TargetMachine> { + base_addr: u64, + code: Vec, + function_symbol_table: FunctionSymbolTable, + module: &'module Module, +} + +impl<'module, TM: TargetMachine> AsmModule<'module, TM> { + pub fn new( + module: &'module Module, + base_addr: u64, + code: Vec, + function_symbol_table: FunctionSymbolTable, + ) -> Self { + Self { + base_addr, + code, + function_symbol_table, + module, + } + } + + pub fn base_addr(&self) -> u64 { + self.base_addr + } + + pub fn code(&self) -> &[u8] { + &self.code + } + + pub fn code_range_of(&self, function_id: FunctionId) -> Option> { + self.function_symbol_table.get(function_id).cloned().map( + |FunctionSymbolTableEntry { offset, size }| { + let start = self.base_addr + offset; + let end = start + size; + start..end + }, + ) + } + + pub fn machine_module(&self) -> &Module { + self.module + } +} diff --git a/ir/crates/back/src/codegen/machine/module/builder.rs b/ir/crates/back/src/codegen/machine/module/builder.rs index 38a4aed..a5247bc 100644 --- a/ir/crates/back/src/codegen/machine/module/builder.rs +++ b/ir/crates/back/src/codegen/machine/module/builder.rs @@ -1,4 +1,9 @@ -use crate::codegen::machine::{Backend, FunctionBuilder, Module, TargetMachine}; +use crate::codegen::machine::{ + backend::Backend, + function::builder::FunctionBuilder, + Module, +}; +use crate::codegen::machine::TargetMachine; #[derive(Debug)] pub struct Builder<'module, TM: TargetMachine> { @@ -17,9 +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.push(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 12988e8..ef0d09c 100644 --- a/ir/crates/back/src/codegen/machine/module/mod.rs +++ b/ir/crates/back/src/codegen/machine/module/mod.rs @@ -1,22 +1,35 @@ -use std::ops::Range; - -use cranelift_entity::{PrimaryMap, SecondaryMap}; -use tracing::{debug, info}; - +use cranelift_entity::PrimaryMap; +use tracing::{ + debug, + info, +}; + +use asm::{FunctionSymbolTable, FunctionSymbolTableEntry}; +pub use asm::AsmModule; pub use builder::Builder; -use crate::codegen::machine::{Abi, Backend, Function, FunctionId, TargetMachine}; -use crate::codegen::register_allocator; -use crate::codegen::register_allocator::RegAllocAlgorithm; +use crate::codegen::{ + machine::{ + backend::Backend, + function::{ + Function, + FunctionId, + }, + }, + register_allocator, + register_allocator::RegAllocAlgorithm, +}; +use crate::codegen::machine::TargetMachine; mod builder; +pub mod asm; #[derive(Debug, Clone)] pub struct Module { - pub(crate) functions: PrimaryMap>, + pub(crate) functions: PrimaryMap>, } -impl Default for Module { +impl Default for Module { fn default() -> Self { Self { functions: PrimaryMap::new(), @@ -25,11 +38,11 @@ impl Default for Module { } impl Module { - pub fn add_function(&mut self, function: Function) -> FunctionId { + pub fn add_function(&mut self, function: Function) -> FunctionId { self.functions.push(function) } - pub fn functions(&self) -> impl ExactSizeIterator)> { + pub fn functions(&self) -> impl ExactSizeIterator)> { self.functions.iter() } @@ -47,12 +60,7 @@ impl Module { addr += assembled_len; result.extend(assembled); } - AsmModule::new( - self, - base_addr, - result, - fst - ) + AsmModule::new(self, base_addr, result, fst) } pub fn run_register_coalescer(&mut self) { @@ -66,7 +74,10 @@ impl Module { function.build_cfg(); debug!("Running register allocator for function {:?}", function_id); let liveness_repr = function.liveness_repr(); - let allocator = register_allocator::RegisterAllocator::<_, register_allocator::linear_scan::RegAlloc>::new(function, &liveness_repr); + let allocator = register_allocator::RegisterAllocator::< + _, + register_allocator::linear_scan::RegAlloc, + >::new(function, &liveness_repr); allocator.run(); debug!("Register allocator finished for function {:?}", function_id); debug!("{}", function); @@ -86,52 +97,6 @@ impl Module { } } -#[derive(Debug, Clone, Eq, PartialEq, Default)] -pub struct FunctionSymbolTableEntry { - pub offset: u64, - pub size: u64, -} - -pub type FunctionSymbolTable = SecondaryMap; - -pub struct AsmModule<'module, TM: TargetMachine> { - base_addr: u64, - code: Vec, - function_symbol_table: FunctionSymbolTable, - module: &'module Module, -} - - -impl<'module, TM: TargetMachine> AsmModule<'module, TM> { - pub fn new(module: &'module Module, base_addr: u64, code: Vec, function_symbol_table: FunctionSymbolTable) -> Self { - Self { base_addr, code, function_symbol_table, module } - } - - pub fn base_addr(&self) -> u64 { - self.base_addr - } - - pub fn code(&self) -> &[u8] { - &self.code - } - - pub fn code_range_of(&self, function_id: FunctionId) -> Option> { - self.function_symbol_table.get(function_id).cloned().map( - |FunctionSymbolTableEntry { - offset, size - }| { - let start = self.base_addr + offset; - let end = start + size; - start..end - } - ) - } - - pub fn machine_module(&self) -> &Module { - self.module - } -} - impl std::fmt::Display for Module { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for (id, function) in self.functions() { @@ -140,3 +105,7 @@ impl std::fmt::Display for Module { Ok(()) } } + +// pub trait Pass { +// fn run(&mut self, function: &mut Function); +// } diff --git a/ir/crates/back/src/codegen/machine/reg.rs b/ir/crates/back/src/codegen/machine/reg.rs new file mode 100644 index 0000000..242e510 --- /dev/null +++ b/ir/crates/back/src/codegen/machine/reg.rs @@ -0,0 +1,81 @@ +use std::fmt::{ + Display, + Formatter, +}; + +use cranelift_entity::entity_impl; + +use crate::codegen::machine::{function::Function, isa::PhysicalRegister, Size, TargetMachine}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum Register { + Virtual(VReg), + Physical(TM::Reg), +} + +impl From for Register { + fn from(vreg: VReg) -> Self { + Self::Virtual(vreg) + } +} + +impl Register { + 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> { + match self { + Register::Virtual(virt_reg) => Some(virt_reg), + Register::Physical(_) => None, + } + } + + pub fn try_as_physical(&self) -> Option { + match self { + Register::Virtual(_) => None, + Register::Physical(phys_reg) => Some(*phys_reg), + } + } + + pub fn size(&self, func: &Function) -> Size { + match self { + Register::Virtual(vreg) => vreg.size(func), + Register::Physical(phys_reg) => phys_reg.size(), + } + } +} + +// impl Copy for Register {} + +impl Display for Register { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Register::Virtual(virt_reg) => write!(f, "{}", virt_reg), + Register::Physical(phys_reg) => write!(f, "${}", phys_reg.name()), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct VReg(u32); + +impl VReg { + pub fn size(self, func: &Function) -> Size { + func.get_vreg(self).size + } +} + +entity_impl!(VReg, "v"); + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct VRegInfo { + pub size: Size, + /// If set, the vreg will be placed in the same location as tied_to + pub tied_to: Option, + /// If set, the vreg is ensured to be placed in the same location as fixed + pub fixed: Option, +} diff --git a/ir/crates/back/src/codegen/mod.rs b/ir/crates/back/src/codegen/mod.rs index 826272b..029fcf8 100644 --- a/ir/crates/back/src/codegen/mod.rs +++ b/ir/crates/back/src/codegen/mod.rs @@ -1,4 +1,4 @@ -pub mod selection_dag; -pub mod isa; pub mod machine; pub mod register_allocator; +pub mod selection_dag; +pub mod targets; diff --git a/ir/crates/back/src/codegen/register_allocator/coalescer.rs b/ir/crates/back/src/codegen/register_allocator/coalescer.rs index 2bbe00e..7ce69b3 100644 --- a/ir/crates/back/src/codegen/register_allocator/coalescer.rs +++ b/ir/crates/back/src/codegen/register_allocator/coalescer.rs @@ -1,6 +1,16 @@ -use tracing::{debug, info}; +use tracing::{ + debug, + info, +}; -use crate::codegen::machine::{Abi, Instr, Module, PseudoInstr, TargetMachine}; +use crate::codegen::machine::{ + instr::{ + Instr, + PseudoInstr, + }, + Module, +}; +use crate::codegen::machine::TargetMachine; pub struct Coalescer<'module, TM: TargetMachine> { module: &'module mut Module, @@ -8,9 +18,7 @@ pub struct Coalescer<'module, TM: TargetMachine> { impl<'module, TM: TargetMachine> Coalescer<'module, TM> { pub fn new(module: &'module mut Module) -> Self { - Self { - module, - } + Self { module } } pub fn run(&mut self) { @@ -20,24 +28,22 @@ impl<'module, TM: TargetMachine> Coalescer<'module, TM> { let mut instructions_to_remove = Vec::new(); for (instr_id, instr) in bb.instructions.iter_enumerated() { match instr { - Instr::Pseudo(instr) => { - match instr { - PseudoInstr::Copy(dest, src) => { - if dest == src { - debug!("Removing redundant copy: {:?}", instr); - instructions_to_remove.push(instr_id); - } + Instr::Pseudo(instr) => match instr { + PseudoInstr::Copy(dest, src) => { + if dest == src { + debug!("Removing redundant copy: {:?}", instr); + instructions_to_remove.push(instr_id); } - PseudoInstr::Ret(_) => {} - PseudoInstr::Phi(dest, operands) => { - if operands.iter().all(|op| op == dest) { - debug!("Removing redundant phi: {:?}", instr); - instructions_to_remove.push(instr_id); - } + } + PseudoInstr::Ret(_) => {} + PseudoInstr::Phi(dest, operands) => { + if operands.iter().all(|op| op == dest) { + debug!("Removing redundant phi: {:?}", instr); + instructions_to_remove.push(instr_id); } - PseudoInstr::Def(_) => {} } - } + PseudoInstr::Def(_) => {} + }, Instr::Machine(_) => {} } } @@ -47,4 +53,4 @@ impl<'module, TM: TargetMachine> Coalescer<'module, TM> { } } } -} \ No newline at end of file +} 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 36a4ff4..552983d 100644 --- a/ir/crates/back/src/codegen/register_allocator/linear_scan.rs +++ b/ir/crates/back/src/codegen/register_allocator/linear_scan.rs @@ -1,24 +1,39 @@ use iter_tools::Itertools; use tracing::debug; -use crate::codegen::machine::{Abi, PhysicalRegister, Size}; -use crate::codegen::register_allocator::{Lifetime, LivenessRepr, ProgPoint, RegAllocAlgorithm, RegAllocHints, RegAllocVReg}; +use crate::codegen::{ + machine::{ + isa::PhysicalRegister, + Size, + }, + register_allocator::{ + Lifetime, + LivenessRepr, + ProgPoint, + RegAllocAlgorithm, + RegAllocHints, + RegAllocVReg, + }, +}; +use crate::codegen::machine::TargetMachine; #[derive(Debug)] -pub struct RegAlloc<'liveness, A: Abi> { +pub struct RegAlloc<'liveness, TM: TargetMachine> { /// Liveness representation. liveness_repr: &'liveness LivenessRepr, /// List of free registers. - free_regs: Vec, - active: Vec<(Lifetime, A::REG)>, - inactive: Vec<(Lifetime, A::REG)>, + free_regs: Vec, + active: Vec<(Lifetime, TM::Reg)>, + inactive: Vec<(Lifetime, TM::Reg)>, } -impl<'liveness, A: Abi> RegAllocAlgorithm<'liveness, A> for RegAlloc<'liveness, A> { +impl<'liveness, TM: TargetMachine> RegAllocAlgorithm<'liveness, TM> for RegAlloc<'liveness, TM> { fn new(liveness_repr: &'liveness LivenessRepr) -> Self { - let free_regs = A::REG::all().iter().copied().filter( - PhysicalRegister::is_gp - ).collect_vec(); + let free_regs = TM::Reg::all() + .iter() + .copied() + .filter(PhysicalRegister::is_gp) + .collect_vec(); debug!("Available registers: {:?}", free_regs); Self { liveness_repr, @@ -27,22 +42,23 @@ impl<'liveness, A: Abi> RegAllocAlgorithm<'liveness, A> for RegAlloc<'liveness, inactive: Vec::new(), } } - fn allocate_arbitrary(&mut self, vreg: &RegAllocVReg, hints: RegAllocHints) -> A::REG { + fn allocate_arbitrary(&mut self, vreg: &RegAllocVReg, hints: RegAllocHints) -> TM::Reg { self.set_cursor(vreg.lifetime.start); - let reg = hints.into_iter().find( - |hint| self.is_free(*hint) - ).unwrap_or_else(|| self.find_reg_with_size(vreg.size).unwrap_or_else( - || { - debug!("Active: {:?}", self.active); - debug!("Inactive: {:?}", self.inactive); - panic!("No free register available for size {}", vreg.size) - } - )); + let reg = hints + .into_iter() + .find(|hint| self.is_free(*hint)) + .unwrap_or_else(|| { + self.find_reg_with_size(vreg.size).unwrap_or_else(|| { + debug!("Active: {:?}", self.active); + debug!("Inactive: {:?}", self.inactive); + panic!("No free register available for size {}", vreg.size) + }) + }); self.insert_active(vreg.lifetime.clone(), reg); 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 { self.set_cursor(vreg.lifetime.start); if self.is_free(reg) { self.insert_active(vreg.lifetime.clone(), reg); @@ -52,17 +68,21 @@ impl<'liveness, A: Abi> RegAllocAlgorithm<'liveness, A> for RegAlloc<'liveness, } } - fn try_evict(&mut self, reg: A::REG) -> bool { + fn try_evict(&mut self, reg: TM::Reg) -> bool { if self.is_free(reg) { return false; } - let i = self.active.iter().position(|(_, r)| r == ®).expect("Register not found in active list even though it is not free"); + let i = self + .active + .iter() + .position(|(_, r)| r == ®) + .expect("Register not found in active list even though it is not free"); self.remove_active(i); true } } -impl RegAlloc<'_, A> { +impl RegAlloc<'_, TM> { /// Configures the algorithm to view allocation state at the given program point. fn set_cursor(&mut self, at: ProgPoint) { debug!("Setting cursor to {}", at); @@ -70,9 +90,8 @@ impl RegAlloc<'_, A> { self.reactivate_intervals(at); } - /// Inserts a new live interval into the active list. - fn insert_active(&mut self, interval: Lifetime, reg: A::REG) { + fn insert_active(&mut self, interval: Lifetime, reg: TM::Reg) { self.free_regs.retain(|r| { if reg.interferes_with(*r) { debug!("Removing {} from free list", r.name()); @@ -90,28 +109,26 @@ impl RegAlloc<'_, A> { self.active.push((interval, reg)); } - fn remove_active(&mut self, i: usize) -> (Lifetime, A::REG) { + fn remove_active(&mut self, i: usize) -> (Lifetime, TM::Reg) { let (lifetime, reg) = self.active.remove(i); debug!("Removing active interval: {}", lifetime); - for reg in reg.regclass().filter( - |r| reg.interferes_with(*r) - ) { + for reg in reg.regclass().filter(|r| reg.interferes_with(*r)) { self.free_reg(reg); } (lifetime, reg) } - fn free_reg(&mut self, reg: ::REG) { + fn free_reg(&mut self, reg: TM::Reg) { debug!("Freeing register {}", reg.name()); self.free_regs.push(reg); } - fn insert_inactive(&mut self, interval: Lifetime, reg: A::REG) { + fn insert_inactive(&mut self, interval: Lifetime, reg: TM::Reg) { debug!("Inserting inactive interval: {}", interval); self.inactive.push((interval, reg)); } - fn remove_inactive(&mut self, i: usize) -> (Lifetime, A::REG) { + fn remove_inactive(&mut self, i: usize) -> (Lifetime, TM::Reg) { let (lifetime, reg) = self.inactive.remove(i); debug!("Removing inactive interval: {}", lifetime); (lifetime, reg) @@ -144,7 +161,7 @@ impl RegAlloc<'_, A> { } /// Finds a free register with the given size - fn find_reg_with_size(&self, size: Size) -> Option { + fn find_reg_with_size(&self, size: Size) -> Option { for reg in &self.free_regs { if reg.size() == size { return Some(*reg); @@ -153,7 +170,7 @@ impl RegAlloc<'_, A> { None } - fn is_free(&self, reg: A::REG) -> bool { + fn is_free(&self, reg: TM::Reg) -> bool { self.free_regs.contains(®) } } diff --git a/ir/crates/back/src/codegen/register_allocator/mod.rs b/ir/crates/back/src/codegen/register_allocator/mod.rs index 4ee2f14..07ee303 100644 --- a/ir/crates/back/src/codegen/register_allocator/mod.rs +++ b/ir/crates/back/src/codegen/register_allocator/mod.rs @@ -1,24 +1,47 @@ -use std::cmp::Ordering; -use std::collections::{BTreeSet, VecDeque}; -use std::fmt::{Display, Formatter}; -use std::ops::{Range, RangeInclusive}; - -use cranelift_entity::{EntityList, EntitySet, SecondaryMap}; -use daggy::petgraph::prelude::DfsPostOrder; +use std::{ + cmp::Ordering, + collections::VecDeque, + fmt::{ + Display, + Formatter, + }, +}; + +use cranelift_entity::SecondaryMap; use daggy::Walker; -use iced_x86::CC_p::p; -use index_vec::IndexVec; use iter_tools::Itertools; -use rustc_hash::{FxHashMap, FxHashSet}; -use smallvec::{smallvec, SmallVec}; +use rustc_hash::{ + FxHashMap, + FxHashSet, +}; +use smallvec::{ + smallvec, + SmallVec, +}; use tracing::debug; pub use coalescer::Coalescer; -use crate::codegen::machine::{Abi, BasicBlockId, Cfg, Function, Instr, InstrId, InstrOperand, InstrOperandMut, PhysicalRegister, PseudoInstr, Register, Size, VReg}; -use crate::codegen::machine::abi::calling_convention::Slot; -use crate::codegen::machine::abi::CallingConvention; -use crate::codegen::register_allocator::linear_scan::RegAlloc; +use crate::codegen::machine::{ + abi::{ + calling_convention::Slot, + CallingConvention, + }, + function::Function, + instr::{ + Instr, + PseudoInstr, + }, + InstrId, + isa::PhysicalRegister, + reg::{ + Register, + VReg, + }, + Size, +}; +use crate::codegen::machine::function::BasicBlockId; +use crate::codegen::machine::TargetMachine; mod coalescer; pub mod linear_scan; @@ -31,10 +54,7 @@ pub struct InstrUid { impl InstrUid { pub fn new(bb: BasicBlockId, instr: InstrId) -> Self { - Self { - bb, - instr, - } + Self { bb, instr } } } @@ -73,8 +93,7 @@ impl PartialOrd for ProgPoint { impl Ord for ProgPoint { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { - (Self::Read(a), Self::Read(b)) | - (Self::Write(a), Self::Write(b)) => a.cmp(b), + (Self::Read(a), Self::Read(b)) | (Self::Write(a), Self::Write(b)) => a.cmp(b), (Self::Read(a), Self::Write(b)) => { if a <= b { Ordering::Less @@ -122,13 +141,13 @@ impl Lifetime { /// Returns true if the two lifetimes overlap. /// /// Two lifetimes l, j overlap if the intersection of their ranges is not empty. - /// This is the case iff. + /// This is the case iff. /// - l.start <= j.end /// and /// - j.start <= l.end - /// + /// /// Overlaps are **symmetric**. - pub fn are_overlapping(l: &Self, j: &Self) -> bool { + pub fn are_overlapping(l: &Self, j: &Self) -> bool { l.start <= j.end && j.start <= l.end } @@ -171,7 +190,10 @@ mod prog_point_tests { #[cfg(test)] mod lifetime_tests { - use crate::codegen::register_allocator::{Lifetime, ProgPoint}; + use crate::codegen::register_allocator::{ + Lifetime, + ProgPoint, + }; #[test] fn lifetimes_overlap() { @@ -209,11 +231,25 @@ mod lifetime_tests { ]; for (l1, l2, should_overlap) in inputs { // Overlaps are symmetric - assert_eq!(l1.overlaps_with(&l2), should_overlap, "{:?} and {:?} should overlap: {}", l1, l2, should_overlap); - assert_eq!(l2.overlaps_with(&l1), should_overlap, "{:?} and {:?} should overlap: {}", l2, l1, should_overlap); + assert_eq!( + l1.overlaps_with(&l2), + should_overlap, + "{:?} and {:?} should overlap: {}", + l1, + l2, + should_overlap + ); + assert_eq!( + l2.overlaps_with(&l1), + should_overlap, + "{:?} and {:?} should overlap: {}", + l2, + l1, + should_overlap + ); } } - + #[test] fn lifetimes_contain() { let inputs = [ @@ -243,7 +279,14 @@ mod lifetime_tests { ), ]; for (lifetime, pp, should_contain) in inputs { - assert_eq!(lifetime.contains(pp), should_contain, "{:?} should contain {:?}: {}", lifetime, pp, should_contain); + assert_eq!( + lifetime.contains(pp), + should_contain, + "{:?} should contain {:?}: {}", + lifetime, + pp, + should_contain + ); } } } @@ -256,14 +299,17 @@ pub struct InstrNumbering { } impl InstrNumbering { - pub fn new(func: &Function) -> Self { + pub fn new(func: &Function) -> Self { debug!("Creating instruction numbering"); - let order = func.cfg().ordered().into_iter().map( - |bb| (bb, func.basic_blocks[bb].instructions.len_idx().raw()) - ).collect_vec(); - debug!("Created instruction numbering with ordering: {:?}",order); + let order = func + .cfg() + .ordered() + .into_iter() + .map(|bb| (bb, func.basic_blocks[bb].instructions.len_idx().raw())) + .collect_vec(); + debug!("Created instruction numbering with ordering: {:?}", order); Self { - toplogical_order: order + toplogical_order: order, } } @@ -300,7 +346,11 @@ impl InstrNumbering { pub fn end_of_bb(&self, bb: BasicBlockId) -> Option { let offset = self.get_bb_offset(bb)?; - let len = self.toplogical_order.iter().find(|(bb_id, _)| *bb_id == bb)?.1; + let len = self + .toplogical_order + .iter() + .find(|(bb_id, _)| *bb_id == bb)? + .1; Some(offset + len) } @@ -309,7 +359,9 @@ impl InstrNumbering { } pub fn iter_enumerated(&self) -> impl Iterator + '_ { - self.iter().enumerate().map(|(nr, instr_uid)| (nr as InstrNr, instr_uid)) + self.iter() + .enumerate() + .map(|(nr, instr_uid)| (nr as InstrNr, instr_uid)) } } @@ -357,16 +409,24 @@ pub struct LivenessRepr { } impl LivenessRepr { - pub fn display<'func, 'liveness, A: Abi>(&'liveness self, func: &'func Function) -> LivenessReprDisplay<'func, 'liveness, A> { + pub fn display<'func, 'liveness, A: TargetMachine>( + &'liveness self, + func: &'func Function, + ) -> LivenessReprDisplay<'func, 'liveness, A> { LivenessReprDisplay(self, func) } } -struct LivenessReprDisplay<'func, 'liveness, A: Abi>(&'liveness LivenessRepr, &'func Function); +struct LivenessReprDisplay<'func, 'liveness, A: TargetMachine>(&'liveness LivenessRepr, &'func Function); -impl Display for LivenessReprDisplay<'_, '_, A> { +impl Display for LivenessReprDisplay<'_, '_, A> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for (reg, lifetime) in self.0.defs.keys().map(|reg| (reg, self.0.lifetime(reg, self.1))) { + for (reg, lifetime) in self + .0 + .defs + .keys() + .map(|reg| (reg, self.0.lifetime(reg, self.1))) + { writeln!(f, "{}: {}", reg, lifetime)?; } Ok(()) @@ -374,7 +434,7 @@ impl Display for LivenessReprDisplay<'_, '_, A> { } impl LivenessRepr { - pub fn new(func: &Function) -> Self { + pub fn new(func: &Function) -> Self { debug!("Creating liveness representation"); Self { defs: SecondaryMap::default(), @@ -388,17 +448,25 @@ impl LivenessRepr { } pub fn record_use(&mut self, reg: VReg, instr_nr: InstrNr) { - let insert_at = self.uses[reg].binary_search(&instr_nr).unwrap_or_else(|x| x); + let insert_at = self.uses[reg] + .binary_search(&instr_nr) + .unwrap_or_else(|x| x); self.uses[reg].insert(insert_at, instr_nr); } - pub fn lifetime(&self, reg: VReg, func: &Function) -> Lifetime { + pub fn lifetime(&self, reg: VReg, func: &Function) -> Lifetime { let start = self.defs[reg]; let end = self.last_use(reg).unwrap_or(start); let end_uid = self.instr_numbering.get_instr_uid(end).unwrap(); if func.basic_blocks[end_uid.bb].instructions[end_uid.instr].is_phi() { - if let Some(second_last_use_in_bb) = Some(self.second_last_use(reg).unwrap_or(start)).and_then(|instr_nr| self.instr_numbering.get_instr_uid(instr_nr)).map(|uid| uid.bb) { - let end = self.instr_numbering.end_of_bb(second_last_use_in_bb).unwrap(); + if let Some(second_last_use_in_bb) = Some(self.second_last_use(reg).unwrap_or(start)) + .and_then(|instr_nr| self.instr_numbering.get_instr_uid(instr_nr)) + .map(|uid| uid.bb) + { + let end = self + .instr_numbering + .end_of_bb(second_last_use_in_bb) + .unwrap(); return Lifetime::new(start, ProgPoint::Write(end)); } } @@ -414,7 +482,7 @@ impl LivenessRepr { } } -pub type RegAllocHints = SmallVec<[A::REG; 2]>; +pub type RegAllocHints = SmallVec<[::Reg; 2]>; #[derive(Debug, Clone)] pub struct RegAllocVReg { @@ -423,31 +491,39 @@ pub struct RegAllocVReg { pub lifetime: Lifetime, } -pub trait RegAllocAlgorithm<'liveness, A: Abi> { +pub trait RegAllocAlgorithm<'liveness, A: TargetMachine> { fn new(liveness_repr: &'liveness LivenessRepr) -> Self; - fn allocate_arbitrary(&mut self, vreg: &RegAllocVReg, hints: RegAllocHints) -> A::REG; + fn allocate_arbitrary(&mut self, vreg: &RegAllocVReg, hints: RegAllocHints) -> A::Reg; - fn try_allocate_fixed(&mut self, vreg: &RegAllocVReg, reg: A::REG) -> bool; + fn try_allocate_fixed(&mut self, vreg: &RegAllocVReg, reg: A::Reg) -> bool; - fn try_evict(&mut self, reg: A::REG) -> bool; + fn try_evict(&mut self, reg: A::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: A::Reg) -> bool { if self.try_allocate_fixed(vreg, reg) { return false; } - assert!(self.try_evict(reg), "{} was ensured to be allocated, but it was not", reg.name()); - assert!(self.try_allocate_fixed(vreg, reg), "{} was ensured to be evicted, but it was not", reg.name()); + assert!( + self.try_evict(reg), + "{} was ensured to be allocated, but it was not", + reg.name() + ); + assert!( + self.try_allocate_fixed(vreg, reg), + "{} was ensured to be evicted, but it was not", + reg.name() + ); true } } -struct VRegAllocations<'liveness, A: Abi> { - map: FxHashMap, +struct VRegAllocations<'liveness, A: TargetMachine> { + map: FxHashMap, liveness_repr: &'liveness LivenessRepr, } -impl<'liveness, A: Abi> VRegAllocations<'liveness, A> { +impl<'liveness, A: TargetMachine> VRegAllocations<'liveness, A> { pub fn new(liveness_repr: &'liveness LivenessRepr) -> Self { Self { map: FxHashMap::default(), @@ -455,16 +531,16 @@ impl<'liveness, A: Abi> VRegAllocations<'liveness, A> { } } - pub fn get_allocated_reg(&self, vreg: VReg) -> Option { + pub fn get_allocated_reg(&self, vreg: VReg) -> Option { self.map.get(&vreg).copied() } - pub fn start_allocation(&mut self, vreg: VReg, reg: A::REG) { + pub fn start_allocation(&mut self, vreg: VReg, reg: A::Reg) { self.map.insert(vreg, reg); } } -pub struct RegisterAllocator<'liveness, 'func, A: Abi, RegAlloc: RegAllocAlgorithm<'liveness, A>> { +pub struct RegisterAllocator<'liveness, 'func, A: TargetMachine, RegAlloc: RegAllocAlgorithm<'liveness, A>> { algo: RegAlloc, func: &'func mut Function, marker: std::marker::PhantomData, @@ -472,8 +548,10 @@ pub struct RegisterAllocator<'liveness, 'func, A: Abi, RegAlloc: RegAllocAlgorit liveness_repr: &'liveness LivenessRepr, } -impl<'liveness, 'func, A: Abi, RegAlloc: RegAllocAlgorithm<'liveness, A>> RegisterAllocator<'liveness, 'func, A, RegAlloc> { - pub fn new(func: &'func mut Function, liveness_repr: &'liveness LivenessRepr) -> Self { +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 { Self { func, algo: RegAlloc::new(liveness_repr), @@ -485,37 +563,38 @@ impl<'liveness, 'func, A: Abi, RegAlloc: RegAllocAlgorithm<'liveness, A>> Regist pub fn run(mut self) { self.insert_fixed_locations_for_function_params(); - let mut worklist = self.liveness_repr.instr_numbering.iter().filter_map( - |instr_uid| { + let mut worklist = self + .liveness_repr + .instr_numbering + .iter() + .filter_map(|instr_uid| { let instr = &self.func.basic_blocks[instr_uid.bb].instructions[instr_uid.instr]; - instr.writes().and_then( - |reg| reg.try_as_virtual() - ) - } - ).collect::>(); + instr.writes().and_then(|reg| reg.try_as_virtual()) + }) + .collect::>(); while let Some(vreg) = worklist.pop_front() { let instr_nr = self.liveness_repr.defs[vreg]; - let instr_uid = self.liveness_repr.instr_numbering.get_instr_uid(instr_nr).unwrap_or_else( - || panic!("No instr uid for {}", instr_nr) - ); + let instr_uid = self + .liveness_repr + .instr_numbering + .get_instr_uid(instr_nr) + .unwrap_or_else(|| panic!("No instr uid for {}", instr_nr)); let instr = &self.func.basic_blocks[instr_uid.bb].instructions[instr_uid.instr]; let mut hints: SmallVec<[_; 2]> = smallvec![]; match instr { - Instr::Pseudo(instr) => { - match instr { - PseudoInstr::Copy(_, src) => { - if let Some(reg) = match src { - Register::Virtual(reg) => self.allocations.get_allocated_reg(*reg), - Register::Physical(reg) => Some(*reg), - } { - hints.push(reg); - } + Instr::Pseudo(instr) => match instr { + PseudoInstr::Copy(_, src) => { + if let Some(reg) = match src { + Register::Virtual(reg) => self.allocations.get_allocated_reg(*reg), + Register::Physical(reg) => Some(*reg), + } { + hints.push(reg); } - PseudoInstr::Ret(_) => {} - PseudoInstr::Phi(_, _) => {} - PseudoInstr::Def(_) => {} } - } + PseudoInstr::Ret(_) => {} + PseudoInstr::Phi(_, _) => {} + PseudoInstr::Def(_) => {} + }, Instr::Machine(_) => {} }; if self.allocations.get_allocated_reg(vreg).is_some() { @@ -530,37 +609,48 @@ impl<'liveness, 'func, A: Abi, RegAlloc: RegAllocAlgorithm<'liveness, A>> Regist lifetime: lifetime.clone(), }; let phys_reg = match vreg_info.fixed { - None => { - match vreg_info.tied_to { - None => { - debug!("Allocating {vreg} at {instr_uid} with hints: {:?} and size {size}", 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"); - assert!(!lifetime.overlaps_with(&self.liveness_repr.lifetime(tied_to, &self.func)), "Tied register {tied_to} overlaps with {vreg}"); - let allocated_reg = self.allocations.get_allocated_reg(tied_to); - match allocated_reg { - None => { - 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()); - if self.algo.allocate_fixed_or_evict( - &alloc_vreg, allocated_reg, - ) { - debug!("Evicted {} to allocate {vreg}", allocated_reg.name()); - } - Some(allocated_reg) + None => match vreg_info.tied_to { + None => { + debug!( + "Allocating {vreg} at {instr_uid} with hints: {:?} and size {size}", + 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" + ); + assert!( + !lifetime + .overlaps_with(&self.liveness_repr.lifetime(tied_to, &self.func)), + "Tied register {tied_to} overlaps with {vreg}" + ); + let allocated_reg = self.allocations.get_allocated_reg(tied_to); + match allocated_reg { + None => { + 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()); + if self + .algo + .allocate_fixed_or_evict(&alloc_vreg, allocated_reg) + { + debug!("Evicted {} to allocate {vreg}", allocated_reg.name()); } + Some(allocated_reg) } } } - } + }, Some(fixed) => { - debug!("Allocating {vreg} at {instr_uid} in fixed register {}", fixed.name()); + debug!( + "Allocating {vreg} at {instr_uid} in fixed register {}", + fixed.name() + ); self.algo.allocate_fixed_or_evict(&alloc_vreg, fixed); Some(fixed) } @@ -579,15 +669,27 @@ impl<'liveness, 'func, A: Abi, RegAlloc: RegAllocAlgorithm<'liveness, A>> Regist 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); - let phys_reg = self.allocations.get_allocated_reg(vreg).unwrap_or_else(|| panic!("{vreg} was not allocated")); + 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")); *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); - let phys_reg = self.allocations.get_allocated_reg(vreg).unwrap_or_else(|| panic!("{vreg} was not allocated")); + 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")); *reg = Register::Physical(phys_reg); } } @@ -595,21 +697,25 @@ impl<'liveness, 'func, A: Abi, RegAlloc: RegAllocAlgorithm<'liveness, A>> Regist } fn insert_fixed_locations_for_function_params(&mut self) { - let slots = A::CallingConvention::parameter_slots( - self.func.params.iter().map(|param| self.func.vregs[*param].size) - ).collect_vec(); + let slots = TM::CallingConvention::parameter_slots( + self.func + .params + .iter() + .map(|param| self.func.vregs[*param].size), + ) + .collect_vec(); for (arg, slot) in self.func.params.iter().copied().zip(slots) { match slot { Slot::Register(reg) => { self.func.vregs[arg].fixed = Some(reg); } - Slot::Stack => unimplemented!() + Slot::Stack => unimplemented!(), } } } } -impl Function { +impl Function { pub fn liveness_repr(&mut self) -> LivenessRepr { #[derive(Default)] struct Liveins(FxHashMap>); @@ -618,9 +724,7 @@ impl Function { Self(FxHashMap::default()) } fn insert(&mut self, bb: BasicBlockId, reg: VReg) { - self.0.entry( - bb - ).or_default().insert(reg); + self.0.entry(bb).or_default().insert(reg); } fn ensure_exists(&mut self, bb: BasicBlockId) { @@ -631,11 +735,11 @@ impl Function { self.0[&bb].iter().copied() } } - let mut liveins = Liveins::default(); + let liveins = Liveins::default(); let mut repr = LivenessRepr::new(self); debug!("Starting liveness analysis"); - let mut worklist = self.cfg().dfs_postorder().collect::>(); + let worklist = self.cfg().dfs_postorder().collect::>(); let mut visited = FxHashSet::default(); for bb_id in self.cfg().ordered().into_iter().rev() { debug!("Looking at {bb_id}"); @@ -673,9 +777,7 @@ impl Function { let mut instr_nr = exit_pp.instr_nr(); for instr in bb.instructions.iter().rev() { let out = instr.writes(); - if let Some(reg) = out.and_then( - |reg| reg.try_as_virtual() - ) { + if let Some(reg) = out.and_then(|reg| reg.try_as_virtual()) { // undeclared_regs.remove(®); // let mut interval = current_intervals.remove(®).unwrap_or_else(|| { // debug!("Creating new interval for {reg} at {instr_nr}"); diff --git a/ir/crates/back/src/codegen/selection_dag/builder.rs b/ir/crates/back/src/codegen/selection_dag/builder.rs index 37ba0ef..efa5332 100644 --- a/ir/crates/back/src/codegen/selection_dag/builder.rs +++ b/ir/crates/back/src/codegen/selection_dag/builder.rs @@ -1,37 +1,64 @@ -use std::collections::VecDeque; -use std::num::NonZeroUsize; - -use cranelift_entity::{EntityRef, SecondaryMap, SparseMap}; -use daggy::{NodeIndex, Walker}; -use daggy::petgraph::visit::IntoNodeIdentifiers; +use cranelift_entity::{ + EntityRef, + SecondaryMap, +}; +use daggy::{ + NodeIndex, + petgraph::visit::IntoNodeIdentifiers, + Walker, +}; use iter_tools::Itertools; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashMap; use tracing::debug; use codegen::selection_dag; -use natrix_middle::cfg::{BasicBlockId, BranchTerm, JumpTarget, Terminator, TerminatorKind}; -use natrix_middle::InstrKind; -use natrix_middle::instruction::{Const, OpInstr, VRegData}; -use natrix_middle::ty::Type; +use natrix_middle::{ + cfg::{ + BasicBlockId, + BranchTerm, + JumpTarget, + Terminator, + TerminatorKind, + }, + instruction::{ + Const, + VRegData, + }, + ty::Type, +}; use selection_dag::SelectionDAG; -use crate::codegen; -use crate::codegen::machine; -use crate::codegen::machine::{Abi, Function, Register, VReg}; -use crate::codegen::machine::abi::calling_convention::Slot; -use crate::codegen::machine::abi::CallingConvention; -use crate::codegen::selection_dag::{Immediate, MachineOp, Op, Operand, PseudoOp}; +use crate::{ + codegen, + codegen::{ + machine::{ + function::Function, + reg::{ + Register, + VReg, + }, + }, + selection_dag::{ + Immediate, + MachineOp, + Op, + Operand, + PseudoOp, + }, + }, +}; +use crate::codegen::machine::TargetMachine; #[derive(Debug)] -pub struct Builder<'func, A: machine::Abi> { - function: &'func mut Function, - sel_dag: SelectionDAG, +pub struct Builder<'func, TM: TargetMachine> { + function: &'func mut Function, + sel_dag: SelectionDAG, reg_mapping: SecondaryMap>, defining_nodes: FxHashMap<(VReg, BasicBlockId), NodeIndex>, } -impl<'func, A: machine::Abi> Builder<'func, A> { - pub fn new(function: &'func mut Function) -> Self { +impl<'func, TM: TargetMachine> Builder<'func, TM> { + pub fn new(function: &'func mut Function) -> Self { Self { function, reg_mapping: SecondaryMap::new(), @@ -40,47 +67,58 @@ impl<'func, A: machine::Abi> Builder<'func, A> { } } - pub fn build(mut self, func: &mut natrix_middle::Function) -> SelectionDAG { + pub fn build(mut self, func: &mut natrix_middle::Function) -> SelectionDAG { debug!("Building SelectionDAGs for function {}", func.name); - let basic_blocks = func.cfg.basic_block_ids().filter( - |bb_id| *bb_id != func.cfg.entry_block() - ).collect::>(); + let basic_blocks = func + .cfg + .basic_block_ids() + .filter(|bb_id| *bb_id != func.cfg.entry_block()) + .collect::>(); for bb_id in basic_blocks { // Eliminate basic block arguments debug!("Eliminating basic block arguments for {}", bb_id); - let bb_args = func.cfg.basic_block(bb_id).arguments() + let bb_args = func + .cfg + .basic_block(bb_id) + .arguments() .map(|arg| (arg, func.cfg.vreg_ty_cloned(arg))) .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).terminator().branch_args(bb_id).unwrap().cloned().collect::>(); + let args = func + .cfg + .basic_block(pred_id) + .terminator() + .branch_args(bb_id) + .unwrap() + .cloned() + .collect::>(); if args.is_empty() { continue; } 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| - { - term.clear_args(bb_id); - }); + func.cfg.basic_block_mut(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) - ); + 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, + )); func.cfg.recompute_successors(critical_edge_split_bb); - func.cfg.basic_block_mut(pred_id).update_terminator(|term| - { - term.clear_args(bb_id); - term.update_references_to_bb(bb_id, critical_edge_split_bb); - }); + func.cfg.basic_block_mut(pred_id).update_terminator(|term| { + term.clear_args(bb_id); + term.update_references_to_bb(bb_id, critical_edge_split_bb); + }); func.cfg.recompute_successors(pred_id); critical_edge_split_bb @@ -93,28 +131,25 @@ impl<'func, A: machine::Abi> Builder<'func, A> { ty: ty.clone(), }); temp_regs.entry(*bb_arg).or_default().push(temp_reg); - let instr = func.cfg.copy_op_instr( - temp_reg, - arg, - ); + let instr = func.cfg.copy_op_instr(temp_reg, arg); func.cfg.add_instruction(copy_instr_bb, ty.clone(), instr); } } let _ = func.cfg.basic_block_mut(bb_id).clear_arguments(); for (arg, _) in bb_args { let mapped_arg = self.map_vreg(arg, func); - let operands = temp_regs.remove(&arg).unwrap().into_iter().map( - |reg| { + let operands = temp_regs + .remove(&arg) + .unwrap() + .into_iter() + .map(|reg| { let mapped = self.map_vreg(reg, 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); Register::Virtual(mapped) - } - ).collect_vec(); - let pseudo_op = PseudoOp::Phi( - Register::Virtual(mapped_arg), - operands, - ); + }) + .collect_vec(); + let pseudo_op = PseudoOp::Phi(Register::Virtual(mapped_arg), operands); debug!("Adding phi to {bb_id}: {:?}", pseudo_op); let phi_op = Op::Pseudo(pseudo_op); self.define_node(bb_id, phi_op); @@ -127,9 +162,7 @@ impl<'func, A: machine::Abi> Builder<'func, A> { for arg in bb.arguments() { let mapped_reg = self.map_vreg(arg, func); self.function.params.push(mapped_reg); - self.define_node(bb_id, Op::Pseudo(PseudoOp::Def( - mapped_reg, - ))); + self.define_node(bb_id, Op::Pseudo(PseudoOp::Def(mapped_reg))); } } for instr in bb.instructions() { @@ -142,10 +175,9 @@ impl<'func, A: machine::Abi> Builder<'func, A> { natrix_middle::InstrKind::Op(op_instr) => { let out_reg = self.map_vreg(op_instr.value, func); let op = match self.map_op(&op_instr.op, func) { - Operand::Reg(reg) => Op::Pseudo(PseudoOp::Copy( - Register::Virtual(out_reg), - reg, - )), + Operand::Reg(reg) => { + Op::Pseudo(PseudoOp::Copy(Register::Virtual(out_reg), reg)) + } Operand::Imm(imm) => Op::Machine(MachineOp::Mov( Register::Virtual(out_reg), Operand::Imm(imm), @@ -157,64 +189,73 @@ impl<'func, A: machine::Abi> Builder<'func, A> { let out_reg = self.map_vreg(sub_instr.value, func); let lhs = self.map_op(&sub_instr.lhs, func); let rhs = self.map_op(&sub_instr.rhs, func); - self.define_node(bb_id, Op::Machine(MachineOp::Sub( - Register::Virtual(out_reg), - lhs, - rhs, - ))); + self.define_node( + bb_id, + Op::Machine(MachineOp::Sub(Register::Virtual(out_reg), lhs, rhs)), + ); } natrix_middle::InstrKind::Add(add_instr) => { let out_reg = self.map_vreg(add_instr.value, func); let lhs = self.map_op(&add_instr.lhs, func); let rhs = self.map_op(&add_instr.rhs, func); - self.define_node(bb_id, Op::Machine(MachineOp::Add( - Register::Virtual(out_reg), - lhs, - rhs, - ))); + self.define_node( + bb_id, + Op::Machine(MachineOp::Add(Register::Virtual(out_reg), lhs, rhs)), + ); } natrix_middle::InstrKind::Cmp(cmp_instr) => { let out_reg = self.map_vreg(cmp_instr.value, func); let lhs = self.map_op(&cmp_instr.lhs, func); let rhs = self.map_op(&cmp_instr.rhs, func); - self.define_node(bb_id, Op::Machine(MachineOp::Cmp( - Register::Virtual(out_reg), - cmp_instr.op, - lhs, - rhs, - ))); + self.define_node( + bb_id, + Op::Machine(MachineOp::Cmp( + Register::Virtual(out_reg), + cmp_instr.op, + lhs, + rhs, + )), + ); } }; } match &bb.terminator().kind { natrix_middle::cfg::TerminatorKind::Ret(ret_term) => { - let value = ret_term.value.as_ref().map(|value| self.map_op(value, func)); - self.define_term_node(bb_id, Op::Pseudo(PseudoOp::Ret( - value - ))); + let value = ret_term + .value + .as_ref() + .map(|value| self.map_op(value, func)); + self.define_term_node(bb_id, Op::Pseudo(PseudoOp::Ret(value))); } natrix_middle::cfg::TerminatorKind::Branch(branch_term) => { - self.define_term_node(bb_id, Op::Machine(MachineOp::Br( - branch_term.target.id - ))); + self.define_term_node(bb_id, Op::Machine(MachineOp::Br(branch_term.target.id))); } natrix_middle::cfg::TerminatorKind::CondBranch(branch_term) => { let op = self.map_op(&branch_term.cond, func); - self.define_term_node(bb_id, Op::Machine(MachineOp::CondBr( - op, - branch_term.true_target.id.into(), - branch_term.false_target.id.into(), - ))); + self.define_term_node( + bb_id, + Op::Machine(MachineOp::CondBr( + op, + branch_term.true_target.id.into(), + branch_term.false_target.id.into(), + )), + ); } } } self.sel_dag } - fn map_op(&mut self, op: &natrix_middle::instruction::Op, func: &natrix_middle::Function) -> Operand { + fn map_op( + &mut self, + op: &natrix_middle::instruction::Op, + func: &natrix_middle::Function, + ) -> Operand { match op { - natrix_middle::instruction::Op::Vreg(vreg) => Operand::Reg(Register::Virtual(self.map_vreg(*vreg, func))), + natrix_middle::instruction::Op::Vreg(vreg) => { + Operand::Reg(Register::Virtual(self.map_vreg(*vreg, func))) + } natrix_middle::instruction::Op::Const(constant) => Operand::Imm(match constant { Const::Int(ty, value) => { let value = *value; @@ -232,27 +273,37 @@ impl<'func, A: machine::Abi> Builder<'func, A> { Type::Ptr(_) => unimplemented!(), } } - }) + }), } } - fn add_dependency(&mut self, bb_id: BasicBlockId, depending_node: NodeIndex, producing_node: NodeIndex) { + fn add_dependency( + &mut self, + bb_id: BasicBlockId, + depending_node: NodeIndex, + producing_node: NodeIndex, + ) { debug!("{:?} depends on {:?}", depending_node, producing_node); - self.sel_dag.get_bb_dag(bb_id).add_edge( - depending_node, - producing_node, - selection_dag::Edge, - ).unwrap(); + self.sel_dag + .get_bb_dag(bb_id) + .add_edge(depending_node, producing_node, selection_dag::Edge) + .unwrap(); } - fn define_node(&mut self, bb_id: natrix_middle::cfg::BasicBlockId, op: Op) -> NodeIndex { + fn define_node(&mut self, bb_id: natrix_middle::cfg::BasicBlockId, op: Op) -> NodeIndex { let used_regs = op.consumed_regs(); let out_reg = op.out().and_then(|reg| reg.try_as_virtual()); - debug!("Defining op {:?}. Out reg: {:?}, used regs: {:?}", op, out_reg, used_regs); + debug!( + "Defining op {:?}. Out reg: {:?}, used regs: {:?}", + op, out_reg, used_regs + ); let node = self.sel_dag.get_bb_dag(bb_id).add_node(op); debug!("Defined op as node {:?}", node); for reg in used_regs { - if let Some(defining_node) = reg.try_as_virtual().and_then(|reg| self.get_defining_node(reg, bb_id)) { + if let Some(defining_node) = reg + .try_as_virtual() + .and_then(|reg| self.get_defining_node(reg, bb_id)) + { self.add_dependency(bb_id, node, defining_node); } } @@ -262,20 +313,21 @@ impl<'func, A: machine::Abi> Builder<'func, A> { node } - fn define_term_node(&mut self, bb_id: natrix_middle::cfg::BasicBlockId, op: Op) -> NodeIndex { + fn define_term_node( + &mut self, + bb_id: natrix_middle::cfg::BasicBlockId, + op: Op, + ) -> NodeIndex { let term_node = self.define_node(bb_id, op); let dag = self.sel_dag.get_bb_dag(bb_id); dag.set_term_node(term_node); debug!("Adding dependency on term node for every node"); - for node in dag.node_identifiers().filter( - |node| *node != term_node - ) { + for node in dag.node_identifiers().filter(|node| *node != term_node) { self.add_dependency(bb_id, term_node, node); } term_node } - fn map_vreg(&mut self, vreg: natrix_middle::VReg, func: &natrix_middle::Function) -> VReg { let mapped = self.reg_mapping[vreg]; match mapped { @@ -297,4 +349,3 @@ impl<'func, A: machine::Abi> Builder<'func, A> { 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 bb9aee4..32d642d 100644 --- a/ir/crates/back/src/codegen/selection_dag/mod.rs +++ b/ir/crates/back/src/codegen/selection_dag/mod.rs @@ -1,29 +1,48 @@ -use std::fmt::Display; -use std::ops::{Deref, DerefMut}; - -use daggy::petgraph::dot::{Config, Dot}; +use std::{ + fmt::Display, + ops::{ + Deref, + DerefMut, + }, +}; + +use daggy::petgraph::dot::{ + Config, + Dot, +}; use rustc_hash::FxHashMap; -use smallvec::{SmallVec, smallvec}; +use smallvec::{ + smallvec, + SmallVec, +}; pub use builder::Builder; -use natrix_middle::cfg::BasicBlockId; -use natrix_middle::instruction::CmpOp; - -use crate::codegen::machine; -use crate::codegen::machine::{Abi, MachineInstr, Pattern, Register, Size, VReg}; +use natrix_middle::{ + cfg::BasicBlockId, + instruction::CmpOp, +}; + +use crate::codegen::machine::{ + reg::{ + Register, + VReg, + }, + Size, +}; +use crate::codegen::machine::TargetMachine; pub mod builder; type Dag = daggy::Dag, Edge>; #[derive(Debug, Clone)] -pub struct BasicBlockDAG { - dag: Dag, +pub struct BasicBlockDAG { + dag: Dag, term_node: Option, bb: natrix_middle::cfg::BasicBlockId, } -impl BasicBlockDAG { +impl BasicBlockDAG { pub fn new(bb: natrix_middle::cfg::BasicBlockId) -> Self { Self { dag: Dag::new(), @@ -45,36 +64,43 @@ impl BasicBlockDAG { pub fn save_graphviz>(&self, path: P) -> std::io::Result<()> { std::fs::create_dir_all(&path)?; - std::fs::write(format!("{}/{}.dot", path.as_ref().display(), self.bb), self.graphviz()) + std::fs::write( + format!("{}/{}.dot", path.as_ref().display(), self.bb), + self.graphviz(), + ) } } -impl Deref for BasicBlockDAG { - type Target = Dag; +impl Deref for BasicBlockDAG { + type Target = Dag; fn deref(&self) -> &Self::Target { &self.dag } } -impl DerefMut for BasicBlockDAG { +impl DerefMut for BasicBlockDAG { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.dag } } #[derive(Debug, Default)] -pub struct SelectionDAG { - pub basic_blocks: FxHashMap>, +pub struct SelectionDAG { + pub basic_blocks: FxHashMap>, } -impl SelectionDAG { - pub fn get_bb_dag(&mut self, basic_block: natrix_middle::cfg::BasicBlockId) -> &mut BasicBlockDAG { - self.basic_blocks.entry(basic_block).or_insert_with(|| BasicBlockDAG::new(basic_block)) +impl SelectionDAG { + pub fn get_bb_dag( + &mut self, + basic_block: natrix_middle::cfg::BasicBlockId, + ) -> &mut BasicBlockDAG { + self.basic_blocks + .entry(basic_block) + .or_insert_with(|| BasicBlockDAG::new(basic_block)) } } - #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Immediate { value: [u8; 8], @@ -190,20 +216,20 @@ impl Display for Edge { } #[derive(Debug, Clone, Eq, PartialEq)] -pub enum Op { - Machine(MachineOp), - Pseudo(PseudoOp), +pub enum Op { + Machine(MachineOp), + Pseudo(PseudoOp), } -impl Op { - pub fn out(&self) -> Option> { +impl Op { + pub fn out(&self) -> Option> { match self { Op::Machine(op) => op.out(), Op::Pseudo(op) => op.out(), } } - pub fn consumed_regs(&self) -> SmallVec<[Register; 2]> { + pub fn consumed_regs(&self) -> SmallVec<[Register; 2]> { match self { Op::Machine(op) => op.consumed_regs(), Op::Pseudo(op) => op.consumed_regs(), @@ -212,50 +238,48 @@ impl Op { } #[derive(Debug, Clone, Eq, PartialEq)] -pub enum PseudoOp { +pub enum PseudoOp { Def(VReg), - Copy(Register, Register), - Ret(Option>), - Phi(Register, Vec>), + Copy(Register, Register), + Ret(Option>), + Phi(Register, Vec>), } -impl PseudoOp { - pub fn out(&self) -> Option> { +impl PseudoOp { + pub fn out(&self) -> Option> { match self { Self::Copy(dest, _) => Some(*dest), Self::Ret(_) => None, Self::Phi(dest, _) => Some(*dest), - Self::Def(dest) => Some(Register::Virtual(*dest)) + Self::Def(dest) => Some(Register::Virtual(*dest)), } } - pub fn consumed_regs(&self) -> SmallVec<[Register; 2]> { + pub fn consumed_regs(&self) -> SmallVec<[Register; 2]> { match self { Self::Copy(_, dest) => smallvec![*dest], - Self::Ret(op) => { - match op { - Some(Operand::Reg(reg)) => smallvec![*reg], - _ => smallvec![] - } - } + Self::Ret(op) => match op { + Some(Operand::Reg(reg)) => smallvec![*reg], + _ => smallvec![], + }, Self::Phi(_, regs) => regs.clone().into(), - Self::Def(_) => smallvec![] + Self::Def(_) => smallvec![], } } } #[derive(Debug, Clone, Eq, PartialEq)] -pub enum MachineOp { - Mov(Register, Operand), - Sub(Register, Operand, Operand), - Add(Register, Operand, Operand), - Cmp(Register, CmpOp, Operand, Operand), +pub enum MachineOp { + Mov(Register, Operand), + Sub(Register, Operand, Operand), + Add(Register, Operand, Operand), + Cmp(Register, CmpOp, Operand, Operand), Br(BasicBlockId), - CondBr(Operand, BasicBlockId, BasicBlockId), + CondBr(Operand, BasicBlockId, BasicBlockId), } -impl MachineOp { - pub fn out(&self) -> Option> { +impl MachineOp { + pub fn out(&self) -> Option> { match self { Self::Mov(dest, _) => Some(*dest), Self::Sub(dest, _, _) => Some(*dest), @@ -266,63 +290,52 @@ impl MachineOp { } } - pub fn consumed_regs(&self) -> SmallVec<[Register; 2]> { + pub fn consumed_regs(&self) -> SmallVec<[Register; 2]> { match self { - MachineOp::Mov(_, src) => { - match src { - Operand::Reg(reg) => - smallvec![*reg], - _ => - smallvec![] - } - } - MachineOp::Sub(_, src, dest) => { - match (src.try_as_register(), dest.try_as_register()) { - (Some(src), Some(dest)) => smallvec![src, dest], - (Some(src), None) => smallvec![src], - (None, Some(dest)) => smallvec![dest], - _ => smallvec![] - } - } - MachineOp::Add(_, src, dest) => { - match (src.try_as_register(), dest.try_as_register()) { - (Some(src), Some(dest)) => smallvec![src, dest], - (Some(src), None) => smallvec![src], - (None, Some(dest)) => smallvec![dest], - _ => smallvec![] - } - } + MachineOp::Mov(_, src) => match src { + Operand::Reg(reg) => smallvec![*reg], + _ => smallvec![], + }, + MachineOp::Sub(_, src, dest) => match (src.try_as_register(), dest.try_as_register()) { + (Some(src), Some(dest)) => smallvec![src, dest], + (Some(src), None) => smallvec![src], + (None, Some(dest)) => smallvec![dest], + _ => smallvec![], + }, + MachineOp::Add(_, src, dest) => match (src.try_as_register(), dest.try_as_register()) { + (Some(src), Some(dest)) => smallvec![src, dest], + (Some(src), None) => smallvec![src], + (None, Some(dest)) => smallvec![dest], + _ => smallvec![], + }, MachineOp::Br(_) => smallvec![], MachineOp::Cmp(_, _, lhs, rhs) => { match (lhs.try_as_register(), rhs.try_as_register()) { (Some(lhs), Some(rhs)) => smallvec![lhs, rhs], (Some(lhs), None) => smallvec![lhs], (None, Some(rhs)) => smallvec![rhs], - _ => smallvec![] - } - } - MachineOp::CondBr(cond, _, _) => { - match cond.try_as_register() { - Some(cond) => smallvec![cond], - None => smallvec![] + _ => smallvec![], } } + MachineOp::CondBr(cond, _, _) => match cond.try_as_register() { + Some(cond) => smallvec![cond], + None => smallvec![], + }, } } } #[derive(Debug, Clone, Eq, PartialEq)] -pub enum Operand { - Reg(Register), +pub enum Operand { + Reg(Register), Imm(Immediate), } -impl Operand { - pub fn try_as_register(&self) -> Option> { +impl Operand { + pub fn try_as_register(&self) -> Option> { match self { Operand::Reg(reg) => Some(*reg), Operand::Imm(_) => None, } } } - diff --git a/ir/crates/back/src/codegen/targets/calling_convention/mod.rs b/ir/crates/back/src/codegen/targets/calling_convention/mod.rs new file mode 100644 index 0000000..9f4d835 --- /dev/null +++ b/ir/crates/back/src/codegen/targets/calling_convention/mod.rs @@ -0,0 +1 @@ +pub mod systemv; \ No newline at end of file diff --git a/ir/crates/back/src/codegen/targets/calling_convention/systemv.rs b/ir/crates/back/src/codegen/targets/calling_convention/systemv.rs new file mode 100644 index 0000000..f95a03c --- /dev/null +++ b/ir/crates/back/src/codegen/targets/calling_convention/systemv.rs @@ -0,0 +1,77 @@ +use crate::codegen::machine::{ + abi::{ + calling_convention::Slot, + CallingConvention, + }, + Size, +}; +use crate::codegen::targets::x86_64; + +#[derive(Default)] +pub struct SystemV; + +impl CallingConvention for SystemV { + type Reg = x86_64::PhysicalRegister; + fn parameter_slots( + params: impl Iterator, + ) -> impl Iterator> { + let mut used_regs = 0; + params.map(move |size| { + let slot = if used_regs < 6 { + let reg = match used_regs { + 0 => match size { + Size::Byte => Self::Reg::DIL, + Size::Word => Self::Reg::DI, + Size::DWord => Self::Reg::EDI, + Size::QWord => Self::Reg::RDI, + }, + 1 => match size { + Size::Byte => Self::Reg::SIL, + Size::Word => Self::Reg::SI, + Size::DWord => Self::Reg::ESI, + Size::QWord => Self::Reg::RSI, + }, + 2 => match size { + Size::Byte => Self::Reg::DL, + Size::Word => Self::Reg::DX, + Size::DWord => Self::Reg::EDX, + Size::QWord => Self::Reg::RDX, + }, + 3 => match size { + Size::Byte => Self::Reg::CL, + Size::Word => Self::Reg::CX, + Size::DWord => Self::Reg::ECX, + Size::QWord => Self::Reg::RCX, + }, + 4 => match size { + Size::Byte => Self::Reg::R8L, + Size::Word => Self::Reg::R8W, + Size::DWord => Self::Reg::R8D, + Size::QWord => Self::Reg::R8, + }, + 5 => match size { + Size::Byte => Self::Reg::R9L, + Size::Word => Self::Reg::R9W, + Size::DWord => Self::Reg::R9D, + Size::QWord => Self::Reg::R9, + }, + _ => unreachable!("Too many parameters"), + }; + used_regs += 1; + Slot::Register(reg) + } else { + Slot::Stack + }; + slot + }) + } + + fn return_slot(size: Size) -> Slot { + match size { + Size::Byte => Slot::Register(Self::Reg::AL), + Size::Word => Slot::Register(Self::Reg::AX), + Size::DWord => Slot::Register(Self::Reg::EAX), + Size::QWord => Slot::Register(Self::Reg::RAX), + } + } +} diff --git a/ir/crates/back/src/codegen/targets/mod.rs b/ir/crates/back/src/codegen/targets/mod.rs new file mode 100644 index 0000000..3fc2bde --- /dev/null +++ b/ir/crates/back/src/codegen/targets/mod.rs @@ -0,0 +1,2 @@ +pub mod x86_64; +pub mod calling_convention; diff --git a/ir/crates/back/src/codegen/isa/x86_64/asm.rs b/ir/crates/back/src/codegen/targets/x86_64/asm.rs similarity index 53% rename from ir/crates/back/src/codegen/isa/x86_64/asm.rs rename to ir/crates/back/src/codegen/targets/x86_64/asm.rs index 6a6c5e2..8f1035d 100644 --- a/ir/crates/back/src/codegen/isa/x86_64/asm.rs +++ b/ir/crates/back/src/codegen/targets/x86_64/asm.rs @@ -1,13 +1,26 @@ -use std::ops::{Deref, DerefMut}; +use std::ops::{ + Deref, + DerefMut, +}; -use iced_x86::{Code, Formatter, Instruction, IntelFormatter, NumberBase, Register}; -use iced_x86::code_asm::{CodeAssembler, CodeLabel}; +use iced_x86::{ + Code, + code_asm::{ + CodeAssembler, + CodeLabel, + }, + Formatter, + Instruction, + IntelFormatter, + NumberBase, + Register, +}; use rustc_hash::FxHashMap; -use crate::codegen::isa::x86_64; -use crate::codegen::isa::x86_64::{CC, PhysicalRegister, X86Instr}; use crate::codegen::machine; -use crate::codegen::machine::BasicBlockId; +use crate::codegen::machine::function::cfg::BasicBlockId; +use crate::codegen::targets::x86_64; +use crate::codegen::targets::x86_64::{CC, PhysicalRegister}; pub struct Assembler { assembler: CodeAssembler, @@ -17,7 +30,15 @@ pub struct Assembler { impl Assembler { pub fn get_or_insert_label(&mut self, bb: BasicBlockId) -> CodeLabel { - self.bb_to_label.entry(bb).or_insert_with(|| (self.assembler.create_label(), self.assembler.instructions().len())).0 + self.bb_to_label + .entry(bb) + .or_insert_with(|| { + ( + self.assembler.create_label(), + self.assembler.instructions().len(), + ) + }) + .0 } } @@ -79,7 +100,8 @@ impl From for Register { } } -impl machine::asm::Assembler for Assembler { +impl machine::asm::Assembler for Assembler { + type TM = x86_64::Target; fn new(base_addr: u64) -> Self { Self { bb_to_label: FxHashMap::default(), @@ -88,167 +110,162 @@ impl machine::asm::Assembler for Assembler { } } - fn begin_basic_block(&mut self, bb_id: machine::BasicBlockId) { + fn begin_basic_block(&mut self, bb_id: BasicBlockId) { let label = &mut self.get_or_insert_label(bb_id); self.assembler.set_label(label).unwrap(); } - fn assemble(&mut self, instr: &::I) { + fn assemble(&mut self, instr: &x86_64::Instr) { match instr { - X86Instr::SUB32ri { dest, immediate } => { + x86_64::Instr::SUB32ri { dest, immediate } => { let dest: Register = dest.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Sub_rm32_imm32, - dest, - immediate.as_encoded_dword().unwrap() - ).unwrap()).unwrap(); + self.add_instruction( + Instruction::with2( + Code::Sub_rm32_imm32, + dest, + immediate.as_encoded_dword().unwrap(), + ) + .unwrap(), + ) + .unwrap(); } - X86Instr::SUB32rr { src, dest } => { + x86_64::Instr::SUB32rr { src, dest } => { let dest: Register = dest.try_as_physical().unwrap().into(); let src: Register = src.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Sub_rm32_r32, - dest, - src, - ).unwrap()).unwrap(); + self.add_instruction(Instruction::with2(Code::Sub_rm32_r32, dest, src).unwrap()) + .unwrap(); } - X86Instr::ADD32ri { dest, immediate } => { + x86_64::Instr::ADD32ri { dest, immediate } => { let dest: Register = dest.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Add_rm32_imm32, - dest, - immediate.as_encoded_dword().unwrap(), - ).unwrap()).unwrap(); + self.add_instruction( + Instruction::with2( + Code::Add_rm32_imm32, + dest, + immediate.as_encoded_dword().unwrap(), + ) + .unwrap(), + ) + .unwrap(); } - X86Instr::ADD32rr { src, dest } => { + x86_64::Instr::ADD32rr { src, dest } => { let dest: Register = dest.try_as_physical().unwrap().into(); let src: Register = src.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Add_rm32_r32, - dest, - src, - ).unwrap()).unwrap(); + self.add_instruction(Instruction::with2(Code::Add_rm32_r32, dest, src).unwrap()) + .unwrap(); } - X86Instr::MOV8ri { dest, immediate } => { + x86_64::Instr::MOV8ri { dest, immediate } => { let dest: Register = dest.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Mov_rm8_imm8, - dest, - immediate.as_encoded_dword().unwrap(), - ).unwrap()).unwrap(); + self.add_instruction( + Instruction::with2( + Code::Mov_rm8_imm8, + dest, + immediate.as_encoded_dword().unwrap(), + ) + .unwrap(), + ) + .unwrap(); } - X86Instr::MOV8rr { src, dest } => { + x86_64::Instr::MOV8rr { src, dest } => { let dest: Register = dest.try_as_physical().unwrap().into(); let src: Register = src.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Mov_rm8_r8, - dest, - src, - ).unwrap()).unwrap(); + self.add_instruction(Instruction::with2(Code::Mov_rm8_r8, dest, src).unwrap()) + .unwrap(); } - X86Instr::MOV16ri { dest, immediate } => { + x86_64::Instr::MOV16ri { dest, immediate } => { let dest: Register = dest.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Mov_rm16_imm16, - dest, - immediate.as_encoded_dword().unwrap(), - ).unwrap()).unwrap(); + self.add_instruction( + Instruction::with2( + Code::Mov_rm16_imm16, + dest, + immediate.as_encoded_dword().unwrap(), + ) + .unwrap(), + ) + .unwrap(); } - X86Instr::MOV16rr { src, dest } => { + x86_64::Instr::MOV16rr { src, dest } => { let dest: Register = dest.try_as_physical().unwrap().into(); let src: Register = src.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Mov_rm16_r16, - dest, - src, - ).unwrap()).unwrap(); + self.add_instruction(Instruction::with2(Code::Mov_rm16_r16, dest, src).unwrap()) + .unwrap(); } - X86Instr::MOV32ri { - dest, - immediate - } => { + x86_64::Instr::MOV32ri { dest, immediate } => { let dest: Register = dest.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Mov_rm32_imm32, - dest, - immediate.as_encoded_dword().unwrap(), - ).unwrap()).unwrap(); + self.add_instruction( + Instruction::with2( + Code::Mov_rm32_imm32, + dest, + immediate.as_encoded_dword().unwrap(), + ) + .unwrap(), + ) + .unwrap(); } - X86Instr::MOV32rr { src, dest } => { + x86_64::Instr::MOV32rr { src, dest } => { let dest: Register = dest.try_as_physical().unwrap().into(); let src: Register = src.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Mov_rm32_r32, - dest, - src, - ).unwrap()).unwrap(); + self.add_instruction(Instruction::with2(Code::Mov_rm32_r32, dest, src).unwrap()) + .unwrap(); } - X86Instr::MOV64ri { - dest, - immediate - } => { + x86_64::Instr::MOV64ri { dest, immediate } => { let dest: Register = dest.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Mov_rm64_imm32, - dest, - immediate.as_encoded_dword().expect("64-bit immediates are too large. Should be stored in memory"), - ).unwrap()).unwrap(); + self.add_instruction( + Instruction::with2( + Code::Mov_rm64_imm32, + dest, + immediate + .as_encoded_dword() + .expect("64-bit immediates are too large. Should be stored in memory"), + ) + .unwrap(), + ) + .unwrap(); } - X86Instr::MOV64rr { src, dest } => { + x86_64::Instr::MOV64rr { src, dest } => { let dest: Register = dest.try_as_physical().unwrap().into(); let src: Register = src.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Mov_rm64_r64, - dest, - src, - ).unwrap()).unwrap(); + self.add_instruction(Instruction::with2(Code::Mov_rm64_r64, dest, src).unwrap()) + .unwrap(); } - X86Instr::RET => { + x86_64::Instr::RET => { self.ret().unwrap(); } - X86Instr::JMP { - target - } => { + x86_64::Instr::JMP { target } => { let label = self.get_or_insert_label(*target); self.jmp(label).unwrap(); } - X86Instr::CMP32rr { lhs, rhs } => { + x86_64::Instr::CMP32rr { lhs, rhs } => { let lhs: Register = lhs.try_as_physical().unwrap().into(); let rhs: Register = rhs.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Cmp_rm32_r32, - lhs, - rhs, - ).unwrap()).unwrap(); + self.add_instruction(Instruction::with2(Code::Cmp_rm32_r32, lhs, rhs).unwrap()) + .unwrap(); } - X86Instr::CMP32ri { lhs, rhs } => { + x86_64::Instr::CMP32ri { lhs, rhs } => { let lhs: Register = lhs.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Cmp_rm32_imm32, - lhs, - rhs.as_encoded_dword().unwrap(), - ).unwrap()).unwrap(); + self.add_instruction( + Instruction::with2(Code::Cmp_rm32_imm32, lhs, rhs.as_encoded_dword().unwrap()) + .unwrap(), + ) + .unwrap(); } - X86Instr::CMP8ri { lhs, rhs } => { + x86_64::Instr::CMP8ri { lhs, rhs } => { let lhs: Register = lhs.try_as_physical().unwrap().into(); - self.add_instruction(Instruction::with2( - Code::Cmp_rm8_imm8, - lhs, - rhs.as_encoded_dword().unwrap(), - ).unwrap()).unwrap(); + self.add_instruction( + Instruction::with2(Code::Cmp_rm8_imm8, lhs, rhs.as_encoded_dword().unwrap()) + .unwrap(), + ) + .unwrap(); } - X86Instr::SETCC { dest, cc } => { + x86_64::Instr::SETCC { dest, cc } => { let dest: Register = dest.try_as_physical().unwrap().into(); let code: Code = match cc { CC::Eq => Code::Sete_rm8, CC::Gt => Code::Setg_rm8, }; - self.add_instruction(Instruction::with1( - code, - dest, - ).unwrap()).unwrap(); + self.add_instruction(Instruction::with1(code, dest).unwrap()) + .unwrap(); } - X86Instr::JCC { target, cc } => { + x86_64::Instr::JCC { target, cc } => { let label = self.get_or_insert_label(*target); match cc { CC::Eq => { @@ -283,4 +300,3 @@ impl machine::asm::Assembler for Assembler { output } } - diff --git a/ir/crates/back/src/codegen/isa/x86_64/mod.rs b/ir/crates/back/src/codegen/targets/x86_64/mod.rs similarity index 52% rename from ir/crates/back/src/codegen/isa/x86_64/mod.rs rename to ir/crates/back/src/codegen/targets/x86_64/mod.rs index f370288..5c0b458 100644 --- a/ir/crates/back/src/codegen/isa/x86_64/mod.rs +++ b/ir/crates/back/src/codegen/targets/x86_64/mod.rs @@ -1,22 +1,51 @@ -use smallvec::{SmallVec, smallvec}; +use smallvec::{ + smallvec, + SmallVec, +}; use strum::VariantArray; +use machine::instr::Instr as MInstr; use natrix_middle::instruction::CmpOp; -use crate::codegen::isa::{Architecture, Endianness}; -use crate::codegen::machine; -use crate::codegen::machine::{BasicBlockId, Function, Instr, InstrOperand, MatchedPattern, PatternInOperand, PatternInOutput, PhysicalRegister as MachPhysicalRegister, PseudoInstr, Size, TargetMachine}; -use crate::codegen::machine::abi::calling_convention::Slot; -use crate::codegen::machine::abi::CallingConvention; -use crate::codegen::selection_dag::Immediate; +use crate::codegen::{ + machine, + machine::{ + Architecture, + backend, + Endianness, + function::{ + BasicBlockId, + builder::{ + MatchedPattern, + PatternInOperand, + PatternInOutput, + }, + Function, + }, + instr::{ + InstrOperand, + PseudoInstr, + }, + isa::PhysicalRegister as MachPhysicalRegister, + Size, + TargetMachine, + }, + selection_dag::Immediate, +}; +use crate::codegen::machine::function::builder::PatternIn; +use crate::codegen::targets::calling_convention::systemv::SystemV; mod asm; -pub struct DefaultTarget; +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +pub struct Target; -impl TargetMachine for DefaultTarget { - type Abi = Abi; +impl TargetMachine for Target { + type Reg = PhysicalRegister; + type Instr = Instr; + type CallingConvention = SystemV; type Backend = Backend; + type Assembler = asm::Assembler; fn endianness() -> Endianness { Endianness::Little @@ -27,7 +56,7 @@ impl TargetMachine for DefaultTarget { } } -pub type Register = machine::Register; +pub type Register = machine::Register; #[derive(Debug, Clone, Copy, PartialEq, Eq, EnumTryAs, IntoStaticStr)] pub enum CC { @@ -40,13 +69,13 @@ impl From for CC { match op { CmpOp::Eq => Self::Eq, CmpOp::Gt => Self::Gt, - _ => unimplemented!() + _ => unimplemented!(), } } } #[derive(Debug, Clone, PartialEq, Eq, EnumTryAs, IntoStaticStr)] -pub enum X86Instr { +pub enum Instr { SUB32ri { dest: Register, immediate: Immediate, @@ -200,15 +229,47 @@ impl MachPhysicalRegister for PhysicalRegister { fn size(&self) -> Size { match self { - Self::AL | Self::AH | Self::BL | Self::BH | Self::CL | Self::CH | Self::DL | Self::DH | Self::SIL | Self::DIL | Self::R8L | Self::R9L => Size::Byte, - Self::AX | Self::BX | Self::CX | Self::DX | Self::SI | Self::DI | Self::R8W | Self::R9W => Size::Word, - Self::EAX | Self::ECX | Self::EDX | Self::EBX | Self::ESI | Self::EDI | Self::R8D | Self::R9D | Self::EFLAGS => Size::DWord, - Self::RAX | Self::RBX | Self::RCX | Self::RDX | Self::R8 | Self::R9 | Self::RSI | Self::RDI => Size::QWord, + Self::AL + | Self::AH + | Self::BL + | Self::BH + | Self::CL + | Self::CH + | Self::DL + | Self::DH + | Self::SIL + | Self::DIL + | Self::R8L + | Self::R9L => Size::Byte, + Self::AX + | Self::BX + | Self::CX + | Self::DX + | Self::SI + | Self::DI + | Self::R8W + | Self::R9W => Size::Word, + Self::EAX + | Self::ECX + | Self::EDX + | Self::EBX + | Self::ESI + | Self::EDI + | Self::R8D + | Self::R9D + | Self::EFLAGS => Size::DWord, + Self::RAX + | Self::RBX + | Self::RCX + | Self::RDX + | Self::R8 + | Self::R9 + | Self::RSI + | Self::RDI => Size::QWord, } } fn into_unicorn_emu_reg(self) -> impl Into { - use unicorn_engine::RegisterX86; match self { Self::AL => RegisterX86::AL, @@ -253,23 +314,19 @@ impl MachPhysicalRegister for PhysicalRegister { fn subregs(&self) -> Option<&'static [Self]> { match self { - Self::AL | - Self::AH => None, + Self::AL | Self::AH => None, Self::AX => Some(&[Self::AL, Self::AH]), Self::EAX => Some(&[Self::AX, Self::AL, Self::AH]), Self::RAX => Some(&[Self::EAX, Self::AX, Self::AL, Self::AH]), - Self::BL | - Self::BH => None, + Self::BL | Self::BH => None, Self::BX => Some(&[Self::BL, Self::BH]), Self::EBX => Some(&[Self::BX, Self::BL, Self::BH]), Self::RBX => Some(&[Self::EBX, Self::BX, Self::BL, Self::BH]), - Self::CL | - Self::CH => None, + Self::CL | Self::CH => None, Self::CX => Some(&[Self::CL, Self::CH]), Self::ECX => Some(&[Self::CX, Self::CL, Self::CH]), Self::RCX => Some(&[Self::ECX, Self::CX, Self::CL, Self::CH]), - Self::DL | - Self::DH => None, + Self::DL | Self::DH => None, Self::DX => Some(&[Self::DL, Self::DH]), Self::EDX => Some(&[Self::DX, Self::DL, Self::DH]), Self::RDX => Some(&[Self::EDX, Self::DX, Self::DL, Self::DH]), @@ -295,39 +352,67 @@ impl MachPhysicalRegister for PhysicalRegister { fn superregs(&self) -> Option<&'static [Self]> { match self { - PhysicalRegister::AL | - PhysicalRegister::AH => Some(&[PhysicalRegister::AX, PhysicalRegister::EAX, PhysicalRegister::RAX]), + PhysicalRegister::AL | PhysicalRegister::AH => Some(&[ + PhysicalRegister::AX, + PhysicalRegister::EAX, + PhysicalRegister::RAX, + ]), PhysicalRegister::AX => Some(&[PhysicalRegister::EAX, PhysicalRegister::RAX]), PhysicalRegister::EAX => Some(&[PhysicalRegister::RAX]), PhysicalRegister::RAX => None, - PhysicalRegister::BL | - PhysicalRegister::BH => Some(&[PhysicalRegister::BX, PhysicalRegister::EBX, PhysicalRegister::RBX]), + PhysicalRegister::BL | PhysicalRegister::BH => Some(&[ + PhysicalRegister::BX, + PhysicalRegister::EBX, + PhysicalRegister::RBX, + ]), PhysicalRegister::BX => Some(&[PhysicalRegister::EBX, PhysicalRegister::RBX]), PhysicalRegister::EBX => Some(&[PhysicalRegister::RBX]), PhysicalRegister::RBX => None, - PhysicalRegister::CL | - PhysicalRegister::CH => Some(&[PhysicalRegister::CX, PhysicalRegister::ECX, PhysicalRegister::RCX]), + PhysicalRegister::CL | PhysicalRegister::CH => Some(&[ + PhysicalRegister::CX, + PhysicalRegister::ECX, + PhysicalRegister::RCX, + ]), PhysicalRegister::CX => Some(&[PhysicalRegister::ECX, PhysicalRegister::RCX]), PhysicalRegister::ECX => Some(&[PhysicalRegister::RCX]), PhysicalRegister::RCX => None, - PhysicalRegister::DL | - PhysicalRegister::DH => Some(&[PhysicalRegister::DX, PhysicalRegister::EDX, PhysicalRegister::RDX]), + PhysicalRegister::DL | PhysicalRegister::DH => Some(&[ + PhysicalRegister::DX, + PhysicalRegister::EDX, + PhysicalRegister::RDX, + ]), PhysicalRegister::DX => Some(&[PhysicalRegister::EDX, PhysicalRegister::RDX]), PhysicalRegister::EDX => Some(&[PhysicalRegister::RDX]), PhysicalRegister::RDX => None, - PhysicalRegister::SIL => Some(&[PhysicalRegister::SI, PhysicalRegister::ESI, PhysicalRegister::RSI]), + PhysicalRegister::SIL => Some(&[ + PhysicalRegister::SI, + PhysicalRegister::ESI, + PhysicalRegister::RSI, + ]), PhysicalRegister::SI => Some(&[PhysicalRegister::ESI, PhysicalRegister::RSI]), PhysicalRegister::ESI => Some(&[PhysicalRegister::RSI]), PhysicalRegister::RSI => None, - PhysicalRegister::DIL => Some(&[PhysicalRegister::DI, PhysicalRegister::EDI, PhysicalRegister::RDI]), + PhysicalRegister::DIL => Some(&[ + PhysicalRegister::DI, + PhysicalRegister::EDI, + PhysicalRegister::RDI, + ]), PhysicalRegister::DI => Some(&[PhysicalRegister::EDI, PhysicalRegister::RDI]), PhysicalRegister::EDI => Some(&[PhysicalRegister::RDI]), PhysicalRegister::RDI => None, - PhysicalRegister::R8L => Some(&[PhysicalRegister::R8W, PhysicalRegister::R8D, PhysicalRegister::R8]), + PhysicalRegister::R8L => Some(&[ + PhysicalRegister::R8W, + PhysicalRegister::R8D, + PhysicalRegister::R8, + ]), PhysicalRegister::R8W => Some(&[PhysicalRegister::R8D, PhysicalRegister::R8]), PhysicalRegister::R8D => Some(&[PhysicalRegister::R8]), PhysicalRegister::R8 => None, - PhysicalRegister::R9L => Some(&[PhysicalRegister::R9W, PhysicalRegister::R9D, PhysicalRegister::R9]), + PhysicalRegister::R9L => Some(&[ + PhysicalRegister::R9W, + PhysicalRegister::R9D, + PhysicalRegister::R9, + ]), PhysicalRegister::R9W => Some(&[PhysicalRegister::R9D, PhysicalRegister::R9]), PhysicalRegister::R9D => Some(&[PhysicalRegister::R9]), PhysicalRegister::R9 => None, @@ -336,8 +421,9 @@ impl MachPhysicalRegister for PhysicalRegister { } } -impl machine::MachineInstr for X86Instr { - type Abi = Abi; + +impl machine::isa::MachInstr for Instr { + type TM = Target; fn name(&self) -> &'static str { self.into() @@ -345,11 +431,14 @@ impl machine::MachineInstr for X86Instr { fn writes(&self) -> Option { match self { - Self::MOV8ri { dest, .. } | Self::MOV8rr { dest, .. } - | Self::MOV16ri { dest, .. } | Self::MOV16rr { dest, .. } - | Self::MOV32ri { dest, .. } | Self::MOV32rr { dest, .. } - | Self::MOV64ri { dest, .. } | Self::MOV64rr { dest, .. } - => Some(*dest), + Self::MOV8ri { dest, .. } + | Self::MOV8rr { dest, .. } + | Self::MOV16ri { dest, .. } + | Self::MOV16rr { dest, .. } + | Self::MOV32ri { dest, .. } + | Self::MOV32rr { dest, .. } + | Self::MOV64ri { dest, .. } + | Self::MOV64rr { dest, .. } => Some(*dest), Self::SUB32ri { dest, .. } => Some(*dest), Self::SUB32rr { dest, .. } => Some(*dest), Self::ADD32ri { dest, .. } => Some(*dest), @@ -364,10 +453,16 @@ impl machine::MachineInstr for X86Instr { } } - fn reads(&self) -> SmallVec<[machine::Register; 2]> { + fn reads(&self) -> SmallVec<[machine::Register; 2]> { match self { - Self::MOV8ri { .. } | Self::MOV16ri { .. } | Self::MOV32ri { .. } | Self::MOV64ri { .. } => smallvec![], - Self::MOV8rr { src, .. } | Self::MOV16rr { src, .. } | Self::MOV32rr { src, .. } | Self::MOV64rr { src, .. } => smallvec![*src], + Self::MOV8ri { .. } + | Self::MOV16ri { .. } + | Self::MOV32ri { .. } + | Self::MOV64ri { .. } => smallvec![], + Self::MOV8rr { src, .. } + | Self::MOV16rr { src, .. } + | Self::MOV32rr { src, .. } + | Self::MOV64rr { src, .. } => smallvec![*src], Self::SUB32ri { dest, .. } => smallvec![*dest], Self::SUB32rr { src, dest } => smallvec![*src, *dest], Self::ADD32ri { dest, .. } => smallvec![*dest], @@ -382,70 +477,53 @@ impl machine::MachineInstr for X86Instr { } } - fn operands(&self) -> SmallVec<[InstrOperand; 3]> { + fn operands(&self) -> SmallVec<[InstrOperand; 3]> { match self { - Self::MOV8ri { dest, immediate } | - Self::MOV16ri { dest, immediate } | - Self::MOV32ri { dest, immediate } | - Self::MOV64ri { dest, immediate } => smallvec![ - InstrOperand::Reg(*dest), - InstrOperand::Imm(*immediate) - ], - Self::MOV8rr { dest, src } | - Self::MOV16rr { dest, src } | - Self::MOV32rr { dest, src } | - Self::MOV64rr { dest, src } => smallvec![ - InstrOperand::Reg(*dest), - InstrOperand::Reg(*src) - ], - Self::SUB32ri { dest, immediate } => smallvec![ - InstrOperand::Reg(*dest), - InstrOperand::Imm(*immediate) - ], - Self::SUB32rr { dest, src } => smallvec![ - InstrOperand::Reg(*dest), - InstrOperand::Reg(*src) - ], - Self::ADD32ri { dest, immediate } => smallvec![ - InstrOperand::Reg(*dest), - InstrOperand::Imm(*immediate) - ], - Self::ADD32rr { dest, src } => smallvec![ - InstrOperand::Reg(*dest), - InstrOperand::Reg(*src) - ], - Self::JMP { target } => smallvec![ - InstrOperand::Label(*target) - ], + Self::MOV8ri { dest, immediate } + | Self::MOV16ri { dest, immediate } + | Self::MOV32ri { dest, immediate } + | Self::MOV64ri { dest, immediate } => { + smallvec![InstrOperand::Reg(*dest), InstrOperand::Imm(*immediate)] + } + Self::MOV8rr { dest, src } + | Self::MOV16rr { dest, src } + | Self::MOV32rr { dest, src } + | Self::MOV64rr { dest, src } => { + smallvec![InstrOperand::Reg(*dest), InstrOperand::Reg(*src)] + } + Self::SUB32ri { dest, immediate } => { + smallvec![InstrOperand::Reg(*dest), InstrOperand::Imm(*immediate)] + } + Self::SUB32rr { dest, src } => { + smallvec![InstrOperand::Reg(*dest), InstrOperand::Reg(*src)] + } + Self::ADD32ri { dest, immediate } => { + smallvec![InstrOperand::Reg(*dest), InstrOperand::Imm(*immediate)] + } + Self::ADD32rr { dest, src } => { + smallvec![InstrOperand::Reg(*dest), InstrOperand::Reg(*src)] + } + Self::JMP { target } => smallvec![InstrOperand::Label(*target)], Self::RET => smallvec![], - Self::CMP32rr { lhs, rhs } => smallvec![ - InstrOperand::Reg(*lhs), - InstrOperand::Reg(*rhs) - ], - Self::CMP32ri { lhs, .. } => smallvec![ - InstrOperand::Reg(*lhs), - ], - Self::CMP8ri { lhs, .. } => smallvec![ - InstrOperand::Reg(*lhs), - ], - Self::SETCC { dest, .. } => smallvec![ - InstrOperand::Reg(*dest) - ], - Self::JCC { target, .. } => smallvec![ - InstrOperand::Label(*target), - ] + Self::CMP32rr { lhs, rhs } => { + smallvec![InstrOperand::Reg(*lhs), InstrOperand::Reg(*rhs)] + } + Self::CMP32ri { lhs, .. } => smallvec![InstrOperand::Reg(*lhs),], + Self::CMP8ri { lhs, .. } => smallvec![InstrOperand::Reg(*lhs),], + Self::SETCC { dest, .. } => smallvec![InstrOperand::Reg(*dest)], + Self::JCC { target, .. } => smallvec![InstrOperand::Label(*target),], } } - fn written_regs_mut(&mut self) -> SmallVec<[&mut machine::Register; 1]> { + fn written_regs_mut(&mut self) -> SmallVec<[&mut machine::Register; 1]> { match self { - Self::MOV8ri { dest, .. } | - Self::MOV8rr { dest, .. } | - Self::MOV16ri { dest, .. } | - Self::MOV16rr { dest, .. } | - Self::MOV32ri { dest, .. } | - Self::MOV32rr { dest, .. } | - Self::MOV64ri { dest, .. } | - Self::MOV64rr { dest, .. } => smallvec![dest], + Self::MOV8ri { dest, .. } + | Self::MOV8rr { dest, .. } + | Self::MOV16ri { dest, .. } + | Self::MOV16rr { dest, .. } + | Self::MOV32ri { dest, .. } + | Self::MOV32rr { dest, .. } + | Self::MOV64ri { dest, .. } + | Self::MOV64rr { dest, .. } => smallvec![dest], Self::SUB32ri { dest, .. } => smallvec![dest], Self::SUB32rr { dest, .. } => smallvec![dest], Self::ADD32ri { dest, .. } => smallvec![dest], @@ -460,16 +538,16 @@ impl machine::MachineInstr for X86Instr { } } - fn read_regs_mut(&mut self) -> SmallVec<[&mut machine::Register; 2]> { + fn read_regs_mut(&mut self) -> SmallVec<[&mut machine::Register; 2]> { match self { - Self::MOV8ri { .. } | - Self::MOV16ri { .. } | - Self::MOV32ri { .. } | - Self::MOV64ri { .. } => smallvec![], - Self::MOV8rr { src, .. } | - Self::MOV16rr { src, .. } | - Self::MOV32rr { src, .. } | - Self::MOV64rr { src, .. } => smallvec![src], + Self::MOV8ri { .. } + | Self::MOV16ri { .. } + | Self::MOV32ri { .. } + | Self::MOV64ri { .. } => smallvec![], + Self::MOV8rr { src, .. } + | Self::MOV16rr { src, .. } + | Self::MOV32rr { src, .. } + | Self::MOV64rr { src, .. } => smallvec![src], Self::SUB32ri { dest, .. } => smallvec![dest], Self::SUB32rr { src, dest } => smallvec![src, dest], Self::ADD32ri { dest, .. } => smallvec![dest], @@ -485,161 +563,103 @@ impl machine::MachineInstr for X86Instr { } } -#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] -pub struct Abi {} - -impl machine::Abi for Abi { - type I = X86Instr; - type ASSEMBLER = asm::Assembler; - - type REG = PhysicalRegister; - type CallingConvention = SystemV; -} - -pub struct SystemV; - -impl CallingConvention for SystemV { - type Reg = PhysicalRegister; - - fn parameter_slots(params: impl Iterator) -> impl Iterator> { - let mut used_regs = 0; - params.map(move |size| { - let slot = if used_regs < 6 { - let reg = match used_regs { - 0 => match size { - Size::Byte => PhysicalRegister::DIL, - Size::Word => PhysicalRegister::DI, - Size::DWord => PhysicalRegister::EDI, - Size::QWord => PhysicalRegister::RDI, - }, - 1 => match size { - Size::Byte => PhysicalRegister::SIL, - Size::Word => PhysicalRegister::SI, - Size::DWord => PhysicalRegister::ESI, - Size::QWord => PhysicalRegister::RSI, - }, - 2 => match size { - Size::Byte => PhysicalRegister::DL, - Size::Word => PhysicalRegister::DX, - Size::DWord => PhysicalRegister::EDX, - Size::QWord => PhysicalRegister::RDX, - }, - 3 => match size { - Size::Byte => PhysicalRegister::CL, - Size::Word => PhysicalRegister::CX, - Size::DWord => PhysicalRegister::ECX, - Size::QWord => PhysicalRegister::RCX, - }, - 4 => match size { - Size::Byte => PhysicalRegister::R8L, - Size::Word => PhysicalRegister::R8W, - Size::DWord => PhysicalRegister::R8D, - Size::QWord => PhysicalRegister::R8, - }, - 5 => match size { - Size::Byte => PhysicalRegister::R9L, - Size::Word => PhysicalRegister::R9W, - Size::DWord => PhysicalRegister::R9D, - Size::QWord => PhysicalRegister::R9, - }, - _ => unreachable!("Too many parameters"), - }; - used_regs += 1; - Slot::Register(reg) - } else { - Slot::Stack - }; - slot - }) - } - - fn return_slot(size: Size) -> Slot { - match size { - Size::Byte => Slot::Register(PhysicalRegister::AL), - Size::Word => Slot::Register(PhysicalRegister::AX), - Size::DWord => Slot::Register(PhysicalRegister::EAX), - Size::QWord => Slot::Register(PhysicalRegister::RAX), - } - } -} - -impl machine::Pattern for Pattern { - type ABI = Abi; +impl backend::Pattern for Pattern { + type TM = Target; - fn in_(&self) -> machine::PatternIn { + fn in_(&self) -> PatternIn { match self { - Self::Mov8ri => - machine::PatternIn::Mov(PatternInOutput::Reg(Size::Byte), PatternInOperand::Imm(Size::Byte)), - Self::Mov8rr => - machine::PatternIn::Mov(PatternInOutput::Reg(Size::Byte), PatternInOperand::Reg(Size::Byte)), - Self::Mov16ri => - machine::PatternIn::Mov(PatternInOutput::Reg(Size::Word), PatternInOperand::Imm(Size::Word)), - Self::Mov16rr => - machine::PatternIn::Mov(PatternInOutput::Reg(Size::Word), PatternInOperand::Reg(Size::Word)), - Self::Mov32ri => - machine::PatternIn::Mov(PatternInOutput::Reg(Size::DWord), PatternInOperand::Imm(Size::DWord)), - Self::Mov32rr => machine::PatternIn::Mov(PatternInOutput::Reg(Size::DWord), PatternInOperand::Reg(Size::DWord)), - Self::Mov64ri => - machine::PatternIn::Mov(PatternInOutput::Reg(Size::QWord), PatternInOperand::Imm(Size::QWord)), - Self::Mov64rr => machine::PatternIn::Mov(PatternInOutput::Reg(Size::QWord), PatternInOperand::Reg(Size::QWord)), - Self::Sub32ri => machine::PatternIn::Sub(PatternInOutput::Reg(Size::DWord), PatternInOperand::Reg(Size::DWord), PatternInOperand::Imm(Size::DWord)), - Self::Sub32rr => machine::PatternIn::Sub(PatternInOutput::Reg(Size::DWord), PatternInOperand::Reg(Size::DWord), PatternInOperand::Reg(Size::DWord)), - Self::Add32ri => machine::PatternIn::Add(PatternInOutput::Reg(Size::DWord), PatternInOperand::Reg(Size::DWord), PatternInOperand::Imm(Size::DWord)), - Self::Add32rr => machine::PatternIn::Add(PatternInOutput::Reg(Size::DWord), PatternInOperand::Reg(Size::DWord), PatternInOperand::Reg(Size::DWord)), - Self::Jmp => machine::PatternIn::Br, - Self::Cmp32rreq => machine::PatternIn::Cmp( + Self::Mov8ri => PatternIn::Mov( + PatternInOutput::Reg(Size::Byte), + PatternInOperand::Imm(Size::Byte), + ), + Self::Mov8rr => PatternIn::Mov( + PatternInOutput::Reg(Size::Byte), + PatternInOperand::Reg(Size::Byte), + ), + Self::Mov16ri => PatternIn::Mov( + PatternInOutput::Reg(Size::Word), + PatternInOperand::Imm(Size::Word), + ), + Self::Mov16rr => PatternIn::Mov( + PatternInOutput::Reg(Size::Word), + PatternInOperand::Reg(Size::Word), + ), + Self::Mov32ri => PatternIn::Mov( + PatternInOutput::Reg(Size::DWord), + PatternInOperand::Imm(Size::DWord), + ), + Self::Mov32rr => PatternIn::Mov( + PatternInOutput::Reg(Size::DWord), + PatternInOperand::Reg(Size::DWord), + ), + Self::Mov64ri => PatternIn::Mov( + PatternInOutput::Reg(Size::QWord), + PatternInOperand::Imm(Size::QWord), + ), + Self::Mov64rr => PatternIn::Mov( + PatternInOutput::Reg(Size::QWord), + PatternInOperand::Reg(Size::QWord), + ), + Self::Sub32ri => PatternIn::Sub( + PatternInOutput::Reg(Size::DWord), + PatternInOperand::Reg(Size::DWord), + PatternInOperand::Imm(Size::DWord), + ), + Self::Sub32rr => PatternIn::Sub( + PatternInOutput::Reg(Size::DWord), + PatternInOperand::Reg(Size::DWord), + PatternInOperand::Reg(Size::DWord), + ), + Self::Add32ri => PatternIn::Add( + PatternInOutput::Reg(Size::DWord), + PatternInOperand::Reg(Size::DWord), + PatternInOperand::Imm(Size::DWord), + ), + Self::Add32rr => PatternIn::Add( + PatternInOutput::Reg(Size::DWord), + PatternInOperand::Reg(Size::DWord), + PatternInOperand::Reg(Size::DWord), + ), + Self::Jmp => PatternIn::Br, + Self::Cmp32rreq => PatternIn::Cmp( PatternInOutput::Reg(Size::Byte), CmpOp::Eq, PatternInOperand::Reg(Size::DWord), PatternInOperand::Reg(Size::DWord), ), - Self::Cmp32rigt => machine::PatternIn::Cmp( + Self::Cmp32rigt => PatternIn::Cmp( PatternInOutput::Reg(Size::Byte), CmpOp::Gt, PatternInOperand::Reg(Size::DWord), PatternInOperand::Imm(Size::DWord), ), - Self::CondJmp => machine::PatternIn::CondBr( - PatternInOperand::Reg(Size::Byte), - ) + Self::CondJmp => PatternIn::CondBr(PatternInOperand::Reg(Size::Byte)), } } - fn into_instr(self, function: &mut Function, matched: MatchedPattern) -> SmallVec<[Instr; 2]> { + fn into_instr( + self, + function: &mut Function, + matched: MatchedPattern, + ) -> SmallVec<[MInstr; 2]> { match self { Self::Mov8rr | Self::Mov16rr | Self::Mov32rr | Self::Mov64rr => { let pattern = matched.try_as_mov().unwrap(); let dest = *pattern.dest.try_as_reg().unwrap(); let src = *pattern.src.try_as_reg().unwrap(); - smallvec![ - Instr::Pseudo(PseudoInstr::Copy (dest, src)) - ] + smallvec![MInstr::Pseudo(PseudoInstr::Copy(dest, src))] } Self::Mov8ri | Self::Mov16ri | Self::Mov32ri | Self::Mov64ri => { let pattern = matched.try_as_mov().unwrap(); let dest = *pattern.dest.try_as_reg().unwrap(); let immediate = pattern.src.try_as_imm().copied().unwrap(); - smallvec![ - match self { - Self::Mov8ri => Instr::Machine(X86Instr::MOV8ri { - dest, - immediate, - }), - Self::Mov16ri => Instr::Machine(X86Instr::MOV16ri { - dest, - immediate, - }), - Self::Mov32ri => Instr::Machine(X86Instr::MOV32ri { - dest, - immediate, - }), - Self::Mov64ri => Instr::Machine(X86Instr::MOV64ri { - dest, - immediate, - }), - _ => unreachable!() - } - ] + smallvec![match self { + Self::Mov8ri => MInstr::Machine(Instr::MOV8ri { dest, immediate }), + Self::Mov16ri => MInstr::Machine(Instr::MOV16ri { dest, immediate }), + Self::Mov32ri => MInstr::Machine(Instr::MOV32ri { dest, immediate }), + Self::Mov64ri => MInstr::Machine(Instr::MOV64ri { dest, immediate }), + _ => unreachable!(), + }] } Self::Sub32ri => { let pattern = matched.try_as_sub().unwrap(); @@ -647,8 +667,8 @@ impl machine::Pattern for Pattern { let lhs = *pattern.lhs.try_as_reg().unwrap(); let rhs = pattern.rhs.try_as_imm().copied().unwrap(); smallvec![ - Instr::Pseudo(PseudoInstr::Copy(dest, lhs)), - Instr::Machine(X86Instr::SUB32ri { + MInstr::Pseudo(PseudoInstr::Copy(dest, lhs)), + MInstr::Machine(Instr::SUB32ri { dest, immediate: rhs, }) @@ -660,14 +680,8 @@ impl machine::Pattern for Pattern { let lhs = *pattern.lhs.try_as_reg().unwrap(); let rhs = *pattern.rhs.try_as_reg().unwrap(); smallvec![ - Instr::Machine(X86Instr::MOV32rr { - dest, - src: lhs, - }), - Instr::Machine(X86Instr::SUB32rr { - dest, - src: rhs, - }) + MInstr::Machine(Instr::MOV32rr { dest, src: lhs }), + MInstr::Machine(Instr::SUB32rr { dest, src: rhs }) ] } Self::Add32ri => { @@ -676,8 +690,8 @@ impl machine::Pattern for Pattern { let lhs = pattern.lhs.try_as_reg().unwrap().clone(); let rhs = pattern.rhs.try_as_imm().cloned().unwrap(); smallvec![ - Instr::Pseudo(PseudoInstr::Copy(dest.clone(), lhs)), - Instr::Machine(X86Instr::ADD32ri { + MInstr::Pseudo(PseudoInstr::Copy(dest.clone(), lhs)), + MInstr::Machine(Instr::ADD32ri { dest, immediate: rhs, }) @@ -689,24 +703,17 @@ impl machine::Pattern for Pattern { let lhs = pattern.lhs.try_as_reg().unwrap().clone(); let rhs = pattern.rhs.try_as_reg().unwrap().clone(); smallvec![ - Instr::Machine(X86Instr::MOV32rr { + MInstr::Machine(Instr::MOV32rr { dest: dest.clone(), src: lhs, }), - Instr::Machine(X86Instr::ADD32rr { - dest, - src: rhs, - }) + MInstr::Machine(Instr::ADD32rr { dest, src: rhs }) ] } Self::Jmp => { let target = matched.try_as_br().unwrap().target; - smallvec![ - Instr::Machine(X86Instr::JMP { - target - }) - ] + smallvec![MInstr::Machine(Instr::JMP { target })] } Self::Cmp32rreq => { let matched = matched.try_as_cmp().unwrap(); @@ -715,18 +722,8 @@ impl machine::Pattern for Pattern { let dest = *matched.dest.try_as_reg().unwrap(); let cc = matched.cmp_op.into(); smallvec![ - Instr::Machine( - X86Instr::CMP32rr { - lhs, - rhs, - } - ), - Instr::Machine( - X86Instr::SETCC { - dest, - cc, - } - ) + MInstr::Machine(Instr::CMP32rr { lhs, rhs }), + MInstr::Machine(Instr::SETCC { dest, cc }) ] } Self::Cmp32rigt => { @@ -736,18 +733,8 @@ impl machine::Pattern for Pattern { let dest = *matched.dest.try_as_reg().unwrap(); let cc = CC::Gt; smallvec![ - Instr::Machine( - X86Instr::CMP32ri { - lhs, - rhs, - } - ), - Instr::Machine( - X86Instr::SETCC { - dest, - cc, - } - ) + MInstr::Machine(Instr::CMP32ri { lhs, rhs }), + MInstr::Machine(Instr::SETCC { dest, cc }) ] } Self::CondJmp => { @@ -757,32 +744,26 @@ impl machine::Pattern for Pattern { let false_target = matched.false_target; let cc = CC::Eq; let cmp_instr = match cond.size(function) { - Size::Byte => X86Instr::CMP8ri { + Size::Byte => Instr::CMP8ri { lhs: cond, rhs: Immediate::from(0u8), }, Size::Word => unimplemented!(), - Size::DWord => X86Instr::CMP32ri { + Size::DWord => Instr::CMP32ri { lhs: cond, rhs: Immediate::from(0u32), }, Size::QWord => unimplemented!(), }; smallvec![ - Instr::Machine( - cmp_instr - ), - Instr::Machine( - X86Instr::JCC { - cc, - target: false_target, - } - ), - Instr::Machine( - X86Instr::JMP { - target: true_target, - } - ) + MInstr::Machine(cmp_instr), + MInstr::Machine(Instr::JCC { + cc, + target: false_target, + }), + MInstr::Machine(Instr::JMP { + target: true_target, + }) ] } } @@ -792,63 +773,50 @@ impl machine::Pattern for Pattern { #[derive(Default)] pub struct Backend {} - impl machine::Backend for Backend { - type ABI = Abi; + type TM = Target; type P = Pattern; fn patterns() -> &'static [Self::P] { Self::P::VARIANTS } - fn mov(dest: PhysicalRegister, src: PhysicalRegister) -> ::I { + fn mov(dest: PhysicalRegister, src: PhysicalRegister) -> Instr { let dest_size = dest.size(); assert_eq!(dest_size, src.size()); let dest = Register::Physical(dest); let src = Register::Physical(src); match dest_size { - Size::Byte => X86Instr::MOV8rr { - dest, - src, - }, - Size::Word => X86Instr::MOV16rr { - dest, - src, - }, - Size::DWord => X86Instr::MOV32rr { - dest, - src, - }, - Size::QWord => X86Instr::MOV64rr { - dest, - src, - } + Size::Byte => Instr::MOV8rr { dest, src }, + Size::Word => Instr::MOV16rr { dest, src }, + Size::DWord => Instr::MOV32rr { dest, src }, + Size::QWord => Instr::MOV64rr { dest, src }, } } - fn mov_imm(dest: PhysicalRegister, imm: Immediate) -> X86Instr { + fn mov_imm(dest: PhysicalRegister, imm: Immediate) -> Instr { let dest = Register::Physical(dest); match imm.size { - Size::Byte => X86Instr::MOV8ri { + Size::Byte => Instr::MOV8ri { dest, immediate: imm, }, - Size::Word => X86Instr::MOV16ri { + Size::Word => Instr::MOV16ri { dest, immediate: imm, }, - Size::DWord => X86Instr::MOV32ri { + Size::DWord => Instr::MOV32ri { dest, immediate: imm, }, - Size::QWord => X86Instr::MOV64ri { + Size::QWord => Instr::MOV64ri { dest, immediate: imm, - } + }, } } - fn ret() -> X86Instr { - X86Instr::RET + fn ret() -> Instr { + Instr::RET } fn new() -> Self { diff --git a/ir/crates/back/src/emu.rs b/ir/crates/back/src/emu.rs index 8762609..03de7d7 100644 --- a/ir/crates/back/src/emu.rs +++ b/ir/crates/back/src/emu.rs @@ -1,16 +1,35 @@ use std::ops::Range; -use anyhow::{anyhow, bail}; -use anyhow::Result; -use tracing::{debug, warn}; -use unicorn_engine::{RegisterARM, RegisterX86, SECOND_SCALE}; -use unicorn_engine::unicorn_const::{Arch, Mode, Permission, uc_error}; +use anyhow::{ + anyhow, + bail, + Result, +}; +use tracing::{ + debug, + warn, +}; +use unicorn_engine::{ + SECOND_SCALE, + unicorn_const::{ + Arch, + Mode, + Permission, + uc_error, + }, +}; -use crate::codegen::isa::{Architecture, Endianness}; -use crate::codegen::machine::{Abi, FunctionId, PhysicalRegister, TargetMachine}; -use crate::codegen::machine::abi::calling_convention::Slot; -use crate::codegen::machine::abi::CallingConvention; -use crate::codegen::machine::module::AsmModule; +use crate::codegen::machine::{ + abi::{ + calling_convention::Slot, + CallingConvention, + }, + Architecture, + function::FunctionId, + isa::PhysicalRegister, + module::asm::AsmModule, + TargetMachine, +}; pub struct Emulator<'module, 'code, 'unicorn, TM: TargetMachine> { emu: unicorn_engine::Unicorn<'unicorn, ()>, @@ -18,64 +37,81 @@ pub struct Emulator<'module, 'code, 'unicorn, TM: TargetMachine> { } impl<'module, 'code, TM: TargetMachine> Emulator<'module, 'code, '_, TM> { - pub fn new( - module: &'code AsmModule<'module, TM>, - ) -> Self { + pub fn new(module: &'code AsmModule<'module, TM>) -> Self { let (arch, mode) = match TM::arch() { Architecture::X86_64 => (Arch::X86, Mode::MODE_64), }; - debug!("Initializing Unicorn instance with arch {:?} and mode {:?}", arch, mode); - let mut emu = unicorn_engine::Unicorn::new(arch, mode).expect("failed to initialize Unicorn instance"); + debug!( + "Initializing Unicorn instance with arch {:?} and mode {:?}", + arch, mode + ); + let mut emu = unicorn_engine::Unicorn::new(arch, mode) + .expect("failed to initialize Unicorn instance"); let unaligned_size = module.code().len() as u64; let unaligned_base_addr = module.base_addr(); let aligned_base_addr = Self::align_down_to_4kb(unaligned_base_addr); let aligned_size = Self::align_up_to_4kb(unaligned_size); - emu.mem_map(aligned_base_addr, aligned_size as usize, Permission::ALL).expect("failed to map code page"); - emu.mem_write(unaligned_base_addr, module.code()).expect("failed to write instructions"); + emu.mem_map(aligned_base_addr, aligned_size as usize, Permission::ALL) + .expect("failed to map code page"); + emu.mem_write(unaligned_base_addr, module.code()) + .expect("failed to write instructions"); Self { emu, module } } pub fn run_function(&mut self, func_id: FunctionId, arguments: &[u64]) -> Result { let function = &self.module.machine_module().functions[func_id]; if function.params.len() != arguments.len() { - bail!("Invalid number of arguments. Expected {}, got {}", function.params.len(), arguments.len()); + bail!( + "Invalid number of arguments. Expected {}, got {}", + function.params.len(), + arguments.len() + ); } - let parameters = function.params.iter().copied().map( - |vreg| function.vregs[vreg].size - ); + let parameters = function + .params + .iter() + .copied() + .map(|vreg| function.vregs[vreg].size); let Range { start: start_addr, end: end_addr, - } = self.module.code_range_of(func_id).ok_or_else(|| anyhow!("Function not found"))?; - let param_slots = <::Abi as Abi>::CallingConvention::parameter_slots(parameters); + } = self + .module + .code_range_of(func_id) + .ok_or_else(|| anyhow!("Function not found"))?; + let param_slots = TM::CallingConvention::parameter_slots(parameters); for (param_slot, arg) in param_slots.zip(arguments.iter().copied()) { match param_slot { Slot::Register(reg) => { - self.emu.reg_write(reg.into_unicorn_emu_reg(), arg).expect("failed to write argument"); + self.emu + .reg_write(reg.into_unicorn_emu_reg(), arg) + .expect("failed to write argument"); } - Slot::Stack => unimplemented!() + Slot::Stack => unimplemented!(), } } debug!("Running function at address 0x{:x}", start_addr); - if let Err(err) = self.emu.emu_start(start_addr, end_addr, 10 * SECOND_SCALE, 0) { + if let Err(err) = self + .emu + .emu_start(start_addr, end_addr, 10 * SECOND_SCALE, 0) + { match err { uc_error::READ_UNMAPPED => { warn!("Read from unmapped memory (potentially the return address that was never pushed)"); } - _ => bail!("Failed to run function: {:?}", err) + _ => bail!("Failed to run function: {:?}", err), } } - let ret_slot = <::Abi as Abi>::CallingConvention::return_slot( - function.return_ty_size - ); + let ret_slot = TM::CallingConvention::return_slot(function.return_ty_size); match ret_slot { - Slot::Register(reg) => { - Ok(self.emu.reg_read(reg.into_unicorn_emu_reg()).expect("failed to read reg")) - } - Slot::Stack => unimplemented!() + Slot::Register(reg) => Ok(self + .emu + .reg_read(reg.into_unicorn_emu_reg()) + .expect("failed to read reg")), + Slot::Stack => unimplemented!(), } } - + const fn align_up_to_4kb(addr: u64) -> u64 { (addr + 0xfff) & !0xfff } @@ -83,4 +119,4 @@ impl<'module, 'code, TM: TargetMachine> Emulator<'module, 'code, '_, TM> { const fn align_down_to_4kb(addr: u64) -> u64 { addr & !0xfff } -} \ No newline at end of file +} diff --git a/ir/crates/back/src/lib.rs b/ir/crates/back/src/lib.rs index 0d4a25f..33b4080 100644 --- a/ir/crates/back/src/lib.rs +++ b/ir/crates/back/src/lib.rs @@ -1,18 +1,11 @@ -#![deny( - clippy::enum_glob_use, -)] -#![warn( - clippy::pedantic, - clippy::nursery, -)] +#![deny(clippy::enum_glob_use)] +#![warn(clippy::pedantic, clippy::nursery)] #![forbid(unsafe_code)] #![allow(clippy::too_many_lines)] -pub mod codegen; -pub mod emu; - #[macro_use] extern crate strum; - +pub mod codegen; +pub mod emu; diff --git a/ir/crates/compiler/src/main.rs b/ir/crates/compiler/src/main.rs index b9adcfd..6f93fbb 100644 --- a/ir/crates/compiler/src/main.rs +++ b/ir/crates/compiler/src/main.rs @@ -3,9 +3,9 @@ use std::path::PathBuf; use anyhow::Result; use clap::Parser; use tracing::debug; -use natrix_back::codegen::isa::{Architecture, Endianness}; use natrix_back::codegen::register_allocator; +use natrix_back::codegen::targets::x86_64; use natrix_back::emu::Emulator; use natrix_middle::{FrontBridge, optimization}; @@ -42,7 +42,7 @@ fn main() -> Result<()> { config.dead_code_elimination = false; // module.optimize(config); println!("{module}"); - let mut x86_mod = natrix_back::codegen::machine::module::Builder::::new(&mut module).build(); + let mut x86_mod = natrix_back::codegen::machine::module::Builder::::new(&mut module).build(); x86_mod.run_register_allocator(); x86_mod.run_register_coalescer(); x86_mod.remove_fallthrough_jumps(); diff --git a/ir/rustfmt.toml b/ir/rustfmt.toml new file mode 100644 index 0000000..348b6df --- /dev/null +++ b/ir/rustfmt.toml @@ -0,0 +1,4 @@ +format_code_in_doc_comments = true +imports_granularity = "Crate" +imports_layout = "Vertical" +group_imports = "StdExternalCrate"