]> git.lizzy.rs Git - rust.git/commitdiff
add mir optimization tests, dump-mir-dir option
authorScott A Carr <s.carr1024@gmail.com>
Thu, 7 Jul 2016 23:40:01 +0000 (16:40 -0700)
committerScott A Carr <s.carr1024@gmail.com>
Thu, 21 Jul 2016 02:41:39 +0000 (19:41 -0700)
mk/tests.mk
src/bootstrap/lib.rs
src/bootstrap/step.rs
src/librustc/session/config.rs
src/librustc_mir/pretty.rs
src/test/mir-opt/README.md [new file with mode: 0644]
src/test/mir-opt/return_an_array.rs [new file with mode: 0644]
src/test/mir-opt/simplify_if.rs [new file with mode: 0644]
src/tools/compiletest/src/common.rs
src/tools/compiletest/src/main.rs
src/tools/compiletest/src/runtest.rs

index ed443147d466e3734bf5a209ff23aed0a7e166f8..201e4cae51d6d9a4e293d2eedfa276f477991cd3 100644 (file)
@@ -277,7 +277,8 @@ check-stage$(1)-T-$(2)-H-$(3)-exec: \
        check-stage$(1)-T-$(2)-H-$(3)-ui-exec \
        check-stage$(1)-T-$(2)-H-$(3)-doc-exec \
        check-stage$(1)-T-$(2)-H-$(3)-doc-error-index-exec \
-       check-stage$(1)-T-$(2)-H-$(3)-pretty-exec
+       check-stage$(1)-T-$(2)-H-$(3)-pretty-exec \
+       check-stage$(1)-T-$(2)-H-$(3)-mir-opt-exec
 
 ifndef CFG_DISABLE_CODEGEN_TESTS
 check-stage$(1)-T-$(2)-H-$(3)-exec: \
@@ -458,6 +459,7 @@ UI_RS := $(call rwildcard,$(S)src/test/ui/,*.rs) \
          $(call rwildcard,$(S)src/test/ui/,*.stdout) \
          $(call rwildcard,$(S)src/test/ui/,*.stderr)
 RUSTDOCCK_RS := $(call rwildcard,$(S)src/test/rustdoc/,*.rs)
+MIR_OPT_RS := $(call rwildcard,$(S)src/test/mir-opt/,*.rs)
 
 RPASS_TESTS := $(RPASS_RS)
 RPASS_VALGRIND_TESTS := $(RPASS_VALGRIND_RS)
@@ -475,6 +477,7 @@ CODEGEN_UNITS_TESTS := $(CODEGEN_UNITS_RS)
 INCREMENTAL_TESTS := $(INCREMENTAL_RS)
 RMAKE_TESTS := $(RMAKE_RS)
 UI_TESTS := $(UI_RS)
+MIR_OPT_TESTS := $(MIR_OPT_RS)
 RUSTDOCCK_TESTS := $(RUSTDOCCK_RS)
 
 CTEST_SRC_BASE_rpass = run-pass
@@ -552,6 +555,11 @@ CTEST_BUILD_BASE_ui = ui
 CTEST_MODE_ui = ui
 CTEST_RUNTOOL_ui = $(CTEST_RUNTOOL)
 
+CTEST_SRC_BASE_mir-opt = mir-opt
+CTEST_BUILD_BASE_mir-opt = mir-opt
+CTEST_MODE_mir-opt = mir-opt
+CTEST_RUNTOOL_mir-opt = $(CTEST_RUNTOOL)
+
 CTEST_SRC_BASE_rustdocck = rustdoc
 CTEST_BUILD_BASE_rustdocck = rustdoc
 CTEST_MODE_rustdocck = rustdoc
@@ -684,6 +692,7 @@ CTEST_DEPS_incremental_$(1)-T-$(2)-H-$(3) = $$(INCREMENTAL_TESTS)
 CTEST_DEPS_rmake_$(1)-T-$(2)-H-$(3) = $$(RMAKE_TESTS) \
        $$(CSREQ$(1)_T_$(3)_H_$(3)) $$(SREQ$(1)_T_$(2)_H_$(3))
 CTEST_DEPS_ui_$(1)-T-$(2)-H-$(3) = $$(UI_TESTS)
+CTEST_DEPS_mir-opt_$(1)-T-$(2)-H-$(3) = $$(MIR_OPT_TESTS)
 CTEST_DEPS_rustdocck_$(1)-T-$(2)-H-$(3) = $$(RUSTDOCCK_TESTS) \
                $$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) \
                $(S)src/etc/htmldocck.py
@@ -755,7 +764,7 @@ endef
 
 CTEST_NAMES = rpass rpass-valgrind rpass-full rfail-full cfail-full rfail cfail pfail \
        debuginfo-gdb debuginfo-lldb codegen codegen-units rustdocck incremental \
-       rmake ui
+       rmake ui mir-opt
 
 $(foreach host,$(CFG_HOST), \
  $(eval $(foreach target,$(CFG_TARGET), \
@@ -964,6 +973,7 @@ TEST_GROUPS = \
        pretty-rfail-full \
        pretty-rfail \
        pretty-pretty \
+       mir-opt \
        $(NULL)
 
 define DEF_CHECK_FOR_STAGE_AND_TARGET_AND_HOST
index 943271fc8a641665734531b3393b32d4f37d1e5e..53a0625744650e97a700c8f68fe719160e1f25bb 100644 (file)
@@ -375,6 +375,10 @@ pub fn build(&mut self) {
                     check::compiletest(self, &compiler, target.target,
                                        "pretty", "run-pass-valgrind");
                 }
+                CheckMirOpt { compiler } => {
+                    check::compiletest(self, &compiler, target.target,
+                                       "mir-opt", "mir-opt");
+                }
                 CheckCodegen { compiler } => {
                     check::compiletest(self, &compiler, target.target,
                                        "codegen", "codegen");
index 4b3be04b57c57948627bbbb601fcb7665c5315b5..bd262cc7721eb037e01bcc021e86d793550e71a6 100644 (file)
@@ -124,6 +124,7 @@ macro_rules! targets {
             (check_codegen_units, CheckCodegenUnits { compiler: Compiler<'a> }),
             (check_incremental, CheckIncremental { compiler: Compiler<'a> }),
             (check_ui, CheckUi { compiler: Compiler<'a> }),
+            (check_mir_opt, CheckMirOpt { compiler: Compiler<'a> }),
             (check_debuginfo, CheckDebuginfo { compiler: Compiler<'a> }),
             (check_rustdoc, CheckRustdoc { compiler: Compiler<'a> }),
             (check_docs, CheckDocs { compiler: Compiler<'a> }),
@@ -444,6 +445,7 @@ pub fn deps(&self, build: &'a Build) -> Vec<Step<'a>> {
                         self.check_pretty_rfail_full(compiler),
                         self.check_rpass_valgrind(compiler),
                         self.check_rmake(compiler),
+                        self.check_mir_opt(compiler),
 
                         // crates
                         self.check_crate_rustc(compiler),
@@ -471,6 +473,7 @@ pub fn deps(&self, build: &'a Build) -> Vec<Step<'a>> {
             Source::CheckTidy { stage } => {
                 vec![self.tool_tidy(stage)]
             }
+            Source::CheckMirOpt { compiler} |
             Source::CheckPrettyRPass { compiler } |
             Source::CheckPrettyRFail { compiler } |
             Source::CheckRFail { compiler } |
index 5ccc96210be78d7d56cc8b739544ec05a1fce18e..2d0b243558ffd8e84fae4218baa0c7974d90c253 100644 (file)
@@ -749,6 +749,8 @@ fn parse_panic_strategy(slot: &mut PanicStrategy, v: Option<&str>) -> bool {
           "set the MIR optimization level (0-3)"),
     dump_mir: Option<String> = (None, parse_opt_string,
           "dump MIR state at various points in translation"),
+    dump_mir_dir: Option<String> = (None, parse_opt_string,
+          "the directory the MIR is dumped into"),
     orbit: bool = (false, parse_bool,
           "get MIR where it belongs - everywhere; most importantly, in orbit"),
 }
index 515620d425389676617e3efa5f9cbba8cf5c96b3..577e0fd086015772bd9a1ab14db009899b4548fa 100644 (file)
@@ -19,6 +19,7 @@
 use std::fs;
 use std::io::{self, Write};
 use syntax::ast::NodeId;
+use std::path::{PathBuf, Path};
 
 const INDENT: &'static str = "    ";
 /// Alignment for lining up comments following MIR statements
@@ -66,9 +67,15 @@ pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         _ => String::new()
     };
 
+    let mut file_path = PathBuf::new();
+    if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
+        let p = Path::new(file_dir);
+        file_path.push(p);
+    };
     let file_name = format!("rustc.node{}{}.{}.{}.mir",
                             node_id, promotion_id, pass_name, disambiguator);
-    let _ = fs::File::create(&file_name).and_then(|mut file| {
+    file_path.push(&file_name);
+    let _ = fs::File::create(&file_path).and_then(|mut file| {
         try!(writeln!(file, "// MIR for `{}`", node_path));
         try!(writeln!(file, "// node_id = {}", node_id));
         try!(writeln!(file, "// pass_name = {}", pass_name));
diff --git a/src/test/mir-opt/README.md b/src/test/mir-opt/README.md
new file mode 100644 (file)
index 0000000..9144e97
--- /dev/null
@@ -0,0 +1,44 @@
+This folder contains tests for MIR optimizations.
+
+The test format is:
+
+```
+(arbitrary rust code)
+// END RUST SOURCE
+// START $file_name_of_some_mir_dump_0
+//  $expected_line_0
+// ...
+// $expected_line_N
+// END $file_name_of_some_mir_dump_0
+// ...
+// START $file_name_of_some_mir_dump_N
+//  $expected_line_0
+// ...
+// $expected_line_N
+// END $file_name_of_some_mir_dump_N
+```
+
+All the test information is in comments so the test is runnable.
+
+For each $file_name, compiletest expects [$expected_line_0, ...,
+$expected_line_N] to appear in the dumped MIR in order.  Currently it allows
+other non-matched lines before, after and in-between.  
+
+Lines match ignoring whitespace, and the prefix "//" is removed.
+
+It also currently strips trailing comments -- partly because the full file path
+in "scope comments" is unpredictable and partly because tidy complains about
+the lines being too long.
+
+compiletest handles dumping the MIR before and after every pass for you.  The
+test writer only has to specify the file names of the dumped files (not the
+full path to the file) and what lines to expect.  I added an option to rustc
+that tells it to dump the mir into some directly (rather then always dumping to
+the current directory).  
+
+Lines match ignoring whitespace, and the prefix "//" is removed of course.
+
+It also currently strips trailing comments -- partly because the full file path
+in "scope comments" is unpredictable and partly because tidy complains about
+the lines being too long.
+
diff --git a/src/test/mir-opt/return_an_array.rs b/src/test/mir-opt/return_an_array.rs
new file mode 100644 (file)
index 0000000..4409f16
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// this tests move up progration, which is not yet implemented
+
+fn foo() -> [u8; 1024] {
+        let x = [0; 1024];
+        return x;
+}
+
+fn main() { }
\ No newline at end of file
diff --git a/src/test/mir-opt/simplify_if.rs b/src/test/mir-opt/simplify_if.rs
new file mode 100644 (file)
index 0000000..dd6a857
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+    if false {
+        println!("hello world!");
+    }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyBranches.initial-before.mir
+// bb0: {
+//     if(const false) -> [true: bb1, false: bb2]; // scope 0 at simplify_if.rs:12:5: 14:6
+// }
+// END rustc.node4.SimplifyBranches.initial-before.mir
+// START rustc.node4.SimplifyBranches.initial-after.mir
+// bb0: {
+//     goto -> bb2;                     // scope 0 at simplify_if.rs:12:5: 14:6
+// }
+// END rustc.node4.SimplifyBranches.initial-after.mir
\ No newline at end of file
index 5ec62e06e37aea6bee989fb95e15229cf1b42b6e..2a35fab9676a71b9f45bea98a44a6409553df18a 100644 (file)
@@ -29,6 +29,7 @@ pub enum Mode {
     Incremental,
     RunMake,
     Ui,
+    MirOpt,
 }
 
 impl FromStr for Mode {
@@ -49,6 +50,7 @@ fn from_str(s: &str) -> Result<Mode, ()> {
           "incremental" => Ok(Incremental),
           "run-make" => Ok(RunMake),
           "ui" => Ok(Ui),
+          "mir-opt" => Ok(MirOpt),
           _ => Err(()),
         }
     }
@@ -71,6 +73,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
             Incremental => "incremental",
             RunMake => "run-make",
             Ui => "ui",
+            MirOpt => "mir-opt",
         }, f)
     }
 }
index 6830f32bb2ce117bb522b0762ef4a57fdf04a15f..cefcc11486fe230c8659e989e3575d0233e7c47c 100644 (file)
@@ -86,7 +86,7 @@ pub fn parse_config(args: Vec<String> ) -> Config {
           reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
           reqopt("", "mode", "which sort of compile tests to run",
                  "(compile-fail|parse-fail|run-fail|run-pass|\
-                  run-pass-valgrind|pretty|debug-info|incremental)"),
+                  run-pass-valgrind|pretty|debug-info|incremental|mir-opt)"),
           optflag("", "ignored", "run tests marked as ignored"),
           optopt("", "runtool", "supervisor program to run tests under \
                                  (eg. emulator, valgrind)", "PROGRAM"),
index 577da5c5af11d2b012eed6d4f860731c3050a452..f2acfa517ced5b3e36aa75afa5537ac5cfbcbf1f 100644 (file)
@@ -11,7 +11,7 @@
 use common::Config;
 use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
 use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
-use common::{Incremental, RunMake, Ui};
+use common::{Incremental, RunMake, Ui, MirOpt};
 use errors::{self, ErrorKind, Error};
 use json;
 use header::TestProps;
@@ -117,6 +117,7 @@ fn run_revision(&self) {
             Incremental => self.run_incremental_test(),
             RunMake => self.run_rmake_test(),
             Ui => self.run_ui_test(),
+            MirOpt => self.run_mir_opt_test(),
         }
     }
 
@@ -1336,7 +1337,22 @@ fn make_compile_args(&self,
                                 .map(|s| s.to_string()));
                 }
             }
+            MirOpt => {
+                args.extend(["-Z",
+                             "dump-mir=all",
+                             "-Z"]
+                            .iter()
+                            .map(|s| s.to_string()));
 
+
+                let mir_dump_dir = self.get_mir_dump_dir();
+                self.create_dir_racy(mir_dump_dir.as_path());
+                let mut dir_opt = "dump-mir-dir=".to_string();
+                dir_opt.push_str(mir_dump_dir.to_str().unwrap());
+                debug!("dir_opt: {:?}", dir_opt);
+
+                args.push(dir_opt);
+            }
             RunFail |
             RunPass |
             RunPassValgrind |
@@ -2145,6 +2161,100 @@ fn run_ui_test(&self) {
         }
     }
 
+    fn run_mir_opt_test(&self) {
+        let proc_res = self.compile_test();
+
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+
+        let proc_res = self.exec_compiled_test();
+
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("test run failed!", &proc_res);
+        }
+        self.check_mir_dump();
+    }
+
+    fn check_mir_dump(&self) {
+        let mut test_file_contents = String::new();
+        fs::File::open(self.testpaths.file.clone()).unwrap()
+                                                   .read_to_string(&mut test_file_contents)
+                                                   .unwrap();
+        if let Some(idx) =  test_file_contents.find("// END RUST SOURCE") {
+            let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len());
+            let tests_text_str = String::from(tests_text);
+            let mut curr_test : Option<&str> = None;
+            let mut curr_test_contents = Vec::new();
+            for l in tests_text_str.lines() {
+                debug!("line: {:?}", l);
+                if l.starts_with("// START ") {
+                    let (_, t) = l.split_at("// START ".len());
+                    curr_test = Some(t);
+                } else if l.starts_with("// END") {
+                    let (_, t) = l.split_at("// END ".len());
+                    if Some(t) != curr_test {
+                        panic!("mismatched START END test name");
+                    }
+                    self.compare_mir_test_output(curr_test.unwrap(), &curr_test_contents);
+                    curr_test = None;
+                    curr_test_contents.clear();
+                } else if l.is_empty() {
+                    // ignore
+                } else if l.starts_with("// ") {
+                    let (_, test_content) = l.split_at("// ".len());
+                    curr_test_contents.push(test_content);
+                }
+            }
+        }
+    }
+
+    fn compare_mir_test_output(&self, test_name: &str, expected_content: &Vec<&str>) {
+        let mut output_file = PathBuf::new();
+        output_file.push(self.get_mir_dump_dir());
+        output_file.push(test_name);
+        debug!("comparing the contests of: {:?}", output_file);
+        debug!("with: {:?}", expected_content);
+
+        let mut dumped_file = fs::File::open(output_file.clone()).unwrap();
+        let mut dumped_string = String::new();
+        dumped_file.read_to_string(&mut dumped_string).unwrap();
+        let mut dumped_lines = dumped_string.lines().filter(|l| !l.is_empty());
+        let mut expected_lines = expected_content.iter().filter(|l| !l.is_empty());
+
+        // We expect each non-empty line from expected_content to appear
+        // in the dump in order, but there may be extra lines interleaved
+        while let Some(expected_line) = expected_lines.next() {
+            let e_norm = normalize_mir_line(expected_line);
+            if e_norm.is_empty() {
+                continue;
+            };
+            let mut found = false;
+            while let Some(dumped_line) = dumped_lines.next() {
+                let d_norm = normalize_mir_line(dumped_line);
+                debug!("found: {:?}", d_norm);
+                debug!("expected: {:?}", e_norm);
+                if e_norm == d_norm {
+                    found = true;
+                    break;
+                };
+            }
+            if !found {
+                panic!("ran out of mir dump output to match against");
+            }
+        }
+    }
+
+    fn get_mir_dump_dir(&self) -> PathBuf {
+        let mut mir_dump_dir = PathBuf::from(self.config.build_base
+                                                    .as_path()
+                                                    .to_str()
+                                                    .unwrap());
+        debug!("input_file: {:?}", self.testpaths.file);
+        mir_dump_dir.push(self.testpaths.file.file_stem().unwrap().to_str().unwrap());
+        mir_dump_dir
+    }
+
     fn normalize_output(&self, output: &str) -> String {
         let parent_dir = self.testpaths.file.parent().unwrap();
         let parent_dir_str = parent_dir.display().to_string();
@@ -2274,3 +2384,12 @@ enum TargetLocation {
     ThisDirectory(PathBuf),
 }
 
+fn normalize_mir_line(line: &str) -> String {
+    let no_comments = if let Some(idx) = line.find("//") {
+        let (l, _) = line.split_at(idx);
+        l
+    } else {
+        line
+    };
+    no_comments.replace(char::is_whitespace, "")
+}