Skip to content

Commit ea86b87

Browse files
authored
Allow fixed-early-def and fixed-late-use constraints (#168)
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.
1 parent 98fbea5 commit ea86b87

3 files changed

Lines changed: 49 additions & 35 deletions

File tree

src/fuzzing/func.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -471,29 +471,27 @@ impl Func {
471471
let i = u.int_in_range(0..=(operands.len() - 1))?;
472472
let op = operands[i];
473473
let fixed_reg = PReg::new(u.int_in_range(0..=62)?, op.class());
474+
if op.kind() == OperandKind::Def && op.pos() == OperandPos::Early {
475+
// Early-defs with fixed constraints conflict with
476+
// any other fixed uses of the same preg.
477+
if fixed_late.contains(&fixed_reg) {
478+
break;
479+
}
480+
}
481+
if op.kind() == OperandKind::Use && op.pos() == OperandPos::Late {
482+
// Late-use with fixed constraints conflict with
483+
// any other fixed uses of the same preg.
484+
if fixed_early.contains(&fixed_reg) {
485+
break;
486+
}
487+
}
474488
let fixed_list = match op.pos() {
475489
OperandPos::Early => &mut fixed_early,
476490
OperandPos::Late => &mut fixed_late,
477491
};
478492
if fixed_list.contains(&fixed_reg) {
479493
break;
480494
}
481-
if op.kind() != OperandKind::Def && op.pos() == OperandPos::Late {
482-
// Late-uses/mods with fixed constraints
483-
// can't be allowed if we're allowing
484-
// different constraints at Early and
485-
// Late, because we can't move something
486-
// into a location between Early and
487-
// Late. Differing constraints only make
488-
// sense if the instruction itself
489-
// produces the newly-constrained values.
490-
break;
491-
}
492-
if op.kind() != OperandKind::Use && op.pos() == OperandPos::Early {
493-
// Likewise, we can *only* allow uses for
494-
// fixed constraints at Early.
495-
break;
496-
}
497495
fixed_list.push(fixed_reg);
498496
operands[i] = Operand::new(
499497
op.vreg(),

src/ion/liveranges.rs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -479,23 +479,18 @@ impl<'a, F: Function> Env<'a, F> {
479479
// for the use, (ii) rewrite the use to have an Any
480480
// constraint, and (ii) move the def to Early position
481481
// to reserve the register for the whole instruction.
482+
//
483+
// We don't touch any fixed-early-def or fixed-late-use
484+
// constraints: the only situation where the same physical
485+
// register can be used multiple times in the same
486+
// instruction is with an early-use and a late-def. Anything
487+
// else is a user error.
482488
let mut operand_rewrites: FxHashMap<usize, Operand> = FxHashMap::default();
483489
let mut late_def_fixed: SmallVec<[PReg; 8]> = smallvec![];
484490
for &operand in self.func.inst_operands(inst) {
485491
if let OperandConstraint::FixedReg(preg) = operand.constraint() {
486-
match operand.pos() {
487-
OperandPos::Late => {
488-
// See note in fuzzing/func.rs: we
489-
// can't allow this, because there
490-
// would be no way to move a value
491-
// into place for a late use *after*
492-
// the early point (i.e. in the middle
493-
// of the instruction).
494-
assert!(
495-
operand.kind() == OperandKind::Def,
496-
"Invalid operand: fixed constraint on Use/Mod at Late point"
497-
);
498-
492+
match (operand.pos(), operand.kind()) {
493+
(OperandPos::Late, OperandKind::Def) => {
499494
late_def_fixed.push(preg);
500495
}
501496
_ => {}
@@ -507,12 +502,11 @@ impl<'a, F: Function> Env<'a, F> {
507502
continue;
508503
}
509504
if let OperandConstraint::FixedReg(preg) = operand.constraint() {
510-
match operand.pos() {
511-
OperandPos::Early if live.get(operand.vreg().vreg()) => {
512-
assert!(operand.kind() == OperandKind::Use,
513-
"Invalid operand: fixed constraint on Def/Mod at Early position");
514-
515-
// If we have a constraint at the
505+
match (operand.pos(), operand.kind()) {
506+
(OperandPos::Early, OperandKind::Use)
507+
if live.get(operand.vreg().vreg()) =>
508+
{
509+
// If we have a use constraint at the
516510
// Early point for a fixed preg, and
517511
// this preg is also constrained with
518512
// a *separate* def at Late or is

src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,28 @@ impl Operand {
694694
)
695695
}
696696

697+
/// Same as `reg_fixed_use` but at `OperandPos::Late`.
698+
#[inline(always)]
699+
pub fn reg_fixed_use_at_end(vreg: VReg, preg: PReg) -> Self {
700+
Operand::new(
701+
vreg,
702+
OperandConstraint::FixedReg(preg),
703+
OperandKind::Use,
704+
OperandPos::Late,
705+
)
706+
}
707+
708+
/// Same as `reg_fixed_def` but at `OperandPos::Early`.
709+
#[inline(always)]
710+
pub fn reg_fixed_def_at_start(vreg: VReg, preg: PReg) -> Self {
711+
Operand::new(
712+
vreg,
713+
OperandConstraint::FixedReg(preg),
714+
OperandKind::Def,
715+
OperandPos::Early,
716+
)
717+
}
718+
697719
/// Create an `Operand` that designates a use of a vreg and places
698720
/// no constraints on its location (i.e., it can be allocated into
699721
/// either a register or on the stack).

0 commit comments

Comments
 (0)