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: \
$(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)
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
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
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
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), \
pretty-rfail-full \
pretty-rfail \
pretty-pretty \
+ mir-opt \
$(NULL)
define DEF_CHECK_FOR_STAGE_AND_TARGET_AND_HOST
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");
(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> }),
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),
Source::CheckTidy { stage } => {
vec![self.tool_tidy(stage)]
}
+ Source::CheckMirOpt { compiler} |
Source::CheckPrettyRPass { compiler } |
Source::CheckPrettyRFail { compiler } |
Source::CheckRFail { compiler } |
"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"),
}
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
_ => 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));
--- /dev/null
+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.
+
--- /dev/null
+// 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
--- /dev/null
+// 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
Incremental,
RunMake,
Ui,
+ MirOpt,
}
impl FromStr for Mode {
"incremental" => Ok(Incremental),
"run-make" => Ok(RunMake),
"ui" => Ok(Ui),
+ "mir-opt" => Ok(MirOpt),
_ => Err(()),
}
}
Incremental => "incremental",
RunMake => "run-make",
Ui => "ui",
+ MirOpt => "mir-opt",
}, f)
}
}
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"),
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;
Incremental => self.run_incremental_test(),
RunMake => self.run_rmake_test(),
Ui => self.run_ui_test(),
+ MirOpt => self.run_mir_opt_test(),
}
}
.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 |
}
}
+ 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();
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, "")
+}