From 84357c340298bf487ee1e4d2d8380f1ab61abcdf Mon Sep 17 00:00:00 2001 From: Morgan Phillips Date: Tue, 12 Jul 2016 13:59:27 -0700 Subject: [PATCH 1/5] Ignore cargo-fmt and vim related files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 0d20b6487c..002a368e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ *.pyc +*.bk +*.swp +*.swo +tags From c6b1388fdcd8eaaa6a49273049bcff0b8aebfe2d Mon Sep 17 00:00:00 2001 From: Morgan Phillips Date: Tue, 12 Jul 2016 14:37:37 -0700 Subject: [PATCH 2/5] Add a Control Flow Graph representation. The CFG must be instantiated against an existing function but may be modified after creation --- src/libcretonne/cfg.rs | 270 ++++++++++++++++++++++++++++++++++++ src/libcretonne/entities.rs | 4 +- src/libcretonne/lib.rs | 1 + 3 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 src/libcretonne/cfg.rs diff --git a/src/libcretonne/cfg.rs b/src/libcretonne/cfg.rs new file mode 100644 index 0000000000..a8640d3459 --- /dev/null +++ b/src/libcretonne/cfg.rs @@ -0,0 +1,270 @@ +//! A control flow graph represented as mappings of extended basic blocks to their predecessors. +//! Predecessors are denoted by tuples of EBB and branch/jump instructions. Each predecessor +//! tuple corresponds to the end of a basic block. +//! +//!```c +//! Ebb0: +//! ... ; beginning of basic block +//! +//! ... +//! +//! brz vx, Ebb1 ; end of basic block +//! +//! ... ; beginning 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`) +//! and (Ebb0, `jmp Ebb2`) respectively. + +use repr::Function; +use entities::{Inst, Ebb}; +use instructions::InstructionData; +use std::collections::{BTreeSet, BTreeMap, btree_map}; + +/// A basic block denoted by its enclosing Ebb and last instruction. +pub type Predecessor = (Ebb, Inst); + +/// Storing predecessors in a BTreeSet ensures that their ordering is +/// stable with no duplicates. +pub type PredecessorSet = BTreeSet; + +/// The Control Flow Graph maintains a mapping of ebbs to their predecessors +/// where predecessors are basic blocks. +#[derive(Debug)] +pub struct ControlFlowGraph { + data: BTreeMap, +} + +impl ControlFlowGraph { + + /// During initialization mappings will be generated for any existing + /// blocks within the CFG's associated function. Basic sanity checks will + /// also be performed to ensure that the blocks are well formed. + pub fn new(func: &Function) -> Result { + let mut cfg = ControlFlowGraph{data: BTreeMap::new()}; + + // Even ebbs without predecessors should show up in the CFG, albeit + // with no entires. + for ebb in func.ebbs_numerically() { + try!(cfg.init_ebb(ebb)); + } + + for ebb in func.ebbs_numerically() { + // Flips to true when a terminating instruction is seen. So that if additional + // instructions occur an error may be returned. + let mut terminated = false; + for inst in func.ebb_insts(ebb) { + if terminated { + return Err(format!("{} contains unreachable instructions.", ebb)); + } + + match func[inst] { + InstructionData::Branch { ty: _, opcode: _, ref data } => { + try!(cfg.add_predecessor(data.destination, (ebb, inst))); + } + InstructionData::Jump { ty: _, opcode: _, ref data } => { + try!(cfg.add_predecessor(data.destination, (ebb, inst))); + terminated = true; + } + InstructionData::Return { ty: _, opcode: _, data: _ } => { + terminated = true; + } + InstructionData::Nullary { ty: _, opcode: _ } => { + terminated = true; + } + _ => () + } + } + } + Ok(cfg) + } + + /// Initializes a predecessor set for some ebb. If an ebb already has an + /// entry it will be clobbered. + pub fn init_ebb(&mut self, ebb: Ebb) -> Result<&mut PredecessorSet, &'static str> { + self.data.insert(ebb, BTreeSet::new()); + match self.data.get_mut(&ebb) { + Some(predecessors) => Ok(predecessors), + None => Err("Ebb initialization failed.") + } + } + + /// Attempts to add a predecessor for some ebb, attempting to initialize + /// any ebb which has no entry. + pub fn add_predecessor(&mut self, ebb: Ebb, predecessor: Predecessor) -> Result<(), &'static str> { + let success = match self.data.get_mut(&ebb) { + Some(predecessors) => predecessors.insert(predecessor), + None => false + }; + + if success { + Ok(()) + } else { + let mut predecessors = try!(self.init_ebb(ebb)); + if predecessors.insert(predecessor) { + return Ok(()); + } + Err("Predecessor insertion failed.") + } + + } + + /// Returns all of the predecessors for some ebb, if it has an entry. + pub fn get_predecessors(&self, ebb: Ebb) -> Option<&PredecessorSet> { + self.data.get(&ebb) + } + + /// An iterator over all of the ebb to predecessor mappings in the CFG. + pub fn iter<'a>(&'a self) -> btree_map::Iter<'a, Ebb, PredecessorSet> { + self.data.iter() + } +} + +#[cfg(test)] +mod tests { + use instructions::*; + use entities::{Ebb, Inst, NO_VALUE}; + use repr::Function; + use super::*; + use types; + + // Some instructions will be re-used in several tests. + + fn nullary(func: &mut Function) -> Inst { + func.make_inst(InstructionData::Nullary { + opcode: Opcode::Iconst, + ty: types::I32, + }) + } + + fn jump(func: &mut Function, dest: Ebb) -> Inst { + func.make_inst(InstructionData::Jump { + opcode: Opcode::Jump, + ty: types::VOID, + data: Box::new(JumpData { + destination: dest, + arguments: VariableArgs::new(), + }), + }) + } + + fn branch(func: &mut Function, dest: Ebb) -> Inst { + func.make_inst(InstructionData::Branch { + opcode: Opcode::Brz, + ty: types::VOID, + data: Box::new(BranchData { + arg: NO_VALUE, + destination: dest, + arguments: VariableArgs::new(), + }), + }) + } + + #[test] + fn empty() { + let func = Function::new(); + let cfg = ControlFlowGraph::new(&func).unwrap(); + assert_eq!(None, cfg.iter().next()); + } + + #[test] + fn no_predecessors() { + let mut func = Function::new(); + func.make_ebb(); + func.make_ebb(); + func.make_ebb(); + let cfg = ControlFlowGraph::new(&func).unwrap(); + let nodes = cfg.iter().collect::>(); + assert_eq!(nodes.len(), 3); + + let mut fun_ebbs = func.ebbs_numerically(); + for (ebb, predecessors) in nodes { + assert_eq!(ebb.index(), fun_ebbs.next().unwrap().index()); + assert_eq!(predecessors.len(), 0); + } + } + + #[test] + #[should_panic(expected = "instructions")] + fn nullable_before_branch() { + // Ensure that branching after a nullary, within an ebb, triggers an error. + let mut func = Function::new(); + let ebb0 = func.make_ebb(); + let ebb1_malformed = func.make_ebb(); + let ebb2 = func.make_ebb(); + + let nullary_inst = nullary(&mut func); + func.append_inst(ebb1_malformed, nullary_inst); + + // This jump should not be recorded since a nullary takes place + // before it appears. + let jmp_ebb1_ebb0 = jump(&mut func, ebb0); + func.append_inst(ebb1_malformed, jmp_ebb1_ebb0); + + let jmp_ebb0_ebb2 = jump(&mut func, ebb2); + func.append_inst(ebb0, jmp_ebb0_ebb2); + + ControlFlowGraph::new(&func).unwrap(); + } + + #[test] + #[should_panic(expected = "instructions")] + fn jump_before_branch() { + // Ensure that branching after a jump, within an ebb, triggers an error. + let mut func = Function::new(); + let ebb0 = func.make_ebb(); + let ebb1_malformed = func.make_ebb(); + let ebb2 = func.make_ebb(); + + let jmp_ebb0_ebb1 = jump(&mut func, ebb2); + func.append_inst(ebb0, jmp_ebb0_ebb1); + + let jmp_ebb1_ebb2 = jump(&mut func, ebb2); + func.append_inst(ebb1_malformed, jmp_ebb1_ebb2); + + // This branch should not be recorded since a jump takes place + // before it appears. + let br_ebb1_ebb0 = branch(&mut func, ebb0); + func.append_inst(ebb1_malformed, br_ebb1_ebb0); + + ControlFlowGraph::new(&func).unwrap(); + } + + #[test] + fn branches_and_jumps() { + let mut func = Function::new(); + let ebb0 = func.make_ebb(); + let ebb1 = func.make_ebb(); + let ebb2 = func.make_ebb(); + + let br_ebb0_ebb2 = branch(&mut func, ebb2); + func.append_inst(ebb0, br_ebb0_ebb2); + + let jmp_ebb0_ebb1 = jump(&mut func, ebb1); + func.append_inst(ebb0, jmp_ebb0_ebb1); + + let br_ebb1_ebb1 = branch(&mut func, ebb1); + func.append_inst(ebb1, br_ebb1_ebb1); + + let jmp_ebb1_ebb2 = jump(&mut func, ebb2); + func.append_inst(ebb1, jmp_ebb1_ebb2); + + let cfg = ControlFlowGraph::new(&func).unwrap(); + let ebb0_predecessors = cfg.get_predecessors(ebb0).unwrap(); + let ebb1_predecessors = cfg.get_predecessors(ebb1).unwrap(); + let ebb2_predecessors = cfg.get_predecessors(ebb2).unwrap(); + assert_eq!(ebb0_predecessors.len(), 0); + assert_eq!(ebb1_predecessors.len(), 2); + assert_eq!(ebb2_predecessors.len(), 2); + + assert_eq!(ebb1_predecessors.contains(&(ebb0, jmp_ebb0_ebb1)), true); + assert_eq!(ebb1_predecessors.contains(&(ebb1, br_ebb1_ebb1)), true); + assert_eq!(ebb2_predecessors.contains(&(ebb0, br_ebb0_ebb2)), true); + assert_eq!(ebb2_predecessors.contains(&(ebb1, jmp_ebb1_ebb2)), true); + } + +} diff --git a/src/libcretonne/entities.rs b/src/libcretonne/entities.rs index 6294a8e8c7..c87ed6a818 100644 --- a/src/libcretonne/entities.rs +++ b/src/libcretonne/entities.rs @@ -24,7 +24,7 @@ use std::fmt::{self, Display, Formatter}; use std::u32; /// An opaque reference to an extended basic block in a function. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub struct Ebb(u32); impl Ebb { @@ -64,7 +64,7 @@ impl Default for Ebb { } /// An opaque reference to an instruction in a function. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] pub struct Inst(u32); impl Inst { diff --git a/src/libcretonne/lib.rs b/src/libcretonne/lib.rs index 26fc74f7ab..f0bf55af39 100644 --- a/src/libcretonne/lib.rs +++ b/src/libcretonne/lib.rs @@ -14,3 +14,4 @@ pub mod entities; pub mod instructions; pub mod repr; pub mod write; +pub mod cfg; From 180eae3bb5c85dbd1a3a12728692489fe91840dc Mon Sep 17 00:00:00 2001 From: Morgan Phillips Date: Tue, 12 Jul 2016 14:42:49 -0700 Subject: [PATCH 3/5] Cargo-fmt fixes --- src/libcretonne/cfg.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libcretonne/cfg.rs b/src/libcretonne/cfg.rs index a8640d3459..cf69d0f184 100644 --- a/src/libcretonne/cfg.rs +++ b/src/libcretonne/cfg.rs @@ -2,7 +2,7 @@ //! Predecessors are denoted by tuples of EBB and branch/jump instructions. Each predecessor //! tuple corresponds to the end of a basic block. //! -//!```c +//! ```c //! Ebb0: //! ... ; beginning of basic block //! @@ -15,7 +15,7 @@ //! ... //! //! jmp Ebb2 ; end of basic block -//!``` +//! ``` //! //! Here Ebb1 and Ebb2 would each have a single predecessor denoted as (Ebb0, `brz vx, Ebb1`) //! and (Ebb0, `jmp Ebb2`) respectively. @@ -40,12 +40,11 @@ pub struct ControlFlowGraph { } impl ControlFlowGraph { - /// During initialization mappings will be generated for any existing /// blocks within the CFG's associated function. Basic sanity checks will /// also be performed to ensure that the blocks are well formed. pub fn new(func: &Function) -> Result { - let mut cfg = ControlFlowGraph{data: BTreeMap::new()}; + let mut cfg = ControlFlowGraph { data: BTreeMap::new() }; // Even ebbs without predecessors should show up in the CFG, albeit // with no entires. @@ -76,7 +75,7 @@ impl ControlFlowGraph { InstructionData::Nullary { ty: _, opcode: _ } => { terminated = true; } - _ => () + _ => (), } } } @@ -89,16 +88,19 @@ impl ControlFlowGraph { self.data.insert(ebb, BTreeSet::new()); match self.data.get_mut(&ebb) { Some(predecessors) => Ok(predecessors), - None => Err("Ebb initialization failed.") + None => Err("Ebb initialization failed."), } } /// Attempts to add a predecessor for some ebb, attempting to initialize /// any ebb which has no entry. - pub fn add_predecessor(&mut self, ebb: Ebb, predecessor: Predecessor) -> Result<(), &'static str> { + pub fn add_predecessor(&mut self, + ebb: Ebb, + predecessor: Predecessor) + -> Result<(), &'static str> { let success = match self.data.get_mut(&ebb) { Some(predecessors) => predecessors.insert(predecessor), - None => false + None => false, }; if success { @@ -120,7 +122,7 @@ impl ControlFlowGraph { /// An iterator over all of the ebb to predecessor mappings in the CFG. pub fn iter<'a>(&'a self) -> btree_map::Iter<'a, Ebb, PredecessorSet> { - self.data.iter() + self.data.iter() } } From 79c7ae6233d2db63e5f0a85868251c3445bebe1c Mon Sep 17 00:00:00 2001 From: Morgan Phillips Date: Tue, 12 Jul 2016 14:46:24 -0700 Subject: [PATCH 4/5] Remove misleading test comments --- src/libcretonne/cfg.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libcretonne/cfg.rs b/src/libcretonne/cfg.rs index cf69d0f184..14f0467fcb 100644 --- a/src/libcretonne/cfg.rs +++ b/src/libcretonne/cfg.rs @@ -202,8 +202,6 @@ mod tests { let nullary_inst = nullary(&mut func); func.append_inst(ebb1_malformed, nullary_inst); - // This jump should not be recorded since a nullary takes place - // before it appears. let jmp_ebb1_ebb0 = jump(&mut func, ebb0); func.append_inst(ebb1_malformed, jmp_ebb1_ebb0); @@ -228,8 +226,6 @@ mod tests { let jmp_ebb1_ebb2 = jump(&mut func, ebb2); func.append_inst(ebb1_malformed, jmp_ebb1_ebb2); - // This branch should not be recorded since a jump takes place - // before it appears. let br_ebb1_ebb0 = branch(&mut func, ebb0); func.append_inst(ebb1_malformed, br_ebb1_ebb0); From 295a4eb03fa93f3ba80654f5bdab7c1c5003478b Mon Sep 17 00:00:00 2001 From: Morgan Phillips Date: Wed, 13 Jul 2016 11:04:39 -0700 Subject: [PATCH 5/5] Replace Results with assertions in invariant cases. It seems reasonable that certain non-recoverable errors during the building of the CFG should crash. --- src/libcretonne/cfg.rs | 107 +++++------------------------------------ 1 file changed, 12 insertions(+), 95 deletions(-) diff --git a/src/libcretonne/cfg.rs b/src/libcretonne/cfg.rs index 14f0467fcb..4fc2f1b292 100644 --- a/src/libcretonne/cfg.rs +++ b/src/libcretonne/cfg.rs @@ -43,76 +43,42 @@ impl ControlFlowGraph { /// During initialization mappings will be generated for any existing /// blocks within the CFG's associated function. Basic sanity checks will /// also be performed to ensure that the blocks are well formed. - pub fn new(func: &Function) -> Result { + pub fn new(func: &Function) -> ControlFlowGraph { let mut cfg = ControlFlowGraph { data: BTreeMap::new() }; // Even ebbs without predecessors should show up in the CFG, albeit // with no entires. for ebb in func.ebbs_numerically() { - try!(cfg.init_ebb(ebb)); + cfg.init_ebb(ebb); } for ebb in func.ebbs_numerically() { // Flips to true when a terminating instruction is seen. So that if additional // instructions occur an error may be returned. - let mut terminated = false; for inst in func.ebb_insts(ebb) { - if terminated { - return Err(format!("{} contains unreachable instructions.", ebb)); - } - match func[inst] { InstructionData::Branch { ty: _, opcode: _, ref data } => { - try!(cfg.add_predecessor(data.destination, (ebb, inst))); + cfg.add_predecessor(data.destination, (ebb, inst)); } InstructionData::Jump { ty: _, opcode: _, ref data } => { - try!(cfg.add_predecessor(data.destination, (ebb, inst))); - terminated = true; - } - InstructionData::Return { ty: _, opcode: _, data: _ } => { - terminated = true; - } - InstructionData::Nullary { ty: _, opcode: _ } => { - terminated = true; + cfg.add_predecessor(data.destination, (ebb, inst)); } _ => (), } } } - Ok(cfg) + cfg } /// Initializes a predecessor set for some ebb. If an ebb already has an /// entry it will be clobbered. - pub fn init_ebb(&mut self, ebb: Ebb) -> Result<&mut PredecessorSet, &'static str> { + pub fn init_ebb(&mut self, ebb: Ebb) -> &mut PredecessorSet { self.data.insert(ebb, BTreeSet::new()); - match self.data.get_mut(&ebb) { - Some(predecessors) => Ok(predecessors), - None => Err("Ebb initialization failed."), - } + self.data.get_mut(&ebb).unwrap() } - /// Attempts to add a predecessor for some ebb, attempting to initialize - /// any ebb which has no entry. - pub fn add_predecessor(&mut self, - ebb: Ebb, - predecessor: Predecessor) - -> Result<(), &'static str> { - let success = match self.data.get_mut(&ebb) { - Some(predecessors) => predecessors.insert(predecessor), - None => false, - }; - - if success { - Ok(()) - } else { - let mut predecessors = try!(self.init_ebb(ebb)); - if predecessors.insert(predecessor) { - return Ok(()); - } - Err("Predecessor insertion failed.") - } - + pub fn add_predecessor(&mut self, ebb: Ebb, predecessor: Predecessor) { + self.data.get_mut(&ebb).unwrap().insert(predecessor); } /// Returns all of the predecessors for some ebb, if it has an entry. @@ -136,13 +102,6 @@ mod tests { // Some instructions will be re-used in several tests. - fn nullary(func: &mut Function) -> Inst { - func.make_inst(InstructionData::Nullary { - opcode: Opcode::Iconst, - ty: types::I32, - }) - } - fn jump(func: &mut Function, dest: Ebb) -> Inst { func.make_inst(InstructionData::Jump { opcode: Opcode::Jump, @@ -169,7 +128,7 @@ mod tests { #[test] fn empty() { let func = Function::new(); - let cfg = ControlFlowGraph::new(&func).unwrap(); + let cfg = ControlFlowGraph::new(&func); assert_eq!(None, cfg.iter().next()); } @@ -179,7 +138,7 @@ mod tests { func.make_ebb(); func.make_ebb(); func.make_ebb(); - let cfg = ControlFlowGraph::new(&func).unwrap(); + let cfg = ControlFlowGraph::new(&func); let nodes = cfg.iter().collect::>(); assert_eq!(nodes.len(), 3); @@ -190,48 +149,6 @@ mod tests { } } - #[test] - #[should_panic(expected = "instructions")] - fn nullable_before_branch() { - // Ensure that branching after a nullary, within an ebb, triggers an error. - let mut func = Function::new(); - let ebb0 = func.make_ebb(); - let ebb1_malformed = func.make_ebb(); - let ebb2 = func.make_ebb(); - - let nullary_inst = nullary(&mut func); - func.append_inst(ebb1_malformed, nullary_inst); - - let jmp_ebb1_ebb0 = jump(&mut func, ebb0); - func.append_inst(ebb1_malformed, jmp_ebb1_ebb0); - - let jmp_ebb0_ebb2 = jump(&mut func, ebb2); - func.append_inst(ebb0, jmp_ebb0_ebb2); - - ControlFlowGraph::new(&func).unwrap(); - } - - #[test] - #[should_panic(expected = "instructions")] - fn jump_before_branch() { - // Ensure that branching after a jump, within an ebb, triggers an error. - let mut func = Function::new(); - let ebb0 = func.make_ebb(); - let ebb1_malformed = func.make_ebb(); - let ebb2 = func.make_ebb(); - - let jmp_ebb0_ebb1 = jump(&mut func, ebb2); - func.append_inst(ebb0, jmp_ebb0_ebb1); - - let jmp_ebb1_ebb2 = jump(&mut func, ebb2); - func.append_inst(ebb1_malformed, jmp_ebb1_ebb2); - - let br_ebb1_ebb0 = branch(&mut func, ebb0); - func.append_inst(ebb1_malformed, br_ebb1_ebb0); - - ControlFlowGraph::new(&func).unwrap(); - } - #[test] fn branches_and_jumps() { let mut func = Function::new(); @@ -251,7 +168,7 @@ mod tests { let jmp_ebb1_ebb2 = jump(&mut func, ebb2); func.append_inst(ebb1, jmp_ebb1_ebb2); - let cfg = ControlFlowGraph::new(&func).unwrap(); + let cfg = ControlFlowGraph::new(&func); let ebb0_predecessors = cfg.get_predecessors(ebb0).unwrap(); let ebb1_predecessors = cfg.get_predecessors(ebb1).unwrap(); let ebb2_predecessors = cfg.get_predecessors(ebb2).unwrap();