2020-04-26 15:58:53 +02:00
|
|
|
use std::collections::BTreeSet;
|
2021-07-12 18:22:15 +02:00
|
|
|
use std::fmt::Display;
|
2020-04-26 15:58:53 +02:00
|
|
|
use std::fmt::Write as _;
|
|
|
|
use std::fs;
|
|
|
|
use std::io::{self, Write};
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
use super::graphviz::write_mir_fn_graphviz;
|
2020-08-29 10:55:46 -07:00
|
|
|
use super::spanview::write_mir_fn_spanview;
|
2020-03-11 11:49:00 +01:00
|
|
|
use either::Either;
|
2016-11-08 14:02:55 +11:00
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
2021-05-11 12:26:53 +02:00
|
|
|
use rustc_hir::def_id::DefId;
|
2019-09-26 05:38:33 +00:00
|
|
|
use rustc_index::vec::Idx;
|
2020-03-29 17:19:48 +02:00
|
|
|
use rustc_middle::mir::interpret::{
|
2021-07-12 18:22:15 +02:00
|
|
|
read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer, Provenance,
|
2020-03-29 17:19:48 +02:00
|
|
|
};
|
|
|
|
use rustc_middle::mir::visit::Visitor;
|
2021-01-01 01:53:25 +01:00
|
|
|
use rustc_middle::mir::MirSource;
|
2020-03-29 17:19:48 +02:00
|
|
|
use rustc_middle::mir::*;
|
2022-02-20 21:56:20 -05:00
|
|
|
use rustc_middle::ty::{self, TyCtxt, TypeFoldable, TypeVisitor};
|
2020-03-31 18:16:47 +02:00
|
|
|
use rustc_target::abi::Size;
|
2022-02-20 21:56:20 -05:00
|
|
|
use std::ops::ControlFlow;
|
2015-12-29 20:06:19 -06:00
|
|
|
|
2018-12-04 12:46:10 +01:00
|
|
|
const INDENT: &str = " ";
|
2016-05-13 00:20:59 +02:00
|
|
|
/// Alignment for lining up comments following MIR statements
|
2017-11-20 16:45:12 -05:00
|
|
|
pub(crate) const ALIGN: usize = 40;
|
2015-12-29 20:06:19 -06:00
|
|
|
|
2017-09-28 00:14:34 -04:00
|
|
|
/// An indication of where we are in the control flow graph. Used for printing
|
|
|
|
/// extra information in `dump_mir`
|
|
|
|
pub enum PassWhere {
|
|
|
|
/// We have not started dumping the control flow graph, but we are about to.
|
|
|
|
BeforeCFG,
|
|
|
|
|
|
|
|
/// We just finished dumping the control flow graph. This is right before EOF
|
|
|
|
AfterCFG,
|
|
|
|
|
|
|
|
/// We are about to start dumping the given basic block.
|
|
|
|
BeforeBlock(BasicBlock),
|
|
|
|
|
2017-11-20 16:45:12 -05:00
|
|
|
/// We are just about to dump the given statement or terminator.
|
|
|
|
BeforeLocation(Location),
|
|
|
|
|
|
|
|
/// We just dumped the given statement or terminator.
|
|
|
|
AfterLocation(Location),
|
2018-06-29 06:47:12 -04:00
|
|
|
|
|
|
|
/// We just dumped the terminator for a block but not the closing `}`.
|
|
|
|
AfterTerminator(BasicBlock),
|
2017-09-28 00:14:34 -04:00
|
|
|
}
|
|
|
|
|
2016-03-22 16:05:28 -04:00
|
|
|
/// If the session is properly configured, dumps a human-readable
|
|
|
|
/// representation of the mir into:
|
|
|
|
///
|
2016-04-13 16:16:14 +05:30
|
|
|
/// ```text
|
2017-04-25 18:23:33 -04:00
|
|
|
/// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
|
2016-03-22 16:05:28 -04:00
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
|
|
|
|
/// where `<filter>` takes the following forms:
|
|
|
|
///
|
|
|
|
/// - `all` -- dump MIR for all fns, all passes, all everything
|
2017-11-17 14:08:47 -05:00
|
|
|
/// - a filter defined by a set of substrings combined with `&` and `|`
|
|
|
|
/// (`&` has higher precedence). At least one of the `|`-separated groups
|
|
|
|
/// must match; an `|`-separated group matches if all of its `&`-separated
|
|
|
|
/// substrings are matched.
|
|
|
|
///
|
|
|
|
/// Example:
|
|
|
|
///
|
|
|
|
/// - `nll` == match if `nll` appears in the name
|
|
|
|
/// - `foo & nll` == match if `foo` and `nll` both appear in the name
|
|
|
|
/// - `foo & nll | typeck` == match if `foo` and `nll` both appear in the name
|
|
|
|
/// or `typeck` appears in the name.
|
|
|
|
/// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name
|
|
|
|
/// or `typeck` and `bar` both appear in the name.
|
2021-10-14 15:26:59 -05:00
|
|
|
#[inline]
|
2019-06-14 00:48:52 +03:00
|
|
|
pub fn dump_mir<'tcx, F>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2018-02-23 10:15:26 -08:00
|
|
|
pass_num: Option<&dyn Display>,
|
2017-11-20 16:45:12 -05:00
|
|
|
pass_name: &str,
|
2018-02-23 10:15:26 -08:00
|
|
|
disambiguator: &dyn Display,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &Body<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data: F,
|
|
|
|
) where
|
2018-02-23 10:15:26 -08:00
|
|
|
F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
|
2017-09-28 00:14:34 -04:00
|
|
|
{
|
2020-10-04 11:01:38 -07:00
|
|
|
if !dump_enabled(tcx, pass_name, body.source.def_id()) {
|
2016-03-22 16:05:28 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-04 11:01:38 -07:00
|
|
|
dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data);
|
2017-04-25 18:23:33 -04:00
|
|
|
}
|
|
|
|
|
2020-03-28 14:07:07 -07:00
|
|
|
pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool {
|
2022-02-19 00:48:49 +01:00
|
|
|
let Some(ref filters) = tcx.sess.opts.debugging_opts.dump_mir else {
|
|
|
|
return false;
|
2017-04-27 16:48:48 -04:00
|
|
|
};
|
2022-02-16 13:04:48 -05:00
|
|
|
// see notes on #41697 below
|
|
|
|
let node_path = ty::print::with_forced_impl_filename_line!(tcx.def_path_str(def_id));
|
2018-07-23 15:32:57 +02:00
|
|
|
filters.split('|').any(|or_filter| {
|
|
|
|
or_filter.split('&').all(|and_filter| {
|
2021-05-31 20:15:52 +02:00
|
|
|
let and_filter_trimmed = and_filter.trim();
|
|
|
|
and_filter_trimmed == "all"
|
|
|
|
|| pass_name.contains(and_filter_trimmed)
|
|
|
|
|| node_path.contains(and_filter_trimmed)
|
2017-11-17 14:08:47 -05:00
|
|
|
})
|
2017-11-20 16:45:12 -05:00
|
|
|
})
|
2017-04-27 16:48:48 -04:00
|
|
|
}
|
|
|
|
|
2017-05-05 21:13:44 -04:00
|
|
|
// #41697 -- we use `with_forced_impl_filename_line()` because
|
2018-12-19 12:31:35 +02:00
|
|
|
// `def_path_str()` would otherwise trigger `type_of`, and this can
|
2017-05-05 21:13:44 -04:00
|
|
|
// run while we are already attempting to evaluate `type_of`.
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
fn dump_matched_mir_node<'tcx, F>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2018-02-23 10:15:26 -08:00
|
|
|
pass_num: Option<&dyn Display>,
|
2017-11-20 16:45:12 -05:00
|
|
|
pass_name: &str,
|
2018-02-23 10:15:26 -08:00
|
|
|
disambiguator: &dyn Display,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &Body<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
mut extra_data: F,
|
|
|
|
) where
|
2018-02-23 10:15:26 -08:00
|
|
|
F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
|
2017-09-28 00:14:34 -04:00
|
|
|
{
|
2019-02-08 20:08:08 +09:00
|
|
|
let _: io::Result<()> = try {
|
2020-10-04 11:01:38 -07:00
|
|
|
let mut file =
|
|
|
|
create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body.source)?;
|
2022-02-16 13:04:48 -05:00
|
|
|
// see notes on #41697 above
|
|
|
|
let def_path =
|
|
|
|
ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
|
2020-03-11 11:49:00 +01:00
|
|
|
write!(file, "// MIR for `{}", def_path)?;
|
2020-10-04 11:01:38 -07:00
|
|
|
match body.source.promoted {
|
2020-03-11 11:49:00 +01:00
|
|
|
None => write!(file, "`")?,
|
|
|
|
Some(promoted) => write!(file, "::{:?}`", promoted)?,
|
|
|
|
}
|
|
|
|
writeln!(file, " {} {}", disambiguator, pass_name)?;
|
2021-01-17 13:27:05 +01:00
|
|
|
if let Some(ref layout) = body.generator_layout() {
|
2020-06-19 20:19:19 -07:00
|
|
|
writeln!(file, "/* generator_layout = {:#?} */", layout)?;
|
2017-11-20 16:45:12 -05:00
|
|
|
}
|
2020-03-07 00:37:51 +01:00
|
|
|
writeln!(file)?;
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::BeforeCFG, &mut file)?;
|
2020-06-04 23:05:28 -04:00
|
|
|
write_user_type_annotations(tcx, body, &mut file)?;
|
2020-10-04 11:01:38 -07:00
|
|
|
write_mir_fn(tcx, body, &mut extra_data, &mut file)?;
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::AfterCFG, &mut file)?;
|
2018-05-10 12:02:19 -06:00
|
|
|
};
|
2017-11-20 16:45:12 -05:00
|
|
|
|
|
|
|
if tcx.sess.opts.debugging_opts.dump_mir_graphviz {
|
2019-02-08 20:08:08 +09:00
|
|
|
let _: io::Result<()> = try {
|
2017-11-22 17:33:26 -05:00
|
|
|
let mut file =
|
2020-10-04 11:01:38 -07:00
|
|
|
create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body.source)?;
|
|
|
|
write_mir_fn_graphviz(tcx, body, false, &mut file)?;
|
2018-05-10 12:02:19 -06:00
|
|
|
};
|
2017-11-20 16:45:12 -05:00
|
|
|
}
|
2020-08-29 10:55:46 -07:00
|
|
|
|
|
|
|
if let Some(spanview) = tcx.sess.opts.debugging_opts.dump_mir_spanview {
|
|
|
|
let _: io::Result<()> = try {
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
let file_basename =
|
|
|
|
dump_file_basename(tcx, pass_num, pass_name, disambiguator, body.source);
|
|
|
|
let mut file = create_dump_file_with_basename(tcx, &file_basename, "html")?;
|
2020-10-04 11:01:38 -07:00
|
|
|
if body.source.def_id().is_local() {
|
2020-10-05 09:26:30 -07:00
|
|
|
write_mir_fn_spanview(tcx, body, spanview, &file_basename, &mut file)?;
|
2020-08-29 10:55:46 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2017-11-20 16:45:12 -05:00
|
|
|
}
|
|
|
|
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
/// Returns the file basename portion (without extension) of a filename path
|
|
|
|
/// where we should dump a MIR representation output files.
|
2021-12-15 19:32:30 -05:00
|
|
|
fn dump_file_basename<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2018-02-23 10:15:26 -08:00
|
|
|
pass_num: Option<&dyn Display>,
|
2017-11-20 16:45:12 -05:00
|
|
|
pass_name: &str,
|
2018-02-23 10:15:26 -08:00
|
|
|
disambiguator: &dyn Display,
|
2019-02-09 12:19:04 +01:00
|
|
|
source: MirSource<'tcx>,
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
) -> String {
|
2017-11-10 19:20:35 +02:00
|
|
|
let promotion_id = match source.promoted {
|
|
|
|
Some(id) => format!("-{:?}", id),
|
2017-11-20 16:45:12 -05:00
|
|
|
None => String::new(),
|
2016-06-16 16:52:20 +03:00
|
|
|
};
|
|
|
|
|
2017-04-25 18:23:33 -04:00
|
|
|
let pass_num = if tcx.sess.opts.debugging_opts.dump_mir_exclude_pass_number {
|
2018-07-28 14:40:32 +02:00
|
|
|
String::new()
|
2017-04-25 18:23:33 -04:00
|
|
|
} else {
|
2017-04-27 16:48:48 -04:00
|
|
|
match pass_num {
|
2018-07-28 14:40:32 +02:00
|
|
|
None => ".-------".to_string(),
|
2017-11-10 13:58:06 +02:00
|
|
|
Some(pass_num) => format!(".{}", pass_num),
|
2017-04-27 16:48:48 -04:00
|
|
|
}
|
2017-04-25 18:23:33 -04:00
|
|
|
};
|
|
|
|
|
2020-07-27 21:22:43 +02:00
|
|
|
let crate_name = tcx.crate_name(source.def_id().krate);
|
2019-12-22 17:42:04 -05:00
|
|
|
let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
|
2019-02-03 13:31:50 +01:00
|
|
|
// All drop shims have the same DefId, so we have to add the type
|
|
|
|
// to get unique file names.
|
|
|
|
let shim_disambiguator = match source.instance {
|
|
|
|
ty::InstanceDef::DropGlue(_, Some(ty)) => {
|
|
|
|
// Unfortunately, pretty-printed typed are not very filename-friendly.
|
|
|
|
// We dome some filtering.
|
|
|
|
let mut s = ".".to_owned();
|
2019-12-22 17:42:04 -05:00
|
|
|
s.extend(ty.to_string().chars().filter_map(|c| match c {
|
|
|
|
' ' => None,
|
|
|
|
':' | '<' | '>' => Some('_'),
|
|
|
|
c => Some(c),
|
|
|
|
}));
|
2019-02-03 13:31:50 +01:00
|
|
|
s
|
|
|
|
}
|
|
|
|
_ => String::new(),
|
|
|
|
};
|
2017-11-20 16:45:12 -05:00
|
|
|
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
format!(
|
|
|
|
"{}.{}{}{}{}.{}.{}",
|
|
|
|
crate_name, item_name, shim_disambiguator, promotion_id, pass_num, pass_name, disambiguator,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the path to the filename where we should dump a given MIR.
|
|
|
|
/// Also used by other bits of code (e.g., NLL inference) that dump
|
|
|
|
/// graphviz data or other things.
|
|
|
|
fn dump_path(tcx: TyCtxt<'_>, basename: &str, extension: &str) -> PathBuf {
|
|
|
|
let mut file_path = PathBuf::new();
|
|
|
|
file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir));
|
|
|
|
|
|
|
|
let file_name = format!("{}.{}", basename, extension,);
|
2017-11-20 16:45:12 -05:00
|
|
|
|
2016-07-07 16:40:01 -07:00
|
|
|
file_path.push(&file_name);
|
2017-10-24 14:48:39 +03:00
|
|
|
|
2017-11-20 16:45:12 -05:00
|
|
|
file_path
|
|
|
|
}
|
|
|
|
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
/// Attempts to open the MIR dump file with the given name and extension.
|
|
|
|
fn create_dump_file_with_basename(
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
file_basename: &str,
|
|
|
|
extension: &str,
|
|
|
|
) -> io::Result<io::BufWriter<fs::File>> {
|
|
|
|
let file_path = dump_path(tcx, file_basename, extension);
|
|
|
|
if let Some(parent) = file_path.parent() {
|
|
|
|
fs::create_dir_all(parent).map_err(|e| {
|
|
|
|
io::Error::new(
|
|
|
|
e.kind(),
|
|
|
|
format!("IO error creating MIR dump directory: {:?}; {}", parent, e),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
}
|
|
|
|
Ok(io::BufWriter::new(fs::File::create(&file_path).map_err(|e| {
|
|
|
|
io::Error::new(e.kind(), format!("IO error creating MIR dump file: {:?}; {}", file_path, e))
|
|
|
|
})?))
|
|
|
|
}
|
|
|
|
|
2017-11-20 16:45:12 -05:00
|
|
|
/// Attempts to open a file where we should dump a given MIR or other
|
|
|
|
/// bit of MIR-related data. Used by `mir-dump`, but also by other
|
|
|
|
/// bits of code (e.g., NLL inference) that dump graphviz data or
|
|
|
|
/// other things, and hence takes the extension as an argument.
|
2021-12-15 19:32:30 -05:00
|
|
|
pub fn create_dump_file<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
extension: &str,
|
2018-02-23 10:15:26 -08:00
|
|
|
pass_num: Option<&dyn Display>,
|
2017-11-20 16:45:12 -05:00
|
|
|
pass_name: &str,
|
2018-02-23 10:15:26 -08:00
|
|
|
disambiguator: &dyn Display,
|
2019-02-09 12:19:04 +01:00
|
|
|
source: MirSource<'tcx>,
|
2019-09-10 13:43:54 +03:00
|
|
|
) -> io::Result<io::BufWriter<fs::File>> {
|
Updates to experimental coverage counter injection
This is a combination of 18 commits.
Commit #2:
Additional examples and some small improvements.
Commit #3:
fixed mir-opt non-mir extensions and spanview title elements
Corrected a fairly recent assumption in runtest.rs that all MIR dump
files end in .mir. (It was appending .mir to the graphviz .dot and
spanview .html file names when generating blessed output files. That
also left outdated files in the baseline alongside the files with the
incorrect names, which I've now removed.)
Updated spanview HTML title elements to match their content, replacing a
hardcoded and incorrect name that was left in accidentally when
originally submitted.
Commit #4:
added more test examples
also improved Makefiles with support for non-zero exit status and to
force validation of tests unless a specific test overrides it with a
specific comment.
Commit #5:
Fixed rare issues after testing on real-world crate
Commit #6:
Addressed PR feedback, and removed temporary -Zexperimental-coverage
-Zinstrument-coverage once again supports the latest capabilities of
LLVM instrprof coverage instrumentation.
Also fixed a bug in spanview.
Commit #7:
Fix closure handling, add tests for closures and inner items
And cleaned up other tests for consistency, and to make it more clear
where spans start/end by breaking up lines.
Commit #8:
renamed "typical" test results "expected"
Now that the `llvm-cov show` tests are improved to normally expect
matching actuals, and to allow individual tests to override that
expectation.
Commit #9:
test coverage of inline generic struct function
Commit #10:
Addressed review feedback
* Removed unnecessary Unreachable filter.
* Replaced a match wildcard with remining variants.
* Added more comments to help clarify the role of successors() in the
CFG traversal
Commit #11:
refactoring based on feedback
* refactored `fn coverage_spans()`.
* changed the way I expand an empty coverage span to improve performance
* fixed a typo that I had accidently left in, in visit.rs
Commit #12:
Optimized use of SourceMap and SourceFile
Commit #13:
Fixed a regression, and synched with upstream
Some generated test file names changed due to some new change upstream.
Commit #14:
Stripping out crate disambiguators from demangled names
These can vary depending on the test platform.
Commit #15:
Ignore llvm-cov show diff on test with generics, expand IO error message
Tests with generics produce llvm-cov show results with demangled names
that can include an unstable "crate disambiguator" (hex value). The
value changes when run in the Rust CI Windows environment. I added a sed
filter to strip them out (in a prior commit), but sed also appears to
fail in the same environment. Until I can figure out a workaround, I'm
just going to ignore this specific test result. I added a FIXME to
follow up later, but it's not that critical.
I also saw an error with Windows GNU, but the IO error did not
specify a path for the directory or file that triggered the error. I
updated the error messages to provide more info for next, time but also
noticed some other tests with similar steps did not fail. Looks
spurious.
Commit #16:
Modify rust-demangler to strip disambiguators by default
Commit #17:
Remove std::process::exit from coverage tests
Due to Issue #77553, programs that call std::process::exit() do not
generate coverage results on Windows MSVC.
Commit #18:
fix: test file paths exceeding Windows max path len
2020-09-01 16:15:17 -07:00
|
|
|
create_dump_file_with_basename(
|
|
|
|
tcx,
|
|
|
|
&dump_file_basename(tcx, pass_num, pass_name, disambiguator, source),
|
|
|
|
extension,
|
|
|
|
)
|
2016-03-22 16:05:28 -04:00
|
|
|
}
|
|
|
|
|
2015-12-29 20:06:19 -06:00
|
|
|
/// Write out a human-readable textual representation for the given MIR.
|
2019-06-14 00:48:52 +03:00
|
|
|
pub fn write_mir_pretty<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
single: Option<DefId>,
|
2018-02-23 10:15:26 -08:00
|
|
|
w: &mut dyn Write,
|
2017-11-20 16:45:12 -05:00
|
|
|
) -> io::Result<()> {
|
2019-12-22 17:42:04 -05:00
|
|
|
writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
|
|
|
|
writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
|
2017-03-03 22:42:49 -05:00
|
|
|
|
2016-05-13 00:20:59 +02:00
|
|
|
let mut first = true;
|
2017-04-25 15:56:02 -04:00
|
|
|
for def_id in dump_mir_def_ids(tcx, single) {
|
2016-05-13 00:20:59 +02:00
|
|
|
if first {
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
// Put empty lines between all items
|
2020-03-07 00:37:51 +01:00
|
|
|
writeln!(w)?;
|
2016-05-13 00:20:59 +02:00
|
|
|
}
|
|
|
|
|
2021-01-11 17:24:41 +00:00
|
|
|
let render_body = |w: &mut dyn Write, body| -> io::Result<()> {
|
2020-10-04 11:01:38 -07:00
|
|
|
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
|
2021-01-11 17:24:41 +00:00
|
|
|
|
|
|
|
for body in tcx.promoted_mir(def_id) {
|
|
|
|
writeln!(w)?;
|
|
|
|
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
};
|
2021-02-10 17:32:45 +03:00
|
|
|
|
|
|
|
// For `const fn` we want to render both the optimized MIR and the MIR for ctfe.
|
|
|
|
if tcx.is_const_fn_raw(def_id) {
|
|
|
|
render_body(w, tcx.optimized_mir(def_id))?;
|
|
|
|
writeln!(w)?;
|
|
|
|
writeln!(w, "// MIR FOR CTFE")?;
|
|
|
|
// Do not use `render_body`, as that would render the promoteds again, but these
|
|
|
|
// are shared between mir_for_ctfe and optimized_mir
|
|
|
|
write_mir_fn(tcx, tcx.mir_for_ctfe(def_id), &mut |_, _| Ok(()), w)?;
|
|
|
|
} else {
|
|
|
|
let instance_mir =
|
|
|
|
tcx.instance_mir(ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)));
|
|
|
|
render_body(w, instance_mir)?;
|
2016-05-03 00:26:41 +03:00
|
|
|
}
|
2015-12-29 20:06:19 -06:00
|
|
|
}
|
2016-03-12 19:07:00 +02:00
|
|
|
Ok(())
|
2015-12-29 20:06:19 -06:00
|
|
|
}
|
|
|
|
|
2020-04-17 13:24:33 -04:00
|
|
|
/// Write out a human-readable textual representation for the given function.
|
2019-06-14 00:48:52 +03:00
|
|
|
pub fn write_mir_fn<'tcx, F>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &Body<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data: &mut F,
|
2018-02-23 10:15:26 -08:00
|
|
|
w: &mut dyn Write,
|
2017-11-20 16:45:12 -05:00
|
|
|
) -> io::Result<()>
|
2017-09-28 00:14:34 -04:00
|
|
|
where
|
2018-02-23 10:15:26 -08:00
|
|
|
F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
|
2017-09-28 00:14:34 -04:00
|
|
|
{
|
2020-10-04 11:01:38 -07:00
|
|
|
write_mir_intro(tcx, body, w)?;
|
2019-06-03 18:26:48 -04:00
|
|
|
for block in body.basic_blocks().indices() {
|
2017-09-28 00:14:34 -04:00
|
|
|
extra_data(PassWhere::BeforeBlock(block), w)?;
|
2019-06-03 18:26:48 -04:00
|
|
|
write_basic_block(tcx, block, body, extra_data, w)?;
|
|
|
|
if block.index() + 1 != body.basic_blocks().len() {
|
2020-03-07 00:37:51 +01:00
|
|
|
writeln!(w)?;
|
2016-05-31 20:27:36 +03:00
|
|
|
}
|
2016-03-22 10:08:44 -04:00
|
|
|
}
|
|
|
|
|
2016-03-19 07:13:54 -04:00
|
|
|
writeln!(w, "}}")?;
|
2020-03-11 11:49:00 +01:00
|
|
|
|
|
|
|
write_allocations(tcx, body, w)?;
|
|
|
|
|
2016-03-19 07:13:54 -04:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2015-12-29 20:06:19 -06:00
|
|
|
/// Write out a human-readable textual representation for the given basic block.
|
2019-06-14 00:48:52 +03:00
|
|
|
pub fn write_basic_block<'tcx, F>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
block: BasicBlock,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &Body<'tcx>,
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data: &mut F,
|
2018-02-23 10:15:26 -08:00
|
|
|
w: &mut dyn Write,
|
2017-11-20 16:45:12 -05:00
|
|
|
) -> io::Result<()>
|
2017-09-28 00:14:34 -04:00
|
|
|
where
|
2018-02-23 10:15:26 -08:00
|
|
|
F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>,
|
2017-09-28 00:14:34 -04:00
|
|
|
{
|
2019-06-03 18:26:48 -04:00
|
|
|
let data = &body[block];
|
2015-12-29 20:06:19 -06:00
|
|
|
|
|
|
|
// Basic block label at the top.
|
2019-03-03 19:04:26 +00:00
|
|
|
let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
|
|
|
|
writeln!(w, "{}{:?}{}: {{", INDENT, block, cleanup_text)?;
|
2015-12-29 20:06:19 -06:00
|
|
|
|
|
|
|
// List of statements in the middle.
|
2020-03-06 19:28:44 +01:00
|
|
|
let mut current_location = Location { block, statement_index: 0 };
|
2015-12-29 20:06:19 -06:00
|
|
|
for statement in &data.statements {
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::BeforeLocation(current_location), w)?;
|
2019-06-03 18:26:48 -04:00
|
|
|
let indented_body = format!("{0}{0}{1:?};", INDENT, statement);
|
2017-11-20 16:45:12 -05:00
|
|
|
writeln!(
|
|
|
|
w,
|
2020-04-25 11:01:43 +02:00
|
|
|
"{:A$} // {}{}",
|
2019-06-03 18:26:48 -04:00
|
|
|
indented_body,
|
2020-04-25 11:01:43 +02:00
|
|
|
if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
|
2017-11-20 16:45:12 -05:00
|
|
|
comment(tcx, statement.source_info),
|
|
|
|
A = ALIGN,
|
|
|
|
)?;
|
2017-11-22 17:33:26 -05:00
|
|
|
|
|
|
|
write_extra(tcx, w, |visitor| {
|
2019-04-22 21:07:14 +01:00
|
|
|
visitor.visit_statement(statement, current_location);
|
2017-11-22 17:33:26 -05:00
|
|
|
})?;
|
|
|
|
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::AfterLocation(current_location), w)?;
|
2016-03-22 10:08:44 -04:00
|
|
|
|
|
|
|
current_location.statement_index += 1;
|
2015-12-29 20:06:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Terminator at the bottom.
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::BeforeLocation(current_location), w)?;
|
2016-05-13 00:20:59 +02:00
|
|
|
let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
|
2017-11-20 16:45:12 -05:00
|
|
|
writeln!(
|
|
|
|
w,
|
2020-04-25 11:01:43 +02:00
|
|
|
"{:A$} // {}{}",
|
2017-11-20 16:45:12 -05:00
|
|
|
indented_terminator,
|
2020-04-25 11:01:43 +02:00
|
|
|
if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
|
2017-11-20 16:45:12 -05:00
|
|
|
comment(tcx, data.terminator().source_info),
|
|
|
|
A = ALIGN,
|
|
|
|
)?;
|
2017-11-22 17:33:26 -05:00
|
|
|
|
|
|
|
write_extra(tcx, w, |visitor| {
|
2019-04-22 21:07:14 +01:00
|
|
|
visitor.visit_terminator(data.terminator(), current_location);
|
2017-11-22 17:33:26 -05:00
|
|
|
})?;
|
|
|
|
|
2017-11-20 16:45:12 -05:00
|
|
|
extra_data(PassWhere::AfterLocation(current_location), w)?;
|
2018-06-29 06:47:12 -04:00
|
|
|
extra_data(PassWhere::AfterTerminator(block), w)?;
|
2015-12-29 20:06:19 -06:00
|
|
|
|
2016-07-14 05:29:50 +09:00
|
|
|
writeln!(w, "{}}}", INDENT)
|
2015-12-29 20:06:19 -06:00
|
|
|
}
|
|
|
|
|
2017-11-22 17:33:26 -05:00
|
|
|
/// After we print the main statement, we sometimes dump extra
|
|
|
|
/// information. There's often a lot of little things "nuzzled up" in
|
|
|
|
/// a statement.
|
2019-06-14 01:32:15 +03:00
|
|
|
fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op: F) -> io::Result<()>
|
2017-11-17 14:08:47 -05:00
|
|
|
where
|
2019-06-14 00:48:52 +03:00
|
|
|
F: FnMut(&mut ExtraComments<'tcx>),
|
2017-11-22 17:33:26 -05:00
|
|
|
{
|
2020-06-04 23:05:28 -04:00
|
|
|
let mut extra_comments = ExtraComments { tcx, comments: vec![] };
|
2017-11-22 17:33:26 -05:00
|
|
|
visit_op(&mut extra_comments);
|
|
|
|
for comment in extra_comments.comments {
|
|
|
|
writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
struct ExtraComments<'tcx> {
|
2020-06-04 23:05:28 -04:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2017-11-22 17:33:26 -05:00
|
|
|
comments: Vec<String>,
|
|
|
|
}
|
|
|
|
|
2021-12-15 19:32:30 -05:00
|
|
|
impl<'tcx> ExtraComments<'tcx> {
|
2017-11-22 17:33:26 -05:00
|
|
|
fn push(&mut self, lines: &str) {
|
2018-07-23 15:32:57 +02:00
|
|
|
for line in lines.split('\n') {
|
2017-11-22 17:33:26 -05:00
|
|
|
self.comments.push(line.to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 14:13:38 +11:00
|
|
|
fn use_verbose<'tcx>(ty: Ty<'tcx>, fn_def: bool) -> bool {
|
|
|
|
match *ty.kind() {
|
2020-12-13 04:14:08 +00:00
|
|
|
ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
|
|
|
|
// Unit type
|
|
|
|
ty::Tuple(g_args) if g_args.is_empty() => false,
|
2022-02-07 16:06:31 +01:00
|
|
|
ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg, fn_def)),
|
2021-06-23 10:39:23 +08:00
|
|
|
ty::Array(ty, _) => use_verbose(ty, fn_def),
|
|
|
|
ty::FnDef(..) => fn_def,
|
2020-12-13 04:14:08 +00:00
|
|
|
_ => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-15 19:32:30 -05:00
|
|
|
impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
|
2017-11-22 17:33:26 -05:00
|
|
|
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
|
|
|
|
self.super_constant(constant, location);
|
2019-08-12 18:15:13 +03:00
|
|
|
let Constant { span, user_ty, literal } = constant;
|
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 14:13:38 +11:00
|
|
|
if use_verbose(literal.ty(), true) {
|
2021-06-23 10:39:23 +08:00
|
|
|
self.push("mir::Constant");
|
|
|
|
self.push(&format!(
|
|
|
|
"+ span: {}",
|
|
|
|
self.tcx.sess.source_map().span_to_embeddable_string(*span)
|
|
|
|
));
|
|
|
|
if let Some(user_ty) = user_ty {
|
|
|
|
self.push(&format!("+ user_ty: {:?}", user_ty));
|
|
|
|
}
|
|
|
|
match literal {
|
|
|
|
ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)),
|
|
|
|
ConstantKind::Val(val, ty) => {
|
|
|
|
// To keep the diffs small, we render this almost like we render ty::Const
|
|
|
|
self.push(&format!("+ literal: Const {{ ty: {}, val: Value({:?}) }}", ty, val))
|
2021-03-08 16:18:03 +00:00
|
|
|
}
|
2020-08-15 13:04:18 -04:00
|
|
|
}
|
2018-08-09 06:18:00 -04:00
|
|
|
}
|
2017-11-22 17:33:26 -05:00
|
|
|
}
|
|
|
|
|
2022-02-02 14:24:45 +11:00
|
|
|
fn visit_const(&mut self, constant: ty::Const<'tcx>, _: Location) {
|
2017-11-22 17:33:26 -05:00
|
|
|
self.super_const(constant);
|
2022-02-02 14:24:45 +11:00
|
|
|
let ty = constant.ty();
|
|
|
|
let val = constant.val();
|
|
|
|
if use_verbose(ty, false) {
|
2020-12-13 04:14:08 +00:00
|
|
|
self.push("ty::Const");
|
|
|
|
self.push(&format!("+ ty: {:?}", ty));
|
2021-03-29 16:35:21 +00:00
|
|
|
let val = match val {
|
|
|
|
ty::ConstKind::Param(p) => format!("Param({})", p),
|
|
|
|
ty::ConstKind::Infer(infer) => format!("Infer({:?})", infer),
|
|
|
|
ty::ConstKind::Bound(idx, var) => format!("Bound({:?}, {:?})", idx, var),
|
|
|
|
ty::ConstKind::Placeholder(ph) => format!("PlaceHolder({:?})", ph),
|
|
|
|
ty::ConstKind::Unevaluated(uv) => format!(
|
|
|
|
"Unevaluated({}, {:?}, {:?})",
|
|
|
|
self.tcx.def_path_str(uv.def.did),
|
2022-01-12 03:19:52 +00:00
|
|
|
uv.substs,
|
|
|
|
uv.promoted,
|
2021-03-29 16:35:21 +00:00
|
|
|
),
|
|
|
|
ty::ConstKind::Value(val) => format!("Value({:?})", val),
|
2021-07-21 22:43:19 +02:00
|
|
|
ty::ConstKind::Error(_) => "Error".to_string(),
|
2021-03-29 16:35:21 +00:00
|
|
|
};
|
|
|
|
self.push(&format!("+ val: {}", val));
|
2020-08-15 13:04:18 -04:00
|
|
|
}
|
2017-11-22 17:33:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
|
|
|
|
self.super_rvalue(rvalue, location);
|
2020-03-22 13:36:56 +01:00
|
|
|
if let Rvalue::Aggregate(kind, _) = rvalue {
|
|
|
|
match **kind {
|
2017-11-22 17:33:26 -05:00
|
|
|
AggregateKind::Closure(def_id, substs) => {
|
2018-07-28 14:40:32 +02:00
|
|
|
self.push("closure");
|
2017-10-07 16:36:28 +02:00
|
|
|
self.push(&format!("+ def_id: {:?}", def_id));
|
|
|
|
self.push(&format!("+ substs: {:#?}", substs));
|
2017-11-22 17:33:26 -05:00
|
|
|
}
|
|
|
|
|
2018-05-02 13:14:30 +02:00
|
|
|
AggregateKind::Generator(def_id, substs, movability) => {
|
2018-07-28 14:40:32 +02:00
|
|
|
self.push("generator");
|
2017-10-07 16:36:28 +02:00
|
|
|
self.push(&format!("+ def_id: {:?}", def_id));
|
|
|
|
self.push(&format!("+ substs: {:#?}", substs));
|
2018-05-02 10:17:12 +02:00
|
|
|
self.push(&format!("+ movability: {:?}", movability));
|
2017-11-22 17:33:26 -05:00
|
|
|
}
|
|
|
|
|
2018-08-09 12:24:52 -04:00
|
|
|
AggregateKind::Adt(_, _, _, Some(user_ty), _) => {
|
|
|
|
self.push("adt");
|
|
|
|
self.push(&format!("+ user_ty: {:?}", user_ty));
|
|
|
|
}
|
|
|
|
|
2017-11-22 17:33:26 -05:00
|
|
|
_ => {}
|
2020-03-22 13:36:56 +01:00
|
|
|
}
|
2017-11-22 17:33:26 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
|
2021-05-03 01:14:25 +01:00
|
|
|
format!("scope {} at {}", scope.index(), tcx.sess.source_map().span_to_embeddable_string(span))
|
2016-03-22 10:08:44 -04:00
|
|
|
}
|
|
|
|
|
2018-05-17 18:32:47 +03:00
|
|
|
/// Prints local variables in a scope tree.
|
2017-11-20 16:45:12 -05:00
|
|
|
fn write_scope_tree(
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'_>,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &Body<'_>,
|
2018-05-28 14:16:09 +03:00
|
|
|
scope_tree: &FxHashMap<SourceScope, Vec<SourceScope>>,
|
2018-02-23 10:15:26 -08:00
|
|
|
w: &mut dyn Write,
|
2018-05-28 14:16:09 +03:00
|
|
|
parent: SourceScope,
|
2017-11-20 16:45:12 -05:00
|
|
|
depth: usize,
|
|
|
|
) -> io::Result<()> {
|
2016-05-31 20:27:36 +03:00
|
|
|
let indent = depth * INDENT.len();
|
2016-05-13 23:36:50 +02:00
|
|
|
|
2018-05-16 18:58:54 +03:00
|
|
|
// Local variable debuginfo.
|
|
|
|
for var_debug_info in &body.var_debug_info {
|
|
|
|
if var_debug_info.source_info.scope != parent {
|
|
|
|
// Not declared in this scope.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let indented_debug_info = format!(
|
|
|
|
"{0:1$}debug {2} => {3:?};",
|
2020-05-30 15:02:32 -04:00
|
|
|
INDENT, indent, var_debug_info.name, var_debug_info.value,
|
2018-05-16 18:58:54 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"{0:1$} // in {2}",
|
|
|
|
indented_debug_info,
|
|
|
|
ALIGN,
|
|
|
|
comment(tcx, var_debug_info.source_info),
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
2020-04-25 11:01:43 +02:00
|
|
|
// Local variable types.
|
2019-06-03 18:26:48 -04:00
|
|
|
for (local, local_decl) in body.local_decls.iter_enumerated() {
|
2019-12-22 17:42:04 -05:00
|
|
|
if (1..body.arg_count + 1).contains(&local.index()) {
|
2018-05-17 18:32:47 +03:00
|
|
|
// Skip over argument locals, they're printed in the signature.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if local_decl.source_info.scope != parent {
|
|
|
|
// Not declared in this scope.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
let mut_str = if local_decl.mutability == Mutability::Mut { "mut " } else { "" };
|
2018-05-17 18:32:47 +03:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
let mut indented_decl =
|
|
|
|
format!("{0:1$}let {2}{3:?}: {4:?}", INDENT, indent, mut_str, local, local_decl.ty);
|
2020-05-06 12:41:15 +10:00
|
|
|
if let Some(user_ty) = &local_decl.user_ty {
|
|
|
|
for user_ty in user_ty.projections() {
|
|
|
|
write!(indented_decl, " as {:?}", user_ty).unwrap();
|
|
|
|
}
|
2018-05-17 18:32:47 +03:00
|
|
|
}
|
2020-09-10 13:57:40 +02:00
|
|
|
indented_decl.push(';');
|
2018-05-17 18:32:47 +03:00
|
|
|
|
2019-12-22 17:42:04 -05:00
|
|
|
let local_name =
|
2020-02-29 00:35:24 +01:00
|
|
|
if local == RETURN_PLACE { " return place".to_string() } else { String::new() };
|
2018-05-17 18:32:47 +03:00
|
|
|
|
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"{0:1$} //{2} in {3}",
|
|
|
|
indented_decl,
|
|
|
|
ALIGN,
|
|
|
|
local_name,
|
|
|
|
comment(tcx, local_decl.source_info),
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
2022-02-19 00:48:49 +01:00
|
|
|
let Some(children) = scope_tree.get(&parent) else {
|
|
|
|
return Ok(());
|
2016-05-13 23:36:50 +02:00
|
|
|
};
|
|
|
|
|
2016-05-31 20:27:36 +03:00
|
|
|
for &child in children {
|
2020-02-08 21:31:09 +02:00
|
|
|
let child_data = &body.source_scopes[child];
|
|
|
|
assert_eq!(child_data.parent_scope, Some(parent));
|
|
|
|
|
2020-09-21 06:52:37 +03:00
|
|
|
let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined {
|
|
|
|
(
|
|
|
|
format!(
|
|
|
|
" (inlined {}{})",
|
|
|
|
if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" },
|
|
|
|
callee
|
|
|
|
),
|
|
|
|
Some(callsite_span),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
(String::new(), None)
|
|
|
|
};
|
|
|
|
|
|
|
|
let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special);
|
|
|
|
|
|
|
|
if let Some(span) = span {
|
2020-02-08 21:31:09 +02:00
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"{0:1$} // at {2}",
|
|
|
|
indented_header,
|
|
|
|
ALIGN,
|
2021-05-03 01:14:25 +01:00
|
|
|
tcx.sess.source_map().span_to_embeddable_string(span),
|
2020-02-08 21:31:09 +02:00
|
|
|
)?;
|
|
|
|
} else {
|
2020-09-21 06:52:37 +03:00
|
|
|
writeln!(w, "{}", indented_header)?;
|
2020-02-08 21:31:09 +02:00
|
|
|
}
|
|
|
|
|
2019-06-03 18:26:48 -04:00
|
|
|
write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?;
|
2016-05-31 20:27:36 +03:00
|
|
|
writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
|
2016-03-22 10:08:44 -04:00
|
|
|
}
|
2016-05-13 00:20:59 +02:00
|
|
|
|
2016-03-22 10:08:44 -04:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2015-12-29 20:06:19 -06:00
|
|
|
/// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
|
|
|
|
/// local variables (both user-defined bindings and compiler temporaries).
|
2019-06-14 00:48:52 +03:00
|
|
|
pub fn write_mir_intro<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &Body<'_>,
|
2018-02-23 10:15:26 -08:00
|
|
|
w: &mut dyn Write,
|
2017-11-20 16:45:12 -05:00
|
|
|
) -> io::Result<()> {
|
2020-10-04 11:01:38 -07:00
|
|
|
write_mir_sig(tcx, body, w)?;
|
2018-01-19 13:56:48 -05:00
|
|
|
writeln!(w, "{{")?;
|
2016-05-31 20:27:36 +03:00
|
|
|
|
|
|
|
// construct a scope tree and write it out
|
2018-10-16 16:57:53 +02:00
|
|
|
let mut scope_tree: FxHashMap<SourceScope, Vec<SourceScope>> = Default::default();
|
2019-06-03 18:26:48 -04:00
|
|
|
for (index, scope_data) in body.source_scopes.iter().enumerate() {
|
2016-05-31 20:27:36 +03:00
|
|
|
if let Some(parent) = scope_data.parent_scope {
|
2019-12-22 17:42:04 -05:00
|
|
|
scope_tree.entry(parent).or_default().push(SourceScope::new(index));
|
2016-05-31 20:27:36 +03:00
|
|
|
} else {
|
|
|
|
// Only the argument scope has no parent, because it's the root.
|
2018-05-28 14:16:09 +03:00
|
|
|
assert_eq!(index, OUTERMOST_SOURCE_SCOPE.index());
|
2016-05-31 20:27:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-03 18:26:48 -04:00
|
|
|
write_scope_tree(tcx, body, &scope_tree, w, OUTERMOST_SOURCE_SCOPE, 1)?;
|
2016-05-31 20:27:36 +03:00
|
|
|
|
2016-09-25 01:38:27 +02:00
|
|
|
// Add an empty line before the first block is printed.
|
2020-03-07 00:37:51 +01:00
|
|
|
writeln!(w)?;
|
2016-09-25 01:38:27 +02:00
|
|
|
|
|
|
|
Ok(())
|
2016-04-28 16:13:44 +02:00
|
|
|
}
|
|
|
|
|
2020-03-11 11:49:00 +01:00
|
|
|
/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
|
|
|
|
/// allocations.
|
|
|
|
pub fn write_allocations<'tcx>(
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
body: &Body<'_>,
|
|
|
|
w: &mut dyn Write,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
|
2021-07-12 18:22:15 +02:00
|
|
|
alloc.relocations().values().map(|id| *id)
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
|
|
|
|
match val {
|
2021-07-12 20:29:05 +02:00
|
|
|
ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _size)) => {
|
2021-07-12 18:22:15 +02:00
|
|
|
Either::Left(Either::Left(std::iter::once(ptr.provenance)))
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
2020-11-01 16:57:03 +00:00
|
|
|
ConstValue::Scalar(interpret::Scalar::Int { .. }) => {
|
2020-03-11 11:49:00 +01:00
|
|
|
Either::Left(Either::Right(std::iter::empty()))
|
|
|
|
}
|
|
|
|
ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => {
|
|
|
|
Either::Right(alloc_ids_from_alloc(alloc))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
struct CollectAllocIds(BTreeSet<AllocId>);
|
2022-02-20 21:56:20 -05:00
|
|
|
impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds {
|
|
|
|
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
2022-02-14 16:48:05 +01:00
|
|
|
if let ty::ConstKind::Value(val) = c.val() {
|
2020-03-11 11:49:00 +01:00
|
|
|
self.0.extend(alloc_ids_from_const(val));
|
|
|
|
}
|
2022-02-20 21:56:20 -05:00
|
|
|
c.super_visit_with(self)
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
let mut visitor = CollectAllocIds(Default::default());
|
2022-02-20 21:56:20 -05:00
|
|
|
body.visit_with(&mut visitor);
|
2020-04-04 13:21:25 +02:00
|
|
|
// `seen` contains all seen allocations, including the ones we have *not* printed yet.
|
|
|
|
// The protocol is to first `insert` into `seen`, and only if that returns `true`
|
|
|
|
// then push to `todo`.
|
2020-03-11 11:49:00 +01:00
|
|
|
let mut seen = visitor.0;
|
|
|
|
let mut todo: Vec<_> = seen.iter().copied().collect();
|
|
|
|
while let Some(id) = todo.pop() {
|
2020-04-04 13:21:25 +02:00
|
|
|
let mut write_allocation_track_relocs =
|
2020-03-11 11:49:00 +01:00
|
|
|
|w: &mut dyn Write, alloc: &Allocation| -> io::Result<()> {
|
2020-04-04 13:21:25 +02:00
|
|
|
// `.rev()` because we are popping them from the back of the `todo` vector.
|
|
|
|
for id in alloc_ids_from_alloc(alloc).rev() {
|
|
|
|
if seen.insert(id) {
|
|
|
|
todo.push(id);
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
}
|
2020-07-28 19:16:09 +02:00
|
|
|
write!(w, "{}", display_allocation(tcx, alloc))
|
2020-03-11 11:49:00 +01:00
|
|
|
};
|
|
|
|
write!(w, "\n{}", id)?;
|
2020-04-24 12:53:18 +02:00
|
|
|
match tcx.get_global_alloc(id) {
|
2020-03-11 11:49:00 +01:00
|
|
|
// This can't really happen unless there are bugs, but it doesn't cost us anything to
|
|
|
|
// gracefully handle it and allow buggy rustc to be debugged via allocation printing.
|
|
|
|
None => write!(w, " (deallocated)")?,
|
|
|
|
Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?,
|
|
|
|
Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
|
2020-07-31 13:27:54 +02:00
|
|
|
match tcx.eval_static_initializer(did) {
|
|
|
|
Ok(alloc) => {
|
2020-03-11 11:49:00 +01:00
|
|
|
write!(w, " (static: {}, ", tcx.def_path_str(did))?;
|
2020-04-04 13:21:25 +02:00
|
|
|
write_allocation_track_relocs(w, alloc)?;
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
Err(_) => write!(
|
|
|
|
w,
|
|
|
|
" (static: {}, error during initializer evaluation)",
|
|
|
|
tcx.def_path_str(did)
|
|
|
|
)?,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(GlobalAlloc::Static(did)) => {
|
|
|
|
write!(w, " (extern static: {})", tcx.def_path_str(did))?
|
|
|
|
}
|
|
|
|
Some(GlobalAlloc::Memory(alloc)) => {
|
|
|
|
write!(w, " (")?;
|
2020-04-04 13:21:25 +02:00
|
|
|
write_allocation_track_relocs(w, alloc)?
|
2020-03-11 11:49:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
writeln!(w)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-04-04 13:21:25 +02:00
|
|
|
/// Dumps the size and metadata and content of an allocation to the given writer.
|
|
|
|
/// The expectation is that the caller first prints other relevant metadata, so the exact
|
|
|
|
/// format of this function is (*without* leading or trailing newline):
|
2020-10-04 19:08:40 -07:00
|
|
|
///
|
|
|
|
/// ```text
|
2020-04-04 13:21:25 +02:00
|
|
|
/// size: {}, align: {}) {
|
|
|
|
/// <bytes>
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// The byte format is similar to how hex editors print bytes. Each line starts with the address of
|
|
|
|
/// the start of the line, followed by all bytes in hex format (space separated).
|
|
|
|
/// If the allocation is small enough to fit into a single line, no start address is given.
|
|
|
|
/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
|
|
|
|
/// characters or characters whose value is larger than 127) with a `.`
|
|
|
|
/// This also prints relocations adequately.
|
2021-12-15 19:32:30 -05:00
|
|
|
pub fn display_allocation<'a, 'tcx, Tag, Extra>(
|
2020-04-04 13:21:25 +02:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2020-07-28 19:16:09 +02:00
|
|
|
alloc: &'a Allocation<Tag, Extra>,
|
|
|
|
) -> RenderAllocation<'a, 'tcx, Tag, Extra> {
|
|
|
|
RenderAllocation { tcx, alloc }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub struct RenderAllocation<'a, 'tcx, Tag, Extra> {
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
alloc: &'a Allocation<Tag, Extra>,
|
|
|
|
}
|
|
|
|
|
2021-12-15 19:48:59 -05:00
|
|
|
impl<'a, 'tcx, Tag: Provenance, Extra> std::fmt::Display
|
|
|
|
for RenderAllocation<'a, 'tcx, Tag, Extra>
|
|
|
|
{
|
2020-07-28 19:16:09 +02:00
|
|
|
fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
let RenderAllocation { tcx, alloc } = *self;
|
2021-05-17 13:30:16 +02:00
|
|
|
write!(w, "size: {}, align: {})", alloc.size().bytes(), alloc.align.bytes())?;
|
|
|
|
if alloc.size() == Size::ZERO {
|
2020-07-28 19:16:09 +02:00
|
|
|
// We are done.
|
|
|
|
return write!(w, " {{}}");
|
|
|
|
}
|
|
|
|
// Write allocation bytes.
|
|
|
|
writeln!(w, " {{")?;
|
|
|
|
write_allocation_bytes(tcx, alloc, w, " ")?;
|
|
|
|
write!(w, "}}")?;
|
|
|
|
Ok(())
|
2020-04-04 13:21:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 19:16:09 +02:00
|
|
|
fn write_allocation_endline(w: &mut dyn std::fmt::Write, ascii: &str) -> std::fmt::Result {
|
2020-03-11 11:49:00 +01:00
|
|
|
for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) {
|
|
|
|
write!(w, " ")?;
|
|
|
|
}
|
|
|
|
writeln!(w, " │ {}", ascii)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Number of bytes to print per allocation hex dump line.
|
|
|
|
const BYTES_PER_LINE: usize = 16;
|
|
|
|
|
|
|
|
/// Prints the line start address and returns the new line start address.
|
|
|
|
fn write_allocation_newline(
|
2020-07-28 19:16:09 +02:00
|
|
|
w: &mut dyn std::fmt::Write,
|
2020-03-11 11:49:00 +01:00
|
|
|
mut line_start: Size,
|
|
|
|
ascii: &str,
|
|
|
|
pos_width: usize,
|
|
|
|
prefix: &str,
|
2020-07-28 19:16:09 +02:00
|
|
|
) -> Result<Size, std::fmt::Error> {
|
2020-03-11 11:49:00 +01:00
|
|
|
write_allocation_endline(w, ascii)?;
|
|
|
|
line_start += Size::from_bytes(BYTES_PER_LINE);
|
|
|
|
write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?;
|
|
|
|
Ok(line_start)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
|
|
|
|
/// is only one line). Note that your prefix should contain a trailing space as the lines are
|
|
|
|
/// printed directly after it.
|
2021-12-15 19:32:30 -05:00
|
|
|
fn write_allocation_bytes<'tcx, Tag: Provenance, Extra>(
|
2020-03-11 11:49:00 +01:00
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
alloc: &Allocation<Tag, Extra>,
|
2020-07-28 19:16:09 +02:00
|
|
|
w: &mut dyn std::fmt::Write,
|
2020-03-11 11:49:00 +01:00
|
|
|
prefix: &str,
|
2020-07-28 19:16:09 +02:00
|
|
|
) -> std::fmt::Result {
|
2021-05-17 13:30:16 +02:00
|
|
|
let num_lines = alloc.size().bytes_usize().saturating_sub(BYTES_PER_LINE);
|
2020-03-11 11:49:00 +01:00
|
|
|
// Number of chars needed to represent all line numbers.
|
2021-06-14 21:32:42 +03:00
|
|
|
let pos_width = hex_number_length(alloc.size().bytes());
|
2020-03-11 11:49:00 +01:00
|
|
|
|
|
|
|
if num_lines > 0 {
|
|
|
|
write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?;
|
|
|
|
} else {
|
|
|
|
write!(w, "{}", prefix)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut i = Size::ZERO;
|
|
|
|
let mut line_start = Size::ZERO;
|
|
|
|
|
|
|
|
let ptr_size = tcx.data_layout.pointer_size;
|
|
|
|
|
|
|
|
let mut ascii = String::new();
|
|
|
|
|
|
|
|
let oversized_ptr = |target: &mut String, width| {
|
|
|
|
if target.len() > width {
|
|
|
|
write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-05-17 13:30:16 +02:00
|
|
|
while i < alloc.size() {
|
2020-03-11 11:49:00 +01:00
|
|
|
// The line start already has a space. While we could remove that space from the line start
|
|
|
|
// printing and unconditionally print a space here, that would cause the single-line case
|
|
|
|
// to have a single space before it, which looks weird.
|
|
|
|
if i != line_start {
|
|
|
|
write!(w, " ")?;
|
|
|
|
}
|
2021-07-12 18:22:15 +02:00
|
|
|
if let Some(&tag) = alloc.relocations().get(&i) {
|
2020-03-11 11:49:00 +01:00
|
|
|
// Memory with a relocation must be defined
|
|
|
|
let j = i.bytes_usize();
|
2020-08-08 07:53:47 -06:00
|
|
|
let offset = alloc
|
|
|
|
.inspect_with_uninit_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
|
2020-03-11 11:49:00 +01:00
|
|
|
let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
|
2020-04-26 15:58:53 +02:00
|
|
|
let offset = Size::from_bytes(offset);
|
2020-03-11 11:49:00 +01:00
|
|
|
let relocation_width = |bytes| bytes * 3;
|
2021-07-12 18:22:15 +02:00
|
|
|
let ptr = Pointer::new(tag, offset);
|
2020-04-26 15:58:53 +02:00
|
|
|
let mut target = format!("{:?}", ptr);
|
2020-04-26 18:14:16 +02:00
|
|
|
if target.len() > relocation_width(ptr_size.bytes_usize() - 1) {
|
|
|
|
// This is too long, try to save some space.
|
|
|
|
target = format!("{:#?}", ptr);
|
|
|
|
}
|
2020-03-11 11:49:00 +01:00
|
|
|
if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {
|
|
|
|
// This branch handles the situation where a relocation starts in the current line
|
|
|
|
// but ends in the next one.
|
|
|
|
let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);
|
|
|
|
let overflow = ptr_size - remainder;
|
|
|
|
let remainder_width = relocation_width(remainder.bytes_usize()) - 2;
|
|
|
|
let overflow_width = relocation_width(overflow.bytes_usize() - 1) + 1;
|
|
|
|
ascii.push('╾');
|
|
|
|
for _ in 0..remainder.bytes() - 1 {
|
|
|
|
ascii.push('─');
|
|
|
|
}
|
|
|
|
if overflow_width > remainder_width && overflow_width >= target.len() {
|
|
|
|
// The case where the relocation fits into the part in the next line
|
|
|
|
write!(w, "╾{0:─^1$}", "", remainder_width)?;
|
|
|
|
line_start =
|
|
|
|
write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
|
|
|
|
ascii.clear();
|
|
|
|
write!(w, "{0:─^1$}╼", target, overflow_width)?;
|
|
|
|
} else {
|
|
|
|
oversized_ptr(&mut target, remainder_width);
|
|
|
|
write!(w, "╾{0:─^1$}", target, remainder_width)?;
|
|
|
|
line_start =
|
|
|
|
write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
|
|
|
|
write!(w, "{0:─^1$}╼", "", overflow_width)?;
|
|
|
|
ascii.clear();
|
|
|
|
}
|
|
|
|
for _ in 0..overflow.bytes() - 1 {
|
|
|
|
ascii.push('─');
|
|
|
|
}
|
|
|
|
ascii.push('╼');
|
|
|
|
i += ptr_size;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
// This branch handles a relocation that starts and ends in the current line.
|
|
|
|
let relocation_width = relocation_width(ptr_size.bytes_usize() - 1);
|
|
|
|
oversized_ptr(&mut target, relocation_width);
|
|
|
|
ascii.push('╾');
|
|
|
|
write!(w, "╾{0:─^1$}╼", target, relocation_width)?;
|
|
|
|
for _ in 0..ptr_size.bytes() - 2 {
|
|
|
|
ascii.push('─');
|
|
|
|
}
|
|
|
|
ascii.push('╼');
|
|
|
|
i += ptr_size;
|
|
|
|
}
|
2020-04-22 03:20:40 -04:00
|
|
|
} else if alloc.init_mask().is_range_initialized(i, i + Size::from_bytes(1)).is_ok() {
|
2020-03-11 11:49:00 +01:00
|
|
|
let j = i.bytes_usize();
|
|
|
|
|
|
|
|
// Checked definedness (and thus range) and relocations. This access also doesn't
|
|
|
|
// influence interpreter execution but is only for debugging.
|
2020-08-08 07:53:47 -06:00
|
|
|
let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0];
|
2020-03-11 11:49:00 +01:00
|
|
|
write!(w, "{:02x}", c)?;
|
|
|
|
if c.is_ascii_control() || c >= 0x80 {
|
|
|
|
ascii.push('.');
|
|
|
|
} else {
|
|
|
|
ascii.push(char::from(c));
|
|
|
|
}
|
|
|
|
i += Size::from_bytes(1);
|
|
|
|
} else {
|
|
|
|
write!(w, "__")?;
|
|
|
|
ascii.push('░');
|
|
|
|
i += Size::from_bytes(1);
|
|
|
|
}
|
|
|
|
// Print a new line header if the next line still has some bytes to print.
|
2021-05-17 13:30:16 +02:00
|
|
|
if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size() {
|
2020-03-11 11:49:00 +01:00
|
|
|
line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
|
|
|
|
ascii.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write_allocation_endline(w, &ascii)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-10-04 11:01:38 -07:00
|
|
|
fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Result<()> {
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir::def::DefKind;
|
2019-02-02 16:59:20 +01:00
|
|
|
|
2020-10-04 11:01:38 -07:00
|
|
|
trace!("write_mir_sig: {:?}", body.source.instance);
|
|
|
|
let def_id = body.source.def_id();
|
|
|
|
let kind = tcx.def_kind(def_id);
|
2019-04-20 19:46:19 +03:00
|
|
|
let is_function = match kind {
|
2020-04-17 21:55:17 +03:00
|
|
|
DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
|
2020-10-04 11:01:38 -07:00
|
|
|
_ => tcx.is_closure(def_id),
|
2019-02-05 23:40:29 +01:00
|
|
|
};
|
2020-10-04 11:01:38 -07:00
|
|
|
match (kind, body.source.promoted) {
|
2019-02-05 21:49:53 +01:00
|
|
|
(_, Some(i)) => write!(w, "{:?} in ", i)?,
|
2020-04-17 21:55:17 +03:00
|
|
|
(DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?,
|
|
|
|
(DefKind::Static, _) => {
|
2020-10-04 11:01:38 -07:00
|
|
|
write!(w, "static {}", if tcx.is_mutable_static(def_id) { "mut " } else { "" })?
|
2019-12-22 17:42:04 -05:00
|
|
|
}
|
2019-02-05 23:40:29 +01:00
|
|
|
(_, _) if is_function => write!(w, "fn ")?,
|
2021-10-02 12:59:26 +01:00
|
|
|
(DefKind::AnonConst | DefKind::InlineConst, _) => {} // things like anon const, not an item
|
2019-04-20 19:46:19 +03:00
|
|
|
_ => bug!("Unexpected def kind {:?}", kind),
|
2016-05-03 00:26:41 +03:00
|
|
|
}
|
|
|
|
|
2022-02-16 13:04:48 -05:00
|
|
|
ty::print::with_forced_impl_filename_line! {
|
2017-11-20 16:45:12 -05:00
|
|
|
// see notes on #41697 elsewhere
|
2022-02-16 13:04:48 -05:00
|
|
|
write!(w, "{}", tcx.def_path_str(def_id))?
|
|
|
|
}
|
2015-12-29 20:06:19 -06:00
|
|
|
|
2020-10-04 11:01:38 -07:00
|
|
|
if body.source.promoted.is_none() && is_function {
|
2019-02-05 23:40:29 +01:00
|
|
|
write!(w, "(")?;
|
2015-12-29 20:06:19 -06:00
|
|
|
|
2019-02-05 23:40:29 +01:00
|
|
|
// fn argument types.
|
2019-06-03 18:26:48 -04:00
|
|
|
for (i, arg) in body.args_iter().enumerate() {
|
2019-02-05 23:40:29 +01:00
|
|
|
if i != 0 {
|
|
|
|
write!(w, ", ")?;
|
|
|
|
}
|
2019-06-24 17:46:09 +02:00
|
|
|
write!(w, "{:?}: {}", Place::from(arg), body.local_decls[arg].ty)?;
|
2016-12-26 14:34:03 +01:00
|
|
|
}
|
2019-02-05 23:40:29 +01:00
|
|
|
|
2019-06-03 18:26:48 -04:00
|
|
|
write!(w, ") -> {}", body.return_ty())?;
|
2019-02-05 23:40:29 +01:00
|
|
|
} else {
|
2019-06-03 18:26:48 -04:00
|
|
|
assert_eq!(body.arg_count, 0);
|
|
|
|
write!(w, ": {} =", body.return_ty())?;
|
2015-12-29 20:06:19 -06:00
|
|
|
}
|
2018-01-19 13:56:48 -05:00
|
|
|
|
2021-01-17 13:27:05 +01:00
|
|
|
if let Some(yield_ty) = body.yield_ty() {
|
2018-01-19 13:56:48 -05:00
|
|
|
writeln!(w)?;
|
|
|
|
writeln!(w, "yields {}", yield_ty)?;
|
|
|
|
}
|
|
|
|
|
2019-02-05 21:49:53 +01:00
|
|
|
write!(w, " ")?;
|
|
|
|
// Next thing that gets printed is the opening {
|
|
|
|
|
2018-01-19 13:56:48 -05:00
|
|
|
Ok(())
|
2016-04-28 16:13:44 +02:00
|
|
|
}
|
2015-12-29 20:06:19 -06:00
|
|
|
|
2020-06-04 23:05:28 -04:00
|
|
|
fn write_user_type_annotations(
|
|
|
|
tcx: TyCtxt<'_>,
|
|
|
|
body: &Body<'_>,
|
|
|
|
w: &mut dyn Write,
|
|
|
|
) -> io::Result<()> {
|
2019-06-03 18:26:48 -04:00
|
|
|
if !body.user_type_annotations.is_empty() {
|
2018-11-25 13:31:34 +01:00
|
|
|
writeln!(w, "| User Type Annotations")?;
|
|
|
|
}
|
2019-06-03 18:26:48 -04:00
|
|
|
for (index, annotation) in body.user_type_annotations.iter_enumerated() {
|
2020-06-04 23:05:28 -04:00
|
|
|
writeln!(
|
|
|
|
w,
|
|
|
|
"| {:?}: {:?} at {}",
|
|
|
|
index.index(),
|
|
|
|
annotation.user_ty,
|
2021-05-03 01:14:25 +01:00
|
|
|
tcx.sess.source_map().span_to_embeddable_string(annotation.span)
|
2020-06-04 23:05:28 -04:00
|
|
|
)?;
|
2018-11-25 13:31:34 +01:00
|
|
|
}
|
2019-06-03 18:26:48 -04:00
|
|
|
if !body.user_type_annotations.is_empty() {
|
2018-11-25 13:31:34 +01:00
|
|
|
writeln!(w, "|")?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
|
2020-04-18 12:58:53 +01:00
|
|
|
if let Some(i) = single {
|
|
|
|
vec![i]
|
|
|
|
} else {
|
2021-05-11 12:26:53 +02:00
|
|
|
tcx.mir_keys(()).iter().map(|def_id| def_id.to_def_id()).collect()
|
2020-04-18 12:58:53 +01:00
|
|
|
}
|
2017-04-25 15:56:02 -04:00
|
|
|
}
|
2021-06-14 21:32:42 +03:00
|
|
|
|
|
|
|
/// Calc converted u64 decimal into hex and return it's length in chars
|
|
|
|
///
|
|
|
|
/// ```ignore (cannot-test-private-function)
|
|
|
|
/// assert_eq!(1, hex_number_length(0));
|
|
|
|
/// assert_eq!(1, hex_number_length(1));
|
|
|
|
/// assert_eq!(2, hex_number_length(16));
|
|
|
|
/// ```
|
|
|
|
fn hex_number_length(x: u64) -> usize {
|
|
|
|
if x == 0 {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
let mut length = 0;
|
|
|
|
let mut x_left = x;
|
|
|
|
while x_left > 0 {
|
|
|
|
x_left /= 16;
|
|
|
|
length += 1;
|
|
|
|
}
|
|
|
|
length
|
|
|
|
}
|