This folder contains tests for MIR optimizations.
-There are two test formats. One allows specifying a pattern to look for in the MIR, which also
-permits leaving placeholders, but requires you to manually change the pattern if anything changes.
-The other emits MIR to extra files that you can automatically update by specifying `--bless` on
-the command line (just like `ui` tests updating `.stderr` files).
+The `mir-opt` test format emits MIR to extra files that you can automatically update by specifying
+`--bless` on the command line (just like `ui` tests updating `.stderr` files).
# `--bless`able test format
```
// EMIT_MIR $file_name_of_some_mir_dump.before.mir
```
-
-# Inline test format
-
-```
-(arbitrary rust code)
-// END RUST SOURCE
-// START $file_name_of_some_mir_dump_0
-// $expected_line_0
-// (lines or elision)
-// $expected_line_N
-// END $file_name_of_some_mir_dump_0
-// (lines or elision)
-// START $file_name_of_some_mir_dump_N
-// $expected_line_0
-// (lines or elision)
-// $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 and after, but not between $expected_lines,
-should you want to skip lines, you must include an elision comment, of the form
-(as a regex) `//\s*...\s*`. The lines will be skipped lazily, that is, if there
-are two identical lines in the output that match the line after the elision
-comment, the first one will be matched.
-
-Examples:
-
-The following blocks will not match the one after it.
-
-```
-bb0: {
- StorageLive(_1);
- _1 = const true;
- StorageDead(_1);
-}
-```
-
-```
-bb0: {
- StorageLive(_1);
- _1 = const true;
- goto -> bb1
-}
-bb1: {
- StorageDead(_1);
- return;
-}
-```
-
-But this will match the one above,
-
-```
-bb0: {
- StorageLive(_1);
- _1 = const true;
- ...
- StorageDead(_1);
- ...
-}
-```
-
-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. There is an option to rustc
-that tells it to dump the mir into some directly (rather then always dumping to
-the current directory).
use std::collections::{HashMap, HashSet, VecDeque};
use std::env;
use std::ffi::{OsStr, OsString};
-use std::fmt;
use std::fs::{self, create_dir_all, File, OpenOptions};
use std::hash::{Hash, Hasher};
use std::io::prelude::*;
}
}
}
-
- 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![ExpectedLine::Elision];
- 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();
- curr_test_contents.push(ExpectedLine::Elision);
- } else if l.is_empty() {
- // ignore
- } else if l.starts_with("//") && l.split_at("//".len()).1.trim() == "..." {
- curr_test_contents.push(ExpectedLine::Elision)
- } else if l.starts_with("// ") {
- let (_, test_content) = l.split_at("// ".len());
- curr_test_contents.push(ExpectedLine::Text(test_content));
- }
- }
- }
}
fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) {
}
}
- fn compare_mir_test_output(&self, test_name: &str, expected_content: &[ExpectedLine<&str>]) {
- let mut output_file = PathBuf::new();
- output_file.push(self.get_mir_dump_dir());
- output_file.push(test_name);
- debug!("comparing the contents of: {:?}", output_file);
- debug!("with: {:?}", expected_content);
- if !output_file.exists() {
- panic!("Output file `{}` from test does not exist", output_file.display());
- }
- self.check_mir_test_timestamp(test_name, &output_file);
-
- let dumped_string = fs::read_to_string(&output_file).unwrap();
- let mut dumped_lines =
- dumped_string.lines().map(|l| nocomment_mir_line(l)).filter(|l| !l.is_empty());
- let mut expected_lines = expected_content
- .iter()
- .filter(|&l| if let &ExpectedLine::Text(l) = l { !l.is_empty() } else { true })
- .peekable();
-
- let compare = |expected_line, dumped_line| {
- let e_norm = normalize_mir_line(expected_line);
- let d_norm = normalize_mir_line(dumped_line);
- debug!("found: {:?}", d_norm);
- debug!("expected: {:?}", e_norm);
- e_norm == d_norm
- };
-
- let error = |expected_line, extra_msg| {
- let normalize_all = dumped_string
- .lines()
- .map(nocomment_mir_line)
- .filter(|l| !l.is_empty())
- .collect::<Vec<_>>()
- .join("\n");
- let f = |l: &ExpectedLine<_>| match l {
- &ExpectedLine::Elision => "... (elided)".into(),
- &ExpectedLine::Text(t) => t,
- };
- let expected_content =
- expected_content.iter().map(|l| f(l)).collect::<Vec<_>>().join("\n");
- panic!(
- "Did not find expected line, error: {}\n\
- Expected Line: {:?}\n\
- Test Name: {}\n\
- Expected:\n{}\n\
- Actual:\n{}",
- extra_msg, expected_line, test_name, expected_content, normalize_all
- );
- };
-
- // We expect each non-empty line to appear consecutively, non-consecutive lines
- // must be separated by at least one Elision
- let mut start_block_line = None;
- while let Some(dumped_line) = dumped_lines.next() {
- match expected_lines.next() {
- Some(&ExpectedLine::Text(expected_line)) => {
- let normalized_expected_line = normalize_mir_line(expected_line);
- if normalized_expected_line.contains(":{") {
- start_block_line = Some(expected_line);
- }
-
- if !compare(expected_line, dumped_line) {
- error!("{:?}", start_block_line);
- error(
- expected_line,
- format!(
- "Mismatch in lines\n\
- Current block: {}\n\
- Actual Line: {:?}",
- start_block_line.unwrap_or("None"),
- dumped_line
- ),
- );
- }
- }
- Some(&ExpectedLine::Elision) => {
- // skip any number of elisions in a row.
- while let Some(&&ExpectedLine::Elision) = expected_lines.peek() {
- expected_lines.next();
- }
- if let Some(&ExpectedLine::Text(expected_line)) = expected_lines.next() {
- let mut found = compare(expected_line, dumped_line);
- if found {
- continue;
- }
- while let Some(dumped_line) = dumped_lines.next() {
- found = compare(expected_line, dumped_line);
- if found {
- break;
- }
- }
- if !found {
- error(expected_line, "ran out of mir dump to match against".into());
- }
- }
- }
- None => {}
- }
- }
- }
-
fn get_mir_dump_dir(&self) -> PathBuf {
let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path());
debug!("input_file: {:?}", self.testpaths.file);
ThisDirectory(PathBuf),
}
-#[derive(Clone, PartialEq, Eq)]
-enum ExpectedLine<T: AsRef<str>> {
- Elision,
- Text(T),
-}
-
enum AllowUnused {
Yes,
No,
}
-impl<T> fmt::Debug for ExpectedLine<T>
-where
- T: AsRef<str> + fmt::Debug,
-{
- fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
- if let &ExpectedLine::Text(ref t) = self {
- write!(formatter, "{:?}", t)
- } else {
- write!(formatter, "\"...\" (Elision)")
- }
- }
-}
-
-fn normalize_mir_line(line: &str) -> String {
- nocomment_mir_line(line).replace(char::is_whitespace, "")
-}
-
-fn nocomment_mir_line(line: &str) -> &str {
- if let Some(idx) = line.find("//") {
- let (l, _) = line.split_at(idx);
- l.trim_end()
- } else {
- line
- }
-}
-
fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
use crate::read2::read2;
use std::mem::replace;