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:
@@ -176,7 +176,11 @@ impl<'a, F: Function> Env<'a, F> {
|
||||
/// Mark `range` as live for the given `vreg`.
|
||||
///
|
||||
/// 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);
|
||||
|
||||
// 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
|
||||
// `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!(
|
||||
self.vregs[vreg.index()].ranges.is_empty()
|
||||
|| range.to
|
||||
<= self.ranges[self.vregs[vreg.index()]
|
||||
.ranges
|
||||
.last()
|
||||
.unwrap()
|
||||
.index
|
||||
.index()]
|
||||
.range
|
||||
.from
|
||||
range.to <= last_range.from,
|
||||
"range {:?}, last_range {:?}",
|
||||
range,
|
||||
last_range
|
||||
);
|
||||
}
|
||||
|
||||
if self.vregs[vreg.index()].ranges.is_empty()
|
||||
|| range.to
|
||||
|
||||
18
src/lib.rs
18
src/lib.rs
@@ -1011,6 +1011,24 @@ pub trait Function {
|
||||
fn multi_spillslot_named_by_last_slot(&self) -> bool {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user