Replace Results with assertions in invariant cases.

It seems reasonable that certain non-recoverable errors during the building of the
CFG should crash.
This commit is contained in:
Morgan Phillips
2016-07-13 11:04:39 -07:00
parent 24b421bd68
commit 985606b12c

View File

@@ -43,76 +43,42 @@ impl ControlFlowGraph {
/// During initialization mappings will be generated for any existing /// During initialization mappings will be generated for any existing
/// blocks within the CFG's associated function. Basic sanity checks will /// blocks within the CFG's associated function. Basic sanity checks will
/// also be performed to ensure that the blocks are well formed. /// also be performed to ensure that the blocks are well formed.
pub fn new(func: &Function) -> Result<ControlFlowGraph, String> { pub fn new(func: &Function) -> ControlFlowGraph {
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 // Even ebbs without predecessors should show up in the CFG, albeit
// with no entires. // with no entires.
for ebb in func.ebbs_numerically() { for ebb in func.ebbs_numerically() {
try!(cfg.init_ebb(ebb)); cfg.init_ebb(ebb);
} }
for ebb in func.ebbs_numerically() { for ebb in func.ebbs_numerically() {
// Flips to true when a terminating instruction is seen. So that if additional // Flips to true when a terminating instruction is seen. So that if additional
// instructions occur an error may be returned. // instructions occur an error may be returned.
let mut terminated = false;
for inst in func.ebb_insts(ebb) { for inst in func.ebb_insts(ebb) {
if terminated {
return Err(format!("{} contains unreachable instructions.", ebb));
}
match func[inst] { match func[inst] {
InstructionData::Branch { ty: _, opcode: _, ref data } => { 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 } => { InstructionData::Jump { ty: _, opcode: _, ref data } => {
try!(cfg.add_predecessor(data.destination, (ebb, inst))); cfg.add_predecessor(data.destination, (ebb, inst));
terminated = true;
}
InstructionData::Return { ty: _, opcode: _, data: _ } => {
terminated = true;
}
InstructionData::Nullary { ty: _, opcode: _ } => {
terminated = true;
} }
_ => (), _ => (),
} }
} }
} }
Ok(cfg) cfg
} }
/// Initializes a predecessor set for some ebb. If an ebb already has an /// Initializes a predecessor set for some ebb. If an ebb already has an
/// entry it will be clobbered. /// 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()); self.data.insert(ebb, BTreeSet::new());
match self.data.get_mut(&ebb) { self.data.get_mut(&ebb).unwrap()
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.")
} }
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. /// 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. // 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 { fn jump(func: &mut Function, dest: Ebb) -> Inst {
func.make_inst(InstructionData::Jump { func.make_inst(InstructionData::Jump {
opcode: Opcode::Jump, opcode: Opcode::Jump,
@@ -169,7 +128,7 @@ mod tests {
#[test] #[test]
fn empty() { fn empty() {
let func = Function::new(); let func = Function::new();
let cfg = ControlFlowGraph::new(&func).unwrap(); let cfg = ControlFlowGraph::new(&func);
assert_eq!(None, cfg.iter().next()); assert_eq!(None, cfg.iter().next());
} }
@@ -179,7 +138,7 @@ mod tests {
func.make_ebb(); func.make_ebb();
func.make_ebb(); 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::<Vec<_>>(); let nodes = cfg.iter().collect::<Vec<_>>();
assert_eq!(nodes.len(), 3); 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] #[test]
fn branches_and_jumps() { fn branches_and_jumps() {
let mut func = Function::new(); let mut func = Function::new();
@@ -251,7 +168,7 @@ mod tests {
let jmp_ebb1_ebb2 = jump(&mut func, ebb2); let jmp_ebb1_ebb2 = jump(&mut func, ebb2);
func.append_inst(ebb1, jmp_ebb1_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 ebb0_predecessors = cfg.get_predecessors(ebb0).unwrap();
let ebb1_predecessors = cfg.get_predecessors(ebb1).unwrap(); let ebb1_predecessors = cfg.get_predecessors(ebb1).unwrap();
let ebb2_predecessors = cfg.get_predecessors(ebb2).unwrap(); let ebb2_predecessors = cfg.get_predecessors(ebb2).unwrap();