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