diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..44014b6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "editor.formatOnSave": true, + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer" + }, + "rust-analyzer.cargo.features": [ + "default" + ] +} \ No newline at end of file diff --git a/src/cfg.rs b/src/cfg.rs index cbb6cea..c68c2f3 100644 --- a/src/cfg.rs +++ b/src/cfg.rs @@ -33,7 +33,7 @@ pub struct CFGInfo { } impl CFGInfo { - pub fn new(f: &F) -> Result { + pub fn new(f: &F, needs_loop_depth: bool) -> Result { let postorder = postorder::calculate(f.num_blocks(), f.entry_block(), |block| { f.block_succs(block) }); @@ -98,22 +98,24 @@ impl CFGInfo { } let mut approx_loop_depth = vec![]; - let mut backedge_stack: SmallVec<[usize; 4]> = smallvec![]; - let mut cur_depth = 0; - for block in 0..f.num_blocks() { - if backedge_in[block] > 0 { - cur_depth += 1; - backedge_stack.push(backedge_in[block]); - } + if needs_loop_depth { + let mut backedge_stack: SmallVec<[usize; 4]> = smallvec![]; + let mut cur_depth = 0; + for block in 0..f.num_blocks() { + if backedge_in[block] > 0 { + cur_depth += 1; + backedge_stack.push(backedge_in[block]); + } - approx_loop_depth.push(cur_depth); + approx_loop_depth.push(cur_depth); - while backedge_stack.len() > 0 && backedge_out[block] > 0 { - backedge_out[block] -= 1; - *backedge_stack.last_mut().unwrap() -= 1; - if *backedge_stack.last().unwrap() == 0 { - cur_depth -= 1; - backedge_stack.pop(); + while backedge_stack.len() > 0 && backedge_out[block] > 0 { + backedge_out[block] -= 1; + *backedge_stack.last_mut().unwrap() -= 1; + if *backedge_stack.last().unwrap() == 0 { + cur_depth -= 1; + backedge_stack.pop(); + } } } } diff --git a/src/ion/fast_alloc.rs b/src/ion/fast_alloc.rs new file mode 100644 index 0000000..2b8d95a --- /dev/null +++ b/src/ion/fast_alloc.rs @@ -0,0 +1,311 @@ +use alloc::vec; +use alloc::vec::Vec; +use smallvec::{smallvec, SmallVec}; +use std::convert::TryFrom; + +use crate::{ + cfg::CFGInfo, Allocation, Block, Edit, Function, Inst, MachineEnv, Operand, OperandConstraint, + OperandKind, OperandPos, Output, PReg, ProgPoint, RegAllocError, RegClass, SpillSlot, VReg, +}; + +#[derive(Default, Clone, Copy)] +struct VRegData { + pub preg: Option, + pub slot_idx: Option, + pub reftype: bool, +} + +#[derive(Default, Clone, Copy)] +struct PRegData { + pub vreg: Option, + pub stack_pseudo: bool, +} + +#[derive(Default, Clone, Copy)] +struct BlockData { + pub allocated: bool, +} + +struct FastAllocState<'a, F: Function> { + pub vregs: Vec, + pub pregs: Vec, + pub blocks: Vec, + + pub preorder: Vec, + pub reg_order_int: Vec, + pub reg_order_float: Vec, + + pub cur_stack_slot_idx: u32, + pub reftype_vregs_in_pregs_count: u32, + + pub stack_slot_count_int: u8, + pub stack_slot_count_float: u8, + + pub allocs: Vec, + pub inst_alloc_offsets: Vec, + pub edits: Vec<(ProgPoint, Edit)>, + pub safepoint_slots: Vec<(ProgPoint, Allocation)>, + + pub func: &'a F, + pub mach_env: &'a MachineEnv, + pub cfg: &'a CFGInfo, +} + +impl<'a, F: Function> FastAllocState<'a, F> { + pub fn init(func: &'a F, mach_env: &'a MachineEnv, cfg: &'a CFGInfo) -> Self { + let vregs = { + let mut vregs = Vec::with_capacity(func.num_vregs()); + vregs.resize(func.num_vregs(), VRegData::default()); + + for vreg in func.reftype_vregs() { + vregs[vreg.vreg()].reftype = true; + } + + vregs + }; + + let pregs = { + let mut pregs = Vec::with_capacity(PReg::NUM_INDEX); + pregs.resize(PReg::NUM_INDEX, PRegData::default()); + + for preg in &mach_env.fixed_stack_slots { + pregs[preg.index()].stack_pseudo = true; + } + pregs + }; + + let blocks = { + let mut blocks = Vec::with_capacity(func.num_blocks()); + blocks.resize(func.num_blocks(), BlockData::default()); + blocks + }; + + let reg_order_int = { + let class = RegClass::Int as usize; + let amount = mach_env.preferred_regs_by_class[class].len() + + mach_env.non_preferred_regs_by_class[class].len(); + let mut reg_order = Vec::with_capacity(amount); + reg_order.extend_from_slice(&mach_env.preferred_regs_by_class[class]); + reg_order.extend_from_slice(&mach_env.non_preferred_regs_by_class[class]); + reg_order + }; + + let reg_order_float = { + let class = RegClass::Float as usize; + let amount = mach_env.preferred_regs_by_class[class].len() + + mach_env.non_preferred_regs_by_class[class].len(); + let mut reg_order = Vec::with_capacity(amount); + reg_order.extend_from_slice(&mach_env.preferred_regs_by_class[class]); + reg_order.extend_from_slice(&mach_env.non_preferred_regs_by_class[class]); + reg_order + }; + + let mut inst_alloc_offsets = Vec::with_capacity(func.num_insts()); + inst_alloc_offsets.resize(func.num_insts(), 0); + + Self { + vregs, + pregs, + blocks, + + preorder: Self::calc_preorder(func), + reg_order_int, + reg_order_float, + + cur_stack_slot_idx: 0, + reftype_vregs_in_pregs_count: 0, + + stack_slot_count_int: u8::try_from(func.spillslot_size(RegClass::Int)) + .expect("that's a big integer"), + stack_slot_count_float: u8::try_from(func.spillslot_size(RegClass::Float)) + .expect("that's a big float"), + + allocs: Vec::new(), + inst_alloc_offsets, + edits: Vec::new(), + safepoint_slots: Vec::new(), + + func, + mach_env, + cfg, + } + } + + pub fn get_or_alloc_stack_slot(&mut self, vreg: VReg) -> u32 { + if let Some(idx) = self.vregs[vreg.vreg()].slot_idx { + return idx; + } + + self.alloc_stack_slot(vreg) + } + + pub fn alloc_stack_slot(&mut self, vreg: VReg) -> u32 { + let data = &mut self.vregs[vreg.vreg()]; + if data.slot_idx.is_some() { + panic!("Trying to allocate already allocated stack slot"); + } + + let size = if vreg.class() == RegClass::Int { + self.stack_slot_count_int + } else { + self.stack_slot_count_float + }; + let idx = self.cur_stack_slot_idx; + self.cur_stack_slot_idx += size as u32; + data.slot_idx = Some(idx); + idx + } + + pub fn move_to_preg(&mut self, vreg: VReg, preg: PReg, pos: ProgPoint) { + if let Some(vreg) = &self.pregs[preg.index()].vreg { + let vdata = &mut self.vregs[*vreg as usize]; + debug_assert!(vdata.preg.is_some()); + debug_assert_eq!(vdata.preg.unwrap(), preg); + vdata.preg = None; + } + + if let Some(preg) = &self.vregs[vreg.vreg()].preg { + // TODO: allow multiple pregs for a single vreg? + let pdata = &mut self.pregs[preg.index()]; + debug_assert!(pdata.vreg.is_some()); + debug_assert_eq!(pdata.vreg.unwrap(), vreg.vreg() as u32); + pdata.vreg = None; + } + + let vdata = &mut self.vregs[vreg.vreg()]; + let pdata = &mut self.pregs[preg.index()]; + + if vdata.slot_idx.is_none() { + panic!("Trying to move from vreg that has no stack slot to preg"); + } + + self.edits.push(( + pos, + Edit::Move { + from: Allocation::stack(SpillSlot::new(vdata.slot_idx.unwrap() as usize)), + to: Allocation::reg(preg), + }, + )); + vdata.preg = Some(preg); + pdata.vreg = Some(vreg.vreg() as u32); + + if vdata.reftype { + self.reftype_vregs_in_pregs_count += 1; + } + } + + pub fn move_to_stack(&mut self, preg: PReg, vreg: VReg, pos: ProgPoint) { + let vdata = &mut self.vregs[vreg.vreg()]; + let pdata = &mut self.pregs[preg.index()]; + if pdata.vreg.is_none() || vdata.preg.is_none() { + panic!("Trying to move from unallocated preg/vreg to stack"); + } + debug_assert_eq!(vdata.preg.unwrap(), preg); + debug_assert_eq!(pdata.vreg.unwrap(), vreg.vreg() as u32); + + if vdata.slot_idx.is_none() { + panic!("Trying to move to vreg without stack slot"); + } + + self.edits.push(( + pos, + Edit::Move { + from: Allocation::reg(preg), + to: Allocation::stack(SpillSlot::new(vdata.slot_idx.unwrap() as usize)), + }, + )); + } + + pub fn clear_preg(&mut self, preg: PReg) { + let pdata = &mut self.pregs[preg.index()]; + if let Some(vreg) = pdata.vreg { + let vdata = &mut self.vregs[vreg as usize]; + debug_assert_eq!(vdata.preg.unwrap(), preg); + vdata.preg = None; + pdata.vreg = None; + if vdata.reftype { + self.reftype_vregs_in_pregs_count -= 1; + } + } + } + + pub fn clear_vreg_from_reg(&mut self, vreg: VReg) { + let vdata = &mut self.vregs[vreg.vreg()]; + if let Some(preg) = vdata.preg { + debug_assert_eq!(self.pregs[preg.index()].vreg.unwrap(), vreg.vreg() as u32); + self.pregs[preg.index()].vreg = None; + vdata.preg = None; + + if vdata.reftype { + self.reftype_vregs_in_pregs_count -= 1; + } + } + } + + fn calc_preorder(func: &F) -> Vec { + let entry = func.entry_block(); + let mut ret = vec![entry]; + + struct State<'a> { + block: Block, + succs: &'a [Block], + next_succ: usize, + } + let mut stack: SmallVec<[State; 64]> = smallvec![]; + + stack.push(State { + block: entry, + succs: func.block_succs(entry), + next_succ: 0, + }); + + while let Some(ref mut state) = stack.last_mut() { + if state.next_succ >= state.succs.len() { + stack.pop(); + continue; + } + + let block = state.succs[state.next_succ]; + let succs = func.block_succs(block); + ret.push(block); + + state.next_succ += 1; + if state.next_succ >= state.succs.len() { + stack.pop(); + } + + if !succs.is_empty() { + stack.push(State { + block, + succs: func.block_succs(block), + next_succ: 0, + }); + } + } + + ret + } +} + +pub fn run( + func: &F, + mach_env: &MachineEnv, + cfg: CFGInfo, +) -> Result { + if func.multi_spillslot_named_by_last_slot() { + panic!("MultiSpillslotIndexPos not supported"); + } + + let mut state = FastAllocState::init(func, mach_env, &cfg); + + todo!("") +} + +fn setup_entry_params<'a, F: Function>( + state: &mut FastAllocState<'a, F>, +) -> Result<(), RegAllocError> { + // we need to set the vreg location for the initial block parameters and copy them to the stack + let entry = state.func.entry_block(); + + todo!("") +} diff --git a/src/ion/fast_alloc.rs.bak b/src/ion/fast_alloc.rs.bak new file mode 100644 index 0000000..2589a8e --- /dev/null +++ b/src/ion/fast_alloc.rs.bak @@ -0,0 +1,594 @@ +use alloc::vec; +use alloc::vec::Vec; +use smallvec::{smallvec, SmallVec}; + +use crate::{ + cfg::CFGInfo, Allocation, Block, Edit, Function, Inst, MachineEnv, Operand, OperandConstraint, + OperandKind, OperandPos, Output, PReg, ProgPoint, RegAllocError, RegClass, SpillSlot, VReg, +}; + +#[derive(Clone, Copy)] +struct VRegInfo { + pub stack_slot: Option, + pub alloc_at_inst: Inst, + pub cur_reg: Option, +} + +impl Default for VRegInfo { + fn default() -> Self { + Self { + stack_slot: None, + alloc_at_inst: Inst::invalid(), + cur_reg: None, + } + } +} + +struct AllocState { + pub vregs: Vec, + pub preorder: Vec, + pub allocs: Vec, + pub inst_alloc_offsets: Vec, + pub edits: Vec<(ProgPoint, Edit)>, + pub cur_stack_slot_idx: u32, + pub safepoint_slots: Vec<(ProgPoint, Allocation)>, +} + +impl AllocState { + pub fn init(func: &F) -> Self { + let mut vregs = Vec::with_capacity(func.num_vregs()); + vregs.resize(func.num_vregs(), VRegInfo::default()); + + let mut inst_alloc_offsets = Vec::with_capacity(func.num_insts()); + inst_alloc_offsets.resize(func.num_insts(), 0); + + Self { + vregs, + preorder: Self::calc_preorder(func), + allocs: Vec::new(), + inst_alloc_offsets, + edits: Vec::new(), + cur_stack_slot_idx: 0, + safepoint_slots: Vec::new(), + } + } + + fn calc_preorder(func: &F) -> Vec { + let entry = func.entry_block(); + let mut ret = vec![entry]; + + struct State<'a> { + block: Block, + succs: &'a [Block], + next_succ: usize, + } + let mut stack: SmallVec<[State; 64]> = smallvec![]; + + stack.push(State { + block: entry, + succs: func.block_succs(entry), + next_succ: 0, + }); + + while let Some(ref mut state) = stack.last_mut() { + if state.next_succ >= state.succs.len() { + stack.pop(); + continue; + } + + let block = state.succs[state.next_succ]; + let succs = func.block_succs(block); + ret.push(block); + + state.next_succ += 1; + if state.next_succ >= state.succs.len() { + stack.pop(); + } + + if !succs.is_empty() { + stack.push(State { + block, + succs: func.block_succs(block), + next_succ: 0, + }); + } + } + + ret + } +} + +pub fn run( + func: &F, + mach_env: &MachineEnv, + cfg: CFGInfo, +) -> Result { + let mut state = AllocState::init(func); + + let reg_order_int = { + let class = RegClass::Int as usize; + let amount = mach_env.preferred_regs_by_class[class].len() + + mach_env.non_preferred_regs_by_class[class].len(); + let mut reg_order = Vec::with_capacity(amount); + reg_order.extend_from_slice(&mach_env.preferred_regs_by_class[class]); + reg_order.extend_from_slice(&mach_env.non_preferred_regs_by_class[class]); + reg_order + }; + + let reg_order_float = { + let class = RegClass::Float as usize; + let amount = mach_env.preferred_regs_by_class[class].len() + + mach_env.non_preferred_regs_by_class[class].len(); + let mut reg_order = Vec::with_capacity(amount); + reg_order.extend_from_slice(&mach_env.preferred_regs_by_class[class]); + reg_order.extend_from_slice(&mach_env.non_preferred_regs_by_class[class]); + reg_order + }; + + let slot_size_int = func.spillslot_size(RegClass::Int) as u32; + let slot_size_float = func.spillslot_size(RegClass::Float) as u32; + + // TODO: this currently does not handle multiple defs/uses of the same vreg properly + + // TODO: move to AllocState, needs to also contain if this is a pseudo-preg and refers to the stack + let mut preg_state: Vec = Vec::with_capacity(PReg::NUM_INDEX); + preg_state.resize(PReg::NUM_INDEX, VReg::invalid()); + + for block in &state.preorder { + for inst in func.block_insns(*block).iter() { + let operands = func.inst_operands(inst); + let alloc_idx = state.allocs.len(); + state.inst_alloc_offsets[inst.index()] = alloc_idx as u32; + + state + .allocs + .resize(alloc_idx + operands.len(), Allocation::none()); + + // both of these smallvecs could hold 16 entries + // late uses may not be chosen as a target for writes + let mut late_use_regs: SmallVec<[PReg; 4]> = smallvec![]; + let mut early_write_regs: SmallVec<[PReg; 4]> = smallvec![]; + + // TODO: wouldnt need this if we look up the inst a vreg was allocated at + let mut regs_allocated: SmallVec<[PReg; 8]> = smallvec![]; + + let mut cur_idx_int = 0; + let mut cur_idx_float = 0; + let mut has_early_write = false; + + // allocate uses; fixed reg first, then the others + for (i, op) in operands.iter().enumerate() { + if op.kind() != OperandKind::Use { + if op.pos() != OperandPos::Early { + continue; + } + has_early_write = true; + } + + let vreg = op.vreg(); + + match op.constraint() { + OperandConstraint::Any | OperandConstraint::Reg | OperandConstraint::Stack => { + continue + } + OperandConstraint::FixedReg(reg) => { + if preg_state[reg.index()] != VReg::invalid() + && preg_state[reg.index()] != vreg + { + return Err(RegAllocError::TooManyLiveRegs); + } + + state.allocs[alloc_idx + i] = Allocation::reg(reg); + preg_state[reg.index()] = vreg; + state.vregs[vreg.vreg()].alloc_at_inst = inst; + state.vregs[vreg.vreg()].cur_reg = Some(reg); + regs_allocated.push(reg); + if op.pos() == OperandPos::Late { + late_use_regs.push(reg); + } + + if op.kind() == OperandKind::Use { + // Early Defs do not need to be moved + match state.vregs[vreg.vreg()].stack_slot { + Some(slot) => { + state.edits.push(( + ProgPoint::before(inst), + Edit::Move { + from: Allocation::stack(SpillSlot::new(slot as usize)), + to: Allocation::reg(reg), + }, + )); + } + None => { + return Err(RegAllocError::SSA(vreg, inst)); + } + } + } else { + early_write_regs.push(reg); + let slot = match state.vregs[vreg.vreg()].stack_slot { + Some(slot) => slot, + None => { + let size = if op.class() == RegClass::Int { + slot_size_int + } else { + slot_size_float + }; + let slot = state.cur_stack_slot_idx; + state.cur_stack_slot_idx += size; + state.vregs[vreg.vreg()].stack_slot = Some(slot); + slot + } + }; + state.edits.push(( + ProgPoint::after(inst), + Edit::Move { + from: Allocation::reg(reg), + to: Allocation::stack(SpillSlot::new(slot as usize)), + }, + )); + } + } + OperandConstraint::Reuse(_) => panic!("Reuse constraint for Use/Early-Write"), + } + } + + for (i, op) in operands.iter().enumerate() { + if op.kind() != OperandKind::Use { + if op.pos() != OperandPos::Early { + continue; + } + continue; + } + + let vreg = op.vreg(); + let vreg_idx = vreg.vreg(); + + match op.constraint() { + OperandConstraint::Stack => { + // values need to be on stack already + match state.vregs[vreg_idx].stack_slot { + Some(slot) => { + state.allocs[alloc_idx + i] = + Allocation::stack(SpillSlot::new(slot as usize)); + } + None => { + if op.kind() == OperandKind::Use { + return Err(RegAllocError::SSA(vreg, inst)); + } + // alloc stack slot + let size = if op.class() == RegClass::Int { + slot_size_int + } else { + slot_size_float + }; + let slot = state.cur_stack_slot_idx; + state.cur_stack_slot_idx += size; + state.allocs[alloc_idx + i] = + Allocation::stack(SpillSlot::new(slot as usize)); + state.vregs[vreg_idx].stack_slot = Some(slot); + } + } + } + // TODO: do any on stack? + OperandConstraint::Any | OperandConstraint::Reg => { + // check if vreg has been allocated before + let mut alloc = None; + for (j, op) in operands.iter().enumerate() { + if j >= i { + break; + } + + if op.vreg() == vreg { + alloc = Some(state.allocs[alloc_idx + j].clone()); + break; + } + } + + if let Some(a) = alloc { + state.allocs[alloc_idx + i] = a; + continue; + } + + // find first non-allocated register + let reg_order = if op.class() == RegClass::Int { + ®_order_int + } else { + ®_order_float + }; + let reg_order_idx = if op.class() == RegClass::Int { + &mut cur_idx_int + } else { + &mut cur_idx_float + }; + loop { + if *reg_order_idx >= reg_order.len() { + break; + } + + if preg_state[reg_order[*reg_order_idx].index()] != VReg::invalid() { + *reg_order_idx += 1; + continue; + } + + let reg = reg_order[*reg_order_idx]; + + // TODO: helper func for alloc + state.allocs[alloc_idx + i] = Allocation::reg(reg); + preg_state[reg.index()] = vreg; + state.vregs[vreg.vreg()].alloc_at_inst = inst; + state.vregs[vreg.vreg()].cur_reg = Some(reg); + regs_allocated.push(reg); + if op.pos() == OperandPos::Late { + late_use_regs.push(reg); + } + + if op.kind() == OperandKind::Use { + match state.vregs[vreg_idx].stack_slot { + Some(slot) => { + state.edits.push(( + ProgPoint::before(inst), + Edit::Move { + from: Allocation::stack(SpillSlot::new( + slot as usize, + )), + to: Allocation::reg(reg), + }, + )); + } + None => { + return Err(RegAllocError::SSA(vreg, inst)); + } + } + } else { + early_write_regs.push(reg); + let slot = match state.vregs[vreg.vreg()].stack_slot { + Some(slot) => slot, + None => { + let size = if op.class() == RegClass::Int { + slot_size_int + } else { + slot_size_float + }; + let slot = state.cur_stack_slot_idx; + state.cur_stack_slot_idx += size; + state.vregs[vreg.vreg()].stack_slot = Some(slot); + slot + } + }; + state.edits.push(( + ProgPoint::after(inst), + Edit::Move { + from: Allocation::reg(reg), + to: Allocation::stack(SpillSlot::new(slot as usize)), + }, + )); + } + } + + // No register available + // TODO: try to evict vreg that does not need to be in a preg + if op.constraint() == OperandConstraint::Reg { + return Err(RegAllocError::TooManyLiveRegs); + } + + // any can live on the stack + match state.vregs[vreg_idx].stack_slot { + Some(slot) => { + state.allocs[alloc_idx + i] = + Allocation::stack(SpillSlot::new(slot as usize)); + } + None => { + if op.kind() == OperandKind::Use { + return Err(RegAllocError::SSA(vreg, inst)); + } + // alloc stack slot + let size = if op.class() == RegClass::Int { + slot_size_int + } else { + slot_size_float + }; + let slot = state.cur_stack_slot_idx; + state.cur_stack_slot_idx += size; + state.allocs[alloc_idx + i] = + Allocation::stack(SpillSlot::new(slot as usize)); + state.vregs[vreg_idx].stack_slot = Some(slot); + } + } + } + OperandConstraint::FixedReg(_) => continue, + OperandConstraint::Reuse(_) => unreachable!(), + } + } + + // Handle writes + for (i, op) in operands.iter().enumerate() { + if op.kind() != OperandKind::Def || op.pos() == OperandPos::Early { + continue; + } + + let vreg = op.vreg(); + let vreg_idx = vreg.vreg(); + + match op.constraint() { + OperandConstraint::FixedReg(reg) => { + let reg_idx = reg.index(); + + if preg_state[reg_idx] != VReg::invalid() { + // if the register is occupied by a late use we abort + // TODO: move the allocation, if possible + if late_use_regs.contains(®) || early_write_regs.contains(®) { + todo!("trying to allocate fixed reg def on late use/early write"); + } + + // overwrite + let vreg = preg_state[reg_idx].vreg(); + state.vregs[vreg].cur_reg = None; + } + + preg_state[reg_idx] = vreg; + state.vregs[vreg_idx].cur_reg = Some(reg); + state.allocs[alloc_idx + i] = Allocation::reg(reg); + + match state.vregs[vreg_idx].stack_slot { + None => { + // alloc stack slot + let size = if op.class() == RegClass::Int { + slot_size_int + } else { + slot_size_float + }; + let slot = state.cur_stack_slot_idx; + state.cur_stack_slot_idx += size; + state.vregs[vreg_idx].stack_slot = Some(slot); + + // move to stack after inst + state.edits.push(( + ProgPoint::after(inst), + Edit::Move { + from: Allocation::reg(reg), + to: Allocation::stack(SpillSlot::new(slot as usize)), + }, + )); + } + Some(_) => { + return Err(RegAllocError::SSA(vreg, inst)); + } + } + } + _ => continue, + } + } + + cur_idx_int = 0; + cur_idx_float = 0; + for (i, op) in operands.iter().enumerate() { + if op.kind() != OperandKind::Def || op.pos() == OperandPos::Early { + continue; + } + + let vreg = op.vreg(); + let vreg_idx = vreg.vreg(); + + match op.constraint() { + OperandConstraint::Reg => { + // find first non-allocated register + let reg_order = if op.class() == RegClass::Int { + ®_order_int + } else { + ®_order_float + }; + let reg_order_idx = if op.class() == RegClass::Int { + &mut cur_idx_int + } else { + &mut cur_idx_float + }; + loop { + if *reg_order_idx >= reg_order.len() { + break; + } + let reg = reg_order[*reg_order_idx]; + + if late_use_regs.contains(®) || early_write_regs.contains(®) { + *reg_order_idx += 1; + continue; + } + + // remove allocated + if preg_state[reg.index()] != VReg::invalid() { + let vreg_idx = preg_state[reg.index()].vreg(); + state.vregs[vreg_idx].cur_reg = None; + } else { + regs_allocated.push(reg); + } + + // TODO: helper func for alloc + state.allocs[alloc_idx + i] = Allocation::reg(reg); + preg_state[reg.index()] = vreg; + state.vregs[vreg.vreg()].alloc_at_inst = inst; + state.vregs[vreg.vreg()].cur_reg = Some(reg); + + // alloc stack slot + let size = if op.class() == RegClass::Int { + slot_size_int + } else { + slot_size_float + }; + let slot = state.cur_stack_slot_idx; + state.cur_stack_slot_idx += size; + state.vregs[vreg_idx].stack_slot = Some(slot); + + match state.vregs[vreg_idx].stack_slot { + Some(slot) => { + state.edits.push(( + ProgPoint::after(inst), + Edit::Move { + from: Allocation::reg(reg), + to: Allocation::stack(SpillSlot::new(slot as usize)), + }, + )); + } + None => { + return Err(RegAllocError::SSA(vreg, inst)); + } + } + } + + // No register available + // TODO: try to evict vreg that does not need to be in a preg + return Err(RegAllocError::TooManyLiveRegs); + } + OperandConstraint::Any | OperandConstraint::Stack => { + // alloc stack slot + let size = if op.class() == RegClass::Int { + slot_size_int + } else { + slot_size_float + }; + let slot = state.cur_stack_slot_idx; + state.cur_stack_slot_idx += size; + state.vregs[vreg_idx].stack_slot = Some(slot); + state.allocs[alloc_idx + i] = + Allocation::stack(SpillSlot::new(slot as usize)); + } + OperandConstraint::Reuse(op_idx) => { + let alloc = state.allocs[alloc_idx + op_idx]; + if alloc.is_none() || operands[op_idx].pos() == OperandPos::Late { + panic!("Invalid reuse"); + } + + state.allocs[alloc_idx + i] = alloc; + if let Some(alloc) = alloc.as_stack() { + state.vregs[vreg_idx].stack_slot = Some(alloc.index() as u32); + state.vregs[operands[op_idx].vreg().vreg()].stack_slot = None; + } else if let Some(reg) = alloc.as_reg() { + state.vregs[operands[op_idx].vreg().vreg()].cur_reg = None; + state.vregs[vreg_idx].cur_reg = Some(reg); + + // alloc stack slot + let size = if op.class() == RegClass::Int { + slot_size_int + } else { + slot_size_float + }; + let slot = state.cur_stack_slot_idx; + state.cur_stack_slot_idx += size; + state.vregs[vreg_idx].stack_slot = Some(slot); + + // move to stack after inst + state.edits.push(( + ProgPoint::after(inst), + Edit::Move { + from: Allocation::reg(reg), + to: Allocation::stack(SpillSlot::new(slot as usize)), + }, + )); + } + } + OperandConstraint::FixedReg(_) => continue, + } + } + } + } + + todo!("") +} diff --git a/src/ion/mod.rs b/src/ion/mod.rs index 550af90..246777b 100644 --- a/src/ion/mod.rs +++ b/src/ion/mod.rs @@ -39,6 +39,7 @@ pub(crate) mod dump; pub(crate) mod moves; pub(crate) mod spill; pub(crate) mod stackmap; +mod fast_alloc; impl<'a, F: Function> Env<'a, F> { pub(crate) fn new( @@ -120,13 +121,18 @@ pub fn run( mach_env: &MachineEnv, enable_annotations: bool, enable_ssa_checker: bool, + use_fast_alloc: bool, ) -> Result { - let cfginfo = CFGInfo::new(func)?; + let cfginfo = CFGInfo::new(func, !use_fast_alloc)?; if enable_ssa_checker { validate_ssa(func, &cfginfo)?; } + if use_fast_alloc { + return fast_alloc::run(func, mach_env, cfginfo); + } + let mut env = Env::new(func, mach_env, cfginfo, enable_annotations); env.init()?; diff --git a/src/lib.rs b/src/lib.rs index 8aa9d4c..252f0a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1491,7 +1491,7 @@ pub fn run( env: &MachineEnv, options: &RegallocOptions, ) -> Result { - ion::run(func, env, options.verbose_log, options.validate_ssa) + ion::run(func, env, options.verbose_log, options.validate_ssa, options.fast_alloc) } /// Options for allocation. @@ -1502,4 +1502,7 @@ pub struct RegallocOptions { /// Run the SSA validator before allocating registers. pub validate_ssa: bool, + + /// Use a fast algorithm that may return suboptimal results. + pub fast_alloc: bool, }