diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml index 297e8c31b3..ee904633f9 100644 --- a/cranelift/Cargo.toml +++ b/cranelift/Cargo.toml @@ -23,7 +23,7 @@ docopt = "0.8.0" serde = "1.0.8" serde_derive = "1.0.8" num_cpus = "1.5.1" -tempdir="0.3.5" +tempdir = "0.3.5" term = "0.5" [workspace] diff --git a/cranelift/docs/testing.rst b/cranelift/docs/testing.rst index 2b84b1c19b..07aa8f8e16 100644 --- a/cranelift/docs/testing.rst +++ b/cranelift/docs/testing.rst @@ -324,6 +324,22 @@ Test the simple GVN pass. The simple GVN pass is run on each function, and then results are run through filecheck. +`test licm` +----------------- + +Test the LICM pass. + +The LICM pass is run on each function, and then results are run +through filecheck. + +`test preopt` +----------------- + +Test the preopt pass. + +The preopt pass is run on each function, and then results are run +through filecheck. + `test compile` -------------- diff --git a/cranelift/src/cat.rs b/cranelift/src/cat.rs index 36ae818e2c..3ae9c843d8 100644 --- a/cranelift/src/cat.rs +++ b/cranelift/src/cat.rs @@ -3,12 +3,9 @@ //! Read a sequence of Cretonne IL files and print them again to stdout. This has the effect of //! normalizing formatting and removing comments. -use std::borrow::Cow; -use cretonne::ir::Function; -use cton_reader::{parse_functions, TestCommand}; +use cton_reader::parse_functions; use CommandResult; use utils::read_to_string; -use filetest::subtest::{self, SubTest, Context, Result as STResult}; pub fn run(files: Vec) -> CommandResult { for (i, f) in files.into_iter().enumerate() { @@ -37,34 +34,3 @@ fn cat_one(filename: String) -> CommandResult { Ok(()) } - -/// Object implementing the `test cat` sub-test. -/// -/// This command is used for testing the parser and function printer. It simply parses a function -/// and prints it out again. -/// -/// The result is verified by filecheck. -struct TestCat; - -pub fn subtest(parsed: &TestCommand) -> STResult> { - assert_eq!(parsed.command, "cat"); - if !parsed.options.is_empty() { - Err(format!("No options allowed on {}", parsed)) - } else { - Ok(Box::new(TestCat)) - } -} - -impl SubTest for TestCat { - fn name(&self) -> Cow { - Cow::from("cat") - } - - fn needs_verifier(&self) -> bool { - false - } - - fn run(&self, func: Cow, context: &Context) -> STResult<()> { - subtest::run_filecheck(&func.display(context.isa).to_string(), context) - } -} diff --git a/cranelift/src/filetest/mod.rs b/cranelift/src/filetest/mod.rs index 5286307811..6d7a5e39d5 100644 --- a/cranelift/src/filetest/mod.rs +++ b/cranelift/src/filetest/mod.rs @@ -7,27 +7,27 @@ use std::path::Path; use std::time; use cton_reader::TestCommand; use CommandResult; -use cat; -use print_cfg; use filetest::runner::TestRunner; -pub mod subtest; - -mod binemit; -mod compile; mod concurrent; -mod domtree; -mod legalizer; -mod licm; -mod preopt; -mod regalloc; mod runner; mod runone; -mod simple_gvn; -mod verifier; +mod subtest; + +mod test_binemit; +mod test_cat; +mod test_compile; +mod test_domtree; +mod test_legalizer; +mod test_licm; +mod test_preopt; +mod test_print_cfg; +mod test_regalloc; +mod test_simple_gvn; +mod test_verifier; /// The result of running the test in a file. -pub type TestResult = Result; +type TestResult = Result; /// Main entry point for `cton-util test`. /// @@ -59,17 +59,17 @@ pub fn run(verbose: bool, files: Vec) -> CommandResult { /// a `.cton` test file. fn new_subtest(parsed: &TestCommand) -> subtest::Result> { match parsed.command { - "binemit" => binemit::subtest(parsed), - "cat" => cat::subtest(parsed), - "compile" => compile::subtest(parsed), - "domtree" => domtree::subtest(parsed), - "legalizer" => legalizer::subtest(parsed), - "licm" => licm::subtest(parsed), - "preopt" => preopt::subtest(parsed), - "print-cfg" => print_cfg::subtest(parsed), - "regalloc" => regalloc::subtest(parsed), - "simple-gvn" => simple_gvn::subtest(parsed), - "verifier" => verifier::subtest(parsed), + "binemit" => test_binemit::subtest(parsed), + "cat" => test_cat::subtest(parsed), + "compile" => test_compile::subtest(parsed), + "domtree" => test_domtree::subtest(parsed), + "legalizer" => test_legalizer::subtest(parsed), + "licm" => test_licm::subtest(parsed), + "preopt" => test_preopt::subtest(parsed), + "print-cfg" => test_print_cfg::subtest(parsed), + "regalloc" => test_regalloc::subtest(parsed), + "simple-gvn" => test_simple_gvn::subtest(parsed), + "verifier" => test_verifier::subtest(parsed), _ => Err(format!("unknown test command '{}'", parsed.command)), } } diff --git a/cranelift/src/filetest/binemit.rs b/cranelift/src/filetest/test_binemit.rs similarity index 100% rename from cranelift/src/filetest/binemit.rs rename to cranelift/src/filetest/test_binemit.rs diff --git a/cranelift/src/filetest/test_cat.rs b/cranelift/src/filetest/test_cat.rs new file mode 100644 index 0000000000..fc4a3cac10 --- /dev/null +++ b/cranelift/src/filetest/test_cat.rs @@ -0,0 +1,37 @@ +//! The `cat` subtest. + +use std::borrow::Cow; +use cretonne::ir::Function; +use cton_reader::TestCommand; +use filetest::subtest::{self, SubTest, Context, Result as STResult}; + +/// Object implementing the `test cat` sub-test. +/// +/// This command is used for testing the parser and function printer. It simply parses a function +/// and prints it out again. +/// +/// The result is verified by filecheck. +struct TestCat; + +pub fn subtest(parsed: &TestCommand) -> STResult> { + assert_eq!(parsed.command, "cat"); + if !parsed.options.is_empty() { + Err(format!("No options allowed on {}", parsed)) + } else { + Ok(Box::new(TestCat)) + } +} + +impl SubTest for TestCat { + fn name(&self) -> Cow { + Cow::from("cat") + } + + fn needs_verifier(&self) -> bool { + false + } + + fn run(&self, func: Cow, context: &Context) -> STResult<()> { + subtest::run_filecheck(&func.display(context.isa).to_string(), context) + } +} diff --git a/cranelift/src/filetest/compile.rs b/cranelift/src/filetest/test_compile.rs similarity index 100% rename from cranelift/src/filetest/compile.rs rename to cranelift/src/filetest/test_compile.rs diff --git a/cranelift/src/filetest/domtree.rs b/cranelift/src/filetest/test_domtree.rs similarity index 100% rename from cranelift/src/filetest/domtree.rs rename to cranelift/src/filetest/test_domtree.rs diff --git a/cranelift/src/filetest/legalizer.rs b/cranelift/src/filetest/test_legalizer.rs similarity index 100% rename from cranelift/src/filetest/legalizer.rs rename to cranelift/src/filetest/test_legalizer.rs diff --git a/cranelift/src/filetest/licm.rs b/cranelift/src/filetest/test_licm.rs similarity index 100% rename from cranelift/src/filetest/licm.rs rename to cranelift/src/filetest/test_licm.rs diff --git a/cranelift/src/filetest/preopt.rs b/cranelift/src/filetest/test_preopt.rs similarity index 100% rename from cranelift/src/filetest/preopt.rs rename to cranelift/src/filetest/test_preopt.rs diff --git a/cranelift/src/filetest/test_print_cfg.rs b/cranelift/src/filetest/test_print_cfg.rs new file mode 100644 index 0000000000..1be9987087 --- /dev/null +++ b/cranelift/src/filetest/test_print_cfg.rs @@ -0,0 +1,37 @@ +//! The `print-cfg` sub-command. +//! +//! Read a series of Cretonne IL files and print their control flow graphs +//! in graphviz format. + +use std::borrow::Cow; + +use cretonne::ir::Function; +use cretonne::cfg_printer::CFGPrinter; +use cton_reader::TestCommand; +use filetest::subtest::{self, SubTest, Context, Result as STResult}; + +/// Object implementing the `test print-cfg` sub-test. +struct TestPrintCfg; + +pub fn subtest(parsed: &TestCommand) -> STResult> { + assert_eq!(parsed.command, "print-cfg"); + if !parsed.options.is_empty() { + Err(format!("No options allowed on {}", parsed)) + } else { + Ok(Box::new(TestPrintCfg)) + } +} + +impl SubTest for TestPrintCfg { + fn name(&self) -> Cow { + Cow::from("print-cfg") + } + + fn needs_verifier(&self) -> bool { + false + } + + fn run(&self, func: Cow, context: &Context) -> STResult<()> { + subtest::run_filecheck(&CFGPrinter::new(&func).to_string(), context) + } +} diff --git a/cranelift/src/filetest/regalloc.rs b/cranelift/src/filetest/test_regalloc.rs similarity index 100% rename from cranelift/src/filetest/regalloc.rs rename to cranelift/src/filetest/test_regalloc.rs diff --git a/cranelift/src/filetest/simple_gvn.rs b/cranelift/src/filetest/test_simple_gvn.rs similarity index 100% rename from cranelift/src/filetest/simple_gvn.rs rename to cranelift/src/filetest/test_simple_gvn.rs diff --git a/cranelift/src/filetest/verifier.rs b/cranelift/src/filetest/test_verifier.rs similarity index 100% rename from cranelift/src/filetest/verifier.rs rename to cranelift/src/filetest/test_verifier.rs diff --git a/cranelift/src/print_cfg.rs b/cranelift/src/print_cfg.rs index df624993ad..08ab439d05 100644 --- a/cranelift/src/print_cfg.rs +++ b/cranelift/src/print_cfg.rs @@ -3,15 +3,9 @@ //! Read a series of Cretonne IL files and print their control flow graphs //! in graphviz format. -use std::borrow::Cow; -use std::fmt::{Result, Write, Display, Formatter}; - use CommandResult; -use cretonne::flowgraph::ControlFlowGraph; -use cretonne::ir::Function; -use cretonne::ir::instructions::BranchInfo; -use cton_reader::{parse_functions, TestCommand}; -use filetest::subtest::{self, SubTest, Context, Result as STResult}; +use cretonne::cfg_printer::CFGPrinter; +use cton_reader::parse_functions; use utils::read_to_string; pub fn run(files: Vec) -> CommandResult { @@ -24,72 +18,6 @@ pub fn run(files: Vec) -> CommandResult { Ok(()) } -struct CFGPrinter<'a> { - func: &'a Function, - cfg: ControlFlowGraph, -} - -impl<'a> CFGPrinter<'a> { - pub fn new(func: &'a Function) -> CFGPrinter<'a> { - CFGPrinter { - func, - cfg: ControlFlowGraph::with_function(func), - } - } - - /// Write the CFG for this function to `w`. - pub fn write(&self, w: &mut Write) -> Result { - self.header(w)?; - self.ebb_nodes(w)?; - self.cfg_connections(w)?; - writeln!(w, "}}") - } - - fn header(&self, w: &mut Write) -> Result { - writeln!(w, "digraph \"{}\" {{", self.func.name)?; - if let Some(entry) = self.func.layout.entry_block() { - writeln!(w, " {{rank=min; {}}}", entry)?; - } - Ok(()) - } - - fn ebb_nodes(&self, w: &mut Write) -> Result { - for ebb in &self.func.layout { - write!(w, " {} [shape=record, label=\"{{{}", ebb, ebb)?; - // Add all outgoing branch instructions to the label. - for inst in self.func.layout.ebb_insts(ebb) { - let idata = &self.func.dfg[inst]; - match idata.analyze_branch(&self.func.dfg.value_lists) { - BranchInfo::SingleDest(dest, _) => { - write!(w, " | <{}>{} {}", inst, idata.opcode(), dest)? - } - BranchInfo::Table(table) => { - write!(w, " | <{}>{} {}", inst, idata.opcode(), table)? - } - BranchInfo::NotABranch => {} - } - } - writeln!(w, "}}\"]")? - } - Ok(()) - } - - fn cfg_connections(&self, w: &mut Write) -> Result { - for ebb in &self.func.layout { - for (parent, inst) in self.cfg.pred_iter(ebb) { - writeln!(w, " {}:{} -> {}", parent, inst, ebb)?; - } - } - Ok(()) - } -} - -impl<'a> Display for CFGPrinter<'a> { - fn fmt(&self, f: &mut Formatter) -> Result { - self.write(f) - } -} - fn print_cfg(filename: String) -> CommandResult { let buffer = read_to_string(&filename).map_err( |e| format!("{}: {}", filename, e), @@ -107,29 +35,3 @@ fn print_cfg(filename: String) -> CommandResult { Ok(()) } - -/// Object implementing the `test print-cfg` sub-test. -struct TestPrintCfg; - -pub fn subtest(parsed: &TestCommand) -> STResult> { - assert_eq!(parsed.command, "print-cfg"); - if !parsed.options.is_empty() { - Err(format!("No options allowed on {}", parsed)) - } else { - Ok(Box::new(TestPrintCfg)) - } -} - -impl SubTest for TestPrintCfg { - fn name(&self) -> Cow { - Cow::from("print-cfg") - } - - fn needs_verifier(&self) -> bool { - false - } - - fn run(&self, func: Cow, context: &Context) -> STResult<()> { - subtest::run_filecheck(&CFGPrinter::new(&func).to_string(), context) - } -} diff --git a/cranelift/test-all.sh b/cranelift/test-all.sh index 2402eb7a39..fec95573f4 100755 --- a/cranelift/test-all.sh +++ b/cranelift/test-all.sh @@ -43,21 +43,19 @@ if [ -n "$needcheck" ]; then fi cd "$topdir" -banner "Rust unit tests" -cargo test --all -# Build cton-util for parser testing. -cd "$topdir" -banner "Rust documentation" -echo "open $topdir/target/doc/cretonne/index.html" +# Make sure the code builds in debug mode. +banner "Rust debug build" +cargo build + +# Make sure the code builds in release mode, and run the unit tests. We run +# these in release mode for speed, but note that the top-level Cargo.toml file +# does enable debug assertions in release builds. +banner "Rust release build and unit tests" +cargo test --all --release + +# Make sure the documentation builds. +banner "Rust documentation: $topdir/target/doc/cretonne/index.html" cargo doc -banner "Rust release build" -cargo build --release - -export CTONUTIL="$topdir/target/release/cton-util" - -cd "$topdir" -banner "File tests" -"$CTONUTIL" test filetests docs banner "OK" diff --git a/cranelift/tests/cton-util-test.rs b/cranelift/tests/cton-util-test.rs new file mode 100644 index 0000000000..d9870351d1 --- /dev/null +++ b/cranelift/tests/cton-util-test.rs @@ -0,0 +1,34 @@ +//! Run `cton-util test` on all available testcases. + +use std::process::{Command, Output}; +use std::env; +use std::path::PathBuf; +use std::io::{self, Write}; + +/// Returns the target directory, where we can find build artifacts +/// and such for the current configuration. +fn get_target_dir() -> PathBuf { + let mut path = env::current_exe().unwrap(); + path.pop(); // chop off exe name + path.pop(); // chop off deps name + path +} + +#[test] +fn cton_util_test() { + let mut cmd = Command::new(&get_target_dir().join("cton-util")); + cmd.arg("test"); + + // We have testcases in the following directories: + cmd.arg("filetests"); + cmd.arg("docs"); + + let Output { + status, + stdout, + stderr, + } = cmd.output().unwrap(); + io::stdout().write(&stdout).unwrap(); + io::stderr().write(&stderr).unwrap(); + assert!(status.success(), "failed with exit status {}", status); +} diff --git a/lib/cretonne/src/cfg_printer.rs b/lib/cretonne/src/cfg_printer.rs new file mode 100644 index 0000000000..736d12f0fc --- /dev/null +++ b/lib/cretonne/src/cfg_printer.rs @@ -0,0 +1,76 @@ +//! The `CFGPrinter` utility. + +use std::fmt::{Result, Write, Display, Formatter}; + +use flowgraph::ControlFlowGraph; +use ir::Function; +use ir::instructions::BranchInfo; + +/// A utility for pretty-printing the CFG of a `Function`. +pub struct CFGPrinter<'a> { + func: &'a Function, + cfg: ControlFlowGraph, +} + +/// A utility for pretty-printing the CFG of a `Function`. +impl<'a> CFGPrinter<'a> { + /// Create a new CFGPrinter. + pub fn new(func: &'a Function) -> CFGPrinter<'a> { + CFGPrinter { + func, + cfg: ControlFlowGraph::with_function(func), + } + } + + /// Write the CFG for this function to `w`. + pub fn write(&self, w: &mut Write) -> Result { + self.header(w)?; + self.ebb_nodes(w)?; + self.cfg_connections(w)?; + writeln!(w, "}}") + } + + fn header(&self, w: &mut Write) -> Result { + writeln!(w, "digraph \"{}\" {{", self.func.name)?; + if let Some(entry) = self.func.layout.entry_block() { + writeln!(w, " {{rank=min; {}}}", entry)?; + } + Ok(()) + } + + fn ebb_nodes(&self, w: &mut Write) -> Result { + for ebb in &self.func.layout { + write!(w, " {} [shape=record, label=\"{{{}", ebb, ebb)?; + // Add all outgoing branch instructions to the label. + for inst in self.func.layout.ebb_insts(ebb) { + let idata = &self.func.dfg[inst]; + match idata.analyze_branch(&self.func.dfg.value_lists) { + BranchInfo::SingleDest(dest, _) => { + write!(w, " | <{}>{} {}", inst, idata.opcode(), dest)? + } + BranchInfo::Table(table) => { + write!(w, " | <{}>{} {}", inst, idata.opcode(), table)? + } + BranchInfo::NotABranch => {} + } + } + writeln!(w, "}}\"]")? + } + Ok(()) + } + + fn cfg_connections(&self, w: &mut Write) -> Result { + for ebb in &self.func.layout { + for (parent, inst) in self.cfg.pred_iter(ebb) { + writeln!(w, " {}:{} -> {}", parent, inst, ebb)?; + } + } + Ok(()) + } +} + +impl<'a> Display for CFGPrinter<'a> { + fn fmt(&self, f: &mut Formatter) -> Result { + self.write(f) + } +} diff --git a/lib/cretonne/src/lib.rs b/lib/cretonne/src/lib.rs index ab7bb51f03..b924f5d9e5 100644 --- a/lib/cretonne/src/lib.rs +++ b/lib/cretonne/src/lib.rs @@ -19,6 +19,7 @@ pub mod entity; pub mod bforest; pub mod binemit; +pub mod cfg_printer; pub mod cursor; pub mod dominator_tree; pub mod flowgraph;