Early defs reserve a register for whole instruction. (#32)

The `Operand` abstraction allows a def to be positioned at the "early"
point of an instruction, before its effect and alongside its normal
uses. This is intended to allow the embedder to express that a def may
be written before all uses are read, so it should not conflict with the
uses.

It's also convenient to use early defs to express temporaries, which
should be available throughout a regalloc-level instruction's emitted
sequence. In such a case, the register should not be used again after
the instruction, so it is dead following the instruction.

Strictly speaking, and according to regalloc2 prior to this PR, then the
temp will *only* conflict with the uses at the early-point, and not the
defs at the late-point (after the instruction), because it's dead past
its point of definition. But for a temp we really want it to register
conflicts not just with the normal uses but with the normal defs as
well.

This PR changes the semantics so that an early def builds a liverange
that spans the early- and late-point of an instruction when the vreg is
dead flowing down from the instruction, giving the semantics we want for
temps.
This commit is contained in:
Chris Fallin
2022-03-18 10:32:49 -07:00
committed by GitHub
parent 4f1161d9e4
commit 433e8b3776
2 changed files with 14 additions and 5 deletions

View File

@@ -943,11 +943,13 @@ impl<'a, F: Function> Env<'a, F> {
OperandKind::Mod => self.cfginfo.block_entry[block.index()], OperandKind::Mod => self.cfginfo.block_entry[block.index()],
_ => unreachable!(), _ => unreachable!(),
}; };
let to = match operand.kind() { // We want to we want to span
OperandKind::Def => pos.next(), // until Before of the next
OperandKind::Mod => pos.next().next(), // both Before and After positions // inst. This ensures that early
_ => unreachable!(), // defs used for temps on an
}; // instruction are reserved across
// the whole instruction.
let to = ProgPoint::before(pos.inst().next());
lr = self.add_liverange_to_vreg( lr = self.add_liverange_to_vreg(
VRegIndex::new(operand.vreg().vreg()), VRegIndex::new(operand.vreg().vreg()),
CodeRange { from, to }, CodeRange { from, to },

View File

@@ -486,6 +486,13 @@ impl Operand {
/// that must be in a register, and that occurs early at the /// that must be in a register, and that occurs early at the
/// "before" point, i.e., must not conflict with any input to the /// "before" point, i.e., must not conflict with any input to the
/// instruction. /// instruction.
///
/// Note that the register allocator will ensure that such an
/// early-def operand is live throughout the instruction, i.e., also
/// at the after-point. Hence it will also avoid conflicts with all
/// outputs to the instruction. As such, early defs are appropriate
/// for use as "temporary registers" that an instruction can use
/// throughout its execution separately from the inputs and outputs.
#[inline(always)] #[inline(always)]
pub fn reg_def_at_start(vreg: VReg) -> Self { pub fn reg_def_at_start(vreg: VReg) -> Self {
Operand::new( Operand::new(