Track transient register counts in Pressure.

The register pressure tracker now has to separate register counts: base
and transient.

The transient counts are used to track spikes of register pressure, such
as dead defs or temporary registers needed to satisfy instruction
constraints.

The base counts represent long-lived variables.
This commit is contained in:
Jakob Stoklund Olesen
2017-06-12 11:55:01 -07:00
parent 5336bbd4cc
commit 9cea092bc3

View File

@@ -26,6 +26,12 @@
//! //!
//! Currently, the only register bank with multiple top-level registers is the `arm32` //! Currently, the only register bank with multiple top-level registers is the `arm32`
//! floating-point register bank which has `S`, `D`, and `Q` top-level classes. //! floating-point register bank which has `S`, `D`, and `Q` top-level classes.
//!
//! # Base and transient counts
//!
//! We maintain two separate register counts per top-level register class: base counts and
//! transient counts. The base counts are adjusted with the `take` and `free` functions. The
//! transient counts are adjusted with `take_transient` and `free_transient`.
// Remove once we're using the pressure tracker. // Remove once we're using the pressure tracker.
#![allow(dead_code)] #![allow(dead_code)]
@@ -37,11 +43,12 @@ use std::iter::ExactSizeIterator;
/// Information per top-level register class. /// Information per top-level register class.
/// ///
/// Everything but the count is static information computed from the constructor arguments. /// Everything but the counts is static information computed from the constructor arguments.
#[derive(Default)] #[derive(Default)]
struct TopRC { struct TopRC {
// Number of registers currently used from this register class. // Number of registers currently used from this register class.
count: u32, base_count: u32,
transient_count: u32,
// Max number of registers that can be allocated. // Max number of registers that can be allocated.
limit: u32, limit: u32,
@@ -56,6 +63,12 @@ struct TopRC {
num_toprcs: u8, num_toprcs: u8,
} }
impl TopRC {
fn total_count(&self) -> u32 {
self.base_count + self.transient_count
}
}
pub struct Pressure { pub struct Pressure {
// Bit mask of top-level register classes that are aliased by other top-level register classes. // Bit mask of top-level register classes that are aliased by other top-level register classes.
// Unaliased register classes can use a simpler interference algorithm. // Unaliased register classes can use a simpler interference algorithm.
@@ -108,12 +121,16 @@ impl Pressure {
/// If not, returns a bit-mask of top-level register classes that are interfering. Register /// If not, returns a bit-mask of top-level register classes that are interfering. Register
/// pressure should be eased in one of the returned top-level register classes before calling /// pressure should be eased in one of the returned top-level register classes before calling
/// `can_take()` to check again. /// `can_take()` to check again.
pub fn check_avail(&self, rc: RegClass) -> RegClassMask { fn check_avail(&self, rc: RegClass) -> RegClassMask {
let entry = &self.toprc[rc.toprc as usize]; let entry = &self.toprc[rc.toprc as usize];
let mask = 1 << rc.toprc; let mask = 1 << rc.toprc;
if self.aliased & mask == 0 { if self.aliased & mask == 0 {
// This is a simple unaliased top-level register class. // This is a simple unaliased top-level register class.
if entry.count < entry.limit { 0 } else { mask } if entry.total_count() < entry.limit {
0
} else {
mask
}
} else { } else {
// This is the more complicated case. The top-level register class has aliases. // This is the more complicated case. The top-level register class has aliases.
self.check_avail_aliased(entry) self.check_avail_aliased(entry)
@@ -142,9 +159,9 @@ impl Pressure {
let u = if rcw < width { let u = if rcw < width {
// We can't take more than the total number of register units in the class. // We can't take more than the total number of register units in the class.
// This matters for arm32 S-registers which can only ever lock out 16 D-registers. // This matters for arm32 S-registers which can only ever lock out 16 D-registers.
min(rc.count * width, rc.limit * rcw) min(rc.total_count() * width, rc.limit * rcw)
} else { } else {
rc.count * rcw rc.total_count() * rcw
}; };
// If this top-level RC on its own is responsible for exceeding our limit, return it // If this top-level RC on its own is responsible for exceeding our limit, return it
@@ -169,20 +186,41 @@ impl Pressure {
/// Take a register from `rc`. /// Take a register from `rc`.
/// ///
/// This assumes that `can_take(rc)` already returned 0. /// This does not check if there are enough registers available.
pub fn take(&mut self, rc: RegClass) { pub fn take(&mut self, rc: RegClass) {
self.toprc[rc.toprc as usize].count += 1 self.toprc[rc.toprc as usize].base_count += 1
} }
/// Free a register in `rc`. /// Free a register in `rc`.
pub fn free(&mut self, rc: RegClass) { pub fn free(&mut self, rc: RegClass) {
self.toprc[rc.toprc as usize].count -= 1 self.toprc[rc.toprc as usize].base_count -= 1
} }
/// Reset all counts to 0. /// Reset all counts to 0, both base and transient.
pub fn reset(&mut self) { pub fn reset(&mut self) {
for e in self.toprc.iter_mut() { for e in self.toprc.iter_mut() {
e.count = 0; e.base_count = 0;
e.transient_count = 0;
}
}
/// Try to increment a transient counter.
///
/// This will fail if there are not enough registers available.
pub fn take_transient(&mut self, rc: RegClass) -> Result<(), RegClassMask> {
let mask = self.check_avail(rc);
if mask == 0 {
self.toprc[rc.toprc as usize].transient_count += 1;
Ok(())
} else {
Err(mask)
}
}
/// Reset all transient counts to 0.
pub fn reset_transient(&mut self) {
for e in self.toprc.iter_mut() {
e.transient_count = 0;
} }
} }
} }