@@ -444,6 +444,53 @@ impl core::ops::IndexMut<VReg> for VRegs {
444444 }
445445}
446446
447+ /// A dedup set of `LiveBundleIndex` values that avoids hashing.
448+ ///
449+ /// Each bundle index is mapped to a slot in a dense array holding the
450+ /// generation at which it was last inserted. `clear` simply bumps the
451+ /// current generation (O(1) in the common case), and `insert` is a
452+ /// single bounds-checked compare-and-store. This is a drop-in
453+ /// replacement for the previous `FxHashSet<LiveBundleIndex>` that is
454+ /// much cheaper when a bundle conflicts with many others (e.g. a
455+ /// function with many locals).
456+ #[ derive( Clone , Debug , Default ) ]
457+ pub struct ConflictSet {
458+ // Generation at which each bundle was last inserted. The value `0`
459+ // means "never inserted"; `generation` is therefore always >= 1
460+ // after the first `clear`.
461+ stamps : Vec < u32 > ,
462+ generation : u32 ,
463+ }
464+
465+ impl ConflictSet {
466+ /// Empty the set. O(1) except on the rare generation wraparound.
467+ #[ inline]
468+ pub fn clear ( & mut self ) {
469+ self . generation = self . generation . wrapping_add ( 1 ) ;
470+ if self . generation == 0 {
471+ // Wrapped around; reset stamps so stale entries don't read
472+ // as present, and skip the reserved `0` generation.
473+ self . stamps . iter_mut ( ) . for_each ( |s| * s = 0 ) ;
474+ self . generation = 1 ;
475+ }
476+ }
477+
478+ /// Insert `bundle`. Returns `true` if it was not already present.
479+ #[ inline]
480+ pub fn insert ( & mut self , bundle : LiveBundleIndex ) -> bool {
481+ let idx = bundle. index ( ) ;
482+ if idx >= self . stamps . len ( ) {
483+ self . stamps . resize ( idx + 1 , 0 ) ;
484+ }
485+ if self . stamps [ idx] == self . generation {
486+ false
487+ } else {
488+ self . stamps [ idx] = self . generation ;
489+ true
490+ }
491+ }
492+ }
493+
447494#[ derive( Default ) ]
448495pub struct Ctx {
449496 pub ( crate ) cfginfo : CFGInfo ,
@@ -484,9 +531,10 @@ pub struct Ctx {
484531 pub ( crate ) debug_annotations : FxHashMap < ProgPoint , Vec < String > > ,
485532 pub ( crate ) annotations_enabled : bool ,
486533
487- // Cached allocation for `try_to_allocate_bundle_to_reg` to avoid allocating
488- // a new HashSet on every call.
489- pub ( crate ) conflict_set : FxHashSet < LiveBundleIndex > ,
534+ // Scratch dedup set for `try_to_allocate_bundle_to_reg`, reused
535+ // across calls. Uses generation stamping to avoid hashing and to
536+ // make clearing O(1).
537+ pub ( crate ) conflict_set : ConflictSet ,
490538
491539 // Output:
492540 pub output : Output ,
0 commit comments