Regalloc: remove the transient LiveRangeContext data structure;
This commit is contained in:
@@ -199,7 +199,8 @@ impl<'a> Context<'a> {
|
|||||||
if self.liveness[param].reaches_use(
|
if self.liveness[param].reaches_use(
|
||||||
pred_inst,
|
pred_inst,
|
||||||
pred_ebb,
|
pred_ebb,
|
||||||
self.liveness.context(&self.func.layout),
|
self.liveness.forest(),
|
||||||
|
&self.func.layout,
|
||||||
) {
|
) {
|
||||||
self.isolate_param(ebb, param);
|
self.isolate_param(ebb, param);
|
||||||
}
|
}
|
||||||
@@ -240,7 +241,6 @@ impl<'a> Context<'a> {
|
|||||||
// `ebb`, it can never be used as an EBB argument.
|
// `ebb`, it can never be used as an EBB argument.
|
||||||
let interference = {
|
let interference = {
|
||||||
let lr = &self.liveness[arg];
|
let lr = &self.liveness[arg];
|
||||||
let ctx = self.liveness.context(&self.func.layout);
|
|
||||||
|
|
||||||
// There are two ways the argument value can interfere with `ebb`:
|
// There are two ways the argument value can interfere with `ebb`:
|
||||||
//
|
//
|
||||||
@@ -255,7 +255,7 @@ impl<'a> Context<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// The only other possibility is that `arg` is live-in to `ebb`.
|
// The only other possibility is that `arg` is live-in to `ebb`.
|
||||||
lr.is_livein(ebb, ctx)
|
lr.is_livein(ebb, self.liveness.forest(), &self.func.layout)
|
||||||
};
|
};
|
||||||
|
|
||||||
if interference {
|
if interference {
|
||||||
@@ -435,8 +435,12 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
// Check for interference between `parent` and `value`. Since `parent` dominates
|
// Check for interference between `parent` and `value`. Since `parent` dominates
|
||||||
// `value`, we only have to check if it overlaps the definition.
|
// `value`, we only have to check if it overlaps the definition.
|
||||||
let ctx = self.liveness.context(&self.func.layout);
|
if self.liveness[parent.value].overlaps_def(
|
||||||
if self.liveness[parent.value].overlaps_def(node.def, node.ebb, ctx) {
|
node.def,
|
||||||
|
node.ebb,
|
||||||
|
self.liveness.forest(),
|
||||||
|
&self.func.layout,
|
||||||
|
) {
|
||||||
// The two values are interfering, so they can't be in the same virtual register.
|
// The two values are interfering, so they can't be in the same virtual register.
|
||||||
debug!("-> interference: {} overlaps def of {}", parent, value);
|
debug!("-> interference: {} overlaps def of {}", parent, value);
|
||||||
return false;
|
return false;
|
||||||
@@ -593,7 +597,6 @@ impl<'a> Context<'a> {
|
|||||||
// This gives us the closest dominating value def for each of the values.
|
// This gives us the closest dominating value def for each of the values.
|
||||||
self.forest.clear();
|
self.forest.clear();
|
||||||
self.values.clear();
|
self.values.clear();
|
||||||
let ctx = self.liveness.context(&self.func.layout);
|
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
// Accumulate ordered values for the new vreg.
|
// Accumulate ordered values for the new vreg.
|
||||||
if node.is_value() {
|
if node.is_value() {
|
||||||
@@ -623,7 +626,12 @@ impl<'a> Context<'a> {
|
|||||||
// Check if the parent value interferes with the virtual copy.
|
// Check if the parent value interferes with the virtual copy.
|
||||||
let inst = node.def.unwrap_inst();
|
let inst = node.def.unwrap_inst();
|
||||||
if node.set_id != parent.set_id
|
if node.set_id != parent.set_id
|
||||||
&& self.liveness[parent.value].reaches_use(inst, node.ebb, ctx)
|
&& self.liveness[parent.value].reaches_use(
|
||||||
|
inst,
|
||||||
|
node.ebb,
|
||||||
|
self.liveness.forest(),
|
||||||
|
&self.func.layout,
|
||||||
|
)
|
||||||
{
|
{
|
||||||
debug!(
|
debug!(
|
||||||
" - interference: {} overlaps vcopy at {}:{}",
|
" - interference: {} overlaps vcopy at {}:{}",
|
||||||
@@ -647,7 +655,12 @@ impl<'a> Context<'a> {
|
|||||||
// Both node and parent are values, so check for interference.
|
// Both node and parent are values, so check for interference.
|
||||||
debug_assert!(node.is_value() && parent.is_value());
|
debug_assert!(node.is_value() && parent.is_value());
|
||||||
if node.set_id != parent.set_id
|
if node.set_id != parent.set_id
|
||||||
&& self.liveness[parent.value].overlaps_def(node.def, node.ebb, ctx)
|
&& self.liveness[parent.value].overlaps_def(
|
||||||
|
node.def,
|
||||||
|
node.ebb,
|
||||||
|
self.liveness.forest(),
|
||||||
|
&self.func.layout,
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// The two values are interfering.
|
// The two values are interfering.
|
||||||
debug!(" - interference: {} overlaps def of {}", parent, node.value);
|
debug!(" - interference: {} overlaps def of {}", parent, node.value);
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ use crate::regalloc::affinity::Affinity;
|
|||||||
use crate::regalloc::diversion::RegDiversions;
|
use crate::regalloc::diversion::RegDiversions;
|
||||||
use crate::regalloc::live_value_tracker::{LiveValue, LiveValueTracker};
|
use crate::regalloc::live_value_tracker::{LiveValue, LiveValueTracker};
|
||||||
use crate::regalloc::liveness::Liveness;
|
use crate::regalloc::liveness::Liveness;
|
||||||
use crate::regalloc::liverange::{LiveRange, LiveRangeContext};
|
use crate::regalloc::liverange::{LiveRange, LiveRangeForest};
|
||||||
use crate::regalloc::register_set::RegisterSet;
|
use crate::regalloc::register_set::RegisterSet;
|
||||||
use crate::regalloc::solver::{Solver, SolverError};
|
use crate::regalloc::solver::{Solver, SolverError};
|
||||||
use crate::timing;
|
use crate::timing;
|
||||||
@@ -461,7 +461,7 @@ impl<'a> Context<'a> {
|
|||||||
"Can't handle EBB arguments: {}",
|
"Can't handle EBB arguments: {}",
|
||||||
self.cur.display_inst(inst)
|
self.cur.display_inst(inst)
|
||||||
);
|
);
|
||||||
self.undivert_regs(|lr, _| !lr.is_local());
|
self.undivert_regs(|lr, _, _| !lr.is_local());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,8 +725,13 @@ impl<'a> Context<'a> {
|
|||||||
// This code runs after calling `solver.inputs_done()` so we must identify
|
// This code runs after calling `solver.inputs_done()` so we must identify
|
||||||
// the new variable as killed or live-through. Always special-case the
|
// the new variable as killed or live-through. Always special-case the
|
||||||
// pinned register as a through variable.
|
// pinned register as a through variable.
|
||||||
let ctx = self.liveness.context(&self.cur.func.layout);
|
let layout = &self.cur.func.layout;
|
||||||
if self.liveness[value].killed_at(inst, ctx.order.pp_ebb(inst), ctx) {
|
if self.liveness[value].killed_at(
|
||||||
|
inst,
|
||||||
|
layout.pp_ebb(inst),
|
||||||
|
self.liveness.forest(),
|
||||||
|
layout,
|
||||||
|
) {
|
||||||
self.solver.add_killed_var(value, op.regclass, cur_reg);
|
self.solver.add_killed_var(value, op.regclass, cur_reg);
|
||||||
} else {
|
} else {
|
||||||
self.solver.add_through_var(value, op.regclass, cur_reg);
|
self.solver.add_through_var(value, op.regclass, cur_reg);
|
||||||
@@ -755,7 +760,7 @@ impl<'a> Context<'a> {
|
|||||||
//
|
//
|
||||||
// Values with a global live range that are not live in to `dest` could appear as branch
|
// Values with a global live range that are not live in to `dest` could appear as branch
|
||||||
// arguments, so they can't always be un-diverted.
|
// arguments, so they can't always be un-diverted.
|
||||||
self.undivert_regs(|lr, ctx| lr.is_livein(dest, ctx));
|
self.undivert_regs(|lr, forest, layout| lr.is_livein(dest, forest, layout));
|
||||||
|
|
||||||
// Now handle the EBB arguments.
|
// Now handle the EBB arguments.
|
||||||
let br_args = self.cur.func.dfg.inst_variable_args(inst);
|
let br_args = self.cur.func.dfg.inst_variable_args(inst);
|
||||||
@@ -825,14 +830,14 @@ impl<'a> Context<'a> {
|
|||||||
/// are reallocated to their global register assignments.
|
/// are reallocated to their global register assignments.
|
||||||
fn undivert_regs<Pred>(&mut self, mut pred: Pred)
|
fn undivert_regs<Pred>(&mut self, mut pred: Pred)
|
||||||
where
|
where
|
||||||
Pred: FnMut(&LiveRange, LiveRangeContext<Layout>) -> bool,
|
Pred: FnMut(&LiveRange, &LiveRangeForest, &Layout) -> bool,
|
||||||
{
|
{
|
||||||
for (&value, rdiv) in self.divert.iter() {
|
for (&value, rdiv) in self.divert.iter() {
|
||||||
let lr = self
|
let lr = self
|
||||||
.liveness
|
.liveness
|
||||||
.get(value)
|
.get(value)
|
||||||
.expect("Missing live range for diverted register");
|
.expect("Missing live range for diverted register");
|
||||||
if pred(lr, self.liveness.context(&self.cur.func.layout)) {
|
if pred(lr, self.liveness.forest(), &self.cur.func.layout) {
|
||||||
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);
|
||||||
// Stack diversions should not be possible here. They only live transiently
|
// Stack diversions should not be possible here. They only live transiently
|
||||||
@@ -1080,20 +1085,21 @@ impl<'a> Context<'a> {
|
|||||||
use crate::ir::instructions::BranchInfo::*;
|
use crate::ir::instructions::BranchInfo::*;
|
||||||
|
|
||||||
let inst = self.cur.current_inst().expect("Not on an instruction");
|
let inst = self.cur.current_inst().expect("Not on an instruction");
|
||||||
let ctx = self.liveness.context(&self.cur.func.layout);
|
let layout = &self.cur.func.layout;
|
||||||
|
let forest = self.liveness.forest();
|
||||||
match self.cur.func.dfg.analyze_branch(inst) {
|
match self.cur.func.dfg.analyze_branch(inst) {
|
||||||
NotABranch => false,
|
NotABranch => false,
|
||||||
SingleDest(ebb, _) => {
|
SingleDest(ebb, _) => {
|
||||||
let lr = &self.liveness[value];
|
let lr = &self.liveness[value];
|
||||||
lr.is_livein(ebb, ctx)
|
lr.is_livein(ebb, forest, layout)
|
||||||
}
|
}
|
||||||
Table(jt, ebb) => {
|
Table(jt, ebb) => {
|
||||||
let lr = &self.liveness[value];
|
let lr = &self.liveness[value];
|
||||||
!lr.is_local()
|
!lr.is_local()
|
||||||
&& (ebb.map_or(false, |ebb| lr.is_livein(ebb, ctx))
|
&& (ebb.map_or(false, |ebb| lr.is_livein(ebb, forest, layout))
|
||||||
|| self.cur.func.jump_tables[jt]
|
|| self.cur.func.jump_tables[jt]
|
||||||
.iter()
|
.iter()
|
||||||
.any(|ebb| lr.is_livein(*ebb, ctx)))
|
.any(|ebb| lr.is_livein(*ebb, forest, layout)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,6 @@ impl LiveValueTracker {
|
|||||||
.idom_sets
|
.idom_sets
|
||||||
.get(&idom)
|
.get(&idom)
|
||||||
.expect("No stored live set for dominator");
|
.expect("No stored live set for dominator");
|
||||||
let ctx = liveness.context(layout);
|
|
||||||
// Get just the values that are live-in to `ebb`.
|
// Get just the values that are live-in to `ebb`.
|
||||||
for &value in idom_live_list.as_slice(&self.idom_pool) {
|
for &value in idom_live_list.as_slice(&self.idom_pool) {
|
||||||
let lr = liveness
|
let lr = liveness
|
||||||
@@ -199,7 +198,7 @@ impl LiveValueTracker {
|
|||||||
.expect("Immediate dominator value has no live range");
|
.expect("Immediate dominator value has no live range");
|
||||||
|
|
||||||
// Check if this value is live-in here.
|
// Check if this value is live-in here.
|
||||||
if let Some(endpoint) = lr.livein_local_end(ebb, ctx) {
|
if let Some(endpoint) = lr.livein_local_end(ebb, liveness.forest(), layout) {
|
||||||
self.live.push(value, endpoint, lr);
|
self.live.push(value, endpoint, lr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ use crate::ir::dfg::ValueDef;
|
|||||||
use crate::ir::{Ebb, Function, Inst, Layout, ProgramPoint, Value};
|
use crate::ir::{Ebb, Function, Inst, Layout, ProgramPoint, Value};
|
||||||
use crate::isa::{EncInfo, OperandConstraint, TargetIsa};
|
use crate::isa::{EncInfo, OperandConstraint, TargetIsa};
|
||||||
use crate::regalloc::affinity::Affinity;
|
use crate::regalloc::affinity::Affinity;
|
||||||
use crate::regalloc::liverange::{LiveRange, LiveRangeContext, LiveRangeForest};
|
use crate::regalloc::liverange::{LiveRange, LiveRangeForest};
|
||||||
use crate::timing;
|
use crate::timing;
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::ops::Index;
|
use core::ops::Index;
|
||||||
@@ -314,16 +314,16 @@ impl Liveness {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Current forest storage.
|
||||||
|
pub fn forest(&self) -> &LiveRangeForest {
|
||||||
|
&self.forest
|
||||||
|
}
|
||||||
|
|
||||||
/// Current live ranges.
|
/// Current live ranges.
|
||||||
pub fn ranges(&self) -> &LiveRangeSet {
|
pub fn ranges(&self) -> &LiveRangeSet {
|
||||||
&self.ranges
|
&self.ranges
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a context needed for working with a `LiveRange`.
|
|
||||||
pub fn context<'a>(&'a self, layout: &'a Layout) -> LiveRangeContext<'a, Layout> {
|
|
||||||
LiveRangeContext::new(layout, &self.forest)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clear all data structures in this liveness analysis.
|
/// Clear all data structures in this liveness analysis.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.ranges.clear();
|
self.ranges.clear();
|
||||||
|
|||||||
@@ -179,32 +179,6 @@ pub struct GenericLiveRange<PO: ProgramOrder> {
|
|||||||
po: PhantomData<*const PO>,
|
po: PhantomData<*const PO>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Context information needed to query a `LiveRange`.
|
|
||||||
pub struct LiveRangeContext<'a, PO: 'a + ProgramOrder> {
|
|
||||||
/// Ordering of EBBs.
|
|
||||||
pub order: &'a PO,
|
|
||||||
/// Memory pool.
|
|
||||||
pub forest: &'a bforest::MapForest<Ebb, Inst>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, PO: ProgramOrder> LiveRangeContext<'a, PO> {
|
|
||||||
/// Make a new context.
|
|
||||||
pub fn new(order: &'a PO, forest: &'a bforest::MapForest<Ebb, Inst>) -> Self {
|
|
||||||
Self { order, forest }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, PO: ProgramOrder> Clone for LiveRangeContext<'a, PO> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
LiveRangeContext {
|
|
||||||
order: self.order,
|
|
||||||
forest: self.forest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, PO: ProgramOrder> Copy for LiveRangeContext<'a, PO> {}
|
|
||||||
|
|
||||||
/// Forest of B-trees used for storing live ranges.
|
/// Forest of B-trees used for storing live ranges.
|
||||||
pub type LiveRangeForest = bforest::MapForest<Ebb, Inst>;
|
pub type LiveRangeForest = bforest::MapForest<Ebb, Inst>;
|
||||||
|
|
||||||
@@ -371,13 +345,13 @@ impl<PO: ProgramOrder> GenericLiveRange<PO> {
|
|||||||
/// If the live range is live through all of `ebb`, the terminator of `ebb` is a correct
|
/// If the live range is live through all of `ebb`, the terminator of `ebb` is a correct
|
||||||
/// answer, but it is also possible that an even later program point is returned. So don't
|
/// answer, but it is also possible that an even later program point is returned. So don't
|
||||||
/// depend on the returned `Inst` to belong to `ebb`.
|
/// depend on the returned `Inst` to belong to `ebb`.
|
||||||
pub fn livein_local_end(&self, ebb: Ebb, ctx: LiveRangeContext<PO>) -> Option<Inst> {
|
pub fn livein_local_end(&self, ebb: Ebb, forest: &LiveRangeForest, order: &PO) -> Option<Inst> {
|
||||||
let cmp = Cmp(ctx.order);
|
let cmp = Cmp(order);
|
||||||
self.liveins
|
self.liveins
|
||||||
.get_or_less(ebb, ctx.forest, &cmp)
|
.get_or_less(ebb, forest, &cmp)
|
||||||
.and_then(|(_, inst)| {
|
.and_then(|(_, inst)| {
|
||||||
// We have an entry that ends at `inst`.
|
// We have an entry that ends at `inst`.
|
||||||
if ctx.order.cmp(inst, ebb) == Ordering::Greater {
|
if order.cmp(inst, ebb) == Ordering::Greater {
|
||||||
Some(inst)
|
Some(inst)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -388,16 +362,16 @@ impl<PO: ProgramOrder> GenericLiveRange<PO> {
|
|||||||
/// Is this value live-in to `ebb`?
|
/// Is this value live-in to `ebb`?
|
||||||
///
|
///
|
||||||
/// An EBB argument is not considered to be live in.
|
/// An EBB argument is not considered to be live in.
|
||||||
pub fn is_livein(&self, ebb: Ebb, ctx: LiveRangeContext<PO>) -> bool {
|
pub fn is_livein(&self, ebb: Ebb, forest: &LiveRangeForest, order: &PO) -> bool {
|
||||||
self.livein_local_end(ebb, ctx).is_some()
|
self.livein_local_end(ebb, forest, order).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all the live-in intervals.
|
/// Get all the live-in intervals.
|
||||||
///
|
///
|
||||||
/// Note that the intervals are stored in a compressed form so each entry may span multiple
|
/// Note that the intervals are stored in a compressed form so each entry may span multiple
|
||||||
/// EBBs where the value is live in.
|
/// EBBs where the value is live in.
|
||||||
pub fn liveins<'a>(&'a self, ctx: LiveRangeContext<'a, PO>) -> bforest::MapIter<'a, Ebb, Inst> {
|
pub fn liveins<'a>(&'a self, forest: &'a LiveRangeForest) -> bforest::MapIter<'a, Ebb, Inst> {
|
||||||
self.liveins.iter(ctx.forest)
|
self.liveins.iter(forest)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if this live range overlaps a definition in `ebb`.
|
/// Check if this live range overlaps a definition in `ebb`.
|
||||||
@@ -405,7 +379,8 @@ impl<PO: ProgramOrder> GenericLiveRange<PO> {
|
|||||||
&self,
|
&self,
|
||||||
def: ExpandedProgramPoint,
|
def: ExpandedProgramPoint,
|
||||||
ebb: Ebb,
|
ebb: Ebb,
|
||||||
ctx: LiveRangeContext<PO>,
|
forest: &LiveRangeForest,
|
||||||
|
order: &PO,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Two defs at the same program point always overlap, even if one is dead.
|
// Two defs at the same program point always overlap, even if one is dead.
|
||||||
if def == self.def_begin.into() {
|
if def == self.def_begin.into() {
|
||||||
@@ -413,38 +388,39 @@ impl<PO: ProgramOrder> GenericLiveRange<PO> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for an overlap with the local range.
|
// Check for an overlap with the local range.
|
||||||
if ctx.order.cmp(def, self.def_begin) != Ordering::Less
|
if order.cmp(def, self.def_begin) != Ordering::Less
|
||||||
&& ctx.order.cmp(def, self.def_end) == Ordering::Less
|
&& order.cmp(def, self.def_end) == Ordering::Less
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for an overlap with a live-in range.
|
// Check for an overlap with a live-in range.
|
||||||
match self.livein_local_end(ebb, ctx) {
|
match self.livein_local_end(ebb, forest, order) {
|
||||||
Some(inst) => ctx.order.cmp(def, inst) == Ordering::Less,
|
Some(inst) => order.cmp(def, inst) == Ordering::Less,
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if this live range reaches a use at `user` in `ebb`.
|
/// Check if this live range reaches a use at `user` in `ebb`.
|
||||||
pub fn reaches_use(&self, user: Inst, ebb: Ebb, ctx: LiveRangeContext<PO>) -> bool {
|
pub fn reaches_use(&self, user: Inst, ebb: Ebb, forest: &LiveRangeForest, order: &PO) -> bool {
|
||||||
// Check for an overlap with the local range.
|
// Check for an overlap with the local range.
|
||||||
if ctx.order.cmp(user, self.def_begin) == Ordering::Greater
|
if order.cmp(user, self.def_begin) == Ordering::Greater
|
||||||
&& ctx.order.cmp(user, self.def_end) != Ordering::Greater
|
&& order.cmp(user, self.def_end) != Ordering::Greater
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for an overlap with a live-in range.
|
// Check for an overlap with a live-in range.
|
||||||
match self.livein_local_end(ebb, ctx) {
|
match self.livein_local_end(ebb, forest, order) {
|
||||||
Some(inst) => ctx.order.cmp(user, inst) != Ordering::Greater,
|
Some(inst) => order.cmp(user, inst) != Ordering::Greater,
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if this live range is killed at `user` in `ebb`.
|
/// Check if this live range is killed at `user` in `ebb`.
|
||||||
pub fn killed_at(&self, user: Inst, ebb: Ebb, ctx: LiveRangeContext<PO>) -> bool {
|
pub fn killed_at(&self, user: Inst, ebb: Ebb, forest: &LiveRangeForest, order: &PO) -> bool {
|
||||||
self.def_local_end() == user.into() || self.livein_local_end(ebb, ctx) == Some(user)
|
self.def_local_end() == user.into()
|
||||||
|
|| self.livein_local_end(ebb, forest, order) == Some(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +433,7 @@ impl<PO: ProgramOrder> SparseMapValue<Value> for GenericLiveRange<PO> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{GenericLiveRange, LiveRangeContext};
|
use super::GenericLiveRange;
|
||||||
use crate::bforest;
|
use crate::bforest;
|
||||||
use crate::entity::EntityRef;
|
use crate::entity::EntityRef;
|
||||||
use crate::ir::{Ebb, Inst, Value};
|
use crate::ir::{Ebb, Inst, Value};
|
||||||
@@ -560,18 +536,17 @@ mod tests {
|
|||||||
let e2 = Ebb::new(2);
|
let e2 = Ebb::new(2);
|
||||||
let lr = GenericLiveRange::new(v0, i1.into(), Default::default());
|
let lr = GenericLiveRange::new(v0, i1.into(), Default::default());
|
||||||
let forest = &bforest::MapForest::new();
|
let forest = &bforest::MapForest::new();
|
||||||
let ctx = LiveRangeContext::new(PO, forest);
|
|
||||||
assert!(lr.is_dead());
|
assert!(lr.is_dead());
|
||||||
assert!(lr.is_local());
|
assert!(lr.is_local());
|
||||||
assert_eq!(lr.def(), i1.into());
|
assert_eq!(lr.def(), i1.into());
|
||||||
assert_eq!(lr.def_local_end(), i1.into());
|
assert_eq!(lr.def_local_end(), i1.into());
|
||||||
assert_eq!(lr.livein_local_end(e2, ctx), None);
|
assert_eq!(lr.livein_local_end(e2, forest, PO), None);
|
||||||
PO.validate(&lr, ctx.forest);
|
PO.validate(&lr, forest);
|
||||||
|
|
||||||
// A dead live range overlaps its own def program point.
|
// A dead live range overlaps its own def program point.
|
||||||
assert!(lr.overlaps_def(i1.into(), e0, ctx));
|
assert!(lr.overlaps_def(i1.into(), e0, forest, PO));
|
||||||
assert!(!lr.overlaps_def(i2.into(), e0, ctx));
|
assert!(!lr.overlaps_def(i2.into(), e0, forest, PO));
|
||||||
assert!(!lr.overlaps_def(e0.into(), e0, ctx));
|
assert!(!lr.overlaps_def(e0.into(), e0, forest, PO));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -580,14 +555,13 @@ mod tests {
|
|||||||
let e2 = Ebb::new(2);
|
let e2 = Ebb::new(2);
|
||||||
let lr = GenericLiveRange::new(v0, e2.into(), Default::default());
|
let lr = GenericLiveRange::new(v0, e2.into(), Default::default());
|
||||||
let forest = &bforest::MapForest::new();
|
let forest = &bforest::MapForest::new();
|
||||||
let ctx = LiveRangeContext::new(PO, forest);
|
|
||||||
assert!(lr.is_dead());
|
assert!(lr.is_dead());
|
||||||
assert!(lr.is_local());
|
assert!(lr.is_local());
|
||||||
assert_eq!(lr.def(), e2.into());
|
assert_eq!(lr.def(), e2.into());
|
||||||
assert_eq!(lr.def_local_end(), e2.into());
|
assert_eq!(lr.def_local_end(), e2.into());
|
||||||
// The def interval of an EBB argument does not count as live-in.
|
// The def interval of an EBB argument does not count as live-in.
|
||||||
assert_eq!(lr.livein_local_end(e2, ctx), None);
|
assert_eq!(lr.livein_local_end(e2, forest, PO), None);
|
||||||
PO.validate(&lr, ctx.forest);
|
PO.validate(&lr, forest);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -664,25 +638,16 @@ mod tests {
|
|||||||
// Adding a live-in interval.
|
// Adding a live-in interval.
|
||||||
assert_eq!(lr.extend_in_ebb(e20, i22, PO, forest), true);
|
assert_eq!(lr.extend_in_ebb(e20, i22, PO, forest), true);
|
||||||
PO.validate(&lr, forest);
|
PO.validate(&lr, forest);
|
||||||
assert_eq!(
|
assert_eq!(lr.livein_local_end(e20, forest, PO), Some(i22));
|
||||||
lr.livein_local_end(e20, LiveRangeContext::new(PO, forest)),
|
|
||||||
Some(i22)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Non-extending the live-in.
|
// Non-extending the live-in.
|
||||||
assert_eq!(lr.extend_in_ebb(e20, i21, PO, forest), false);
|
assert_eq!(lr.extend_in_ebb(e20, i21, PO, forest), false);
|
||||||
assert_eq!(
|
assert_eq!(lr.livein_local_end(e20, forest, PO), Some(i22));
|
||||||
lr.livein_local_end(e20, LiveRangeContext::new(PO, forest)),
|
|
||||||
Some(i22)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Extending the existing live-in.
|
// Extending the existing live-in.
|
||||||
assert_eq!(lr.extend_in_ebb(e20, i23, PO, forest), false);
|
assert_eq!(lr.extend_in_ebb(e20, i23, PO, forest), false);
|
||||||
PO.validate(&lr, forest);
|
PO.validate(&lr, forest);
|
||||||
assert_eq!(
|
assert_eq!(lr.livein_local_end(e20, forest, PO), Some(i23));
|
||||||
lr.livein_local_end(e20, LiveRangeContext::new(PO, forest)),
|
|
||||||
Some(i23)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -699,52 +664,29 @@ mod tests {
|
|||||||
let forest = &mut bforest::MapForest::new();
|
let forest = &mut bforest::MapForest::new();
|
||||||
|
|
||||||
assert_eq!(lr.extend_in_ebb(e30, i31, PO, forest), true);
|
assert_eq!(lr.extend_in_ebb(e30, i31, PO, forest), true);
|
||||||
assert_eq!(
|
assert_eq!(lr.liveins(forest).collect::<Vec<_>>(), [(e30, i31)]);
|
||||||
lr.liveins(LiveRangeContext::new(PO, forest))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
[(e30, i31)]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Coalesce to previous
|
// Coalesce to previous
|
||||||
assert_eq!(lr.extend_in_ebb(e40, i41, PO, forest), true);
|
assert_eq!(lr.extend_in_ebb(e40, i41, PO, forest), true);
|
||||||
assert_eq!(
|
assert_eq!(lr.liveins(forest).collect::<Vec<_>>(), [(e30, i41)]);
|
||||||
lr.liveins(LiveRangeContext::new(PO, forest))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
[(e30, i41)]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Coalesce to next
|
// Coalesce to next
|
||||||
assert_eq!(lr.extend_in_ebb(e20, i21, PO, forest), true);
|
assert_eq!(lr.extend_in_ebb(e20, i21, PO, forest), true);
|
||||||
assert_eq!(
|
assert_eq!(lr.liveins(forest).collect::<Vec<_>>(), [(e20, i41)]);
|
||||||
lr.liveins(LiveRangeContext::new(PO, forest))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
[(e20, i41)]
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut lr = GenericLiveRange::new(v0, i11.into(), Default::default());
|
let mut lr = GenericLiveRange::new(v0, i11.into(), Default::default());
|
||||||
|
|
||||||
assert_eq!(lr.extend_in_ebb(e40, i41, PO, forest), true);
|
assert_eq!(lr.extend_in_ebb(e40, i41, PO, forest), true);
|
||||||
assert_eq!(
|
assert_eq!(lr.liveins(forest).collect::<Vec<_>>(), [(e40, i41)]);
|
||||||
lr.liveins(LiveRangeContext::new(PO, forest))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
[(e40, i41)]
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(lr.extend_in_ebb(e20, i21, PO, forest), true);
|
assert_eq!(lr.extend_in_ebb(e20, i21, PO, forest), true);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lr.liveins(LiveRangeContext::new(PO, forest))
|
lr.liveins(forest).collect::<Vec<_>>(),
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
[(e20, i21), (e40, i41)]
|
[(e20, i21), (e40, i41)]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Coalesce to previous and next
|
// Coalesce to previous and next
|
||||||
assert_eq!(lr.extend_in_ebb(e30, i31, PO, forest), true);
|
assert_eq!(lr.extend_in_ebb(e30, i31, PO, forest), true);
|
||||||
assert_eq!(
|
assert_eq!(lr.liveins(forest).collect::<Vec<_>>(), [(e20, i41)]);
|
||||||
lr.liveins(LiveRangeContext::new(PO, forest))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
[(e20, i41)]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add more tests that exercise the binary search algorithm.
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -319,17 +319,18 @@ impl<'a> Context<'a> {
|
|||||||
for (idx, (op, &arg)) in constraints.ins.iter().zip(args).enumerate() {
|
for (idx, (op, &arg)) in constraints.ins.iter().zip(args).enumerate() {
|
||||||
let mut reguse = RegUse::new(arg, idx, op.regclass.into());
|
let mut reguse = RegUse::new(arg, idx, op.regclass.into());
|
||||||
let lr = &self.liveness[arg];
|
let lr = &self.liveness[arg];
|
||||||
let ctx = self.liveness.context(&self.cur.func.layout);
|
|
||||||
match op.kind {
|
match op.kind {
|
||||||
ConstraintKind::Stack => continue,
|
ConstraintKind::Stack => continue,
|
||||||
ConstraintKind::FixedReg(_) => reguse.fixed = true,
|
ConstraintKind::FixedReg(_) => reguse.fixed = true,
|
||||||
ConstraintKind::Tied(_) => {
|
ConstraintKind::Tied(_) => {
|
||||||
// A tied operand must kill the used value.
|
// A tied operand must kill the used value.
|
||||||
reguse.tied = !lr.killed_at(inst, ebb, ctx);
|
reguse.tied =
|
||||||
|
!lr.killed_at(inst, ebb, self.liveness.forest(), &self.cur.func.layout);
|
||||||
}
|
}
|
||||||
ConstraintKind::FixedTied(_) => {
|
ConstraintKind::FixedTied(_) => {
|
||||||
reguse.fixed = true;
|
reguse.fixed = true;
|
||||||
reguse.tied = !lr.killed_at(inst, ebb, ctx);
|
reguse.tied =
|
||||||
|
!lr.killed_at(inst, ebb, self.liveness.forest(), &self.cur.func.layout);
|
||||||
}
|
}
|
||||||
ConstraintKind::Reg => {}
|
ConstraintKind::Reg => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ where
|
|||||||
ebbs.sort_by_key(|ebb| func.offsets[*ebb]); // Ensure inst offsets always increase
|
ebbs.sort_by_key(|ebb| func.offsets[*ebb]); // Ensure inst offsets always increase
|
||||||
let encinfo = isa.encoding_info();
|
let encinfo = isa.encoding_info();
|
||||||
let values_locations = &func.locations;
|
let values_locations = &func.locations;
|
||||||
let liveness_context = regalloc.liveness().context(&func.layout);
|
|
||||||
let liveness_ranges = regalloc.liveness().ranges();
|
let liveness_ranges = regalloc.liveness().ranges();
|
||||||
|
let liveness_forest = regalloc.liveness().forest();
|
||||||
|
|
||||||
let mut ranges = HashMap::new();
|
let mut ranges = HashMap::new();
|
||||||
let mut add_range = |label, range: (u32, u32), loc: ValueLoc| {
|
let mut add_range = |label, range: (u32, u32), loc: ValueLoc| {
|
||||||
@@ -126,7 +126,10 @@ where
|
|||||||
// Remove killed values.
|
// Remove killed values.
|
||||||
tracked_values.retain(|(x, label, start_offset, last_loc)| {
|
tracked_values.retain(|(x, label, start_offset, last_loc)| {
|
||||||
let range = liveness_ranges.get(*x);
|
let range = liveness_ranges.get(*x);
|
||||||
if range.expect("value").killed_at(inst, ebb, liveness_context) {
|
if range
|
||||||
|
.expect("value")
|
||||||
|
.killed_at(inst, ebb, &liveness_forest, &func.layout)
|
||||||
|
{
|
||||||
add_range(*label, (*start_offset, end_offset), *last_loc);
|
add_range(*label, (*start_offset, end_offset), *last_loc);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -173,7 +176,7 @@ where
|
|||||||
// Ignore dead/inactive Values.
|
// Ignore dead/inactive Values.
|
||||||
let range = liveness_ranges.get(*v);
|
let range = liveness_ranges.get(*v);
|
||||||
match range {
|
match range {
|
||||||
Some(r) => r.reaches_use(inst, ebb, liveness_context),
|
Some(r) => r.reaches_use(inst, ebb, &liveness_forest, &func.layout),
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -118,8 +118,12 @@ impl<'a> CssaVerifier<'a> {
|
|||||||
if self.preorder.dominates(prev_ebb, def_ebb)
|
if self.preorder.dominates(prev_ebb, def_ebb)
|
||||||
&& self.domtree.dominates(prev_def, def, &self.func.layout)
|
&& self.domtree.dominates(prev_def, def, &self.func.layout)
|
||||||
{
|
{
|
||||||
let ctx = self.liveness.context(&self.func.layout);
|
if self.liveness[prev_val].overlaps_def(
|
||||||
if self.liveness[prev_val].overlaps_def(def, def_ebb, ctx) {
|
def,
|
||||||
|
def_ebb,
|
||||||
|
self.liveness.forest(),
|
||||||
|
&self.func.layout,
|
||||||
|
) {
|
||||||
return fatal!(
|
return fatal!(
|
||||||
errors,
|
errors,
|
||||||
val,
|
val,
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ impl<'a> LivenessVerifier<'a> {
|
|||||||
|
|
||||||
/// Check all instructions.
|
/// Check all instructions.
|
||||||
fn check_insts(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
fn check_insts(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
||||||
let lr_ctx = self.liveness.context(&self.func.layout);
|
|
||||||
for ebb in self.func.layout.ebbs() {
|
for ebb in self.func.layout.ebbs() {
|
||||||
for inst in self.func.layout.ebb_insts(ebb) {
|
for inst in self.func.layout.ebb_insts(ebb) {
|
||||||
let encoding = self.func.encodings[inst];
|
let encoding = self.func.encodings[inst];
|
||||||
@@ -107,8 +106,8 @@ impl<'a> LivenessVerifier<'a> {
|
|||||||
None => return fatal!(errors, inst, "{} has no live range", val),
|
None => return fatal!(errors, inst, "{} has no live range", val),
|
||||||
};
|
};
|
||||||
|
|
||||||
debug_assert!(lr_ctx.order.inst_ebb(inst).unwrap() == ebb);
|
debug_assert!(self.func.layout.inst_ebb(inst).unwrap() == ebb);
|
||||||
if !lr.reaches_use(inst, ebb, lr_ctx) {
|
if !lr.reaches_use(inst, ebb, self.liveness.forest(), &self.func.layout) {
|
||||||
return fatal!(errors, inst, "{} is not live at this use", val);
|
return fatal!(errors, inst, "{} is not live at this use", val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +179,7 @@ impl<'a> LivenessVerifier<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now check the live-in intervals against the CFG.
|
// Now check the live-in intervals against the CFG.
|
||||||
for (mut ebb, end) in lr.liveins(self.liveness.context(l)) {
|
for (mut ebb, end) in lr.liveins(self.liveness.forest()) {
|
||||||
if !l.is_ebb_inserted(ebb) {
|
if !l.is_ebb_inserted(ebb) {
|
||||||
return fatal!(
|
return fatal!(
|
||||||
errors,
|
errors,
|
||||||
@@ -204,13 +203,11 @@ impl<'a> LivenessVerifier<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let lr_ctx = self.liveness.context(&self.func.layout);
|
|
||||||
|
|
||||||
// Check all the EBBs in the interval independently.
|
// Check all the EBBs in the interval independently.
|
||||||
loop {
|
loop {
|
||||||
// If `val` is live-in at `ebb`, it must be live at all the predecessors.
|
// If `val` is live-in at `ebb`, it must be live at all the predecessors.
|
||||||
for BasicBlock { inst: pred, ebb } in self.cfg.pred_iter(ebb) {
|
for BasicBlock { inst: pred, ebb } in self.cfg.pred_iter(ebb) {
|
||||||
if !lr.reaches_use(pred, ebb, lr_ctx) {
|
if !lr.reaches_use(pred, ebb, self.liveness.forest(), &self.func.layout) {
|
||||||
return fatal!(
|
return fatal!(
|
||||||
errors,
|
errors,
|
||||||
pred,
|
pred,
|
||||||
|
|||||||
@@ -334,10 +334,10 @@ impl<'a> LocationVerifier<'a> {
|
|||||||
let lr = &liveness[value];
|
let lr = &liveness[value];
|
||||||
if is_after_branch && unique_predecessor {
|
if is_after_branch && unique_predecessor {
|
||||||
// Forward diversions based on the targeted branch.
|
// Forward diversions based on the targeted branch.
|
||||||
if !lr.is_livein(ebb, liveness.context(&self.func.layout)) {
|
if !lr.is_livein(ebb, liveness.forest(), &self.func.layout) {
|
||||||
val_to_remove.push(value)
|
val_to_remove.push(value)
|
||||||
}
|
}
|
||||||
} else if lr.is_livein(ebb, liveness.context(&self.func.layout)) {
|
} else if lr.is_livein(ebb, liveness.forest(), &self.func.layout) {
|
||||||
return fatal!(
|
return fatal!(
|
||||||
errors,
|
errors,
|
||||||
inst,
|
inst,
|
||||||
@@ -359,7 +359,7 @@ impl<'a> LocationVerifier<'a> {
|
|||||||
for (&value, d) in divert.iter() {
|
for (&value, d) in divert.iter() {
|
||||||
let lr = &liveness[value];
|
let lr = &liveness[value];
|
||||||
if let Some(ebb) = ebb {
|
if let Some(ebb) = ebb {
|
||||||
if lr.is_livein(ebb, liveness.context(&self.func.layout)) {
|
if lr.is_livein(ebb, liveness.forest(), &self.func.layout) {
|
||||||
return fatal!(
|
return fatal!(
|
||||||
errors,
|
errors,
|
||||||
inst,
|
inst,
|
||||||
@@ -371,7 +371,7 @@ impl<'a> LocationVerifier<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for ebb in self.func.jump_tables[jt].iter() {
|
for ebb in self.func.jump_tables[jt].iter() {
|
||||||
if lr.is_livein(*ebb, liveness.context(&self.func.layout)) {
|
if lr.is_livein(*ebb, liveness.forest(), &self.func.layout) {
|
||||||
return fatal!(
|
return fatal!(
|
||||||
errors,
|
errors,
|
||||||
inst,
|
inst,
|
||||||
|
|||||||
Reference in New Issue
Block a user