diff --git a/src/ion/fast_alloc2.rs b/src/ion/fast_alloc2.rs index 5374b48..6d361ea 100644 --- a/src/ion/fast_alloc2.rs +++ b/src/ion/fast_alloc2.rs @@ -23,7 +23,7 @@ struct VRegData { preg: PReg, stack_slot: u32, - // 1 = reftype, 2 = preg_valid, 4 = stack_slot_valid + // 1 = reftype, 2 = preg_valid, 4 = stack_slot_valid, 8 = stack_slot_allocated flags: u8, // use information @@ -114,7 +114,8 @@ impl Default for VRegData { impl VRegData { const FLAG_REFTYPE: u8 = 1; const FLAG_PREG: u8 = 2; - const FLAG_SLOT: u8 = 4; + const FLAG_SLOT_VALID: u8 = 4; + const FLAG_SLOT_ALLOCATED: u8 = 8; pub fn preg(&self) -> Option { if self.flags & Self::FLAG_PREG != 0 { @@ -137,22 +138,48 @@ impl VRegData { } pub fn stack_slot(&self) -> Option { - if self.flags & Self::FLAG_SLOT != 0 { + if self.flags & Self::FLAG_SLOT_VALID != 0 { + if cfg!(debug_assertions) { + if self.flags & Self::FLAG_SLOT_ALLOCATED == 0 { + panic!("Encountered stack slot that is valid but not allocated"); + } + } Some(self.stack_slot) } else { None } } - pub fn set_stack_slot(&mut self, slot: u32) { - self.stack_slot = slot; - self.flags |= Self::FLAG_SLOT; + pub fn allocated_stack_slot(&self) -> Option { + if self.flags & Self::FLAG_SLOT_ALLOCATED != 0 { + Some(self.stack_slot) + } else { + None + } } - pub fn clear_stack_slot(&mut self) { - self.flags &= !Self::FLAG_SLOT; + pub fn set_stack_slot_allocated(&mut self, slot: u32) { + self.stack_slot = slot; + self.flags |= Self::FLAG_SLOT_ALLOCATED; + } + + pub fn set_stack_slot_valid(&mut self) { if cfg!(debug_assertions) { - self.stack_slot = 0xFFFFFFFF; + if (self.flags & Self::FLAG_SLOT_ALLOCATED) == 0 { + panic!("Trying to set a nonallocated stack slot to valid"); + } + } + self.flags |= Self::FLAG_SLOT_VALID; + } + + pub fn clear_stack_slot(&mut self, only_valid: bool) { + if only_valid { + self.flags &= !Self::FLAG_SLOT_VALID; + } else { + self.flags &= !(Self::FLAG_SLOT_ALLOCATED | Self::FLAG_SLOT_VALID); + if cfg!(debug_assertions) { + self.stack_slot = 0xFFFFFFFF; + } } } @@ -631,10 +658,12 @@ impl<'a, F: Function> FastAlloc<'a, F> { let data = &mut self.vregs[vreg]; match loc.slot() { Some(slot) => { - data.set_stack_slot(slot); + debug_assert!(data.allocated_stack_slot().is_some()); + debug_assert_eq!(data.allocated_stack_slot().unwrap(), slot); + data.set_stack_slot_valid(); } None => { - data.clear_stack_slot(); + data.clear_stack_slot(true); } } @@ -912,7 +941,7 @@ impl<'a, F: Function> FastAlloc<'a, F> { let new_preg = self.find_free_reg( op.vreg().class(), reg_blacklist, - true, + Some(ProgPoint::before(inst)), block, block_after_pos, )?; @@ -949,7 +978,7 @@ impl<'a, F: Function> FastAlloc<'a, F> { let preg = self.find_free_reg( op.vreg().class(), reg_blacklist, - true, + Some(ProgPoint::before(inst)), block, block_after_pos, )?; @@ -974,7 +1003,7 @@ impl<'a, F: Function> FastAlloc<'a, F> { let preg = self.find_free_reg( op.vreg().class(), reg_blacklist, - true, + Some(ProgPoint::before(inst)), block, block_after_pos, )?; @@ -1102,7 +1131,7 @@ impl<'a, F: Function> FastAlloc<'a, F> { let preg = self.find_free_reg( op.vreg().class(), late_def_disallow, - true, + Some(ProgPoint::before(inst)), block, block_after_pos, )?; @@ -1133,7 +1162,7 @@ impl<'a, F: Function> FastAlloc<'a, F> { let preg = self.find_free_reg( op.vreg().class(), reg_blacklist, - false, + None, block, block_after_pos, ); @@ -1209,9 +1238,10 @@ impl<'a, F: Function> FastAlloc<'a, F> { trace!(" -> {} mapping to {} is killed", out_vreg, succ_param); if let Some(slot) = self.vregs[out_vreg.vreg()].stack_slot() { trace!(" -> Transferring slot {} to {}", slot, succ_param); - self.vregs[succ_param.vreg()].set_stack_slot(slot); + self.vregs[succ_param.vreg()].set_stack_slot_allocated(slot); + self.vregs[succ_param.vreg()].set_stack_slot_valid(); if cfg!(debug_assertions) { - self.vregs[out_vreg.vreg()].clear_stack_slot(); + self.vregs[out_vreg.vreg()].clear_stack_slot(false); } } if let Some(preg) = self.vregs[out_vreg.vreg()].preg() { @@ -1282,7 +1312,7 @@ impl<'a, F: Function> FastAlloc<'a, F> { let preg = self.find_free_reg( succ_param.class(), pregs_allocated, - true, + Some(ProgPoint::before(last_inst)), block, self.cur_inst_pos + 1, )?; @@ -1392,7 +1422,7 @@ impl<'a, F: Function> FastAlloc<'a, F> { &mut self, reg_class: RegClass, reg_blacklist: PRegSet, - spill: bool, + spill_pos: Option, // does not spill if None block: Block, block_after_pos: usize, ) -> Result { @@ -1411,13 +1441,19 @@ impl<'a, F: Function> FastAlloc<'a, F> { match self.pregs[preg.index()].vreg() { None => return Ok(preg), Some(vreg) => { - if self.vreg_killed(vreg.vreg(), inst, block, block_last_inst, true) { + if spill_pos.is_none() { + continue; + } + + if self.vreg_killed(vreg.vreg(), block, block_after_pos, true) { self.clear_preg(preg.index()); return Ok(preg); } // TODO: prefer regs that are already spilled? - let next_use = self.next_use(vreg.vreg()); + let next_use = self + .next_use(vreg.vreg()) + .expect("vreg should have next use"); if let Some((use_pos, vreg)) = ffu_reg { if use_pos < next_use { ffu_reg = Some((next_use, vreg)); @@ -1436,7 +1472,7 @@ impl<'a, F: Function> FastAlloc<'a, F> { let (_, vreg) = ffu_reg.unwrap(); if self.vregs[vreg.vreg()].stack_slot().is_none() { // TODO: do we need to specify the progpoint? - self.alloc_and_move_to_stack(vreg, ProgPoint::before(inst)); + self.alloc_and_move_to_stack(vreg, spill_pos.unwrap()); } let preg = self.vregs[vreg.vreg()].preg().unwrap(); @@ -1455,7 +1491,25 @@ impl<'a, F: Function> FastAlloc<'a, F> { block: Block, block_after_pos: usize, ) { - todo!("") + trace!("Moving {} to {} at {:?}", vreg, preg, pos); + if let Some(cur_vreg) = self.pregs[preg.index()].vreg() { + if !self.vreg_killed(cur_vreg.vreg(), block, block_after_pos, true) { + if self.vregs[cur_vreg.vreg()].stack_slot().is_none() { + self.alloc_and_move_to_stack(cur_vreg, pos); + } + self.clear_preg(preg.index()); + } + } + + let slot = self.vregs[vreg.vreg()].stack_slot().unwrap(); + trace!(" -> from slot {}", slot); + self.edits.push(( + pos, + Edit::Move { + from: Allocation::stack(SpillSlot::new(slot as usize)), + to: Allocation::reg(preg), + }, + )); } // Allocates a stack slot for a vreg and moves it there @@ -1468,7 +1522,7 @@ impl<'a, F: Function> FastAlloc<'a, F> { // Moves a vreg to stack // panics if there is no stack slot or the vreg is not in a preg fn move_to_stack(&mut self, vreg: VReg, pos: ProgPoint) { - let slot = if let Some(slot) = self.vregs[vreg.vreg()].stack_slot() { + let slot = if let Some(slot) = self.vregs[vreg.vreg()].allocated_stack_slot() { slot } else { panic!("Trying to move {} to stack but it has no slot", vreg); @@ -1488,10 +1542,11 @@ impl<'a, F: Function> FastAlloc<'a, F> { to: Allocation::stack(SpillSlot::new(slot as usize)), }, )); + self.vregs[vreg.vreg()].set_stack_slot_valid(); } // Allocates a stack slot for a vreg - // panics if there is a slot already + // panics if there is a valid slot already fn alloc_stack_slot(&mut self, vreg: VReg) -> u32 { if self.vregs[vreg.vreg()].stack_slot().is_some() { panic!( @@ -1500,9 +1555,19 @@ impl<'a, F: Function> FastAlloc<'a, F> { ); } - let slot = self.create_stack_slot(vreg.class()); - self.vregs[vreg.vreg()].set_stack_slot(slot); - trace!("Allocated slot {} for {}", slot, vreg); + let slot = match self.vregs[vreg.vreg()].allocated_stack_slot() { + Some(slot) => { + trace!("Using already allocated slot {} for {}", slot, vreg); + slot + } + None => { + let slot = self.create_stack_slot(vreg.class()); + self.vregs[vreg.vreg()].set_stack_slot_allocated(slot); + trace!("Allocated slot {} for {}", slot, vreg); + slot + } + }; + slot } @@ -1522,6 +1587,7 @@ impl<'a, F: Function> FastAlloc<'a, F> { // State Helpers // assigns vreg to preg and saves any live vreg in the preg + // does no moving of the vreg fn allocate_preg_for_vreg( &mut self, preg: PReg, @@ -1530,7 +1596,17 @@ impl<'a, F: Function> FastAlloc<'a, F> { block: Block, block_after_pos: usize, ) { - todo!("") + trace!("Assigning {} to {} at {:?}", vreg, preg, pos); + if let Some(cur_vreg) = self.pregs[preg.index()].vreg() { + if !self.vreg_killed(cur_vreg.vreg(), block, block_after_pos, true) { + if self.vregs[cur_vreg.vreg()].stack_slot().is_none() { + self.alloc_and_move_to_stack(cur_vreg, pos); + } + self.clear_preg(preg.index()); + } + } + + self.assign_preg(preg, vreg); } fn assign_preg(&mut self, preg: PReg, vreg: VReg) { @@ -1646,14 +1722,6 @@ impl<'a, F: Function> FastAlloc<'a, F> { return false; } - fn next_use(&self, vreg: usize) -> u32 { - let data = &self.vregs[vreg]; - if data.cur_use_idx as usize >= data.uses.len() { - panic!("next_use called on {} which does not have one", vreg); - } - return data.uses[data.cur_use_idx as usize]; - } - // Misc /// Make sure all reftype vregs currently in pregs have a stack slot