Fix fuzzbug: add checker metadata for new vreg on multi-fixed-reg fixup move.

When an instruction uses the same vreg constrained to multiple different
fixed registers, the allocator converts all but one of the fixed
constraints to `Any` and then records a special fixup move that copies
the value to the other fixed registers just before the instruction. This
allows the allocator to maintain the invariant that a value lives in
only one place at a time throughout most of its logic, and constrains
the complexity-fallout of this corner case to just a special last-minute
edit.

Unfortunately some recent CPU time thrown at the fuzzer has uncovered
a subtle interaction with the redundant move eliminator that confuses
the checker.

Specifically, when the correct value is *already* in the second
constrained fixed reg, because of an unrelated other move (e.g. because
of a blockparam or other vreg moved from the original), the redundant
move eliminator can delete the fixup move without telling the checker
that it has done so.

Such an optimization is perfectly valid, and the generated code is
correct; but the checker thinks that some other vreg (the one that was
copied from the original) is in the second preg, and panics.

The fix is to use the mechanism that indicates "this move defines a new
vreg" (emitting a `defalloc` checker-instruction) to force the checker
to understand that after the fixup move, the given preg actually
contains the appropriate vreg.
This commit is contained in:
Chris Fallin
2021-12-04 23:30:30 -08:00
parent 822f2bc937
commit ef6c8f3226
3 changed files with 8 additions and 6 deletions

View File

@@ -331,7 +331,7 @@ pub struct Env<'a, F: Function> {
// was to the approprate PReg.
//
// (progpoint, copy-from-preg, copy-to-preg, to-slot)
pub multi_fixed_reg_fixups: Vec<(ProgPoint, PRegIndex, PRegIndex, usize)>,
pub multi_fixed_reg_fixups: Vec<(ProgPoint, PRegIndex, PRegIndex, VRegIndex, usize)>,
pub inserted_moves: Vec<InsertedMove>,

View File

@@ -1167,6 +1167,7 @@ impl<'a, F: Function> Env<'a, F> {
ProgPoint,
PRegIndex,
PRegIndex,
VRegIndex,
usize,
)>| {
if last_point.is_some() && Some(pos) != last_point {
@@ -1189,7 +1190,7 @@ impl<'a, F: Function> Env<'a, F> {
let orig_preg = first_preg[idx];
if orig_preg != preg_idx {
log::trace!(" -> duplicate; switching to constraint Reg");
fixups.push((pos, orig_preg, preg_idx, slot));
fixups.push((pos, orig_preg, preg_idx, vreg_idx, slot));
*op = Operand::new(
op.vreg(),
OperandConstraint::Reg,

View File

@@ -685,21 +685,22 @@ impl<'a, F: Function> Env<'a, F> {
}
// Handle multi-fixed-reg constraints by copying.
for (progpoint, from_preg, to_preg, slot) in
for (progpoint, from_preg, to_preg, to_vreg, slot) in
std::mem::replace(&mut self.multi_fixed_reg_fixups, vec![])
{
log::trace!(
"multi-fixed-move constraint at {:?} from p{} to p{}",
"multi-fixed-move constraint at {:?} from p{} to p{} for v{}",
progpoint,
from_preg.index(),
to_preg.index()
to_preg.index(),
to_vreg.index(),
);
self.insert_move(
progpoint,
InsertMovePrio::MultiFixedReg,
Allocation::reg(self.pregs[from_preg.index()].reg),
Allocation::reg(self.pregs[to_preg.index()].reg),
None,
Some(self.vreg_regs[to_vreg.index()]),
);
self.set_alloc(
progpoint.inst(),