@@ -560,6 +560,69 @@ pub struct Stats {
560
560
edits_count : usize ,
561
561
}
562
562
563
+ /// This iterator represents a traversal through all allocatable
564
+ /// registers of a given class, in a certain order designed to
565
+ /// minimize allocation contention.
566
+ ///
567
+ /// The order in which we try registers is somewhat complex:
568
+ /// - First, if there is a hint, we try that.
569
+ /// - Then, we try registers in a traversal order that is based on an
570
+ /// "offset" (usually the bundle index) spreading pressure evenly
571
+ /// among registers to reduce commitment-map contention.
572
+ /// - Within that scan, we try registers in two groups: first,
573
+ /// prferred registers; then, non-preferred registers. (In normal
574
+ /// usage, these consist of caller-save and callee-save registers
575
+ /// respectively, to minimize clobber-saves; but they need not.)
576
+ struct RegTraversalIter < ' a > {
577
+ env : & ' a MachineEnv ,
578
+ class : usize ,
579
+ hint_reg : Option < PReg > ,
580
+ pref_idx : usize ,
581
+ non_pref_idx : usize ,
582
+ offset : usize ,
583
+ }
584
+
585
+ impl < ' a > RegTraversalIter < ' a > {
586
+ pub fn new (
587
+ env : & ' a MachineEnv ,
588
+ class : RegClass ,
589
+ hint_reg : Option < PReg > ,
590
+ offset : usize ,
591
+ ) -> Self {
592
+ Self {
593
+ env,
594
+ class : class as u8 as usize ,
595
+ hint_reg,
596
+ pref_idx : 0 ,
597
+ non_pref_idx : 0 ,
598
+ offset,
599
+ }
600
+ }
601
+ }
602
+
603
+ impl < ' a > std:: iter:: Iterator for RegTraversalIter < ' a > {
604
+ type Item = PReg ;
605
+
606
+ fn next ( & mut self ) -> Option < PReg > {
607
+ if let Some ( preg) = self . hint_reg . take ( ) {
608
+ return Some ( preg) ;
609
+ }
610
+ if self . pref_idx < self . env . preferred_regs_by_class [ self . class ] . len ( ) {
611
+ let arr = & self . env . preferred_regs_by_class [ self . class ] [ ..] ;
612
+ let r = arr[ ( self . pref_idx + self . offset ) % arr. len ( ) ] ;
613
+ self . pref_idx += 1 ;
614
+ return Some ( r) ;
615
+ }
616
+ if self . non_pref_idx < self . env . non_preferred_regs_by_class [ self . class ] . len ( ) {
617
+ let arr = & self . env . non_preferred_regs_by_class [ self . class ] [ ..] ;
618
+ let r = arr[ ( self . non_pref_idx + self . offset ) % arr. len ( ) ] ;
619
+ self . non_pref_idx += 1 ;
620
+ return Some ( r) ;
621
+ }
622
+ None
623
+ }
624
+ }
625
+
563
626
impl < ' a , F : Function > Env < ' a , F > {
564
627
pub ( crate ) fn new ( func : & ' a F , env : & ' a MachineEnv , cfginfo : CFGInfo ) -> Self {
565
628
Self {
@@ -987,7 +1050,7 @@ impl<'a, F: Function> Env<'a, F> {
987
1050
// return), create blockparam_out entries.
988
1051
if self . func . is_branch ( insns. last ( ) ) {
989
1052
let operands = self . func . inst_operands ( insns. last ( ) ) ;
990
- let mut i = 0 ;
1053
+ let mut i = self . func . branch_blockparam_arg_offset ( block , insns . last ( ) ) ;
991
1054
for & succ in self . func . block_succs ( block) {
992
1055
for & blockparam in self . func . block_params ( succ) {
993
1056
let from_vreg = VRegIndex :: new ( operands[ i] . vreg ( ) . vreg ( ) ) ;
@@ -2671,12 +2734,7 @@ impl<'a, F: Function> Env<'a, F> {
2671
2734
Requirement :: Register ( class) => {
2672
2735
// Scan all pregs and attempt to allocate.
2673
2736
let mut lowest_cost_conflict_set: Option < LiveBundleVec > = None ;
2674
- let n_regs = self . env . regs_by_class [ class as u8 as usize ] . len ( ) ;
2675
- let loop_count = if hint_reg. is_some ( ) {
2676
- n_regs + 1
2677
- } else {
2678
- n_regs
2679
- } ;
2737
+
2680
2738
// Heuristic: start the scan for an available
2681
2739
// register at an offset influenced both by our
2682
2740
// location in the code and by the bundle we're
@@ -2688,35 +2746,8 @@ impl<'a, F: Function> Env<'a, F> {
2688
2746
. inst
2689
2747
. index ( )
2690
2748
+ bundle. index ( ) ;
2691
- for i in 0 ..loop_count {
2692
- // The order in which we try registers is somewhat complex:
2693
- // - First, if there is a hint, we try that.
2694
- // - Then, we try registers in a traversal
2695
- // order that is based on the bundle index,
2696
- // spreading pressure evenly among registers
2697
- // to reduce commitment-map
2698
- // contention. (TODO: account for
2699
- // caller-save vs. callee-saves here too.)
2700
- // Note that we avoid retrying the hint_reg;
2701
- // this is why the loop count is n_regs + 1
2702
- // if there is a hint reg, because we always
2703
- // skip one iteration.
2704
- let preg = match ( i, hint_reg) {
2705
- ( 0 , Some ( hint_reg) ) => hint_reg,
2706
- ( i, Some ( hint_reg) ) => {
2707
- let reg = self . env . regs_by_class [ class as u8 as usize ]
2708
- [ ( i - 1 + scan_offset) % n_regs] ;
2709
- if reg == hint_reg {
2710
- continue ;
2711
- }
2712
- reg
2713
- }
2714
- ( i, None ) => {
2715
- self . env . regs_by_class [ class as u8 as usize ]
2716
- [ ( i + scan_offset) % n_regs]
2717
- }
2718
- } ;
2719
2749
2750
+ for preg in RegTraversalIter :: new ( self . env , class, hint_reg, scan_offset) {
2720
2751
self . stats . process_bundle_reg_probes_any += 1 ;
2721
2752
let preg_idx = PRegIndex :: new ( preg. index ( ) ) ;
2722
2753
match self . try_to_allocate_bundle_to_reg ( bundle, preg_idx) {
@@ -2828,10 +2859,7 @@ impl<'a, F: Function> Env<'a, F> {
2828
2859
let class = any_vreg. class ( ) ;
2829
2860
let mut success = false ;
2830
2861
self . stats . spill_bundle_reg_probes += 1 ;
2831
- let nregs = self . env . regs_by_class [ class as u8 as usize ] . len ( ) ;
2832
- for i in 0 ..nregs {
2833
- let i = ( i + bundle. index ( ) ) % nregs;
2834
- let preg = self . env . regs_by_class [ class as u8 as usize ] [ i] ; // don't borrow self
2862
+ for preg in RegTraversalIter :: new ( self . env , class, None , bundle. index ( ) ) {
2835
2863
let preg_idx = PRegIndex :: new ( preg. index ( ) ) ;
2836
2864
if let AllocRegResult :: Allocated ( _) =
2837
2865
self . try_to_allocate_bundle_to_reg ( bundle, preg_idx)
0 commit comments