35
35
* - Rematerialization
36
36
*/
37
37
38
+ /*
39
+ Performance ideas:
40
+
41
+ - moves: don't consider as normal inst with uses/defs
42
+ - explicit list of src/dst pairs? elide completely if
43
+ remain in same bundle; otherwise only should appear
44
+ when generating halfmoves
45
+ - sorted list of (inst, src-vreg, dst-vreg) and
46
+ (vreg, inst)
47
+ - ignore inst during all passes over code
48
+ - when same bundle at both ends, ignore during resolution
49
+ - otherwise, fill in move-list alloc slots during scans
50
+
51
+ - conflict hints? (note on one bundle that it definitely conflicts
52
+ with another, so avoid probing the other's alloc)
53
+
54
+ - partial allocation -- place one LR, split rest off into separate
55
+ bundle, in one pass?
56
+ */
57
+
38
58
#![ allow( dead_code, unused_imports) ]
39
59
40
60
use crate :: bitvec:: BitVec ;
@@ -118,6 +138,9 @@ struct LiveRange {
118
138
119
139
next_in_bundle : LiveRangeIndex ,
120
140
next_in_reg : LiveRangeIndex ,
141
+
142
+ reg_hint : Option < PReg > , // if a bundle partly fits, this is used to
143
+ // record LRs that do fit
121
144
}
122
145
123
146
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
@@ -202,7 +225,7 @@ impl LiveBundle {
202
225
203
226
#[ inline( always) ]
204
227
fn cached_spill_weight ( & self ) -> u32 {
205
- self . spill_weight_and_props & ! ( ( 1 << 30 ) - 1 )
228
+ self . spill_weight_and_props & ( ( 1 << 30 ) - 1 )
206
229
}
207
230
}
208
231
@@ -521,6 +544,8 @@ pub struct Stats {
521
544
process_bundle_count : usize ,
522
545
process_bundle_reg_probes_fixed : usize ,
523
546
process_bundle_reg_success_fixed : usize ,
547
+ process_bundle_bounding_range_probes_any : usize ,
548
+ process_bundle_bounding_range_success_any : usize ,
524
549
process_bundle_reg_probes_any : usize ,
525
550
process_bundle_reg_success_any : usize ,
526
551
evict_bundle_event : usize ,
@@ -558,7 +583,8 @@ pub struct Stats {
558
583
struct RegTraversalIter < ' a > {
559
584
env : & ' a MachineEnv ,
560
585
class : usize ,
561
- hint_reg : Option < PReg > ,
586
+ hints : [ Option < PReg > ; 2 ] ,
587
+ hint_idx : usize ,
562
588
pref_idx : usize ,
563
589
non_pref_idx : usize ,
564
590
offset : usize ,
@@ -568,13 +594,20 @@ impl<'a> RegTraversalIter<'a> {
568
594
pub fn new (
569
595
env : & ' a MachineEnv ,
570
596
class : RegClass ,
571
- hint_reg : Option < PReg > ,
597
+ mut hint_reg : Option < PReg > ,
598
+ mut hint2_reg : Option < PReg > ,
572
599
offset : usize ,
573
600
) -> Self {
601
+ if hint_reg. is_none ( ) {
602
+ hint_reg = hint2_reg;
603
+ hint2_reg = None ;
604
+ }
605
+ let hints = [ hint_reg, hint2_reg] ;
574
606
Self {
575
607
env,
576
608
class : class as u8 as usize ,
577
- hint_reg,
609
+ hints,
610
+ hint_idx : 0 ,
578
611
pref_idx : 0 ,
579
612
non_pref_idx : 0 ,
580
613
offset,
@@ -586,19 +619,27 @@ impl<'a> std::iter::Iterator for RegTraversalIter<'a> {
586
619
type Item = PReg ;
587
620
588
621
fn next ( & mut self ) -> Option < PReg > {
589
- if let Some ( preg) = self . hint_reg . take ( ) {
590
- return Some ( preg) ;
622
+ if self . hint_idx < 2 && self . hints [ self . hint_idx ] . is_some ( ) {
623
+ let h = self . hints [ self . hint_idx ] ;
624
+ self . hint_idx += 1 ;
625
+ return h;
591
626
}
592
- if self . pref_idx < self . env . preferred_regs_by_class [ self . class ] . len ( ) {
627
+ while self . pref_idx < self . env . preferred_regs_by_class [ self . class ] . len ( ) {
593
628
let arr = & self . env . preferred_regs_by_class [ self . class ] [ ..] ;
594
629
let r = arr[ ( self . pref_idx + self . offset ) % arr. len ( ) ] ;
595
630
self . pref_idx += 1 ;
631
+ if Some ( r) == self . hints [ 0 ] || Some ( r) == self . hints [ 1 ] {
632
+ continue ;
633
+ }
596
634
return Some ( r) ;
597
635
}
598
- if self . non_pref_idx < self . env . non_preferred_regs_by_class [ self . class ] . len ( ) {
636
+ while self . non_pref_idx < self . env . non_preferred_regs_by_class [ self . class ] . len ( ) {
599
637
let arr = & self . env . non_preferred_regs_by_class [ self . class ] [ ..] ;
600
638
let r = arr[ ( self . non_pref_idx + self . offset ) % arr. len ( ) ] ;
601
639
self . non_pref_idx += 1 ;
640
+ if Some ( r) == self . hints [ 0 ] || Some ( r) == self . hints [ 1 ] {
641
+ continue ;
642
+ }
602
643
return Some ( r) ;
603
644
}
604
645
None
@@ -699,6 +740,7 @@ impl<'a, F: Function> Env<'a, F> {
699
740
last_use : UseIndex :: invalid ( ) ,
700
741
next_in_bundle : LiveRangeIndex :: invalid ( ) ,
701
742
next_in_reg : LiveRangeIndex :: invalid ( ) ,
743
+ reg_hint : None ,
702
744
} ) ;
703
745
LiveRangeIndex :: new ( idx)
704
746
}
@@ -1889,6 +1931,26 @@ impl<'a, F: Function> Env<'a, F> {
1889
1931
Some ( needed)
1890
1932
}
1891
1933
1934
+ fn bundle_bounding_range_if_multiple ( & self , bundle : LiveBundleIndex ) -> Option < CodeRange > {
1935
+ let first_range = self . bundles [ bundle. index ( ) ] . first_range ;
1936
+ let last_range = self . bundles [ bundle. index ( ) ] . last_range ;
1937
+ if first_range. is_invalid ( ) || first_range == last_range {
1938
+ return None ;
1939
+ }
1940
+ Some ( CodeRange {
1941
+ from : self . ranges [ first_range. index ( ) ] . range . from ,
1942
+ to : self . ranges [ last_range. index ( ) ] . range . to ,
1943
+ } )
1944
+ }
1945
+
1946
+ fn range_definitely_fits_in_reg ( & self , range : CodeRange , reg : PRegIndex ) -> bool {
1947
+ self . pregs [ reg. index ( ) ]
1948
+ . allocations
1949
+ . btree
1950
+ . get ( & LiveRangeKey :: from_range ( & range) )
1951
+ . is_none ( )
1952
+ }
1953
+
1892
1954
fn try_to_allocate_bundle_to_reg (
1893
1955
& mut self ,
1894
1956
bundle : LiveBundleIndex ,
@@ -1899,6 +1961,7 @@ impl<'a, F: Function> Env<'a, F> {
1899
1961
let mut iter = self . bundles [ bundle. index ( ) ] . first_range ;
1900
1962
while iter. is_valid ( ) {
1901
1963
let range = & self . ranges [ iter. index ( ) ] ;
1964
+ let next = range. next_in_bundle ;
1902
1965
log:: debug!( " -> range {:?}" , range) ;
1903
1966
// Note that the comparator function here tests for *overlap*, so we
1904
1967
// are checking whether the BTree contains any preg range that
@@ -1923,8 +1986,10 @@ impl<'a, F: Function> Env<'a, F> {
1923
1986
// range from a direct use of the PReg (due to clobber).
1924
1987
return AllocRegResult :: ConflictWithFixed ;
1925
1988
}
1989
+ } else {
1990
+ self . ranges [ iter. index ( ) ] . reg_hint = Some ( self . pregs [ reg. index ( ) ] . reg ) ;
1926
1991
}
1927
- iter = range . next_in_bundle ;
1992
+ iter = next ;
1928
1993
}
1929
1994
1930
1995
if conflicts. len ( ) > 0 {
@@ -1985,11 +2050,18 @@ impl<'a, F: Function> Env<'a, F> {
1985
2050
}
1986
2051
1987
2052
fn maximum_spill_weight_in_bundle_set ( & self , bundles : & LiveBundleVec ) -> u32 {
1988
- bundles
2053
+ log:: debug!( "maximum_spill_weight_in_bundle_set: {:?}" , bundles) ;
2054
+ let m = bundles
1989
2055
. iter ( )
1990
- . map ( |& b| self . bundles [ b. index ( ) ] . cached_spill_weight ( ) )
2056
+ . map ( |& b| {
2057
+ let w = self . bundles [ b. index ( ) ] . cached_spill_weight ( ) ;
2058
+ log:: debug!( "bundle{}: {}" , b. index( ) , w) ;
2059
+ w
2060
+ } )
1991
2061
. max ( )
1992
- . unwrap_or ( 0 )
2062
+ . unwrap_or ( 0 ) ;
2063
+ log:: debug!( " -> max: {}" , m) ;
2064
+ m
1993
2065
}
1994
2066
1995
2067
fn recompute_bundle_properties ( & mut self , bundle : LiveBundleIndex ) {
@@ -2055,7 +2127,7 @@ impl<'a, F: Function> Env<'a, F> {
2055
2127
) ;
2056
2128
total / self . bundles [ bundle. index ( ) ] . prio
2057
2129
} else {
2058
- total
2130
+ 0
2059
2131
}
2060
2132
} ;
2061
2133
@@ -2577,13 +2649,19 @@ impl<'a, F: Function> Env<'a, F> {
2577
2649
// Find any requirements: for every LR, for every def/use, gather
2578
2650
// requirements (fixed-reg, any-reg, any) and merge them.
2579
2651
let req = self . compute_requirement ( bundle) ;
2580
- // Grab a hint from our spillset, if any.
2652
+ // Grab a hint from our spillset, if any, and from the first LR, if any .
2581
2653
let hint_reg = self . spillsets [ self . bundles [ bundle. index ( ) ] . spillset . index ( ) ] . reg_hint ;
2654
+ let hint2_reg = if self . bundles [ bundle. index ( ) ] . first_range . is_valid ( ) {
2655
+ self . ranges [ self . bundles [ bundle. index ( ) ] . first_range . index ( ) ] . reg_hint
2656
+ } else {
2657
+ None
2658
+ } ;
2582
2659
log:: debug!(
2583
- "process_bundle: bundle {:?} requirement {:?} hint {:?}" ,
2660
+ "process_bundle: bundle {:?} requirement {:?} hint {:?} hint2 {:?} " ,
2584
2661
bundle,
2585
2662
req,
2586
2663
hint_reg,
2664
+ hint2_reg
2587
2665
) ;
2588
2666
2589
2667
// Try to allocate!
@@ -2636,7 +2714,35 @@ impl<'a, F: Function> Env<'a, F> {
2636
2714
. index ( )
2637
2715
+ bundle. index ( ) ;
2638
2716
2639
- for preg in RegTraversalIter :: new ( self . env , class, hint_reg, scan_offset) {
2717
+ // If the bundle is more than one range, see if we
2718
+ // can find a reg that the bounding range fits
2719
+ // completely in first. Use that if so. Otherwise,
2720
+ // do a detailed (liverange-by-liverange) probe of
2721
+ // each reg in preference order.
2722
+ let bounding_range = self . bundle_bounding_range_if_multiple ( bundle) ;
2723
+ if let Some ( bounding_range) = bounding_range {
2724
+ for preg in
2725
+ RegTraversalIter :: new ( self . env , class, hint_reg, hint2_reg, scan_offset)
2726
+ {
2727
+ let preg_idx = PRegIndex :: new ( preg. index ( ) ) ;
2728
+ self . stats . process_bundle_bounding_range_probes_any += 1 ;
2729
+ if self . range_definitely_fits_in_reg ( bounding_range, preg_idx) {
2730
+ let result = self . try_to_allocate_bundle_to_reg ( bundle, preg_idx) ;
2731
+ self . stats . process_bundle_bounding_range_success_any += 1 ;
2732
+ let alloc = match result {
2733
+ AllocRegResult :: Allocated ( alloc) => alloc,
2734
+ _ => panic ! ( "Impossible result: {:?}" , result) ,
2735
+ } ;
2736
+ self . spillsets [ self . bundles [ bundle. index ( ) ] . spillset . index ( ) ]
2737
+ . reg_hint = Some ( alloc. as_reg ( ) . unwrap ( ) ) ;
2738
+ return ;
2739
+ }
2740
+ }
2741
+ }
2742
+
2743
+ for preg in
2744
+ RegTraversalIter :: new ( self . env , class, hint_reg, hint2_reg, scan_offset)
2745
+ {
2640
2746
self . stats . process_bundle_reg_probes_any += 1 ;
2641
2747
let preg_idx = PRegIndex :: new ( preg. index ( ) ) ;
2642
2748
match self . try_to_allocate_bundle_to_reg ( bundle, preg_idx) {
@@ -2709,9 +2815,13 @@ impl<'a, F: Function> Env<'a, F> {
2709
2815
2710
2816
// If the maximum spill weight in the conflicting-bundles set is >= this bundle's spill
2711
2817
// weight, then don't evict.
2712
- if self . maximum_spill_weight_in_bundle_set ( & conflicting_bundles)
2713
- >= self . bundle_spill_weight ( bundle)
2714
- {
2818
+ let max_spill_weight = self . maximum_spill_weight_in_bundle_set ( & conflicting_bundles) ;
2819
+ log:: debug!(
2820
+ " -> max_spill_weight = {}; our spill weight {}" ,
2821
+ max_spill_weight,
2822
+ self . bundle_spill_weight( bundle)
2823
+ ) ;
2824
+ if max_spill_weight >= self . bundle_spill_weight ( bundle) {
2715
2825
log:: debug!( " -> we're already the cheapest bundle to spill -- going to split" ) ;
2716
2826
break ;
2717
2827
}
@@ -2748,7 +2858,7 @@ impl<'a, F: Function> Env<'a, F> {
2748
2858
let class = any_vreg. class ( ) ;
2749
2859
let mut success = false ;
2750
2860
self . stats . spill_bundle_reg_probes += 1 ;
2751
- for preg in RegTraversalIter :: new ( self . env , class, None , bundle. index ( ) ) {
2861
+ for preg in RegTraversalIter :: new ( self . env , class, None , None , bundle. index ( ) ) {
2752
2862
let preg_idx = PRegIndex :: new ( preg. index ( ) ) ;
2753
2863
if let AllocRegResult :: Allocated ( _) =
2754
2864
self . try_to_allocate_bundle_to_reg ( bundle, preg_idx)
0 commit comments