diff --git a/src/fuzzing/func.rs b/src/fuzzing/func.rs index fac10179..8bdb326e 100644 --- a/src/fuzzing/func.rs +++ b/src/fuzzing/func.rs @@ -471,6 +471,20 @@ 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, @@ -478,22 +492,6 @@ impl Func { 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(), diff --git a/src/ion/liveranges.rs b/src/ion/liveranges.rs index 164a5aad..b3ee32cb 100644 --- a/src/ion/liveranges.rs +++ b/src/ion/liveranges.rs @@ -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 = 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); } _ => {} @@ -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 diff --git a/src/lib.rs b/src/lib.rs index fb0e9dda..3fe8267b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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).