Make multiple defs of one vreg possible on one instruction. (#38)

Currently, if this is done, overlapping liveranges are created, and we
hit an assert that ensures our non-overlapping and
built-in-reverse-order invariants during liverange construction.

One could argue that multiple defs of a single vreg don't make a ton of
sense -- which def's value is valid after the instruction? -- but if
they all get the same alloc, then the answer is "whatever was put in
that alloc", and this is just a case of an instruction being a bit
over-eager when listing its registers.

This can arise in practice when operand lists come from combinations or
concatenations: for example, in Cranelift's s390x backend, there is a
"Loop" pseudo-instruction, and the operands of the Loop are the operands
of all the sub-instructions.

It seems more logically cohesive overall to say that one can state an
operand as many times as one likes; so this PR makes it so.
This commit is contained in:
Chris Fallin
2022-04-04 16:22:05 -07:00
committed by GitHub
parent a369150213
commit 0cb08095e6
2 changed files with 47 additions and 13 deletions

View File

@@ -176,7 +176,11 @@ impl<'a, F: Function> Env<'a, F> {
/// Mark `range` as live for the given `vreg`. /// Mark `range` as live for the given `vreg`.
/// ///
/// Returns the liverange that contains the given range. /// Returns the liverange that contains the given range.
pub fn add_liverange_to_vreg(&mut self, vreg: VRegIndex, range: CodeRange) -> LiveRangeIndex { pub fn add_liverange_to_vreg(
&mut self,
vreg: VRegIndex,
mut range: CodeRange,
) -> LiveRangeIndex {
trace!("add_liverange_to_vreg: vreg {:?} range {:?}", vreg, range); trace!("add_liverange_to_vreg: vreg {:?} range {:?}", vreg, range);
// Invariant: as we are building liveness information, we // Invariant: as we are building liveness information, we
@@ -190,18 +194,30 @@ impl<'a, F: Function> Env<'a, F> {
// array, then reverse them at the end of // array, then reverse them at the end of
// `compute_liveness()`. // `compute_liveness()`.
if !self.vregs[vreg.index()].ranges.is_empty() {
let last_range_index = self.vregs[vreg.index()].ranges.last().unwrap().index;
let last_range = self.ranges[last_range_index.index()].range;
if self.func.allow_multiple_vreg_defs() {
if last_range.contains(&range) {
// Special case (may occur when multiple defs of pinned
// physical regs occur): if this new range overlaps the
// existing range, return it.
return last_range_index;
}
// If this range's end falls in the middle of the last
// range, truncate it to be contiguous so we can merge
// below.
if range.to >= last_range.from && range.to <= last_range.to {
range.to = last_range.from;
}
}
debug_assert!( debug_assert!(
self.vregs[vreg.index()].ranges.is_empty() range.to <= last_range.from,
|| range.to "range {:?}, last_range {:?}",
<= self.ranges[self.vregs[vreg.index()] range,
.ranges last_range
.last()
.unwrap()
.index
.index()]
.range
.from
); );
}
if self.vregs[vreg.index()].ranges.is_empty() if self.vregs[vreg.index()].ranges.is_empty()
|| range.to || range.to

View File

@@ -1011,6 +1011,24 @@ pub trait Function {
fn multi_spillslot_named_by_last_slot(&self) -> bool { fn multi_spillslot_named_by_last_slot(&self) -> bool {
false false
} }
// -----------
// Misc config
// -----------
/// Allow a single instruction to define a vreg multiple times. If
/// allowed, the semantics are as if the definition occurs only
/// once, and all defs will get the same alloc. This flexibility is
/// meant to allow the embedder to more easily aggregate operands
/// together in macro/pseudoinstructions, or e.g. add additional
/// clobbered vregs without taking care to deduplicate. This may be
/// particularly useful when referring to physical registers via
/// pinned vregs. It is optional functionality because a strict mode
/// (at most one def per vreg) is also useful for finding bugs in
/// other applications.
fn allow_multiple_vreg_defs(&self) -> bool {
false
}
} }
/// A position before or after an instruction at which we can make an /// A position before or after an instruction at which we can make an