WIP: Handle moves between realregs (pregs) and vregs somewhat specially, by converting into operand constraints

Still has a fuzzbug in interaction between R->R and V->R moves. Will
likely rework to make pinned-vreg handling more general but want to save
a checkpoint here; idea for rework:
- set allocs immediately if an Operand is a pinned vreg;
- reserve preg ranges;
- then, in rest of liveness computation / LR construction, convert
  pinned-vregs to operands with constraints, but otherwise do not
  special-case as we do in this commit.
This commit is contained in:
Chris Fallin
2021-05-20 19:53:16 -07:00
parent f0b24cf9fa
commit 2a5f571b80
3 changed files with 356 additions and 119 deletions

View File

@@ -589,6 +589,12 @@ impl<'a, F: Function> Checker<'a, F> {
.unwrap() .unwrap()
.push(CheckerInst::Move { into: to, from }); .push(CheckerInst::Move { into: to, from });
} }
&Edit::DefAlloc { .. } => {
unimplemented!(concat!(
"DefAlloc is used only when dealing with pinned vregs, ",
"which are only used by regalloc.rs shim; use checker at that level!"
));
}
&Edit::BlockParams { &Edit::BlockParams {
ref vregs, ref vregs,
ref allocs, ref allocs,

View File

@@ -830,12 +830,15 @@ impl<'a, F: Function> Env<'a, F> {
let mut i = 0; let mut i = 0;
while i < self.vregs[vreg.index()].ranges.len() { while i < self.vregs[vreg.index()].ranges.len() {
let entry = self.vregs[vreg.index()].ranges[i]; let entry = self.vregs[vreg.index()].ranges[i];
if entry.range.overlaps(&range) { // Don't use `entry.range`; it is not kept up-to-date as
if entry.range.from < range.from { // we are building LRs.
range.from = entry.range.from; let this_range = self.ranges[entry.index.index()].range;
if range.overlaps(&this_range) {
if this_range.from < range.from {
range.from = this_range.from;
} }
if entry.range.to > range.to { if this_range.to > range.to {
range.to = entry.range.to; range.to = this_range.to;
} }
if merged.is_none() { if merged.is_none() {
merged = Some(i); merged = Some(i);
@@ -1124,6 +1127,228 @@ impl<'a, F: Function> Env<'a, F> {
assert_eq!(dst.kind(), OperandKind::Def); assert_eq!(dst.kind(), OperandKind::Def);
assert_eq!(dst.pos(), OperandPos::After); assert_eq!(dst.pos(), OperandPos::After);
// If exactly one of source and dest (but not
// both) is a pinned-vreg, convert this into a
// ghost use on the other vreg with a FixedReg
// policy.
if self.vregs[src.vreg().vreg()].is_pinned
^ self.vregs[dst.vreg().vreg()].is_pinned
{
log::debug!(
" -> exactly one of src/dst is pinned; converting to ghost use"
);
let (preg, vreg, pinned_vreg, kind, pos, progpoint) =
if self.vregs[src.vreg().vreg()].is_pinned {
// Source is pinned: this is a def on the dst with a pinned preg.
(
self.func.is_pinned_vreg(src.vreg()).unwrap(),
dst.vreg(),
src.vreg(),
OperandKind::Def,
OperandPos::After,
ProgPoint::after(inst),
)
} else {
// Dest is pinned: this is a use on the src with a pinned preg.
(
self.func.is_pinned_vreg(dst.vreg()).unwrap(),
src.vreg(),
dst.vreg(),
OperandKind::Use,
// Use comes late to avoid interfering with a potential
// prior move instruction
// reserving the preg, which
// creates a use or def *at*
// our Before pos (because
// progmoves happen between
// insts).
OperandPos::After,
ProgPoint::after(inst),
)
};
let policy = OperandPolicy::FixedReg(preg);
let operand = Operand::new(vreg, policy, kind, pos);
log::debug!(
concat!(
" -> preg {:?} vreg {:?} kind {:?} ",
"pos {:?} progpoint {:?} policy {:?} operand {:?}"
),
preg,
vreg,
kind,
pos,
progpoint,
policy,
operand
);
// Get the LR for the vreg; if none, create one.
let mut lr = vreg_ranges[vreg.vreg()];
if !live.get(vreg.vreg()) {
let from = match kind {
OperandKind::Use => self.cfginfo.block_entry[block.index()],
OperandKind::Def => progpoint,
_ => unreachable!(),
};
let to = progpoint.next();
lr = self.add_liverange_to_vreg(
VRegIndex::new(vreg.vreg()),
CodeRange { from, to },
);
log::debug!(" -> dead; created LR");
}
log::debug!(" -> LR {:?}", lr);
self.insert_use_into_liverange(
lr,
Use::new(operand, progpoint, SLOT_NONE),
);
if kind == OperandKind::Def {
live.set(vreg.vreg(), false);
if self.ranges[lr.index()].range.from
== self.cfginfo.block_entry[block.index()]
{
self.ranges[lr.index()].range.from = progpoint;
}
self.ranges[lr.index()].set_flag(LiveRangeFlag::StartsAtDef);
} else {
live.set(vreg.vreg(), true);
vreg_ranges[vreg.vreg()] = lr;
}
// Handle liveness of the other vreg. Note
// that this is somewhat special. For the
// destination case, we want the pinned
// vreg's LR to start just *after* the
// operand we inserted above, because
// otherwise it would overlap, and
// interfere, and prevent allocation. For
// the source case, we want to "poke a
// hole" in the LR: if it's live going
// downward, end it just after the operand
// and restart it before; if it isn't
// (this is the last use), start it
// before.
if kind == OperandKind::Def {
log::debug!(" -> src on pinned vreg {:?}", pinned_vreg);
// The *other* vreg is a def, so the pinned-vreg
// mention is a use. If already live,
// end the existing LR just *after*
// the `progpoint` defined above and
// start a new one just *before* the
// `progpoint` defined above,
// preserving the start. If not, start
// a new one live back to the top of
// the block, starting just before
// `progpoint`.
if live.get(pinned_vreg.vreg()) {
let pinned_lr = vreg_ranges[pinned_vreg.vreg()];
let orig_start = self.ranges[pinned_lr.index()].range.from;
self.ranges[pinned_lr.index()].range.from = progpoint.next();
let new_lr = self.add_liverange_to_vreg(
VRegIndex::new(pinned_vreg.vreg()),
CodeRange {
from: orig_start,
to: progpoint,
},
);
vreg_ranges[pinned_vreg.vreg()] = new_lr;
log::debug!(
" -> live with LR {:?}; truncating to start at {:?}",
pinned_lr,
progpoint.next()
);
log::debug!(" -> created LR {:?} with remaining range from {:?} to {:?}", new_lr, orig_start, progpoint);
// Add an edit right now to indicate that at
// this program point, the given
// preg is now known as that vreg,
// not the preg, but immediately
// after, it is known as the preg
// again. This is used by the
// checker.
self.add_edit(
ProgPoint::before(inst),
InsertMovePrio::Regular,
Edit::DefAlloc {
alloc: Allocation::reg(preg),
vreg: dst.vreg(),
},
);
self.add_edit(
ProgPoint::after(inst),
InsertMovePrio::Regular,
Edit::DefAlloc {
alloc: Allocation::reg(preg),
vreg: src.vreg(),
},
);
} else {
let new_lr = self.add_liverange_to_vreg(
VRegIndex::new(pinned_vreg.vreg()),
CodeRange {
from: self.cfginfo.block_entry[block.index()],
to: progpoint,
},
);
vreg_ranges[pinned_vreg.vreg()] = new_lr;
live.set(pinned_vreg.vreg(), true);
log::debug!(" -> was not live; created new LR {:?}", new_lr);
// Add an edit right now to indicate that at
// this program point, the given
// preg is now known as that vreg,
// not the preg. This is used by
// the checker.
self.add_edit(
ProgPoint::before(inst),
InsertMovePrio::Regular,
Edit::DefAlloc {
alloc: Allocation::reg(preg),
vreg: dst.vreg(),
},
);
}
} else {
log::debug!(" -> dst on pinned vreg {:?}", pinned_vreg);
// The *other* vreg is a use, so the pinned-vreg
// mention is a def. Truncate its LR
// just *after* the `progpoint`
// defined above.
if live.get(pinned_vreg.vreg()) {
let pinned_lr = vreg_ranges[pinned_vreg.vreg()];
self.ranges[pinned_lr.index()].range.from = progpoint.next();
log::debug!(
" -> was live with LR {:?}; truncated start to {:?}",
pinned_lr,
progpoint.next()
);
live.set(pinned_vreg.vreg(), false);
// Add a no-op edit right now to indicate that
// at this program point, the
// given preg is now known as that
// preg, not the vreg. This is
// used by the checker.
self.add_edit(
ProgPoint::after(inst),
InsertMovePrio::Regular,
Edit::DefAlloc {
alloc: Allocation::reg(preg),
vreg: dst.vreg(),
},
);
}
// Otherwise, if dead, no need to create
// a dummy LR -- there is no
// reservation to make (the other vreg
// will land in the reg with the
// fixed-reg operand constraint, but
// it's a dead move anyway).
}
} else {
// Redefine src and dst operands to have // Redefine src and dst operands to have
// positions of After and Before respectively // positions of After and Before respectively
// (see note below), and to have Any // (see note below), and to have Any
@@ -1219,8 +1444,8 @@ impl<'a, F: Function> Env<'a, F> {
from: self.cfginfo.block_entry[block.index()], from: self.cfginfo.block_entry[block.index()],
to: pos.next(), to: pos.next(),
}; };
let src_lr = let src_lr = self
self.add_liverange_to_vreg(VRegIndex::new(src.vreg().vreg()), range); .add_liverange_to_vreg(VRegIndex::new(src.vreg().vreg()), range);
vreg_ranges[src.vreg().vreg()] = src_lr; vreg_ranges[src.vreg().vreg()] = src_lr;
log::debug!(" -> src LR {:?}", src_lr); log::debug!(" -> src LR {:?}", src_lr);
@@ -1244,6 +1469,7 @@ impl<'a, F: Function> Env<'a, F> {
self.prog_move_merges.push((src_lr, dst_lr)); self.prog_move_merges.push((src_lr, dst_lr));
} }
} }
}
continue; continue;
} }
@@ -1316,6 +1542,8 @@ impl<'a, F: Function> Env<'a, F> {
CodeRange { from, to }, CodeRange { from, to },
); );
log::debug!(" -> invalid; created {:?}", lr); log::debug!(" -> invalid; created {:?}", lr);
vreg_ranges[operand.vreg().vreg()] = lr;
live.set(operand.vreg().vreg(), true);
} }
// Create the use in the LiveRange. // Create the use in the LiveRange.
self.insert_use_into_liverange(lr, Use::new(operand, pos, i as u8)); self.insert_use_into_liverange(lr, Use::new(operand, pos, i as u8));
@@ -3477,7 +3705,7 @@ impl<'a, F: Function> Env<'a, F> {
InsertMovePrio::Regular, InsertMovePrio::Regular,
prev_alloc, prev_alloc,
alloc, alloc,
None, Some(self.vreg_regs[vreg.index()]),
); );
} }
} }

View File

@@ -906,6 +906,9 @@ pub enum Edit {
vregs: Vec<VReg>, vregs: Vec<VReg>,
allocs: Vec<Allocation>, allocs: Vec<Allocation>,
}, },
/// Define a particular Allocation to contain a particular VReg. Useful
/// for the checker.
DefAlloc { alloc: Allocation, vreg: VReg },
} }
/// A machine envrionment tells the register allocator which registers /// A machine envrionment tells the register allocator which registers