Speling.
This commit is contained in:
@@ -38,7 +38,7 @@ impl AllocatableSet {
|
||||
AllocatableSet { avail: [!0; 3] }
|
||||
}
|
||||
|
||||
/// Returns `true` if the spoecified register is available.
|
||||
/// Returns `true` if the specified register is available.
|
||||
pub fn is_avail(&self, rc: RegClass, reg: RegUnit) -> bool {
|
||||
let (idx, bits) = bitmask(rc, reg);
|
||||
(self.avail[idx] & bits) == bits
|
||||
@@ -71,7 +71,7 @@ impl AllocatableSet {
|
||||
for idx in 0..self.avail.len() {
|
||||
// If a single unit in a register is unavailable, the whole register can't be used.
|
||||
// If a register straddles a word boundary, it will be marked as unavailable.
|
||||
// There's an assertion in cdsl/registers.py to check for that.
|
||||
// There's an assertion in `cdsl/registers.py` to check for that.
|
||||
for i in 0..rc.width {
|
||||
rsi.regs[idx] &= self.avail[idx] >> i;
|
||||
}
|
||||
@@ -102,7 +102,7 @@ impl Iterator for RegSetIter {
|
||||
|
||||
return Some(unit);
|
||||
}
|
||||
// How many register units was there in the word? This is a constant 32 for u32 etc.
|
||||
// How many register units was there in the word? This is a constant 32 for `u32` etc.
|
||||
unit_offset += 8 * size_of_val(word) as RegUnit;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ mod tests {
|
||||
fn put_and_take() {
|
||||
let mut regs = AllocatableSet::new();
|
||||
|
||||
// GPR has units 28-36.
|
||||
// `GPR` has units 28-36.
|
||||
assert_eq!(regs.iter(GPR).count(), 8);
|
||||
assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [28, 30, 33, 35]);
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
//!
|
||||
//! The primary consumer of the liveness analysis is the SSA coloring pass which goes through each
|
||||
//! EBB and assigns a register to the defined values. This algorithm needs to maintain a set of the
|
||||
//! curently live values as it is iterating down the instructions in the EBB. It asks the following
|
||||
//! questions:
|
||||
//! currently live values as it is iterating down the instructions in the EBB. It asks the
|
||||
//! following questions:
|
||||
//!
|
||||
//! - What is the set of live values at the entry to the EBB?
|
||||
//! - When moving past a use of a value, is that value still alive in the EBB, or was that the last
|
||||
@@ -18,7 +18,7 @@
|
||||
//!
|
||||
//! The set of `LiveRange` instances can answer these questions through their `def_local_end` and
|
||||
//! `livein_local_end` queries. The coloring algorithm visits EBBs in a topological order of the
|
||||
//! dominator tree, so it can compute the set of live values at the begining of an EBB by starting
|
||||
//! dominator tree, so it can compute the set of live values at the beginning of an EBB by starting
|
||||
//! from the set of live values at the dominating branch instruction and filtering it with
|
||||
//! `livein_local_end`. These sets do not need to be stored in the liveness analysis.
|
||||
//!
|
||||
@@ -32,11 +32,11 @@
|
||||
//! A number of different liveness analysis algorithms exist, so it is worthwhile to look at a few
|
||||
//! alternatives.
|
||||
//!
|
||||
//! ## Dataflow equations
|
||||
//! ## Data-flow equations
|
||||
//!
|
||||
//! The classic *live variables analysis* that you will find in all compiler books from the
|
||||
//! previous century does not depend on SSA form. It is typically implemented by iteratively
|
||||
//! solving dataflow equations on bitvectors of variables. The result is a live-out bitvector of
|
||||
//! solving data-flow equations on bit-vectors of variables. The result is a live-out bit-vector of
|
||||
//! variables for every basic block in the program.
|
||||
//!
|
||||
//! This algorithm has some disadvantages that makes us look elsewhere:
|
||||
@@ -44,18 +44,18 @@
|
||||
//! - Quadratic memory use. We need a bit per variable per basic block in the function.
|
||||
//! - Sparse representation. In practice, the majority of SSA values never leave their basic block,
|
||||
//! and those that do span basic blocks rarely span a large number of basic blocks. This makes
|
||||
//! the bitvectors quite sparse.
|
||||
//! - Traditionally, the dataflow equations were solved for real program *variables* which does not
|
||||
//! include temporaries used in evaluating expressions. We have an SSA form program which blurs
|
||||
//! the distinction between temporaries and variables. This makes the quadratic memory problem
|
||||
//! worse because there are many more SSA values than there was variables in the original
|
||||
//! the bit-vectors quite sparse.
|
||||
//! - Traditionally, the data-flow equations were solved for real program *variables* which does
|
||||
//! not include temporaries used in evaluating expressions. We have an SSA form program which
|
||||
//! blurs the distinction between temporaries and variables. This makes the quadratic memory
|
||||
//! problem worse because there are many more SSA values than there was variables in the original
|
||||
//! program, and we don't know a priori which SSA values leave their basic block.
|
||||
//! - Missing last-use information. For values that are not live-out of a basic block, we would
|
||||
//! need to store information about the last use in the block somewhere. LLVM stores this
|
||||
//! information as a 'kill bit' on the last use in the IR. Maintaining these kill bits has been a
|
||||
//! source of problems for LLVM's register allocator.
|
||||
//!
|
||||
//! Dataflow equations can detect when a variable is used uninitialized, and they can handle
|
||||
//! Data-flow equations can detect when a variable is used uninitialized, and they can handle
|
||||
//! multiple definitions of the same variable. We don't need this generality since we already have
|
||||
//! a program in SSA form.
|
||||
//!
|
||||
@@ -83,7 +83,7 @@
|
||||
//! The iterative SSA form reconstruction can be skipped if the depth-first search only encountered
|
||||
//! one SSA value.
|
||||
//!
|
||||
//! This algorithm has some advantages compared to the dataflow equations:
|
||||
//! This algorithm has some advantages compared to the data-flow equations:
|
||||
//!
|
||||
//! - The live ranges of local virtual registers are computed very quickly without ever traversing
|
||||
//! the CFG. The memory needed to store these live ranges is independent of the number of basic
|
||||
@@ -106,11 +106,11 @@
|
||||
//! was presented at CGO 2008:
|
||||
//!
|
||||
//! > Boissinot, B., Hack, S., Grund, D., de Dinechin, B. D., & Rastello, F. (2008). *Fast Liveness
|
||||
//! Checking for SSA-Form Programs.* CGO.
|
||||
//! Checking for SSA-Form Programs.* CGO.
|
||||
//!
|
||||
//! This analysis uses a global precomputation that only depends on the CFG of the function. It
|
||||
//! This analysis uses a global pre-computation that only depends on the CFG of the function. It
|
||||
//! then allows liveness queries for any (value, program point) pair. Each query traverses the use
|
||||
//! chain of the value and performs lookups in the precomputed bitvectors.
|
||||
//! chain of the value and performs lookups in the precomputed bit-vectors.
|
||||
//!
|
||||
//! I did not seriously consider this analysis for Cretonne because:
|
||||
//!
|
||||
@@ -118,8 +118,8 @@
|
||||
//! - Popular variables like the `this` pointer in a C++ method can have very large use chains.
|
||||
//! Traversing such a long use chain on every liveness lookup has the potential for some nasty
|
||||
//! quadratic behavior in unfortunate cases.
|
||||
//! - It says "fast" in the title, but the paper only claims to be 16% faster than a dataflow based
|
||||
//! approach, which isn't that impressive.
|
||||
//! - It says "fast" in the title, but the paper only claims to be 16% faster than a data-flow
|
||||
//! based approach, which isn't that impressive.
|
||||
//!
|
||||
//! Nevertheless, the property of only depending in the CFG structure is very useful. If Cretonne
|
||||
//! gains use chains, this approach would be worth a proper evaluation.
|
||||
@@ -171,7 +171,7 @@
|
||||
//! - Related values should be stored on the same cache line. The current sparse set implementation
|
||||
//! does a decent job of that.
|
||||
//! - For global values, the list of live-in intervals is very likely to fit on a single cache
|
||||
//! line. These lists are very likely ot be found in L2 cache at least.
|
||||
//! line. These lists are very likely to be found in L2 cache at least.
|
||||
//!
|
||||
//! There is some room for improvement.
|
||||
|
||||
@@ -271,10 +271,10 @@ impl Liveness {
|
||||
self.worklist.push(ebb);
|
||||
}
|
||||
|
||||
// The worklist contains those EBBs where we have learned that the value needs to be
|
||||
// The work list contains those EBBs where we have learned that the value needs to be
|
||||
// live-in.
|
||||
//
|
||||
// This algorithm bcomes a depth-first traversal up the CFG, enumerating all paths through
|
||||
// This algorithm becomes a depth-first traversal up the CFG, enumerating all paths through
|
||||
// the CFG from the existing live range to `ebb`.
|
||||
//
|
||||
// Extend the live range as we go. The live range itself also serves as a visited set since
|
||||
|
||||
@@ -45,13 +45,13 @@
|
||||
//! handle *early clobbers* which are output registers that are not allowed to alias any input
|
||||
//! registers.
|
||||
//!
|
||||
//! If i1 < i2 < i3 are program points, we have:
|
||||
//! If `i1 < i2 < i3` are program points, we have:
|
||||
//!
|
||||
//! - i1-i2 and i1-i3 interfere because the intervals overlap.
|
||||
//! - i1-i2 and i2-i3 don't interfere.
|
||||
//! - i1-i3 and i2-i2 do interfere because the dead def would clobber the register.
|
||||
//! - i1-i2 and i2-i2 don't interfere.
|
||||
//! - i2-i3 and i2-i2 do interfere.
|
||||
//! - `i1-i2` and `i1-i3` interfere because the intervals overlap.
|
||||
//! - `i1-i2` and `i2-i3` don't interfere.
|
||||
//! - `i1-i3` and `i2-i2` do interfere because the dead def would clobber the register.
|
||||
//! - `i1-i2` and `i2-i2` don't interfere.
|
||||
//! - `i2-i3` and `i2-i2` do interfere.
|
||||
//!
|
||||
//! Because of this behavior around interval end points, live range interference is not completely
|
||||
//! equivalent to mathematical intersection of open or half-open intervals.
|
||||
@@ -415,7 +415,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton ProgramOrder for tests below.
|
||||
// Singleton `ProgramOrder` for tests below.
|
||||
const PO: &'static ProgOrder = &ProgOrder {};
|
||||
|
||||
#[test]
|
||||
@@ -441,7 +441,7 @@ mod tests {
|
||||
assert!(lr.is_local());
|
||||
assert_eq!(lr.def(), e2.into());
|
||||
assert_eq!(lr.def_local_end(), e2.into());
|
||||
// The def interval of an EBB arg does not count as live-in.
|
||||
// The def interval of an EBB argument does not count as live-in.
|
||||
assert_eq!(lr.livein_local_end(e2, PO), None);
|
||||
PO.validate(&lr);
|
||||
}
|
||||
@@ -478,8 +478,8 @@ mod tests {
|
||||
let i13 = Inst::new(13);
|
||||
let mut lr = LiveRange::new(v0, e10);
|
||||
|
||||
// Extending a dead EBB arg in its own block should not indicate that a live-in interval
|
||||
// was created.
|
||||
// Extending a dead EBB argument in its own block should not indicate that a live-in
|
||||
// interval was created.
|
||||
assert_eq!(lr.extend_in_ebb(e10, i12, PO), false);
|
||||
PO.validate(&lr);
|
||||
assert!(!lr.is_dead());
|
||||
|
||||
Reference in New Issue
Block a user