From 2c63d5b0cb299b0424799cb1f300349beeb27d03 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Sat, 5 Apr 2025 20:00:28 -0700 Subject: [PATCH] Redefine a minimal bundle to have only one LiveRange. When exerting additional pressure on regalloc with bytecodealliance/wasmtime#10502, which can lead to call instructions that have significantly more (`any`-constrained) defs, we hit panics in RA2 where (i) a bundle merged several LiveRanges, (ii) one of these LiveRanges had a fixed-reg constraint on an early use, (iii) this fixed-reg constraint conflicted with a clobber (which always happens at the late-point), (iv) the bundle merged in another LiveRange of some arbitrary def at the late point. This would make a bundle (which is the atomic unit of allocation) that covers the whole inst, including the late point; and is required to be in the fixed reg; this is unallocatable because the clobber is also at the late point in that reg. Our allocate-or-split-and-retry logic does not split if a bundle is "minimal". This is meant to give a base case to the retries: when bundles break down into their minimal pieces, any solvable set of constraints should result in allocations. Unfortunately the "is minimal" predicate definition did not account for multiple LiveRanges, but rather only tested whether the total program-point range of the bundle was over one instruction. If there are multiple LiveRanges, we can still split them off, and the resulting split bundles may cover only half the instruction, avoiding the clobbers. --- src/ion/process.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ion/process.rs b/src/ion/process.rs index cc57effb..75abadd9 100644 --- a/src/ion/process.rs +++ b/src/ion/process.rs @@ -268,6 +268,7 @@ impl<'a, F: Function> Env<'a, F> { let mut fixed = false; let mut fixed_def = false; let bundledata = &self.ctx.bundles[bundle]; + let num_ranges = bundledata.ranges.len(); let first_range = bundledata.ranges[0].index; let first_range_data = &self.ctx.ranges[first_range]; @@ -277,7 +278,7 @@ impl<'a, F: Function> Env<'a, F> { trace!(" -> no vreg; minimal and fixed"); minimal = true; fixed = true; - } else { + } else if num_ranges == 1 { for u in &first_range_data.uses { trace!(" -> use: {:?}", u); if let OperandConstraint::FixedReg(_) = u.operand.constraint() { @@ -291,8 +292,9 @@ impl<'a, F: Function> Env<'a, F> { break; } } - // Minimal if the range covers only one instruction. Note - // that it could cover just one ProgPoint, + // Minimal if there is only one LR and the ProgPoint range + // covers only one instruction. Note that it could cover + // just one ProgPoint, // i.e. X.Before..X.After, or two ProgPoints, // i.e. X.Before..X+1.Before. trace!(" -> first range has range {:?}", first_range_data.range); @@ -300,6 +302,8 @@ impl<'a, F: Function> Env<'a, F> { let bundle_end = self.ctx.bundles[bundle].ranges.last().unwrap().range.to; minimal = bundle_start.inst() == bundle_end.prev().inst(); trace!(" -> minimal: {}", minimal); + } else { + minimal = false; } let spill_weight = if minimal {