This commit is contained in:
Jakob Stoklund Olesen
2017-02-03 12:28:07 -08:00
parent 4293bed745
commit 4ae7fd2a37
22 changed files with 112 additions and 110 deletions

View File

@@ -1,8 +1,9 @@
//! A control flow graph represented as mappings of extended basic blocks to their predecessors //! A control flow graph represented as mappings of extended basic blocks to their predecessors
//! and successors. Successors are represented as extended basic blocks while predecessors are //! and successors.
//! represented by basic blocks. //!
//! BasicBlocks are denoted by tuples of EBB and branch/jump instructions. Each predecessor //! Successors are represented as extended basic blocks while predecessors are represented by basic
//! tuple corresponds to the end of a basic block. //! blocks. Basic blocks are denoted by tuples of EBB and branch/jump instructions. Each
//! predecessor tuple corresponds to the end of a basic block.
//! //!
//! ```c //! ```c
//! Ebb0: //! Ebb0:
@@ -19,8 +20,8 @@
//! jmp Ebb2 ; end of basic block //! jmp Ebb2 ; end of basic block
//! ``` //! ```
//! //!
//! Here Ebb1 and Ebb2 would each have a single predecessor denoted as (Ebb0, `brz vx, Ebb1`) //! Here `Ebb1` and `Ebb2` would each have a single predecessor denoted as `(Ebb0, brz)`
//! and (Ebb0, `jmp Ebb2`) respectively. //! and `(Ebb0, jmp Ebb2)` respectively.
use ir::{Function, Inst, Ebb}; use ir::{Function, Inst, Ebb};
use ir::instructions::BranchInfo; use ir::instructions::BranchInfo;
@@ -91,7 +92,7 @@ impl ControlFlowGraph {
&self.data[ebb].successors &self.data[ebb].successors
} }
/// Return [reachable] ebbs in postorder. /// Return [reachable] ebbs in post-order.
pub fn postorder_ebbs(&self) -> Vec<Ebb> { pub fn postorder_ebbs(&self) -> Vec<Ebb> {
let entry_block = match self.entry_block { let entry_block = match self.entry_block {
None => { None => {
@@ -111,7 +112,7 @@ impl ControlFlowGraph {
// This is a white node. Mark it as gray. // This is a white node. Mark it as gray.
grey.insert(node); grey.insert(node);
stack.push(node); stack.push(node);
// Get any children weve never seen before. // Get any children we've never seen before.
for child in self.get_successors(node) { for child in self.get_successors(node) {
if !grey.contains(child) { if !grey.contains(child) {
stack.push(child.clone()); stack.push(child.clone());
@@ -125,7 +126,7 @@ impl ControlFlowGraph {
postorder postorder
} }
/// An iterator across all of the ebbs stored in the cfg. /// An iterator across all of the ebbs stored in the CFG.
pub fn ebbs(&self) -> Keys<Ebb> { pub fn ebbs(&self) -> Keys<Ebb> {
self.data.keys() self.data.keys()
} }

View File

@@ -67,7 +67,7 @@ mod tests {
#[test] #[test]
fn basic() { fn basic() {
// c.f. meta/constant_hash.py tests. // c.f. `meta/constant_hash.py` tests.
assert_eq!(simple_hash("Hello"), 0x2fa70c01); assert_eq!(simple_hash("Hello"), 0x2fa70c01);
assert_eq!(simple_hash("world"), 0x5b0c31d5); assert_eq!(simple_hash("world"), 0x5b0c31d5);
} }

View File

@@ -52,7 +52,7 @@ impl DominatorTree {
self.nodes[ebb].idom.into() self.nodes[ebb].idom.into()
} }
/// Compare two EBBs relative to a reverse pst-order traversal of the control-flow graph. /// Compare two EBBs relative to a reverse post-order traversal of the control-flow graph.
/// ///
/// Return `Ordering::Less` if `a` comes before `b` in the RPO. /// Return `Ordering::Less` if `a` comes before `b` in the RPO.
pub fn rpo_cmp(&self, a: Ebb, b: Ebb) -> Ordering { pub fn rpo_cmp(&self, a: Ebb, b: Ebb) -> Ordering {
@@ -124,7 +124,7 @@ impl DominatorTree {
pub fn new(func: &Function, cfg: &ControlFlowGraph) -> DominatorTree { pub fn new(func: &Function, cfg: &ControlFlowGraph) -> DominatorTree {
let mut domtree = DominatorTree { nodes: EntityMap::with_capacity(func.dfg.num_ebbs()) }; let mut domtree = DominatorTree { nodes: EntityMap::with_capacity(func.dfg.num_ebbs()) };
// We'll be iterating over a reverse postorder of the CFG. // We'll be iterating over a reverse post-order of the CFG.
// This vector only contains reachable EBBs. // This vector only contains reachable EBBs.
let mut postorder = cfg.postorder_ebbs(); let mut postorder = cfg.postorder_ebbs();
@@ -154,8 +154,8 @@ impl DominatorTree {
} }
} }
// Now that we have RPO numbers for everything and initial idom estimates, iterate until // Now that we have RPO numbers for everything and initial immediate dominator estimates,
// convergence. // iterate until convergence.
// //
// If the function is free of irreducible control flow, this will exit after one iteration. // If the function is free of irreducible control flow, this will exit after one iteration.
let mut changed = true; let mut changed = true;
@@ -173,7 +173,8 @@ impl DominatorTree {
domtree domtree
} }
// Compute the idom for `ebb` using the current `idom` states for the reachable nodes. // Compute the immediate dominator for `ebb` using the current `idom` states for the reachable
// nodes.
fn compute_idom(&self, ebb: Ebb, cfg: &ControlFlowGraph, layout: &Layout) -> Inst { fn compute_idom(&self, ebb: Ebb, cfg: &ControlFlowGraph, layout: &Layout) -> Inst {
// Get an iterator with just the reachable predecessors to `ebb`. // Get an iterator with just the reachable predecessors to `ebb`.
// Note that during the first pass, `is_reachable` returns false for blocks that haven't // Note that during the first pass, `is_reachable` returns false for blocks that haven't

View File

@@ -178,7 +178,7 @@ impl<T: EntityRef> ListPool<T> {
/// Returns two mutable slices representing the two requested blocks. /// Returns two mutable slices representing the two requested blocks.
/// ///
/// The two returned slices can be longer than the blocks. Each block is located at the front /// The two returned slices can be longer than the blocks. Each block is located at the front
/// the the respective slice. /// of the respective slice.
fn mut_slices(&mut self, block0: usize, block1: usize) -> (&mut [T], &mut [T]) { fn mut_slices(&mut self, block0: usize, block1: usize) -> (&mut [T], &mut [T]) {
if block0 < block1 { if block0 < block1 {
let (s0, s1) = self.data.split_at_mut(block1); let (s0, s1) = self.data.split_at_mut(block1);

View File

@@ -139,7 +139,7 @@ impl<K, V> EntityMap<K, V>
self.elems.resize(n, V::default()); self.elems.resize(n, V::default());
} }
/// Ensure that `k` is a valid key but adding default entries if necesssary. /// Ensure that `k` is a valid key but adding default entries if necessary.
/// ///
/// Return a mutable reference to the corresponding entry. /// Return a mutable reference to the corresponding entry.
pub fn ensure(&mut self, k: K) -> &mut V { pub fn ensure(&mut self, k: K) -> &mut V {
@@ -202,7 +202,7 @@ impl<K> Iterator for Keys<K>
mod tests { mod tests {
use super::*; use super::*;
// EntityRef impl for testing. // `EntityRef` impl for testing.
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct E(u32); struct E(u32);

View File

@@ -13,7 +13,7 @@ use ir::condcodes::{IntCC, FloatCC};
/// ///
/// The `InstBuilderBase` trait provides the basic functionality required by the methods of the /// The `InstBuilderBase` trait provides the basic functionality required by the methods of the
/// generated `InstBuilder` trait. These methods should not normally be used directly. Use the /// generated `InstBuilder` trait. These methods should not normally be used directly. Use the
/// methods in the `InstBuilder trait instead. /// methods in the `InstBuilder` trait instead.
/// ///
/// Any data type that implements `InstBuilderBase` also gets all the methods of the `InstBuilder` /// Any data type that implements `InstBuilderBase` also gets all the methods of the `InstBuilder`
/// trait. /// trait.
@@ -104,7 +104,7 @@ impl<'c, 'fc, 'fd> InstBuilderBase<'fd> for InsertBuilder<'c, 'fc, 'fd> {
/// ///
/// If the old instruction still has secondary result values attached, it is assumed that the new /// If the old instruction still has secondary result values attached, it is assumed that the new
/// instruction produces the same number and types of results. The old secondary values are /// instruction produces the same number and types of results. The old secondary values are
/// preserved. If the replacemant instruction format does not support multiple results, the builder /// preserved. If the replacement instruction format does not support multiple results, the builder
/// panics. It is a bug to leave result values dangling. /// panics. It is a bug to leave result values dangling.
/// ///
/// If the old instruction was capable of producing secondary results, but the values have been /// If the old instruction was capable of producing secondary results, but the values have been

View File

@@ -12,7 +12,7 @@ use packed_option::PackedOption;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
use std::u16; use std::u16;
/// A data flow graph defines all instuctions and extended basic blocks in a function as well as /// A data flow graph defines all instructions and extended basic blocks in a function as well as
/// the data flow dependencies between them. The DFG also tracks values which can be either /// the data flow dependencies between them. The DFG also tracks values which can be either
/// instruction results or EBB arguments. /// instruction results or EBB arguments.
/// ///
@@ -213,7 +213,7 @@ impl DataFlowGraph {
original: original, original: original,
}; };
} else { } else {
panic!("Cannot change dirrect value {} into an alias", dest); panic!("Cannot change direct value {} into an alias", dest);
} }
} }
} }
@@ -328,7 +328,7 @@ impl DataFlowGraph {
// Additional values form a linked list starting from the second result value. Generate // Additional values form a linked list starting from the second result value. Generate
// the list backwards so we don't have to modify value table entries in place. (This // the list backwards so we don't have to modify value table entries in place. (This
// causes additional result values to be numbered backwards which is not the aestetic // causes additional result values to be numbered backwards which is not the aesthetic
// choice, but since it is only visible in extremely rare instructions with 3+ results, // choice, but since it is only visible in extremely rare instructions with 3+ results,
// we don't care). // we don't care).
let mut head = None; let mut head = None;
@@ -498,7 +498,7 @@ impl DataFlowGraph {
return num as usize + 1; return num as usize + 1;
} }
} }
panic!("inconsistent value table entry for EBB arg"); panic!("inconsistent value table entry for EBB argument");
} }
} }
} }
@@ -514,7 +514,7 @@ impl DataFlowGraph {
next: None.into(), next: None.into(),
}); });
match self.ebbs[ebb].last_arg.expand() { match self.ebbs[ebb].last_arg.expand() {
// If last_arg is `None`, we're adding the first EBB argument. // If last_argument is `None`, we're adding the first EBB argument.
None => { None => {
self.ebbs[ebb].first_arg = val.into(); self.ebbs[ebb].first_arg = val.into();
} }
@@ -525,11 +525,11 @@ impl DataFlowGraph {
if let ValueData::Arg { ref mut next, .. } = self.extended_values[idx] { if let ValueData::Arg { ref mut next, .. } = self.extended_values[idx] {
*next = val.into(); *next = val.into();
} else { } else {
panic!("inconsistent value table entry for EBB arg"); panic!("inconsistent value table entry for EBB argument");
} }
} }
ExpandedValue::Direct(_) => { ExpandedValue::Direct(_) => {
panic!("inconsistent value table entry for EBB arg") panic!("inconsistent value table entry for EBB argument")
} }
} }
} }
@@ -556,7 +556,7 @@ impl DataFlowGraph {
struct EbbData { struct EbbData {
// First argument to this EBB, or `None` if the block has no arguments. // First argument to this EBB, or `None` if the block has no arguments.
// //
// The arguments are all ValueData::Argument entries that form a linked list from `first_arg` // The arguments are all `ValueData::Argument` entries that form a linked list from `first_arg`
// to `last_arg`. // to `last_arg`.
first_arg: PackedOption<Value>, first_arg: PackedOption<Value>,
@@ -680,14 +680,14 @@ mod tests {
_ => panic!(), _ => panic!(),
}; };
// Detach the 'c' value from iadd. // Detach the 'c' value from `iadd`.
{ {
let mut vals = dfg.detach_secondary_results(iadd); let mut vals = dfg.detach_secondary_results(iadd);
assert_eq!(vals.next(), Some(c)); assert_eq!(vals.next(), Some(c));
assert_eq!(vals.next(), None); assert_eq!(vals.next(), None);
} }
// Replace iadd_cout with a normal iadd and an icmp. // Replace `iadd_cout` with a normal `iadd` and an `icmp`.
dfg.replace(iadd).iadd(v1, arg0); dfg.replace(iadd).iadd(v1, arg0);
let c2 = dfg.ins(pos).icmp(IntCC::UnsignedLessThan, s, v1); let c2 = dfg.ins(pos).icmp(IntCC::UnsignedLessThan, s, v1);
dfg.change_to_alias(c, c2); dfg.change_to_alias(c, c2);

View File

@@ -65,7 +65,7 @@ pub struct Ebb(u32);
entity_impl!(Ebb, "ebb"); entity_impl!(Ebb, "ebb");
impl Ebb { impl Ebb {
/// Create a new EBB reference from its number. This corresponds to the ebbNN representation. /// Create a new EBB reference from its number. This corresponds to the `ebbNN` representation.
/// ///
/// This method is for use by the parser. /// This method is for use by the parser.
pub fn with_number(n: u32) -> Option<Ebb> { pub fn with_number(n: u32) -> Option<Ebb> {
@@ -90,7 +90,7 @@ pub enum ExpandedValue {
impl Value { impl Value {
/// Create a `Direct` value from its number representation. /// Create a `Direct` value from its number representation.
/// This is the number in the vNN notation. /// This is the number in the `vNN` notation.
/// ///
/// This method is for use by the parser. /// This method is for use by the parser.
pub fn direct_with_number(n: u32) -> Option<Value> { pub fn direct_with_number(n: u32) -> Option<Value> {
@@ -104,7 +104,7 @@ impl Value {
} }
/// Create a `Table` value from its number representation. /// Create a `Table` value from its number representation.
/// This is the number in the vxNN notation. /// This is the number in the `vxNN` notation.
/// ///
/// This method is for use by the parser. /// This method is for use by the parser.
pub fn table_with_number(n: u32) -> Option<Value> { pub fn table_with_number(n: u32) -> Option<Value> {

View File

@@ -60,7 +60,7 @@ impl Function {
} }
} }
/// Create a new empty, anomymous function. /// Create a new empty, anonymous function.
pub fn new() -> Function { pub fn new() -> Function {
Self::with_name_signature(FunctionName::default(), Signature::new()) Self::with_name_signature(FunctionName::default(), Signature::new())
} }

View File

@@ -62,7 +62,7 @@ impl Display for Imm64 {
impl FromStr for Imm64 { impl FromStr for Imm64 {
type Err = &'static str; type Err = &'static str;
// Parse a decimal or hexadecimal Imm64, formatted as above. // Parse a decimal or hexadecimal `Imm64`, formatted as above.
fn from_str(s: &str) -> Result<Imm64, &'static str> { fn from_str(s: &str) -> Result<Imm64, &'static str> {
let mut value: u64 = 0; let mut value: u64 = 0;
let mut digits = 0; let mut digits = 0;
@@ -149,7 +149,7 @@ pub struct Ieee64(f64);
// Format a floating point number in a way that is reasonably human-readable, and that can be // Format a floating point number in a way that is reasonably human-readable, and that can be
// converted back to binary without any rounding issues. The hexadecimal formatting of normal and // converted back to binary without any rounding issues. The hexadecimal formatting of normal and
// subnormal numbers is compatible with C99 and the printf "%a" format specifier. The NaN and Inf // subnormal numbers is compatible with C99 and the `printf "%a"` format specifier. The NaN and Inf
// formats are not supported by C99. // formats are not supported by C99.
// //
// The encoding parameters are: // The encoding parameters are:
@@ -380,7 +380,7 @@ impl Ieee32 {
Ieee32(x) Ieee32(x)
} }
/// Construct Ieee32 immediate from raw bits. /// Construct `Ieee32` immediate from raw bits.
pub fn from_bits(x: u32) -> Ieee32 { pub fn from_bits(x: u32) -> Ieee32 {
Ieee32(unsafe { mem::transmute(x) }) Ieee32(unsafe { mem::transmute(x) })
} }
@@ -410,7 +410,7 @@ impl Ieee64 {
Ieee64(x) Ieee64(x)
} }
/// Construct Ieee64 immediate from raw bits. /// Construct `Ieee64` immediate from raw bits.
pub fn from_bits(x: u64) -> Ieee64 { pub fn from_bits(x: u64) -> Ieee64 {
Ieee64(unsafe { mem::transmute(x) }) Ieee64(unsafe { mem::transmute(x) })
} }
@@ -496,7 +496,7 @@ mod tests {
"Negative number too small for Imm64"); "Negative number too small for Imm64");
parse_ok::<Imm64>("18446744073709551615", "-1"); parse_ok::<Imm64>("18446744073709551615", "-1");
parse_ok::<Imm64>("-9223372036854775808", "0x8000_0000_0000_0000"); parse_ok::<Imm64>("-9223372036854775808", "0x8000_0000_0000_0000");
// Overflow both the checked_add and checked_mul. // Overflow both the `checked_add` and `checked_mul`.
parse_err::<Imm64>("18446744073709551616", "Too large decimal Imm64"); parse_err::<Imm64>("18446744073709551616", "Too large decimal Imm64");
parse_err::<Imm64>("184467440737095516100", "Too large decimal Imm64"); parse_err::<Imm64>("184467440737095516100", "Too large decimal Imm64");
parse_err::<Imm64>("-9223372036854775809", parse_err::<Imm64>("-9223372036854775809",

View File

@@ -57,9 +57,9 @@ impl Opcode {
} }
} }
// This trait really belongs in lib/reader where it is used by the .cton file parser, but since it // This trait really belongs in lib/reader where it is used by the `.cton` file parser, but since
// critically depends on the `opcode_name()` function which is needed here anyway, it lives in this // it critically depends on the `opcode_name()` function which is needed here anyway, it lives in
// module. This also saves us from running the build script twice to generate code for the two // this module. This also saves us from running the build script twice to generate code for the two
// separate crates. // separate crates.
impl FromStr for Opcode { impl FromStr for Opcode {
type Err = &'static str; type Err = &'static str;
@@ -141,7 +141,7 @@ pub enum InstructionData {
arg: Value, arg: Value,
imm: Imm64, imm: Imm64,
}, },
// Same as BinaryImm, but the immediate is the lhs operand. // Same as `BinaryImm`, but the immediate is the left-hand-side operand.
BinaryImmRev { BinaryImmRev {
opcode: Opcode, opcode: Opcode,
ty: Type, ty: Type,
@@ -246,7 +246,7 @@ impl VariableArgs {
} }
} }
// Coerce VariableArgs into a &[Value] slice. // Coerce `VariableArgs` into a `&[Value]` slice.
impl Deref for VariableArgs { impl Deref for VariableArgs {
type Target = [Value]; type Target = [Value];
@@ -311,7 +311,7 @@ impl Display for TernaryOverflowData {
} }
/// Payload data for jump instructions. These need to carry lists of EBB arguments that won't fit /// Payload data for jump instructions. These need to carry lists of EBB arguments that won't fit
/// in the allowed InstructionData size. /// in the allowed `InstructionData` size.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct JumpData { pub struct JumpData {
/// Jump destination EBB. /// Jump destination EBB.
@@ -331,7 +331,7 @@ impl Display for JumpData {
} }
/// Payload data for branch instructions. These need to carry lists of EBB arguments that won't fit /// Payload data for branch instructions. These need to carry lists of EBB arguments that won't fit
/// in the allowed InstructionData size. /// in the allowed `InstructionData` size.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BranchData { pub struct BranchData {
/// Value argument controlling the branch. /// Value argument controlling the branch.
@@ -702,11 +702,10 @@ mod tests {
#[test] #[test]
fn instruction_data() { fn instruction_data() {
use std::mem; use std::mem;
// The size of the InstructionData enum is important for performance. It should not exceed // The size of the `InstructionData` enum is important for performance. It should not
// 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that require // exceed 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that
// more space than that. // require more space than that. It would be fine with a data structure smaller than 16
// It would be fine with a data structure smaller than 16 bytes, but what are the odds of // bytes, but what are the odds of that?
// that?
assert_eq!(mem::size_of::<InstructionData>(), 16); assert_eq!(mem::size_of::<InstructionData>(), 16);
} }

View File

@@ -33,7 +33,7 @@ impl JumpTableData {
/// Set a table entry. /// Set a table entry.
/// ///
/// The table will grow as needed to fit 'idx'. /// The table will grow as needed to fit `idx`.
pub fn set_entry(&mut self, idx: usize, dest: Ebb) { pub fn set_entry(&mut self, idx: usize, dest: Ebb) {
// Resize table to fit `idx`. // Resize table to fit `idx`.
if idx >= self.table.len() { if idx >= self.table.len() {

View File

@@ -573,10 +573,10 @@ impl<'f> DoubleEndedIterator for Insts<'f> {
/// ///
/// A `Cursor` represents a position in a function layout where instructions can be inserted and /// A `Cursor` represents a position in a function layout where instructions can be inserted and
/// removed. It can be used to iterate through the instructions of a function while editing them at /// removed. It can be used to iterate through the instructions of a function while editing them at
/// the same time. A normal instruction iterator can't do this since it holds an immutable refernce /// the same time. A normal instruction iterator can't do this since it holds an immutable
/// to the Layout. /// reference to the Layout.
/// ///
/// When new instructions are added, the cursor can either apend them to an EBB or insert them /// When new instructions are added, the cursor can either append them to an EBB or insert them
/// before the current instruction. /// before the current instruction.
pub struct Cursor<'f> { pub struct Cursor<'f> {
layout: &'f mut Layout, layout: &'f mut Layout,
@@ -592,7 +592,7 @@ pub enum CursorPosition {
/// New instructions will be inserted *before* the current instruction. /// New instructions will be inserted *before* the current instruction.
At(Inst), At(Inst),
/// Cursor is before the beginning of an EBB. No instructions can be inserted. Calling /// Cursor is before the beginning of an EBB. No instructions can be inserted. Calling
/// `next_inst()` wil move to the first instruction in the EBB. /// `next_inst()` will move to the first instruction in the EBB.
Before(Ebb), Before(Ebb),
/// Cursor is pointing after the end of an EBB. /// Cursor is pointing after the end of an EBB.
/// New instructions will be appended to the EBB. /// New instructions will be appended to the EBB.
@@ -851,7 +851,7 @@ impl<'f> Cursor<'f> {
/// - If pointing at the bottom of an EBB, the new instruction is appended to the EBB. /// - If pointing at the bottom of an EBB, the new instruction is appended to the EBB.
/// - Otherwise panic. /// - Otherwise panic.
/// ///
/// In either case, the cursor is not moved, such that repeates calls to `insert_inst()` causes /// In either case, the cursor is not moved, such that repeated calls to `insert_inst()` causes
/// instructions to appear in insertion order in the EBB. /// instructions to appear in insertion order in the EBB.
pub fn insert_inst(&mut self, inst: Inst) { pub fn insert_inst(&mut self, inst: Inst) {
use self::CursorPosition::*; use self::CursorPosition::*;
@@ -1263,7 +1263,7 @@ mod tests {
assert_eq!(cur.prev_ebb(), None); assert_eq!(cur.prev_ebb(), None);
} }
// Check ProgramOrder. // Check `ProgramOrder`.
assert_eq!(layout.cmp(e2, e2), Ordering::Equal); assert_eq!(layout.cmp(e2, e2), Ordering::Equal);
assert_eq!(layout.cmp(e2, i2), Ordering::Less); assert_eq!(layout.cmp(e2, i2), Ordering::Less);
assert_eq!(layout.cmp(i3, i2), Ordering::Greater); assert_eq!(layout.cmp(i3, i2), Ordering::Greater);

View File

@@ -43,7 +43,7 @@ impl Type {
Type(self.0 & 0x0f) Type(self.0 & 0x0f)
} }
/// Get log2 of the number of bits in a lane. /// Get log_2 of the number of bits in a lane.
pub fn log2_lane_bits(self) -> u8 { pub fn log2_lane_bits(self) -> u8 {
match self.lane_type() { match self.lane_type() {
B1 => 0, B1 => 0,
@@ -157,7 +157,7 @@ impl Type {
} }
} }
/// Get log2 of the number of lanes in this SIMD vector type. /// Get log_2 of the number of lanes in this SIMD vector type.
/// ///
/// All SIMD types have a lane count that is a power of two and no larger than 256, so this /// All SIMD types have a lane count that is a power of two and no larger than 256, so this
/// will be a number in the range 0-8. /// will be a number in the range 0-8.

View File

@@ -69,8 +69,8 @@ pub fn legalize_function(func: &mut Function, isa: &TargetIsa) {
} }
} }
// Include legalization patterns that were generated by gen_legalizer.py from the XForms in // Include legalization patterns that were generated by `gen_legalizer.py` from the `XForms` in
// meta/cretonne/legalize.py. // `meta/cretonne/legalize.py`.
// //
// Concretely, this defines private functions `narrow()`, and `expand()`. // Concretely, this defines private functions `narrow()`, and `expand()`.
include!(concat!(env!("OUT_DIR"), "/legalizer.rs")); include!(concat!(env!("OUT_DIR"), "/legalizer.rs"));

View File

@@ -20,7 +20,7 @@ pub fn is_signed_int<T: Into<i64>>(x: T, wd: u8, sc: u8) -> bool {
#[allow(dead_code)] #[allow(dead_code)]
pub fn is_unsigned_int<T: Into<i64>>(x: T, wd: u8, sc: u8) -> bool { pub fn is_unsigned_int<T: Into<i64>>(x: T, wd: u8, sc: u8) -> bool {
let u = x.into() as u64; let u = x.into() as u64;
// Bitmask of the permitted bits. // Bit-mask of the permitted bits.
let m = (1 << wd) - (1 << sc); let m = (1 << wd) - (1 << sc);
u == (u & m) u == (u & m)
} }
@@ -40,7 +40,7 @@ mod tests {
assert!(is_signed_int(x2, 2, 0)); assert!(is_signed_int(x2, 2, 0));
assert!(!is_signed_int(x2, 2, 1)); assert!(!is_signed_int(x2, 2, 1));
// u32 doesn't sign-extend when converted to i64. // `u32` doesn't sign-extend when converted to `i64`.
assert!(!is_signed_int(x3, 8, 0)); assert!(!is_signed_int(x3, 8, 0));
assert!(is_unsigned_int(x1, 1, 0)); assert!(is_unsigned_int(x1, 1, 0));

View File

@@ -38,7 +38,7 @@ impl AllocatableSet {
AllocatableSet { avail: [!0; 3] } 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 { pub fn is_avail(&self, rc: RegClass, reg: RegUnit) -> bool {
let (idx, bits) = bitmask(rc, reg); let (idx, bits) = bitmask(rc, reg);
(self.avail[idx] & bits) == bits (self.avail[idx] & bits) == bits
@@ -71,7 +71,7 @@ impl AllocatableSet {
for idx in 0..self.avail.len() { 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 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. // 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 { for i in 0..rc.width {
rsi.regs[idx] &= self.avail[idx] >> i; rsi.regs[idx] &= self.avail[idx] >> i;
} }
@@ -102,7 +102,7 @@ impl Iterator for RegSetIter {
return Some(unit); 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; unit_offset += 8 * size_of_val(word) as RegUnit;
} }
@@ -134,7 +134,7 @@ mod tests {
fn put_and_take() { fn put_and_take() {
let mut regs = AllocatableSet::new(); 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(GPR).count(), 8);
assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [28, 30, 33, 35]); assert_eq!(regs.iter(DPR).collect::<Vec<_>>(), [28, 30, 33, 35]);

View File

@@ -8,8 +8,8 @@
//! //!
//! The primary consumer of the liveness analysis is the SSA coloring pass which goes through each //! 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 //! 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 //! currently live values as it is iterating down the instructions in the EBB. It asks the
//! questions: //! following questions:
//! //!
//! - What is the set of live values at the entry to the EBB? //! - 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 //! - 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 //! 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 //! `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 //! 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. //! `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 //! A number of different liveness analysis algorithms exist, so it is worthwhile to look at a few
//! alternatives. //! alternatives.
//! //!
//! ## Dataflow equations //! ## Data-flow equations
//! //!
//! The classic *live variables analysis* that you will find in all compiler books from the //! 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 //! 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. //! variables for every basic block in the program.
//! //!
//! This algorithm has some disadvantages that makes us look elsewhere: //! 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. //! - 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, //! - 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 //! and those that do span basic blocks rarely span a large number of basic blocks. This makes
//! the bitvectors quite sparse. //! the bit-vectors quite sparse.
//! - Traditionally, the dataflow equations were solved for real program *variables* which does not //! - Traditionally, the data-flow equations were solved for real program *variables* which does
//! include temporaries used in evaluating expressions. We have an SSA form program which blurs //! not include temporaries used in evaluating expressions. We have an SSA form program which
//! the distinction between temporaries and variables. This makes the quadratic memory problem //! blurs the distinction between temporaries and variables. This makes the quadratic memory
//! worse because there are many more SSA values than there was variables in the original //! 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. //! 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 //! - 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 //! 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 //! 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. //! 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 //! multiple definitions of the same variable. We don't need this generality since we already have
//! a program in SSA form. //! a program in SSA form.
//! //!
@@ -83,7 +83,7 @@
//! The iterative SSA form reconstruction can be skipped if the depth-first search only encountered //! The iterative SSA form reconstruction can be skipped if the depth-first search only encountered
//! one SSA value. //! 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 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 //! the CFG. The memory needed to store these live ranges is independent of the number of basic
@@ -108,9 +108,9 @@
//! > Boissinot, B., Hack, S., Grund, D., de Dinechin, B. D., & Rastello, F. (2008). *Fast Liveness //! > 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 //! 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: //! 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. //! - 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 //! Traversing such a long use chain on every liveness lookup has the potential for some nasty
//! quadratic behavior in unfortunate cases. //! quadratic behavior in unfortunate cases.
//! - It says "fast" in the title, but the paper only claims to be 16% faster than a dataflow based //! - It says "fast" in the title, but the paper only claims to be 16% faster than a data-flow
//! approach, which isn't that impressive. //! based approach, which isn't that impressive.
//! //!
//! Nevertheless, the property of only depending in the CFG structure is very useful. If Cretonne //! 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. //! 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 //! - Related values should be stored on the same cache line. The current sparse set implementation
//! does a decent job of that. //! does a decent job of that.
//! - For global values, the list of live-in intervals is very likely to fit on a single cache //! - 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. //! There is some room for improvement.
@@ -274,7 +274,7 @@ impl Liveness {
// The work list 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. // 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`. // 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 // Extend the live range as we go. The live range itself also serves as a visited set since

View File

@@ -45,13 +45,13 @@
//! handle *early clobbers* which are output registers that are not allowed to alias any input //! handle *early clobbers* which are output registers that are not allowed to alias any input
//! registers. //! 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 `i1-i3` interfere because the intervals overlap.
//! - i1-i2 and i2-i3 don't interfere. //! - `i1-i2` and `i2-i3` don't interfere.
//! - i1-i3 and i2-i2 do interfere because the dead def would clobber the register. //! - `i1-i3` and `i2-i2` do interfere because the dead def would clobber the register.
//! - i1-i2 and i2-i2 don't interfere. //! - `i1-i2` and `i2-i2` don't interfere.
//! - i2-i3 and i2-i2 do interfere. //! - `i2-i3` and `i2-i2` do interfere.
//! //!
//! Because of this behavior around interval end points, live range interference is not completely //! Because of this behavior around interval end points, live range interference is not completely
//! equivalent to mathematical intersection of open or half-open intervals. //! 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 {}; const PO: &'static ProgOrder = &ProgOrder {};
#[test] #[test]
@@ -441,7 +441,7 @@ mod tests {
assert!(lr.is_local()); assert!(lr.is_local());
assert_eq!(lr.def(), e2.into()); assert_eq!(lr.def(), e2.into());
assert_eq!(lr.def_local_end(), 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); assert_eq!(lr.livein_local_end(e2, PO), None);
PO.validate(&lr); PO.validate(&lr);
} }
@@ -478,8 +478,8 @@ mod tests {
let i13 = Inst::new(13); let i13 = Inst::new(13);
let mut lr = LiveRange::new(v0, e10); let mut lr = LiveRange::new(v0, e10);
// Extending a dead EBB arg in its own block should not indicate that a live-in interval // Extending a dead EBB argument in its own block should not indicate that a live-in
// was created. // interval was created.
assert_eq!(lr.extend_in_ebb(e10, i12, PO), false); assert_eq!(lr.extend_in_ebb(e10, i12, PO), false);
PO.validate(&lr); PO.validate(&lr);
assert!(!lr.is_dead()); assert!(!lr.is_dead());

View File

@@ -27,7 +27,7 @@
//! about creating new mappings to the default value. It doesn't distinguish clearly between an //! about creating new mappings to the default value. It doesn't distinguish clearly between an
//! unmapped key and one that maps to the default value. `SparseMap` does not require `Default` //! unmapped key and one that maps to the default value. `SparseMap` does not require `Default`
//! values, and it tracks accurately if a key has been mapped or not. //! values, and it tracks accurately if a key has been mapped or not.
//! - Iterating over the contants of an `EntityMap` is linear in the size of the *key space*, while //! - Iterating over the contents of an `EntityMap` is linear in the size of the *key space*, while
//! iterating over a `SparseMap` is linear in the number of elements in the mapping. This is an //! iterating over a `SparseMap` is linear in the number of elements in the mapping. This is an
//! advantage precisely when the mapping is sparse. //! advantage precisely when the mapping is sparse.
//! - `SparseMap::clear()` is constant time and super-fast. `EntityMap::clear()` is linear in the //! - `SparseMap::clear()` is constant time and super-fast. `EntityMap::clear()` is linear in the
@@ -45,7 +45,7 @@ use std::u32;
/// All values stored in a `SparseMap` must keep track of their own key in the map and implement /// All values stored in a `SparseMap` must keep track of their own key in the map and implement
/// this trait to provide access to the key. /// this trait to provide access to the key.
pub trait SparseMapValue<K> { pub trait SparseMapValue<K> {
/// Get the key of this sparse map value. This key is not alowed to change while the value /// Get the key of this sparse map value. This key is not allowed to change while the value
/// is a member of the map. /// is a member of the map.
fn key(&self) -> K; fn key(&self) -> K;
} }

View File

@@ -3,18 +3,18 @@
//! //!
//! EBB integrity //! EBB integrity
//! //!
//! - All instructions reached from the ebb_insts iterator must belong to //! - All instructions reached from the `ebb_insts` iterator must belong to
//! the EBB as reported by inst_ebb(). //! the EBB as reported by `inst_ebb()`.
//! - Every EBB must end in a terminator instruction, and no other instruction //! - Every EBB must end in a terminator instruction, and no other instruction
//! can be a terminator. //! can be a terminator.
//! - Every value in the ebb_args iterator belongs to the EBB as reported by value_ebb. //! - Every value in the `ebb_args` iterator belongs to the EBB as reported by `value_ebb`.
//! //!
//! Instruction integrity //! Instruction integrity
//! //!
//! - The instruction format must match the opcode. //! - The instruction format must match the opcode.
//! TODO: //! TODO:
//! - All result values must be created for multi-valued instructions. //! - All result values must be created for multi-valued instructions.
//! - Instructions with no results must have a VOID first_type(). //! - Instructions with no results must have a VOID `first_type()`.
//! - All referenced entities must exist. (Values, EBBs, stack slots, ...) //! - All referenced entities must exist. (Values, EBBs, stack slots, ...)
//! //!
//! SSA form //! SSA form
@@ -33,7 +33,7 @@
//! - Compare input and output values against the opcode's type constraints. //! - Compare input and output values against the opcode's type constraints.
//! For polymorphic opcodes, determine the controlling type variable first. //! For polymorphic opcodes, determine the controlling type variable first.
//! - Branches and jumps must pass arguments to destination EBBs that match the //! - Branches and jumps must pass arguments to destination EBBs that match the
//! expected types excatly. The number of arguments must match. //! expected types exactly. The number of arguments must match.
//! - All EBBs in a jump_table must take no arguments. //! - All EBBs in a jump_table must take no arguments.
//! - Function calls are type checked against their signature. //! - Function calls are type checked against their signature.
//! - The entry block must take arguments that match the signature of the current //! - The entry block must take arguments that match the signature of the current
@@ -44,10 +44,10 @@
//! Ad hoc checking //! Ad hoc checking
//! //!
//! - Stack slot loads and stores must be in-bounds. //! - Stack slot loads and stores must be in-bounds.
//! - Immediate constraints for certain opcodes, like udiv_imm v3, 0. //! - Immediate constraints for certain opcodes, like `udiv_imm v3, 0`.
//! - Extend / truncate instructions have more type constraints: Source type can't be //! - Extend / truncate instructions have more type constraints: Source type can't be
//! larger / smaller than result type. //! larger / smaller than result type.
//! - Insertlane and extractlane instructions have immediate lane numbers that must be in //! - `Insertlane` and `extractlane` instructions have immediate lane numbers that must be in
//! range for their polymorphic type. //! range for their polymorphic type.
//! - Swizzle and shuffle instructions take a variable number of lane arguments. The number //! - Swizzle and shuffle instructions take a variable number of lane arguments. The number
//! of arguments must match the destination type, and the lane indexes must be in range. //! of arguments must match the destination type, and the lane indexes must be in range.
@@ -76,7 +76,7 @@ impl Display for Error {
/// Verifier result. /// Verifier result.
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
// Create an `Err` variant of `Result<X>` from a location and `format!` args. // Create an `Err` variant of `Result<X>` from a location and `format!` arguments.
macro_rules! err { macro_rules! err {
( $loc:expr, $msg:expr ) => { ( $loc:expr, $msg:expr ) => {
Err(Error { Err(Error {

View File

@@ -43,7 +43,8 @@ fn write_preamble(w: &mut Write, func: &Function) -> result::Result<bool, Error>
try!(writeln!(w, " {} = {}", ss, func.stack_slots[ss])); try!(writeln!(w, " {} = {}", ss, func.stack_slots[ss]));
} }
// Write out all signatures before functions since function decls can refer to signatures. // Write out all signatures before functions since function declarations can refer to
// signatures.
for sig in func.dfg.signatures.keys() { for sig in func.dfg.signatures.keys() {
any = true; any = true;
try!(writeln!(w, " {} = signature{}", sig, func.dfg.signatures[sig])); try!(writeln!(w, " {} = signature{}", sig, func.dfg.signatures[sig]));
@@ -93,7 +94,7 @@ pub fn write_ebb_header(w: &mut Write, func: &Function, ebb: Ebb) -> Result {
try!(write_arg(w, func, arg)); try!(write_arg(w, func, arg));
} }
} }
// Remaining args. // Remaining arguments.
for arg in args { for arg in args {
try!(write!(w, ", ")); try!(write!(w, ", "));
try!(write_arg(w, func, arg)); try!(write_arg(w, func, arg));