Convert the CFG traversal tests to file tests.

Add a "cfg_postorder:" printout to the "test domtree" file tests and use
that to check the computed CFG post-order instead of doing it manually
with Rust code.
This commit is contained in:
Jakob Stoklund Olesen
2017-12-08 13:54:19 -08:00
parent a7eb13a151
commit 7d5f2f0404
10 changed files with 125 additions and 240 deletions

View File

@@ -225,7 +225,7 @@ instruction that produces the verifier error*. Both the error message and
reported location of the error is verified::
test verifier
function %test(i32) {
ebb0(v0: i32):
jump ebb1 ; error: terminator
@@ -249,20 +249,20 @@ command::
; For testing cfg generation. This code is nonsense.
test print-cfg
test verifier
function %nonsense(i32, i32) -> f32 {
; check: digraph %nonsense {
; regex: I=\binst\d+\b
; check: label="{ebb0 | <$(BRZ=$I)>brz ebb2 | <$(JUMP=$I)>jump ebb1}"]
ebb0(v1: i32, v2: i32):
brz v2, ebb2 ; unordered: ebb0:$BRZ -> ebb2
v4 = iconst.i32 0
jump ebb1(v4) ; unordered: ebb0:$JUMP -> ebb1
ebb1(v5: i32):
return v1
ebb2:
v100 = f32const 0.0
return v100
@@ -275,7 +275,7 @@ Compute the dominator tree of each function and validate it against the
``dominates:`` annotations::
test domtree
function %test(i32) {
ebb0(v0: i32):
jump ebb1 ; dominates: ebb1
@@ -293,6 +293,8 @@ Every reachable extended basic block except for the entry block has an
if the ``dominates:`` annotations on the immediate dominator instructions are
both correct and complete.
This test also sends the computed CFG post-order through filecheck.
`test legalizer`
----------------

View File

@@ -3,7 +3,7 @@ test print-cfg
test verifier
function %nonsense(i32, i32) -> f32 {
; check: digraph %nonsense {
; check: digraph "%nonsense" {
; regex: I=\binst\d+\b
; check: label="{ebb0 | <$(BRZ=$I)>brz ebb2 | <$(JUMP=$I)>jump ebb1}"]

View File

@@ -4,7 +4,7 @@ test print-cfg
test verifier
function %nonsense(i32) {
; check: digraph %nonsense {
; check: digraph "%nonsense" {
ebb0(v1: i32):
trap user0 ; error: terminator instruction was encountered before the end

View File

@@ -2,7 +2,7 @@
test print-cfg
function %not_reached(i32) -> i32 {
; check: digraph %not_reached {
; check: digraph "%not_reached" {
; check: ebb0 [shape=record, label="{ebb0 | <inst0>brnz ebb2}"]
; check: ebb1 [shape=record, label="{ebb1 | <inst4>jump ebb0}"]
; check: ebb2 [shape=record, label="{ebb2}"]

View File

@@ -11,3 +11,8 @@ function %test(i32) {
ebb3:
return
}
; check: cfg_postorder:
; sameln: ebb2
; sameln: ebb3
; sameln: ebb1
; sameln: ebb0

View File

@@ -18,3 +18,67 @@ function %test(i32) {
brz v0, ebb4
return
}
; Fall-through-first, prune-at-source DFT:
;
; ebb0 {
; ebb0:brz v0, ebb1 {
; ebb0:jump ebb2 {
; ebb2 {
; ebb2:brz v2, ebb2 -
; ebb2:brz v3, ebb1 -
; ebb2:brz v4, ebb4 {
; ebb2: jump ebb5 {
; ebb5 {}
; }
; ebb4 {}
; }
; } ebb2
; }
; ebb1 {
; ebb1:jump ebb3 {
; ebb3 {}
; }
; } ebb1
; }
; } ebb0
;
; check: cfg_postorder:
; sameln: ebb5
; sameln: ebb3
; sameln: ebb4
; sameln: ebb2
; sameln: ebb1
; sameln: ebb0
function %loop2(i32) native {
ebb0(v0: i32):
brz v0, ebb1 ; dominates: ebb1 ebb3 ebb4 ebb5
jump ebb2 ; dominates: ebb2
ebb1:
jump ebb3
ebb2:
brz v0, ebb4
jump ebb5
ebb3:
jump ebb4
ebb4:
brz v0, ebb3
brnz v0, ebb5
jump ebb6 ; dominates: ebb6
ebb5:
brz v0, ebb4
trap user0
ebb6:
jump ebb7 ; dominates: ebb7
ebb7:
return
}
; check: cfg_postorder:
; sameln: ebb5
; sameln: ebb7
; sameln: ebb6
; sameln: ebb3
; sameln: ebb4
; sameln: ebb2
; sameln: ebb1
; sameln: ebb0

View File

@@ -29,3 +29,29 @@ function %test(i32) {
ebb9:
return
}
function %test(i32) native {
ebb0(v0: i32):
brz v0, ebb1 ; dominates: ebb1 ebb3 ebb4 ebb5
jump ebb2 ; dominates: ebb2
ebb1:
jump ebb3
ebb2:
brz v0, ebb4
jump ebb5
ebb3:
jump ebb4
ebb4:
brz v0, ebb3
jump ebb5
ebb5:
brz v0, ebb4
return
}
; check: cfg_postorder:
; sameln: ebb5
; sameln: ebb3
; sameln: ebb4
; sameln: ebb2
; sameln: ebb1
; sameln: ebb0

View File

@@ -15,9 +15,11 @@ use cretonne::flowgraph::ControlFlowGraph;
use cretonne::ir::Function;
use cretonne::ir::entities::AnyEntity;
use cton_reader::TestCommand;
use filetest::subtest::{SubTest, Context, Result};
use filetest::subtest::{SubTest, Context, Result, run_filecheck};
use std::borrow::{Borrow, Cow};
use std::collections::HashMap;
use std::fmt::{self, Write};
use std::result;
use utils::match_directive;
struct TestDomtree;
@@ -108,6 +110,20 @@ impl SubTest for TestDomtree {
}
}
Ok(())
let text = filecheck_text(&domtree).expect("formatting error");
run_filecheck(&text, context)
}
}
// Generate some output for filecheck testing
fn filecheck_text(domtree: &DominatorTree) -> result::Result<String, fmt::Error> {
let mut s = String::new();
write!(s, "cfg_postorder:")?;
for &ebb in domtree.cfg_postorder() {
write!(s, " {}", ebb)?;
}
writeln!(s, "")?;
Ok(s)
}

View File

@@ -46,7 +46,7 @@ impl<'a> CFGPrinter<'a> {
}
fn header(&self, w: &mut Write) -> Result {
writeln!(w, "digraph {} {{", self.func.name)?;
writeln!(w, "digraph \"{}\" {{", self.func.name)?;
if let Some(entry) = self.func.layout.entry_block() {
writeln!(w, " {{rank=min; {}}}", entry)?;
}

View File

@@ -1,228 +0,0 @@
extern crate cretonne;
extern crate cton_reader;
use self::cretonne::flowgraph::ControlFlowGraph;
use self::cretonne::dominator_tree::DominatorTree;
use self::cretonne::ir::Ebb;
use self::cton_reader::parse_functions;
fn test_reverse_postorder_traversal(function_source: &str, ebb_order: Vec<u32>) {
let func = &parse_functions(function_source).unwrap()[0];
let cfg = ControlFlowGraph::with_function(&func);
let domtree = DominatorTree::with_function(&func, &cfg);
let got = domtree
.cfg_postorder()
.iter()
.rev()
.cloned()
.collect::<Vec<_>>();
let want = ebb_order
.iter()
.map(|&n| Ebb::with_number(n).unwrap())
.collect::<Vec<_>>();
assert_eq!(got, want);
}
#[test]
fn simple_traversal() {
// Fall-through-first, prune-at-source DFT:
//
// ebb0 {
// ebb0:brz v0, ebb1 {
// ebb0:jump ebb2 {
// ebb2 {
// ebb2:brz v2, ebb2 -
// ebb2:brz v3, ebb1 -
// ebb2:brz v4, ebb4 {
// ebb2: jump ebb5 {
// ebb5 {}
// }
// ebb4 {}
// }
// } ebb2
// }
// ebb1 {
// ebb1:jump ebb3 {
// ebb3 {}
// }
// } ebb1
// }
// } ebb0
test_reverse_postorder_traversal(
"
function %test(i32) native {
ebb0(v0: i32):
brz v0, ebb1
jump ebb2
ebb1:
jump ebb3
ebb2:
v1 = iconst.i32 1
v2 = iadd v1, v0
brz v2, ebb2
v3 = iadd v1, v2
brz v3, ebb1
v4 = iadd v1, v3
brz v4, ebb4
jump ebb5
ebb3:
trap user0
ebb4:
trap user0
ebb5:
trap user0
}
",
vec![0, 1, 3, 2, 4, 5],
);
}
#[test]
fn loops_one() {
// Fall-through-first, prune-at-source DFT:
// ebb0 {
// ebb0:jump ebb1 {
// ebb1 {
// ebb1:brnz v0, ebb3 {
// ebb1:jump ebb2 {
// ebb2 {
// ebb2:jump ebb3 -
// } ebb2
// }
// ebb3 {}
// }
// } ebb1
// }
// } ebb0
test_reverse_postorder_traversal(
"
function %test(i32) native {
ebb0(v0: i32):
jump ebb1
ebb1:
brnz v0, ebb3
jump ebb2
ebb2:
jump ebb3
ebb3:
return
}
",
vec![0, 1, 3, 2],
);
}
#[test]
fn loops_two() {
// Fall-through-first, prune-at-source DFT:
// ebb0 {
// ebb0:brz v0, ebb1 {
// ebb0:jump ebb2 {
// ebb2 {
// ebb2:brz v0, ebb4 {
// ebb2:jump ebb5 {
// ebb5 {
// brz v0, ebb4 -
// } ebb5
// }
// ebb4 {
// ebb4:brz v0, ebb3 {
// ebb4:jump ebb5 -
// ebb3 {
// ebb3:jump ebb4 -
// } ebb3
// }
// } ebb4
// }
// } ebb2
// }
// ebb1 {
// ebb1:jump ebb3 -
// } ebb1
// }
// } ebb0
test_reverse_postorder_traversal(
"
function %test(i32) native {
ebb0(v0: i32):
brz v0, ebb1
jump ebb2
ebb1:
jump ebb3
ebb2:
brz v0, ebb4
jump ebb5
ebb3:
jump ebb4
ebb4:
brz v0, ebb3
jump ebb5
ebb5:
brz v0, ebb4
return
}
",
vec![0, 1, 2, 4, 3, 5],
);
}
#[test]
fn loops_three() {
test_reverse_postorder_traversal(
"
function %test(i32) native {
ebb0(v0: i32):
brz v0, ebb1
jump ebb2
ebb1:
jump ebb3
ebb2:
brz v0, ebb4
jump ebb5
ebb3:
jump ebb4
ebb4:
brz v0, ebb3
brnz v0, ebb5
jump ebb6
ebb5:
brz v0, ebb4
trap user0
ebb6:
jump ebb7
ebb7:
return
}
",
vec![0, 1, 2, 4, 3, 6, 7, 5],
);
}
#[test]
fn back_edge_one() {
test_reverse_postorder_traversal(
"
function %test(i32) native {
ebb0(v0: i32):
brz v0, ebb1
jump ebb2
ebb1:
jump ebb3
ebb2:
brz v0, ebb0
jump ebb4
ebb3:
brz v0, ebb2
brnz v0, ebb0
return
ebb4:
trap user0
}
",
vec![0, 1, 3, 2, 4],
);
}