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:
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,19 +142,14 @@ 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 {
|
|
||||||
Some(regs) => {
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
" {}: {} -> {}",
|
" {}: {} -> {}",
|
||||||
div.value,
|
div.value,
|
||||||
regs.display_regunit(div.from),
|
div.from.display(self.1),
|
||||||
regs.display_regunit(div.to)
|
div.to.display(self.1)
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user