Fuzzbug fix: don't merge bundles that have conflicting requirements. (Normally splitting would fix this, but let's just not merge in the first place.)
This commit is contained in:
146
src/ion/mod.rs
146
src/ion/mod.rs
@@ -160,6 +160,7 @@ struct LiveRange {
|
||||
uses: UseList,
|
||||
|
||||
merged_into: LiveRangeIndex,
|
||||
requirement: Requirement,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -237,6 +238,7 @@ struct LiveBundle {
|
||||
allocation: Allocation,
|
||||
prio: u32, // recomputed after every bulk update
|
||||
spill_weight_and_props: u32,
|
||||
requirement: Requirement,
|
||||
}
|
||||
|
||||
impl LiveBundle {
|
||||
@@ -524,36 +526,61 @@ fn spill_weight_from_policy(policy: OperandPolicy, is_hot: bool, is_def: bool) -
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum Requirement {
|
||||
Unknown,
|
||||
Fixed(PReg),
|
||||
Register(RegClass),
|
||||
Stack(RegClass),
|
||||
Any(RegClass),
|
||||
Conflict,
|
||||
}
|
||||
impl Requirement {
|
||||
#[inline(always)]
|
||||
fn class(self) -> RegClass {
|
||||
match self {
|
||||
Requirement::Unknown => panic!("No class for unknown Requirement"),
|
||||
Requirement::Fixed(preg) => preg.class(),
|
||||
Requirement::Register(class) | Requirement::Any(class) | Requirement::Stack(class) => {
|
||||
class
|
||||
}
|
||||
Requirement::Conflict => panic!("No class for conflicted Requirement"),
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn merge(self, other: Requirement) -> Option<Requirement> {
|
||||
if self.class() != other.class() {
|
||||
return None;
|
||||
}
|
||||
fn merge(self, other: Requirement) -> Requirement {
|
||||
match (self, other) {
|
||||
(other, Requirement::Any(_)) | (Requirement::Any(_), other) => Some(other),
|
||||
(Requirement::Stack(_), Requirement::Stack(_)) => Some(self),
|
||||
(Requirement::Register(_), Requirement::Fixed(preg))
|
||||
| (Requirement::Fixed(preg), Requirement::Register(_)) => {
|
||||
Some(Requirement::Fixed(preg))
|
||||
(Requirement::Unknown, other) | (other, Requirement::Unknown) => other,
|
||||
(Requirement::Conflict, _) | (_, Requirement::Conflict) => Requirement::Conflict,
|
||||
(other, Requirement::Any(rc)) | (Requirement::Any(rc), other) => {
|
||||
if other.class() == rc {
|
||||
other
|
||||
} else {
|
||||
Requirement::Conflict
|
||||
}
|
||||
}
|
||||
(Requirement::Register(_), Requirement::Register(_)) => Some(self),
|
||||
(Requirement::Fixed(a), Requirement::Fixed(b)) if a == b => Some(self),
|
||||
_ => None,
|
||||
(Requirement::Stack(rc1), Requirement::Stack(rc2)) => {
|
||||
if rc1 == rc2 {
|
||||
self
|
||||
} else {
|
||||
Requirement::Conflict
|
||||
}
|
||||
}
|
||||
(Requirement::Register(rc), Requirement::Fixed(preg))
|
||||
| (Requirement::Fixed(preg), Requirement::Register(rc)) => {
|
||||
if rc == preg.class() {
|
||||
Requirement::Fixed(preg)
|
||||
} else {
|
||||
Requirement::Conflict
|
||||
}
|
||||
}
|
||||
(Requirement::Register(rc1), Requirement::Register(rc2)) => {
|
||||
if rc1 == rc2 {
|
||||
self
|
||||
} else {
|
||||
Requirement::Conflict
|
||||
}
|
||||
}
|
||||
(Requirement::Fixed(a), Requirement::Fixed(b)) if a == b => self,
|
||||
_ => Requirement::Conflict,
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
@@ -841,6 +868,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
uses: smallvec![],
|
||||
|
||||
merged_into: LiveRangeIndex::invalid(),
|
||||
requirement: Requirement::Unknown,
|
||||
});
|
||||
|
||||
LiveRangeIndex::new(idx)
|
||||
@@ -926,7 +954,9 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
weight,
|
||||
);
|
||||
|
||||
let req = Requirement::from_operand(u.operand);
|
||||
self.ranges[into.index()].uses.push(u);
|
||||
self.ranges[into.index()].requirement = self.ranges[into.index()].requirement.merge(req);
|
||||
|
||||
// Update stats.
|
||||
self.ranges[into.index()].uses_spill_weight_and_flags += weight;
|
||||
@@ -1953,6 +1983,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
spillset: SpillSetIndex::invalid(),
|
||||
prio: 0,
|
||||
spill_weight_and_props: 0,
|
||||
requirement: Requirement::Unknown,
|
||||
});
|
||||
LiveBundleIndex::new(bundle)
|
||||
}
|
||||
@@ -1997,7 +2028,25 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for overlap in LiveRanges.
|
||||
log::debug!(
|
||||
"bundle{} has req {:?}, bundle{} has req {:?}",
|
||||
from.index(),
|
||||
self.bundles[from.index()].requirement,
|
||||
to.index(),
|
||||
self.bundles[to.index()].requirement
|
||||
);
|
||||
|
||||
if self.bundles[from.index()]
|
||||
.requirement
|
||||
.merge(self.bundles[to.index()].requirement)
|
||||
== Requirement::Conflict
|
||||
{
|
||||
log::debug!(" -> conflicting requirements; aborting merge");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for overlap in LiveRanges and for conflicting
|
||||
// requirements.
|
||||
let ranges_from = &self.bundles[from.index()].ranges[..];
|
||||
let ranges_to = &self.bundles[to.index()].ranges[..];
|
||||
let mut idx_from = 0;
|
||||
@@ -2076,28 +2125,36 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
ranges_from,
|
||||
ranges_to
|
||||
);
|
||||
let mut req = Requirement::Unknown;
|
||||
while idx_from < ranges_from.len() || idx_to < ranges_to.len() {
|
||||
if idx_from < ranges_from.len() && idx_to < ranges_to.len() {
|
||||
if ranges_from[idx_from].range.from <= ranges_to[idx_to].range.from {
|
||||
self.ranges[ranges_from[idx_from].index.index()].bundle = to;
|
||||
req = req.merge(self.ranges[ranges_from[idx_from].index.index()].requirement);
|
||||
merged.push(ranges_from[idx_from]);
|
||||
idx_from += 1;
|
||||
} else {
|
||||
req = req.merge(self.ranges[ranges_to[idx_to].index.index()].requirement);
|
||||
merged.push(ranges_to[idx_to]);
|
||||
idx_to += 1;
|
||||
}
|
||||
} else if idx_from < ranges_from.len() {
|
||||
for entry in &ranges_from[idx_from..] {
|
||||
self.ranges[entry.index.index()].bundle = to;
|
||||
req = req.merge(self.ranges[entry.index.index()].requirement);
|
||||
}
|
||||
merged.extend_from_slice(&ranges_from[idx_from..]);
|
||||
break;
|
||||
} else {
|
||||
assert!(idx_to < ranges_to.len());
|
||||
for entry in &ranges_to[idx_to..] {
|
||||
req = req.merge(self.ranges[entry.index.index()].requirement);
|
||||
}
|
||||
merged.extend_from_slice(&ranges_to[idx_to..]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.bundles[to.index()].requirement = req;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
@@ -2167,6 +2224,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
let bundle = self.create_bundle();
|
||||
self.bundles[bundle.index()].ranges = self.vregs[vreg.index()].ranges.clone();
|
||||
log::debug!("vreg v{} gets bundle{}", vreg.index(), bundle.index());
|
||||
let mut req = Requirement::Unknown;
|
||||
for entry in &self.bundles[bundle.index()].ranges {
|
||||
log::debug!(
|
||||
" -> with LR range{}: {:?}",
|
||||
@@ -2174,7 +2232,9 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
entry.range
|
||||
);
|
||||
self.ranges[entry.index.index()].bundle = bundle;
|
||||
req = req.merge(self.ranges[entry.index.index()].requirement);
|
||||
}
|
||||
self.bundles[bundle.index()].requirement = req;
|
||||
|
||||
// Create a spillslot for this bundle.
|
||||
let ssidx = SpillSetIndex::new(self.spillsets.len());
|
||||
@@ -2389,14 +2449,10 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_requirement(&self, bundle: LiveBundleIndex) -> Option<Requirement> {
|
||||
fn compute_requirement(&self, bundle: LiveBundleIndex) -> Requirement {
|
||||
log::debug!("compute_requirement: bundle {:?}", bundle);
|
||||
|
||||
let class = self.spillsets[self.bundles[bundle.index()].spillset.index()].class;
|
||||
log::debug!(" -> class = {:?}", class);
|
||||
|
||||
let mut needed = Requirement::Any(class);
|
||||
|
||||
let mut needed = Requirement::Unknown;
|
||||
for entry in &self.bundles[bundle.index()].ranges {
|
||||
let range = &self.ranges[entry.index.index()];
|
||||
log::debug!(
|
||||
@@ -2405,21 +2461,12 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
entry.index,
|
||||
entry.range
|
||||
);
|
||||
for u in &range.uses {
|
||||
let use_req = Requirement::from_operand(u.operand);
|
||||
log::debug!(
|
||||
" -> use at {:?} op {:?} req {:?}",
|
||||
u.pos,
|
||||
u.operand,
|
||||
use_req
|
||||
);
|
||||
needed = needed.merge(use_req)?;
|
||||
log::debug!(" -> needed {:?}", needed);
|
||||
}
|
||||
needed = needed.merge(range.requirement);
|
||||
log::debug!(" -> needed {:?}", needed);
|
||||
}
|
||||
|
||||
log::debug!(" -> final needed: {:?}", needed);
|
||||
Some(needed)
|
||||
needed
|
||||
}
|
||||
|
||||
fn try_to_allocate_bundle_to_reg(
|
||||
@@ -2613,16 +2660,19 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
log::debug!(" -> minimal: {}", minimal);
|
||||
}
|
||||
|
||||
let spill_weight = if minimal {
|
||||
if fixed {
|
||||
let (spill_weight, req) = if minimal {
|
||||
let w = if fixed {
|
||||
log::debug!(" -> fixed and minimal: spill weight 2000000");
|
||||
2_000_000
|
||||
} else {
|
||||
log::debug!(" -> non-fixed and minimal: spill weight 1000000");
|
||||
1_000_000
|
||||
}
|
||||
};
|
||||
let req = self.ranges[first_range.index()].requirement;
|
||||
(w, req)
|
||||
} else {
|
||||
let mut total = 0;
|
||||
let mut req = Requirement::Unknown;
|
||||
for entry in &self.bundles[bundle.index()].ranges {
|
||||
let range_data = &self.ranges[entry.index.index()];
|
||||
log::debug!(
|
||||
@@ -2630,6 +2680,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
range_data.uses_spill_weight()
|
||||
);
|
||||
total += range_data.uses_spill_weight();
|
||||
req = req.merge(range_data.requirement);
|
||||
}
|
||||
|
||||
if self.bundles[bundle.index()].prio > 0 {
|
||||
@@ -2638,9 +2689,9 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
self.bundles[bundle.index()].prio,
|
||||
total / self.bundles[bundle.index()].prio
|
||||
);
|
||||
total / self.bundles[bundle.index()].prio
|
||||
(total / self.bundles[bundle.index()].prio, req)
|
||||
} else {
|
||||
0
|
||||
(0, req)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2649,6 +2700,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
minimal,
|
||||
fixed,
|
||||
);
|
||||
self.bundles[bundle.index()].requirement = req;
|
||||
}
|
||||
|
||||
fn minimal_bundle(&mut self, bundle: LiveBundleIndex) -> bool {
|
||||
@@ -2657,8 +2709,14 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
|
||||
fn recompute_range_properties(&mut self, range: LiveRangeIndex) {
|
||||
let mut rangedata = &mut self.ranges[range.index()];
|
||||
let w = rangedata.uses.iter().map(|u| u.weight as u32).sum();
|
||||
let mut w = 0;
|
||||
let mut req = Requirement::Unknown;
|
||||
for u in &rangedata.uses {
|
||||
w += u.weight as u32;
|
||||
req = req.merge(Requirement::from_operand(u.operand));
|
||||
}
|
||||
rangedata.uses_spill_weight_and_flags = w;
|
||||
rangedata.requirement = req;
|
||||
if rangedata.uses.len() > 0 && rangedata.uses[0].operand.kind() == OperandKind::Def {
|
||||
rangedata.set_flag(LiveRangeFlag::StartsAtDef);
|
||||
}
|
||||
@@ -2882,12 +2940,6 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
attempts += 1;
|
||||
log::debug!("attempt {}, req {:?}", attempts, req);
|
||||
debug_assert!(attempts < 100 * self.func.insts());
|
||||
let req = match req {
|
||||
Some(r) => r,
|
||||
// `None` means conflicting requirements, hence impossible to
|
||||
// allocate.
|
||||
None => break,
|
||||
};
|
||||
|
||||
let (conflicting_bundles, latest_first_conflict_point, latest_first_conflict_reg) =
|
||||
match req {
|
||||
@@ -3011,13 +3063,17 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Requirement::Any(_) => {
|
||||
Requirement::Any(_) | Requirement::Unknown => {
|
||||
// If a register is not *required*, spill now (we'll retry
|
||||
// allocation on spilled bundles later).
|
||||
log::debug!("spilling bundle {:?} to spilled_bundles list", bundle);
|
||||
self.spilled_bundles.push(bundle);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Requirement::Conflict => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
log::debug!(" -> conflict set {:?}", conflicting_bundles);
|
||||
@@ -3067,7 +3123,7 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
|
||||
// A minimal bundle cannot be split.
|
||||
if self.minimal_bundle(bundle) {
|
||||
if let Some(Requirement::Register(class)) = req {
|
||||
if let Requirement::Register(class) = req {
|
||||
// Check if this is a too-many-live-registers situation.
|
||||
let range = self.bundles[bundle.index()].ranges[0].range;
|
||||
let mut min_bundles_assigned = 0;
|
||||
|
||||
Reference in New Issue
Block a user