Generalize RegDiversions to track stack locations too.

For emergency spilling, we need to be able to temporarily divert an SSA
value to a stack slot if there are no available registers.
This commit is contained in:
Jakob Stoklund Olesen
2017-10-04 12:13:25 -07:00
parent e32aa8ab60
commit d4aeec6ece
2 changed files with 69 additions and 39 deletions

View File

@@ -547,7 +547,14 @@ impl<'a> Context<'a> {
if pred(lr, &self.cur.func) { if pred(lr, &self.cur.func) {
if let Affinity::Reg(rci) = lr.affinity { if let Affinity::Reg(rci) = lr.affinity {
let rc = self.reginfo.rc(rci); let rc = self.reginfo.rc(rci);
self.solver.reassign_in(rdiv.value, rc, rdiv.to, rdiv.from); // Stack diversions should not be possible here. The only live transiently
// during `shuffle_inputs()`.
self.solver.reassign_in(
rdiv.value,
rc,
rdiv.to.unwrap_reg(),
rdiv.from.unwrap_reg(),
);
} else { } else {
panic!( panic!(
"Diverted register {} with {} affinity", "Diverted register {} with {} affinity",
@@ -758,11 +765,11 @@ impl<'a> Context<'a> {
for lv in kills { for lv in kills {
if let Affinity::Reg(rci) = lv.affinity { if let Affinity::Reg(rci) = lv.affinity {
let rc = self.reginfo.rc(rci); let rc = self.reginfo.rc(rci);
let reg = match self.divert.remove(lv.value) { let loc = match self.divert.remove(lv.value) {
Some(r) => r, Some(loc) => loc,
None => self.cur.func.locations[lv.value].unwrap_reg(), None => self.cur.func.locations[lv.value],
}; };
regs.free(rc, reg); regs.free(rc, loc.unwrap_reg());
} }
} }
} }

View File

@@ -7,12 +7,11 @@
//! These register diversions are local to an EBB. No values can be diverted when entering a new //! These register diversions are local to an EBB. No values can be diverted when entering a new
//! EBB. //! EBB.
use entity::EntityMap; use ir::{Value, ValueLoc, ValueLocations, StackSlot};
use ir::{Value, ValueLoc};
use isa::{RegUnit, RegInfo}; use isa::{RegUnit, RegInfo};
use std::fmt; use std::fmt;
/// A diversion of a value from its original register location to a new register. /// A diversion of a value from its original location to a new register or stack location.
/// ///
/// In IL, a diversion is represented by a `regmove` instruction, possibly a chain of them for the /// In IL, a diversion is represented by a `regmove` instruction, possibly a chain of them for the
/// same value. /// same value.
@@ -23,20 +22,21 @@ use std::fmt;
pub struct Diversion { pub struct Diversion {
/// The value that is diverted. /// The value that is diverted.
pub value: Value, pub value: Value,
/// The original register value location. /// The original value location.
pub from: RegUnit, pub from: ValueLoc,
/// The current register value location. /// The current value location.
pub to: RegUnit, pub to: ValueLoc,
} }
impl Diversion { impl Diversion {
/// Make a new register diversion. /// Make a new diversion.
pub fn new(value: Value, from: RegUnit, to: RegUnit) -> Diversion { pub fn new(value: Value, from: ValueLoc, to: ValueLoc) -> Diversion {
debug_assert!(from.is_assigned() && to.is_assigned());
Diversion { value, from, to } Diversion { value, from, to }
} }
} }
/// Keep track of register diversions in an EBB. /// Keep track of diversions in an EBB.
pub struct RegDiversions { pub struct RegDiversions {
current: Vec<Diversion>, current: Vec<Diversion>,
} }
@@ -67,17 +67,30 @@ impl RegDiversions {
self.current.as_slice() self.current.as_slice()
} }
/// Get the current register location for `value`. Fall back to the assignment map for /// Get the current location for `value`. Fall back to the assignment map for non-diverted
/// non-diverted values. /// values
pub fn reg(&self, value: Value, locations: &EntityMap<Value, ValueLoc>) -> RegUnit { pub fn get(&self, value: Value, locations: &ValueLocations) -> ValueLoc {
match self.diversion(value) { match self.diversion(value) {
Some(d) => d.to, Some(d) => d.to,
None => locations[value].unwrap_reg(), None => locations[value],
} }
} }
/// Record a register move. /// Get the current register location for `value`, or panic if `value` isn't in a register.
pub fn regmove(&mut self, value: Value, from: RegUnit, to: RegUnit) { pub fn reg(&self, value: Value, locations: &ValueLocations) -> RegUnit {
self.get(value, locations).unwrap_reg()
}
/// Get the current stack location for `value`, or panic if `value` isn't in a stack slot.
pub fn stack(&self, value: Value, locations: &ValueLocations) -> StackSlot {
self.get(value, locations).unwrap_stack()
}
/// Record any kind of move.
///
/// The `from` location must match an existing `to` location, if any.
pub fn divert(&mut self, value: Value, from: ValueLoc, to: ValueLoc) {
debug_assert!(from.is_assigned() && to.is_assigned());
if let Some(i) = self.current.iter().position(|d| d.value == value) { if let Some(i) = self.current.iter().position(|d| d.value == value) {
debug_assert_eq!(self.current[i].to, from, "Bad regmove chain for {}", value); debug_assert_eq!(self.current[i].to, from, "Bad regmove chain for {}", value);
if self.current[i].from != to { if self.current[i].from != to {
@@ -90,10 +103,25 @@ impl RegDiversions {
} }
} }
/// Drop any recorded register move for `value`. /// Record a register -> register move.
pub fn regmove(&mut self, value: Value, from: RegUnit, to: RegUnit) {
self.divert(value, ValueLoc::Reg(from), ValueLoc::Reg(to));
}
/// Record a register -> stack move.
pub fn regspill(&mut self, value: Value, from: RegUnit, to: StackSlot) {
self.divert(value, ValueLoc::Reg(from), ValueLoc::Stack(to));
}
/// Record a stack -> register move.
pub fn regfill(&mut self, value: Value, from: StackSlot, to: RegUnit) {
self.divert(value, ValueLoc::Stack(from), ValueLoc::Reg(to));
}
/// Drop any recorded move for `value`.
/// ///
/// Returns the `to` register of the removed diversion. /// Returns the `to` location of the removed diversion.
pub fn remove(&mut self, value: Value) -> Option<RegUnit> { pub fn remove(&mut self, value: Value) -> Option<ValueLoc> {
self.current.iter().position(|d| d.value == value).map( self.current.iter().position(|d| d.value == value).map(
|i| { |i| {
self.current.swap_remove(i).to self.current.swap_remove(i).to
@@ -114,18 +142,13 @@ impl<'a> fmt::Display for DisplayDiversions<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{{")?; write!(f, "{{")?;
for div in self.0.all() { for div in self.0.all() {
match self.1 { write!(
Some(regs) => { f,
write!( " {}: {} -> {}",
f, div.value,
" {}: {} -> {}", div.from.display(self.1),
div.value, div.to.display(self.1)
regs.display_regunit(div.from), )?
regs.display_regunit(div.to)
)?
}
None => write!(f, " {}: %{} -> %{}", div.value, div.from, div.to)?,
}
} }
write!(f, " }}") write!(f, " }}")
} }
@@ -148,14 +171,14 @@ mod tests {
divs.diversion(v1), divs.diversion(v1),
Some(&Diversion { Some(&Diversion {
value: v1, value: v1,
from: 10, from: ValueLoc::Reg(10),
to: 12, to: ValueLoc::Reg(12),
}) })
); );
assert_eq!(divs.diversion(v2), None); assert_eq!(divs.diversion(v2), None);
divs.regmove(v1, 12, 11); divs.regmove(v1, 12, 11);
assert_eq!(divs.diversion(v1).unwrap().to, 11); assert_eq!(divs.diversion(v1).unwrap().to, ValueLoc::Reg(11));
divs.regmove(v1, 11, 10); divs.regmove(v1, 11, 10);
assert_eq!(divs.diversion(v1), None); assert_eq!(divs.diversion(v1), None);
} }