Skip to content

Commit 1a7a0c5

Browse files
committed
Some performance tweaks -- try to reduce register probe count with soem more hints. Also fix spill-weight caching.
1 parent 80cdd0c commit 1a7a0c5

File tree

1 file changed

+130
-20
lines changed

1 file changed

+130
-20
lines changed

src/ion/mod.rs

+130-20
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,26 @@
3535
* - Rematerialization
3636
*/
3737

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+
3858
#![allow(dead_code, unused_imports)]
3959

4060
use crate::bitvec::BitVec;
@@ -118,6 +138,9 @@ struct LiveRange {
118138

119139
next_in_bundle: LiveRangeIndex,
120140
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
121144
}
122145

123146
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -202,7 +225,7 @@ impl LiveBundle {
202225

203226
#[inline(always)]
204227
fn cached_spill_weight(&self) -> u32 {
205-
self.spill_weight_and_props & !((1 << 30) - 1)
228+
self.spill_weight_and_props & ((1 << 30) - 1)
206229
}
207230
}
208231

@@ -521,6 +544,8 @@ pub struct Stats {
521544
process_bundle_count: usize,
522545
process_bundle_reg_probes_fixed: usize,
523546
process_bundle_reg_success_fixed: usize,
547+
process_bundle_bounding_range_probes_any: usize,
548+
process_bundle_bounding_range_success_any: usize,
524549
process_bundle_reg_probes_any: usize,
525550
process_bundle_reg_success_any: usize,
526551
evict_bundle_event: usize,
@@ -558,7 +583,8 @@ pub struct Stats {
558583
struct RegTraversalIter<'a> {
559584
env: &'a MachineEnv,
560585
class: usize,
561-
hint_reg: Option<PReg>,
586+
hints: [Option<PReg>; 2],
587+
hint_idx: usize,
562588
pref_idx: usize,
563589
non_pref_idx: usize,
564590
offset: usize,
@@ -568,13 +594,20 @@ impl<'a> RegTraversalIter<'a> {
568594
pub fn new(
569595
env: &'a MachineEnv,
570596
class: RegClass,
571-
hint_reg: Option<PReg>,
597+
mut hint_reg: Option<PReg>,
598+
mut hint2_reg: Option<PReg>,
572599
offset: usize,
573600
) -> Self {
601+
if hint_reg.is_none() {
602+
hint_reg = hint2_reg;
603+
hint2_reg = None;
604+
}
605+
let hints = [hint_reg, hint2_reg];
574606
Self {
575607
env,
576608
class: class as u8 as usize,
577-
hint_reg,
609+
hints,
610+
hint_idx: 0,
578611
pref_idx: 0,
579612
non_pref_idx: 0,
580613
offset,
@@ -586,19 +619,27 @@ impl<'a> std::iter::Iterator for RegTraversalIter<'a> {
586619
type Item = PReg;
587620

588621
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;
591626
}
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() {
593628
let arr = &self.env.preferred_regs_by_class[self.class][..];
594629
let r = arr[(self.pref_idx + self.offset) % arr.len()];
595630
self.pref_idx += 1;
631+
if Some(r) == self.hints[0] || Some(r) == self.hints[1] {
632+
continue;
633+
}
596634
return Some(r);
597635
}
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() {
599637
let arr = &self.env.non_preferred_regs_by_class[self.class][..];
600638
let r = arr[(self.non_pref_idx + self.offset) % arr.len()];
601639
self.non_pref_idx += 1;
640+
if Some(r) == self.hints[0] || Some(r) == self.hints[1] {
641+
continue;
642+
}
602643
return Some(r);
603644
}
604645
None
@@ -699,6 +740,7 @@ impl<'a, F: Function> Env<'a, F> {
699740
last_use: UseIndex::invalid(),
700741
next_in_bundle: LiveRangeIndex::invalid(),
701742
next_in_reg: LiveRangeIndex::invalid(),
743+
reg_hint: None,
702744
});
703745
LiveRangeIndex::new(idx)
704746
}
@@ -1889,6 +1931,26 @@ impl<'a, F: Function> Env<'a, F> {
18891931
Some(needed)
18901932
}
18911933

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+
18921954
fn try_to_allocate_bundle_to_reg(
18931955
&mut self,
18941956
bundle: LiveBundleIndex,
@@ -1899,6 +1961,7 @@ impl<'a, F: Function> Env<'a, F> {
18991961
let mut iter = self.bundles[bundle.index()].first_range;
19001962
while iter.is_valid() {
19011963
let range = &self.ranges[iter.index()];
1964+
let next = range.next_in_bundle;
19021965
log::debug!(" -> range {:?}", range);
19031966
// Note that the comparator function here tests for *overlap*, so we
19041967
// are checking whether the BTree contains any preg range that
@@ -1923,8 +1986,10 @@ impl<'a, F: Function> Env<'a, F> {
19231986
// range from a direct use of the PReg (due to clobber).
19241987
return AllocRegResult::ConflictWithFixed;
19251988
}
1989+
} else {
1990+
self.ranges[iter.index()].reg_hint = Some(self.pregs[reg.index()].reg);
19261991
}
1927-
iter = range.next_in_bundle;
1992+
iter = next;
19281993
}
19291994

19301995
if conflicts.len() > 0 {
@@ -1985,11 +2050,18 @@ impl<'a, F: Function> Env<'a, F> {
19852050
}
19862051

19872052
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
19892055
.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+
})
19912061
.max()
1992-
.unwrap_or(0)
2062+
.unwrap_or(0);
2063+
log::debug!(" -> max: {}", m);
2064+
m
19932065
}
19942066

19952067
fn recompute_bundle_properties(&mut self, bundle: LiveBundleIndex) {
@@ -2055,7 +2127,7 @@ impl<'a, F: Function> Env<'a, F> {
20552127
);
20562128
total / self.bundles[bundle.index()].prio
20572129
} else {
2058-
total
2130+
0
20592131
}
20602132
};
20612133

@@ -2577,13 +2649,19 @@ impl<'a, F: Function> Env<'a, F> {
25772649
// Find any requirements: for every LR, for every def/use, gather
25782650
// requirements (fixed-reg, any-reg, any) and merge them.
25792651
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.
25812653
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+
};
25822659
log::debug!(
2583-
"process_bundle: bundle {:?} requirement {:?} hint {:?}",
2660+
"process_bundle: bundle {:?} requirement {:?} hint {:?} hint2 {:?}",
25842661
bundle,
25852662
req,
25862663
hint_reg,
2664+
hint2_reg
25872665
);
25882666

25892667
// Try to allocate!
@@ -2636,7 +2714,35 @@ impl<'a, F: Function> Env<'a, F> {
26362714
.index()
26372715
+ bundle.index();
26382716

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+
{
26402746
self.stats.process_bundle_reg_probes_any += 1;
26412747
let preg_idx = PRegIndex::new(preg.index());
26422748
match self.try_to_allocate_bundle_to_reg(bundle, preg_idx) {
@@ -2709,9 +2815,13 @@ impl<'a, F: Function> Env<'a, F> {
27092815

27102816
// If the maximum spill weight in the conflicting-bundles set is >= this bundle's spill
27112817
// 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) {
27152825
log::debug!(" -> we're already the cheapest bundle to spill -- going to split");
27162826
break;
27172827
}
@@ -2748,7 +2858,7 @@ impl<'a, F: Function> Env<'a, F> {
27482858
let class = any_vreg.class();
27492859
let mut success = false;
27502860
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()) {
27522862
let preg_idx = PRegIndex::new(preg.index());
27532863
if let AllocRegResult::Allocated(_) =
27542864
self.try_to_allocate_bundle_to_reg(bundle, preg_idx)

0 commit comments

Comments
 (0)