diff --git a/lib/cretonne/src/regalloc/coloring.rs b/lib/cretonne/src/regalloc/coloring.rs index 56244bcc3c..878656ae84 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -147,8 +147,11 @@ impl<'a> Context<'a> { &mut regs, &mut func.locations, &func.signature); - tracker.drop_dead(inst); + } else { + let (_throughs, kills) = tracker.process_ghost(inst); + self.process_ghost_kills(kills, &mut regs, &func.locations); } + tracker.drop_dead(inst); } } @@ -377,6 +380,9 @@ impl<'a> Context<'a> { } } } + + self.forget_diverted(kills); + *regs = output_regs; } @@ -569,4 +575,36 @@ impl<'a> Context<'a> { dfg.ins(pos).regmove(m.value, m.from, m.to); } } + + /// Forget about any register diversions in `kills`. + fn forget_diverted(&mut self, kills: &[LiveValue]) { + if self.divert.is_empty() { + return; + } + + for lv in kills { + if lv.affinity.is_reg() { + self.divert.remove(lv.value); + } + } + } + + /// Process kills on a ghost instruction. + /// - Forget diversions. + /// - Free killed registers. + fn process_ghost_kills(&mut self, + kills: &[LiveValue], + regs: &mut AllocatableSet, + locations: &ValueLocations) { + for lv in kills { + if let Affinity::Reg(rci) = lv.affinity { + let rc = self.reginfo.rc(rci); + let reg = match self.divert.remove(lv.value) { + Some(r) => r, + None => locations[lv.value].unwrap_reg(), + }; + regs.free(rc, reg); + } + } + } } diff --git a/lib/cretonne/src/regalloc/diversion.rs b/lib/cretonne/src/regalloc/diversion.rs index e8e8d3d190..e07b694ab9 100644 --- a/lib/cretonne/src/regalloc/diversion.rs +++ b/lib/cretonne/src/regalloc/diversion.rs @@ -51,6 +51,11 @@ impl RegDiversions { self.current.clear() } + /// Are there any diversions? + pub fn is_empty(&self) -> bool { + self.current.is_empty() + } + /// Get the current diversion of `value`, if any. pub fn diversion(&self, value: Value) -> Option<&Diversion> { self.current.iter().find(|d| d.value == value) @@ -83,6 +88,16 @@ impl RegDiversions { self.current.push(Diversion::new(value, from, to)); } } + + /// Drop any recorded register move for `value`. + /// + /// Returns the `to` register of the removed diversion. + pub fn remove(&mut self, value: Value) -> Option { + self.current + .iter() + .position(|d| d.value == value) + .map(|i| self.current.swap_remove(i).to) + } } #[cfg(test)] diff --git a/lib/cretonne/src/regalloc/live_value_tracker.rs b/lib/cretonne/src/regalloc/live_value_tracker.rs index ccaf6c9209..b243b93923 100644 --- a/lib/cretonne/src/regalloc/live_value_tracker.rs +++ b/lib/cretonne/src/regalloc/live_value_tracker.rs @@ -280,6 +280,16 @@ impl LiveValueTracker { &self.live.values[first_def..]) } + /// Prepare to move past a ghost instruction. + /// + /// This is like `process_inst`, except any defs are ignored. + /// + /// Returns `(throughs, kills)`. + pub fn process_ghost(&mut self, inst: Inst) -> (&[LiveValue], &[LiveValue]) { + let first_kill = self.live.live_after(inst); + self.live.values.as_slice().split_at(first_kill) + } + /// Drop the values that are now dead after moving past `inst`. /// /// This removes both live values that were killed by `inst` and dead defines on `inst` itself. diff --git a/lib/cretonne/src/regalloc/spilling.rs b/lib/cretonne/src/regalloc/spilling.rs index f1feff3f4b..09559c9d26 100644 --- a/lib/cretonne/src/regalloc/spilling.rs +++ b/lib/cretonne/src/regalloc/spilling.rs @@ -129,9 +129,12 @@ impl<'a> Context<'a> { while let Some(inst) = pos.next_inst() { if let Some(constraints) = self.encinfo.operand_constraints(self.encodings[inst]) { self.visit_inst(inst, constraints, &mut pos, dfg, tracker); - tracker.drop_dead(inst); - self.process_spills(tracker); + } else { + let (_throughs, kills) = tracker.process_ghost(inst); + self.free_regs(kills); } + tracker.drop_dead(inst); + self.process_spills(tracker); } }