Skip to content

Commit

Permalink
Allow fixed-early-def and fixed-late-use constraints (#168)
Browse files Browse the repository at this point in the history
These are useful to model fixed registers which should not be reused for
other uses/defs. These were disallowed in #54, but this was too
conservative.

Fundamentally, the only situation where a preg can be used in multiple
fixed constraints within a single instruction is with an early-use and a
late-def. Anything else is a user error because the live ranges will
overlap.

As such this PR relaxes the operand rewrite from #54 to only apply in
this specific situation. Fixed-late-use and fixed-early-def operands are
left unchanged.
  • Loading branch information
Amanieu committed Dec 6, 2023
1 parent 98fbea5 commit ea86b87
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 35 deletions.
30 changes: 14 additions & 16 deletions src/fuzzing/func.rs
Expand Up @@ -471,29 +471,27 @@ impl Func {
let i = u.int_in_range(0..=(operands.len() - 1))?;
let op = operands[i];
let fixed_reg = PReg::new(u.int_in_range(0..=62)?, op.class());
if op.kind() == OperandKind::Def && op.pos() == OperandPos::Early {
// Early-defs with fixed constraints conflict with
// any other fixed uses of the same preg.
if fixed_late.contains(&fixed_reg) {
break;
}
}
if op.kind() == OperandKind::Use && op.pos() == OperandPos::Late {
// Late-use with fixed constraints conflict with
// any other fixed uses of the same preg.
if fixed_early.contains(&fixed_reg) {
break;
}
}
let fixed_list = match op.pos() {
OperandPos::Early => &mut fixed_early,
OperandPos::Late => &mut fixed_late,
};
if fixed_list.contains(&fixed_reg) {
break;
}
if op.kind() != OperandKind::Def && op.pos() == OperandPos::Late {
// Late-uses/mods with fixed constraints
// can't be allowed if we're allowing
// different constraints at Early and
// Late, because we can't move something
// into a location between Early and
// Late. Differing constraints only make
// sense if the instruction itself
// produces the newly-constrained values.
break;
}
if op.kind() != OperandKind::Use && op.pos() == OperandPos::Early {
// Likewise, we can *only* allow uses for
// fixed constraints at Early.
break;
}
fixed_list.push(fixed_reg);
operands[i] = Operand::new(
op.vreg(),
Expand Down
32 changes: 13 additions & 19 deletions src/ion/liveranges.rs
Expand Up @@ -479,23 +479,18 @@ impl<'a, F: Function> Env<'a, F> {
// for the use, (ii) rewrite the use to have an Any
// constraint, and (ii) move the def to Early position
// to reserve the register for the whole instruction.
//
// We don't touch any fixed-early-def or fixed-late-use
// constraints: the only situation where the same physical
// register can be used multiple times in the same
// instruction is with an early-use and a late-def. Anything
// else is a user error.
let mut operand_rewrites: FxHashMap<usize, Operand> = FxHashMap::default();
let mut late_def_fixed: SmallVec<[PReg; 8]> = smallvec![];
for &operand in self.func.inst_operands(inst) {
if let OperandConstraint::FixedReg(preg) = operand.constraint() {
match operand.pos() {
OperandPos::Late => {
// See note in fuzzing/func.rs: we
// can't allow this, because there
// would be no way to move a value
// into place for a late use *after*
// the early point (i.e. in the middle
// of the instruction).
assert!(
operand.kind() == OperandKind::Def,
"Invalid operand: fixed constraint on Use/Mod at Late point"
);

match (operand.pos(), operand.kind()) {
(OperandPos::Late, OperandKind::Def) => {
late_def_fixed.push(preg);
}
_ => {}
Expand All @@ -507,12 +502,11 @@ impl<'a, F: Function> Env<'a, F> {
continue;
}
if let OperandConstraint::FixedReg(preg) = operand.constraint() {
match operand.pos() {
OperandPos::Early if live.get(operand.vreg().vreg()) => {
assert!(operand.kind() == OperandKind::Use,
"Invalid operand: fixed constraint on Def/Mod at Early position");

// If we have a constraint at the
match (operand.pos(), operand.kind()) {
(OperandPos::Early, OperandKind::Use)
if live.get(operand.vreg().vreg()) =>
{
// If we have a use constraint at the
// Early point for a fixed preg, and
// this preg is also constrained with
// a *separate* def at Late or is
Expand Down
22 changes: 22 additions & 0 deletions src/lib.rs
Expand Up @@ -694,6 +694,28 @@ impl Operand {
)
}

/// Same as `reg_fixed_use` but at `OperandPos::Late`.
#[inline(always)]
pub fn reg_fixed_use_at_end(vreg: VReg, preg: PReg) -> Self {
Operand::new(
vreg,
OperandConstraint::FixedReg(preg),
OperandKind::Use,
OperandPos::Late,
)
}

/// Same as `reg_fixed_def` but at `OperandPos::Early`.
#[inline(always)]
pub fn reg_fixed_def_at_start(vreg: VReg, preg: PReg) -> Self {
Operand::new(
vreg,
OperandConstraint::FixedReg(preg),
OperandKind::Def,
OperandPos::Early,
)
}

/// Create an `Operand` that designates a use of a vreg and places
/// no constraints on its location (i.e., it can be allocated into
/// either a register or on the stack).
Expand Down

0 comments on commit ea86b87

Please sign in to comment.