Rollup merge of #145421 - nnethercote:dump_mir-cleanups, r=davidtwco
`dump_mir` cleanups I found this code hard to read, so I cleaned it up. Details in individual commits. r? ``@davidtwco``
This commit is contained in:
commit
d17b3fb220
14 changed files with 474 additions and 519 deletions
|
|
@ -8,8 +8,8 @@ use std::str::FromStr;
|
|||
use polonius_engine::{Algorithm, AllFacts, Output};
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_index::IndexSlice;
|
||||
use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
|
||||
use rustc_middle::mir::{Body, PassWhere, Promoted, create_dump_file, dump_enabled, dump_mir};
|
||||
use rustc_middle::mir::pretty::PrettyPrintMirOptions;
|
||||
use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
|
|
@ -68,7 +68,9 @@ pub(crate) fn replace_regions_in_mir<'tcx>(
|
|||
// Replace all remaining regions with fresh inference variables.
|
||||
renumber::renumber_mir(infcx, body, promoted);
|
||||
|
||||
dump_mir(infcx.tcx, false, "renumber", &0, body, |_, _| Ok(()));
|
||||
if let Some(dumper) = MirDumper::new(infcx.tcx, "renumber", body) {
|
||||
dumper.dump_mir(body);
|
||||
}
|
||||
|
||||
universal_regions
|
||||
}
|
||||
|
|
@ -175,9 +177,7 @@ pub(super) fn dump_nll_mir<'tcx>(
|
|||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
let tcx = infcx.tcx;
|
||||
if !dump_enabled(tcx, "nll", body.source.def_id()) {
|
||||
return;
|
||||
}
|
||||
let Some(dumper) = MirDumper::new(tcx, "nll", body) else { return };
|
||||
|
||||
// We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in
|
||||
// #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example,
|
||||
|
|
@ -188,27 +188,24 @@ pub(super) fn dump_nll_mir<'tcx>(
|
|||
MirIncludeSpans::On | MirIncludeSpans::Nll
|
||||
),
|
||||
};
|
||||
dump_mir_with_options(
|
||||
tcx,
|
||||
false,
|
||||
"nll",
|
||||
&0,
|
||||
body,
|
||||
|pass_where, out| {
|
||||
emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
|
||||
},
|
||||
options,
|
||||
);
|
||||
|
||||
let extra_data = &|pass_where, out: &mut dyn std::io::Write| {
|
||||
emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
|
||||
};
|
||||
|
||||
let dumper = dumper.set_extra_data(extra_data).set_options(options);
|
||||
|
||||
dumper.dump_mir(body);
|
||||
|
||||
// Also dump the region constraint graph as a graphviz file.
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = create_dump_file(tcx, "regioncx.all.dot", false, "nll", &0, body)?;
|
||||
let mut file = dumper.create_dump_file("regioncx.all.dot", body)?;
|
||||
regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?;
|
||||
};
|
||||
|
||||
// Also dump the region constraint SCC graph as a graphviz file.
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = create_dump_file(tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
|
||||
let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?;
|
||||
regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ use std::io;
|
|||
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::pretty::{
|
||||
PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer,
|
||||
};
|
||||
use rustc_middle::mir::pretty::{MirDumper, PassWhere, PrettyPrintMirOptions};
|
||||
use rustc_middle::mir::{Body, Location};
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
|
@ -33,22 +31,41 @@ pub(crate) fn dump_polonius_mir<'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
if !dump_enabled(tcx, "polonius", body.source.def_id()) {
|
||||
return;
|
||||
}
|
||||
let Some(dumper) = MirDumper::new(tcx, "polonius", body) else { return };
|
||||
|
||||
let polonius_diagnostics =
|
||||
polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`");
|
||||
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?;
|
||||
emit_polonius_dump(
|
||||
let extra_data = &|pass_where, out: &mut dyn io::Write| {
|
||||
emit_polonius_mir(
|
||||
tcx,
|
||||
regioncx,
|
||||
closure_region_requirements,
|
||||
borrow_set,
|
||||
&polonius_diagnostics.localized_outlives_constraints,
|
||||
pass_where,
|
||||
out,
|
||||
)
|
||||
};
|
||||
// We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
|
||||
// mir-include-spans` on the CLI still has priority.
|
||||
let options = PrettyPrintMirOptions {
|
||||
include_extra_comments: matches!(
|
||||
tcx.sess.opts.unstable_opts.mir_include_spans,
|
||||
MirIncludeSpans::On | MirIncludeSpans::Nll
|
||||
),
|
||||
};
|
||||
|
||||
let dumper = dumper.set_extra_data(extra_data).set_options(options);
|
||||
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = dumper.create_dump_file("html", body)?;
|
||||
emit_polonius_dump(
|
||||
&dumper,
|
||||
body,
|
||||
regioncx,
|
||||
borrow_set,
|
||||
&polonius_diagnostics.localized_outlives_constraints,
|
||||
closure_region_requirements,
|
||||
&mut file,
|
||||
)?;
|
||||
};
|
||||
|
|
@ -61,12 +78,11 @@ pub(crate) fn dump_polonius_mir<'tcx>(
|
|||
/// - a mermaid graph of the NLL regions and the constraints between them
|
||||
/// - a mermaid graph of the NLL SCCs and the constraints between them
|
||||
fn emit_polonius_dump<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
dumper: &MirDumper<'_, '_, 'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
out: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
// Prepare the HTML dump file prologue.
|
||||
|
|
@ -79,15 +95,7 @@ fn emit_polonius_dump<'tcx>(
|
|||
writeln!(out, "<div>")?;
|
||||
writeln!(out, "Raw MIR dump")?;
|
||||
writeln!(out, "<pre><code>")?;
|
||||
emit_html_mir(
|
||||
tcx,
|
||||
body,
|
||||
regioncx,
|
||||
borrow_set,
|
||||
&localized_outlives_constraints,
|
||||
closure_region_requirements,
|
||||
out,
|
||||
)?;
|
||||
emit_html_mir(dumper, body, out)?;
|
||||
writeln!(out, "</code></pre>")?;
|
||||
writeln!(out, "</div>")?;
|
||||
|
||||
|
|
@ -116,7 +124,7 @@ fn emit_polonius_dump<'tcx>(
|
|||
writeln!(out, "<div>")?;
|
||||
writeln!(out, "NLL regions")?;
|
||||
writeln!(out, "<pre class='mermaid'>")?;
|
||||
emit_mermaid_nll_regions(tcx, regioncx, out)?;
|
||||
emit_mermaid_nll_regions(dumper.tcx(), regioncx, out)?;
|
||||
writeln!(out, "</pre>")?;
|
||||
writeln!(out, "</div>")?;
|
||||
|
||||
|
|
@ -124,7 +132,7 @@ fn emit_polonius_dump<'tcx>(
|
|||
writeln!(out, "<div>")?;
|
||||
writeln!(out, "NLL SCCs")?;
|
||||
writeln!(out, "<pre class='mermaid'>")?;
|
||||
emit_mermaid_nll_sccs(tcx, regioncx, out)?;
|
||||
emit_mermaid_nll_sccs(dumper.tcx(), regioncx, out)?;
|
||||
writeln!(out, "</pre>")?;
|
||||
writeln!(out, "</div>")?;
|
||||
|
||||
|
|
@ -149,45 +157,14 @@ fn emit_polonius_dump<'tcx>(
|
|||
|
||||
/// Emits the polonius MIR, as escaped HTML.
|
||||
fn emit_html_mir<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
dumper: &MirDumper<'_, '_, 'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
out: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
// Buffer the regular MIR dump to be able to escape it.
|
||||
let mut buffer = Vec::new();
|
||||
|
||||
// We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
|
||||
// mir-include-spans` on the CLI still has priority.
|
||||
let options = PrettyPrintMirOptions {
|
||||
include_extra_comments: matches!(
|
||||
tcx.sess.opts.unstable_opts.mir_include_spans,
|
||||
MirIncludeSpans::On | MirIncludeSpans::Nll
|
||||
),
|
||||
};
|
||||
|
||||
dump_mir_to_writer(
|
||||
tcx,
|
||||
"polonius",
|
||||
&0,
|
||||
body,
|
||||
&mut buffer,
|
||||
|pass_where, out| {
|
||||
emit_polonius_mir(
|
||||
tcx,
|
||||
regioncx,
|
||||
closure_region_requirements,
|
||||
borrow_set,
|
||||
localized_outlives_constraints,
|
||||
pass_where,
|
||||
out,
|
||||
)
|
||||
},
|
||||
options,
|
||||
)?;
|
||||
dumper.dump_mir_to_writer(body, &mut buffer)?;
|
||||
|
||||
// Escape the handful of characters that need it. We don't need to be particularly efficient:
|
||||
// we're actually writing into a buffered writer already. Note that MIR dumps are valid UTF-8.
|
||||
|
|
|
|||
|
|
@ -44,9 +44,8 @@ pub(crate) fn codegen_fn<'tcx>(
|
|||
let _mir_guard = crate::PrintOnPanic(|| {
|
||||
let mut buf = Vec::new();
|
||||
with_no_trimmed_paths!({
|
||||
use rustc_middle::mir::pretty;
|
||||
let options = pretty::PrettyPrintMirOptions::from_cli(tcx);
|
||||
pretty::write_mir_fn(tcx, mir, &mut |_, _| Ok(()), &mut buf, options).unwrap();
|
||||
let writer = pretty::MirWriter::new(tcx);
|
||||
writer.write_mir_fn(mir, &mut buf).unwrap();
|
||||
});
|
||||
String::from_utf8_lossy(&buf).into_owned()
|
||||
});
|
||||
|
|
|
|||
|
|
@ -62,9 +62,7 @@ pub use terminator::*;
|
|||
|
||||
pub use self::generic_graph::graphviz_safe_def_name;
|
||||
pub use self::graphviz::write_mir_graphviz;
|
||||
pub use self::pretty::{
|
||||
PassWhere, create_dump_file, display_allocation, dump_enabled, dump_mir, write_mir_pretty,
|
||||
};
|
||||
pub use self::pretty::{MirDumper, PassWhere, display_allocation, write_mir_pretty};
|
||||
|
||||
/// Types for locals
|
||||
pub type LocalDecls<'tcx> = IndexSlice<Local, LocalDecl<'tcx>>;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ pub enum PassWhere {
|
|||
}
|
||||
|
||||
/// Cosmetic options for pretty-printing the MIR contents, gathered from the CLI. Each pass can
|
||||
/// override these when dumping its own specific MIR information with [`dump_mir_with_options`].
|
||||
/// override these when dumping its own specific MIR information with `dump_mir`.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PrettyPrintMirOptions {
|
||||
/// Whether to include extra comments, like span info. From `-Z mir-include-spans`.
|
||||
|
|
@ -58,277 +58,253 @@ impl PrettyPrintMirOptions {
|
|||
}
|
||||
}
|
||||
|
||||
/// If the session is properly configured, dumps a human-readable representation of the MIR (with
|
||||
/// default pretty-printing options) into:
|
||||
///
|
||||
/// ```text
|
||||
/// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
|
||||
/// ```
|
||||
///
|
||||
/// 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
|
||||
/// - 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.
|
||||
#[inline]
|
||||
pub fn dump_mir<'tcx, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
pass_num: bool,
|
||||
pass_name: &str,
|
||||
disambiguator: &dyn Display,
|
||||
body: &Body<'tcx>,
|
||||
extra_data: F,
|
||||
) where
|
||||
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
||||
{
|
||||
dump_mir_with_options(
|
||||
tcx,
|
||||
pass_num,
|
||||
pass_name,
|
||||
disambiguator,
|
||||
body,
|
||||
extra_data,
|
||||
PrettyPrintMirOptions::from_cli(tcx),
|
||||
);
|
||||
/// Manages MIR dumping, which is MIR writing done to a file with a specific name. In particular,
|
||||
/// it makes it impossible to dump MIR to one of these files when it hasn't been requested from the
|
||||
/// command line. Layered on top of `MirWriter`, which does the actual writing.
|
||||
pub struct MirDumper<'dis, 'de, 'tcx> {
|
||||
show_pass_num: bool,
|
||||
pass_name: &'static str,
|
||||
disambiguator: &'dis dyn Display,
|
||||
writer: MirWriter<'de, 'tcx>,
|
||||
}
|
||||
|
||||
/// If the session is properly configured, dumps a human-readable representation of the MIR, with
|
||||
/// the given [pretty-printing options][PrettyPrintMirOptions].
|
||||
///
|
||||
/// See [`dump_mir`] for more details.
|
||||
///
|
||||
#[inline]
|
||||
pub fn dump_mir_with_options<'tcx, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
pass_num: bool,
|
||||
pass_name: &str,
|
||||
disambiguator: &dyn Display,
|
||||
body: &Body<'tcx>,
|
||||
extra_data: F,
|
||||
options: PrettyPrintMirOptions,
|
||||
) where
|
||||
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
||||
{
|
||||
if !dump_enabled(tcx, pass_name, body.source.def_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data, options);
|
||||
}
|
||||
|
||||
pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool {
|
||||
let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir else {
|
||||
return false;
|
||||
};
|
||||
// see notes on #41697 below
|
||||
let node_path = ty::print::with_forced_impl_filename_line!(tcx.def_path_str(def_id));
|
||||
filters.split('|').any(|or_filter| {
|
||||
or_filter.split('&').all(|and_filter| {
|
||||
let and_filter_trimmed = and_filter.trim();
|
||||
and_filter_trimmed == "all"
|
||||
|| pass_name.contains(and_filter_trimmed)
|
||||
|| node_path.contains(and_filter_trimmed)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// #41697 -- we use `with_forced_impl_filename_line()` because
|
||||
// `def_path_str()` would otherwise trigger `type_of`, and this can
|
||||
// run while we are already attempting to evaluate `type_of`.
|
||||
|
||||
/// Most use-cases of dumping MIR should use the [dump_mir] entrypoint instead, which will also
|
||||
/// check if dumping MIR is enabled, and if this body matches the filters passed on the CLI.
|
||||
///
|
||||
/// That being said, if the above requirements have been validated already, this function is where
|
||||
/// most of the MIR dumping occurs, if one needs to export it to a file they have created with
|
||||
/// [create_dump_file], rather than to a new file created as part of [dump_mir], or to stdout/stderr
|
||||
/// for debugging purposes.
|
||||
pub fn dump_mir_to_writer<'tcx, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
pass_name: &str,
|
||||
disambiguator: &dyn Display,
|
||||
body: &Body<'tcx>,
|
||||
w: &mut dyn io::Write,
|
||||
mut extra_data: F,
|
||||
options: PrettyPrintMirOptions,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
||||
{
|
||||
// see notes on #41697 above
|
||||
let def_path =
|
||||
ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
|
||||
// ignore-tidy-odd-backticks the literal below is fine
|
||||
write!(w, "// MIR for `{def_path}")?;
|
||||
match body.source.promoted {
|
||||
None => write!(w, "`")?,
|
||||
Some(promoted) => write!(w, "::{promoted:?}`")?,
|
||||
}
|
||||
writeln!(w, " {disambiguator} {pass_name}")?;
|
||||
if let Some(ref layout) = body.coroutine_layout_raw() {
|
||||
writeln!(w, "/* coroutine_layout = {layout:#?} */")?;
|
||||
}
|
||||
writeln!(w)?;
|
||||
extra_data(PassWhere::BeforeCFG, w)?;
|
||||
write_user_type_annotations(tcx, body, w)?;
|
||||
write_mir_fn(tcx, body, &mut extra_data, w, options)?;
|
||||
extra_data(PassWhere::AfterCFG, w)
|
||||
}
|
||||
|
||||
fn dump_matched_mir_node<'tcx, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
pass_num: bool,
|
||||
pass_name: &str,
|
||||
disambiguator: &dyn Display,
|
||||
body: &Body<'tcx>,
|
||||
extra_data: F,
|
||||
options: PrettyPrintMirOptions,
|
||||
) where
|
||||
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
||||
{
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?;
|
||||
dump_mir_to_writer(tcx, pass_name, disambiguator, body, &mut file, extra_data, options)?;
|
||||
};
|
||||
|
||||
if tcx.sess.opts.unstable_opts.dump_mir_graphviz {
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body)?;
|
||||
write_mir_fn_graphviz(tcx, body, false, &mut file)?;
|
||||
impl<'dis, 'de, 'tcx> MirDumper<'dis, 'de, 'tcx> {
|
||||
// If dumping should be performed (e.g. because it was requested on the
|
||||
// CLI), returns a `MirDumper` with default values for the following fields:
|
||||
// - `show_pass_num`: `false`
|
||||
// - `disambiguator`: `&0`
|
||||
// - `writer.extra_data`: a no-op
|
||||
// - `writer.options`: default options derived from CLI flags
|
||||
pub fn new(tcx: TyCtxt<'tcx>, pass_name: &'static str, body: &Body<'tcx>) -> Option<Self> {
|
||||
let dump_enabled = if let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir {
|
||||
// see notes on #41697 below
|
||||
let node_path =
|
||||
ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
|
||||
filters.split('|').any(|or_filter| {
|
||||
or_filter.split('&').all(|and_filter| {
|
||||
let and_filter_trimmed = and_filter.trim();
|
||||
and_filter_trimmed == "all"
|
||||
|| pass_name.contains(and_filter_trimmed)
|
||||
|| node_path.contains(and_filter_trimmed)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
dump_enabled.then_some(MirDumper {
|
||||
show_pass_num: false,
|
||||
pass_name,
|
||||
disambiguator: &0,
|
||||
writer: MirWriter::new(tcx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
extension: &str,
|
||||
pass_num: bool,
|
||||
pass_name: &str,
|
||||
disambiguator: &dyn Display,
|
||||
body: &Body<'tcx>,
|
||||
) -> PathBuf {
|
||||
let source = body.source;
|
||||
let promotion_id = match source.promoted {
|
||||
Some(id) => format!("-{id:?}"),
|
||||
None => String::new(),
|
||||
};
|
||||
pub fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.writer.tcx
|
||||
}
|
||||
|
||||
let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
|
||||
String::new()
|
||||
} else if pass_num {
|
||||
let (dialect_index, phase_index) = body.phase.index();
|
||||
format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count)
|
||||
} else {
|
||||
".-------".to_string()
|
||||
};
|
||||
#[must_use]
|
||||
pub fn set_show_pass_num(mut self) -> Self {
|
||||
self.show_pass_num = true;
|
||||
self
|
||||
}
|
||||
|
||||
let crate_name = tcx.crate_name(source.def_id().krate);
|
||||
let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
|
||||
// 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::InstanceKind::DropGlue(_, Some(ty)) => {
|
||||
// Unfortunately, pretty-printed typed are not very filename-friendly.
|
||||
// We dome some filtering.
|
||||
let mut s = ".".to_owned();
|
||||
s.extend(ty.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s
|
||||
}
|
||||
ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
|
||||
let mut s = ".".to_owned();
|
||||
s.extend(ty.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s
|
||||
}
|
||||
ty::InstanceKind::AsyncDropGlue(_, ty) => {
|
||||
let ty::Coroutine(_, args) = ty.kind() else {
|
||||
bug!();
|
||||
#[must_use]
|
||||
pub fn set_disambiguator(mut self, disambiguator: &'dis dyn Display) -> Self {
|
||||
self.disambiguator = disambiguator;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn set_extra_data(
|
||||
mut self,
|
||||
extra_data: &'de dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
||||
) -> Self {
|
||||
self.writer.extra_data = extra_data;
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn set_options(mut self, options: PrettyPrintMirOptions) -> Self {
|
||||
self.writer.options = options;
|
||||
self
|
||||
}
|
||||
|
||||
/// If the session is properly configured, dumps a human-readable representation of the MIR
|
||||
/// (with default pretty-printing options) into:
|
||||
///
|
||||
/// ```text
|
||||
/// rustc.node<node_id>.<pass_num>.<pass_name>.<disambiguator>
|
||||
/// ```
|
||||
///
|
||||
/// 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
|
||||
/// - 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.
|
||||
pub fn dump_mir(&self, body: &Body<'tcx>) {
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = self.create_dump_file("mir", body)?;
|
||||
self.dump_mir_to_writer(body, &mut file)?;
|
||||
};
|
||||
|
||||
if self.tcx().sess.opts.unstable_opts.dump_mir_graphviz {
|
||||
let _: io::Result<()> = try {
|
||||
let mut file = self.create_dump_file("dot", body)?;
|
||||
write_mir_fn_graphviz(self.tcx(), body, false, &mut file)?;
|
||||
};
|
||||
let ty = args.first().unwrap().expect_ty();
|
||||
let mut s = ".".to_owned();
|
||||
s.extend(ty.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s
|
||||
}
|
||||
ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => {
|
||||
let mut s = ".".to_owned();
|
||||
s.extend(proxy_cor.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s.push('.');
|
||||
s.extend(impl_cor.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s
|
||||
}
|
||||
_ => String::new(),
|
||||
};
|
||||
|
||||
let mut file_path = PathBuf::new();
|
||||
file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
|
||||
|
||||
let file_name = format!(
|
||||
"{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}",
|
||||
);
|
||||
|
||||
file_path.push(&file_name);
|
||||
|
||||
file_path
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn create_dump_file<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
extension: &str,
|
||||
pass_num: bool,
|
||||
pass_name: &str,
|
||||
disambiguator: &dyn Display,
|
||||
body: &Body<'tcx>,
|
||||
) -> io::Result<io::BufWriter<fs::File>> {
|
||||
let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, body);
|
||||
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}"),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
fs::File::create_buffered(&file_path).map_err(|e| {
|
||||
io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
|
||||
})
|
||||
|
||||
// #41697 -- we use `with_forced_impl_filename_line()` because `def_path_str()` would otherwise
|
||||
// trigger `type_of`, and this can run while we are already attempting to evaluate `type_of`.
|
||||
pub fn dump_mir_to_writer(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {
|
||||
// see notes on #41697 above
|
||||
let def_path = ty::print::with_forced_impl_filename_line!(
|
||||
self.tcx().def_path_str(body.source.def_id())
|
||||
);
|
||||
// ignore-tidy-odd-backticks the literal below is fine
|
||||
write!(w, "// MIR for `{def_path}")?;
|
||||
match body.source.promoted {
|
||||
None => write!(w, "`")?,
|
||||
Some(promoted) => write!(w, "::{promoted:?}`")?,
|
||||
}
|
||||
writeln!(w, " {} {}", self.disambiguator, self.pass_name)?;
|
||||
if let Some(ref layout) = body.coroutine_layout_raw() {
|
||||
writeln!(w, "/* coroutine_layout = {layout:#?} */")?;
|
||||
}
|
||||
writeln!(w)?;
|
||||
(self.writer.extra_data)(PassWhere::BeforeCFG, w)?;
|
||||
write_user_type_annotations(self.tcx(), body, w)?;
|
||||
self.writer.write_mir_fn(body, w)?;
|
||||
(self.writer.extra_data)(PassWhere::AfterCFG, w)
|
||||
}
|
||||
|
||||
/// 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(&self, extension: &str, body: &Body<'tcx>) -> PathBuf {
|
||||
let tcx = self.tcx();
|
||||
let source = body.source;
|
||||
let promotion_id = match source.promoted {
|
||||
Some(id) => format!("-{id:?}"),
|
||||
None => String::new(),
|
||||
};
|
||||
|
||||
let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number {
|
||||
String::new()
|
||||
} else if self.show_pass_num {
|
||||
let (dialect_index, phase_index) = body.phase.index();
|
||||
format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count)
|
||||
} else {
|
||||
".-------".to_string()
|
||||
};
|
||||
|
||||
let crate_name = tcx.crate_name(source.def_id().krate);
|
||||
let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate();
|
||||
// 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::InstanceKind::DropGlue(_, Some(ty)) => {
|
||||
// Unfortunately, pretty-printed types are not very filename-friendly.
|
||||
// We do some filtering.
|
||||
let mut s = ".".to_owned();
|
||||
s.extend(ty.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s
|
||||
}
|
||||
ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => {
|
||||
let mut s = ".".to_owned();
|
||||
s.extend(ty.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s
|
||||
}
|
||||
ty::InstanceKind::AsyncDropGlue(_, ty) => {
|
||||
let ty::Coroutine(_, args) = ty.kind() else {
|
||||
bug!();
|
||||
};
|
||||
let ty = args.first().unwrap().expect_ty();
|
||||
let mut s = ".".to_owned();
|
||||
s.extend(ty.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s
|
||||
}
|
||||
ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => {
|
||||
let mut s = ".".to_owned();
|
||||
s.extend(proxy_cor.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s.push('.');
|
||||
s.extend(impl_cor.to_string().chars().filter_map(|c| match c {
|
||||
' ' => None,
|
||||
':' | '<' | '>' => Some('_'),
|
||||
c => Some(c),
|
||||
}));
|
||||
s
|
||||
}
|
||||
_ => String::new(),
|
||||
};
|
||||
|
||||
let mut file_path = PathBuf::new();
|
||||
file_path.push(Path::new(&tcx.sess.opts.unstable_opts.dump_mir_dir));
|
||||
|
||||
let pass_name = self.pass_name;
|
||||
let disambiguator = self.disambiguator;
|
||||
let file_name = format!(
|
||||
"{crate_name}.{item_name}{shim_disambiguator}{promotion_id}{pass_num}.{pass_name}.{disambiguator}.{extension}",
|
||||
);
|
||||
|
||||
file_path.push(&file_name);
|
||||
|
||||
file_path
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn create_dump_file(
|
||||
&self,
|
||||
extension: &str,
|
||||
body: &Body<'tcx>,
|
||||
) -> io::Result<io::BufWriter<fs::File>> {
|
||||
let file_path = self.dump_path(extension, body);
|
||||
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}"),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
fs::File::create_buffered(&file_path).map_err(|e| {
|
||||
io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -341,7 +317,7 @@ pub fn write_mir_pretty<'tcx>(
|
|||
single: Option<DefId>,
|
||||
w: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
let options = PrettyPrintMirOptions::from_cli(tcx);
|
||||
let writer = MirWriter::new(tcx);
|
||||
|
||||
writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
|
||||
writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
|
||||
|
|
@ -357,11 +333,11 @@ pub fn write_mir_pretty<'tcx>(
|
|||
}
|
||||
|
||||
let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {
|
||||
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
|
||||
writer.write_mir_fn(body, w)?;
|
||||
|
||||
for body in tcx.promoted_mir(def_id) {
|
||||
writeln!(w)?;
|
||||
write_mir_fn(tcx, body, &mut |_, _| Ok(()), w, options)?;
|
||||
writer.write_mir_fn(body, w)?;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
|
@ -373,7 +349,7 @@ pub fn write_mir_pretty<'tcx>(
|
|||
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, options)?;
|
||||
writer.write_mir_fn(tcx.mir_for_ctfe(def_id), w)?;
|
||||
} else {
|
||||
let instance_mir = tcx.instance_mir(ty::InstanceKind::Item(def_id));
|
||||
render_body(w, instance_mir)?;
|
||||
|
|
@ -382,31 +358,35 @@ pub fn write_mir_pretty<'tcx>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Write out a human-readable textual representation for the given function.
|
||||
pub fn write_mir_fn<'tcx, F>(
|
||||
/// Does the writing of MIR to output, e.g. a file.
|
||||
pub struct MirWriter<'de, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
extra_data: &mut F,
|
||||
w: &mut dyn io::Write,
|
||||
extra_data: &'de dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
||||
options: PrettyPrintMirOptions,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
||||
{
|
||||
write_mir_intro(tcx, body, w, options)?;
|
||||
for block in body.basic_blocks.indices() {
|
||||
extra_data(PassWhere::BeforeBlock(block), w)?;
|
||||
write_basic_block(tcx, block, body, extra_data, w, options)?;
|
||||
if block.index() + 1 != body.basic_blocks.len() {
|
||||
writeln!(w)?;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'tcx> MirWriter<'de, 'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
|
||||
MirWriter { tcx, extra_data: &|_, _| Ok(()), options: PrettyPrintMirOptions::from_cli(tcx) }
|
||||
}
|
||||
|
||||
writeln!(w, "}}")?;
|
||||
/// Write out a human-readable textual representation for the given function.
|
||||
pub fn write_mir_fn(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> {
|
||||
write_mir_intro(self.tcx, body, w, self.options)?;
|
||||
for block in body.basic_blocks.indices() {
|
||||
(self.extra_data)(PassWhere::BeforeBlock(block), w)?;
|
||||
self.write_basic_block(block, body, w)?;
|
||||
if block.index() + 1 != body.basic_blocks.len() {
|
||||
writeln!(w)?;
|
||||
}
|
||||
}
|
||||
|
||||
write_allocations(tcx, body, w)?;
|
||||
writeln!(w, "}}")?;
|
||||
|
||||
Ok(())
|
||||
write_allocations(self.tcx, body, w)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints local variables in a scope tree.
|
||||
|
|
@ -719,95 +699,88 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> {
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// Basic blocks and their parts (statements, terminators, ...)
|
||||
|
||||
/// Write out a human-readable textual representation for the given basic block.
|
||||
fn write_basic_block<'tcx, F>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
block: BasicBlock,
|
||||
body: &Body<'tcx>,
|
||||
extra_data: &mut F,
|
||||
w: &mut dyn io::Write,
|
||||
options: PrettyPrintMirOptions,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
|
||||
{
|
||||
let data = &body[block];
|
||||
impl<'de, 'tcx> MirWriter<'de, 'tcx> {
|
||||
/// Write out a human-readable textual representation for the given basic block.
|
||||
fn write_basic_block(
|
||||
&self,
|
||||
block: BasicBlock,
|
||||
body: &Body<'tcx>,
|
||||
w: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
let data = &body[block];
|
||||
|
||||
// Basic block label at the top.
|
||||
let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
|
||||
writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
|
||||
// Basic block label at the top.
|
||||
let cleanup_text = if data.is_cleanup { " (cleanup)" } else { "" };
|
||||
writeln!(w, "{INDENT}{block:?}{cleanup_text}: {{")?;
|
||||
|
||||
// List of statements in the middle.
|
||||
let mut current_location = Location { block, statement_index: 0 };
|
||||
for statement in &data.statements {
|
||||
extra_data(PassWhere::BeforeLocation(current_location), w)?;
|
||||
let indented_body = format!("{INDENT}{INDENT}{statement:?};");
|
||||
if options.include_extra_comments {
|
||||
writeln!(
|
||||
// List of statements in the middle.
|
||||
let mut current_location = Location { block, statement_index: 0 };
|
||||
for statement in &data.statements {
|
||||
(self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
|
||||
let indented_body = format!("{INDENT}{INDENT}{statement:?};");
|
||||
if self.options.include_extra_comments {
|
||||
writeln!(
|
||||
w,
|
||||
"{:A$} // {}{}",
|
||||
indented_body,
|
||||
if self.tcx.sess.verbose_internals() {
|
||||
format!("{current_location:?}: ")
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
comment(self.tcx, statement.source_info),
|
||||
A = ALIGN,
|
||||
)?;
|
||||
} else {
|
||||
writeln!(w, "{indented_body}")?;
|
||||
}
|
||||
|
||||
write_extra(
|
||||
self.tcx,
|
||||
w,
|
||||
"{:A$} // {}{}",
|
||||
indented_body,
|
||||
if tcx.sess.verbose_internals() {
|
||||
format!("{current_location:?}: ")
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
comment(tcx, statement.source_info),
|
||||
A = ALIGN,
|
||||
&|visitor| visitor.visit_statement(statement, current_location),
|
||||
self.options,
|
||||
)?;
|
||||
} else {
|
||||
writeln!(w, "{indented_body}")?;
|
||||
|
||||
(self.extra_data)(PassWhere::AfterLocation(current_location), w)?;
|
||||
|
||||
current_location.statement_index += 1;
|
||||
}
|
||||
|
||||
write_extra(
|
||||
tcx,
|
||||
w,
|
||||
|visitor| {
|
||||
visitor.visit_statement(statement, current_location);
|
||||
},
|
||||
options,
|
||||
)?;
|
||||
// Terminator at the bottom.
|
||||
(self.extra_data)(PassWhere::BeforeLocation(current_location), w)?;
|
||||
if data.terminator.is_some() {
|
||||
let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
|
||||
if self.options.include_extra_comments {
|
||||
writeln!(
|
||||
w,
|
||||
"{:A$} // {}{}",
|
||||
indented_terminator,
|
||||
if self.tcx.sess.verbose_internals() {
|
||||
format!("{current_location:?}: ")
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
comment(self.tcx, data.terminator().source_info),
|
||||
A = ALIGN,
|
||||
)?;
|
||||
} else {
|
||||
writeln!(w, "{indented_terminator}")?;
|
||||
}
|
||||
|
||||
extra_data(PassWhere::AfterLocation(current_location), w)?;
|
||||
|
||||
current_location.statement_index += 1;
|
||||
}
|
||||
|
||||
// Terminator at the bottom.
|
||||
extra_data(PassWhere::BeforeLocation(current_location), w)?;
|
||||
if data.terminator.is_some() {
|
||||
let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
|
||||
if options.include_extra_comments {
|
||||
writeln!(
|
||||
write_extra(
|
||||
self.tcx,
|
||||
w,
|
||||
"{:A$} // {}{}",
|
||||
indented_terminator,
|
||||
if tcx.sess.verbose_internals() {
|
||||
format!("{current_location:?}: ")
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
comment(tcx, data.terminator().source_info),
|
||||
A = ALIGN,
|
||||
&|visitor| visitor.visit_terminator(data.terminator(), current_location),
|
||||
self.options,
|
||||
)?;
|
||||
} else {
|
||||
writeln!(w, "{indented_terminator}")?;
|
||||
}
|
||||
|
||||
write_extra(
|
||||
tcx,
|
||||
w,
|
||||
|visitor| {
|
||||
visitor.visit_terminator(data.terminator(), current_location);
|
||||
},
|
||||
options,
|
||||
)?;
|
||||
(self.extra_data)(PassWhere::AfterLocation(current_location), w)?;
|
||||
(self.extra_data)(PassWhere::AfterTerminator(block), w)?;
|
||||
|
||||
writeln!(w, "{INDENT}}}")
|
||||
}
|
||||
|
||||
extra_data(PassWhere::AfterLocation(current_location), w)?;
|
||||
extra_data(PassWhere::AfterTerminator(block), w)?;
|
||||
|
||||
writeln!(w, "{INDENT}}}")
|
||||
}
|
||||
|
||||
impl Debug for Statement<'_> {
|
||||
|
|
@ -1374,15 +1347,12 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) ->
|
|||
/// After we print the main statement, we sometimes dump extra
|
||||
/// information. There's often a lot of little things "nuzzled up" in
|
||||
/// a statement.
|
||||
fn write_extra<'tcx, F>(
|
||||
fn write_extra<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
write: &mut dyn io::Write,
|
||||
mut visit_op: F,
|
||||
visit_op: &dyn Fn(&mut ExtraComments<'tcx>),
|
||||
options: PrettyPrintMirOptions,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
F: FnMut(&mut ExtraComments<'tcx>),
|
||||
{
|
||||
) -> io::Result<()> {
|
||||
if options.include_extra_comments {
|
||||
let mut extra_comments = ExtraComments { tcx, comments: vec![] };
|
||||
visit_op(&mut extra_comments);
|
||||
|
|
|
|||
|
|
@ -806,10 +806,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
);
|
||||
body.coverage_info_hi = self.coverage_info.as_ref().map(|b| b.as_done());
|
||||
|
||||
use rustc_middle::mir::pretty;
|
||||
let options = pretty::PrettyPrintMirOptions::from_cli(self.tcx);
|
||||
pretty::write_mir_fn(self.tcx, &body, &mut |_, _| Ok(()), &mut std::io::stdout(), options)
|
||||
.unwrap();
|
||||
let writer = pretty::MirWriter::new(self.tcx);
|
||||
writer.write_mir_fn(&body, &mut std::io::stdout()).unwrap();
|
||||
}
|
||||
|
||||
fn finish(self) -> Body<'tcx> {
|
||||
|
|
@ -827,18 +825,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
);
|
||||
body.coverage_info_hi = self.coverage_info.map(|b| b.into_done());
|
||||
|
||||
let writer = pretty::MirWriter::new(self.tcx);
|
||||
for (index, block) in body.basic_blocks.iter().enumerate() {
|
||||
if block.terminator.is_none() {
|
||||
use rustc_middle::mir::pretty;
|
||||
let options = pretty::PrettyPrintMirOptions::from_cli(self.tcx);
|
||||
pretty::write_mir_fn(
|
||||
self.tcx,
|
||||
&body,
|
||||
&mut |_, _| Ok(()),
|
||||
&mut std::io::stdout(),
|
||||
options,
|
||||
)
|
||||
.unwrap();
|
||||
writer.write_mir_fn(&body, &mut std::io::stdout()).unwrap();
|
||||
span_bug!(self.fn_span, "no terminator on block {:?}", index);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ use std::{io, ops, str};
|
|||
use regex::Regex;
|
||||
use rustc_index::bit_set::DenseBitSet;
|
||||
use rustc_middle::mir::{
|
||||
self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name,
|
||||
traversal,
|
||||
self, BasicBlock, Body, Location, MirDumper, graphviz_safe_def_name, traversal,
|
||||
};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
|
|
@ -61,11 +60,13 @@ where
|
|||
fs::File::create_buffered(&path)?
|
||||
}
|
||||
|
||||
None if dump_enabled(tcx, A::NAME, def_id) => {
|
||||
create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
|
||||
None => {
|
||||
let Some(dumper) = MirDumper::new(tcx, A::NAME, body) else {
|
||||
return Ok(());
|
||||
};
|
||||
let disambiguator = &pass_name.unwrap_or("-----");
|
||||
dumper.set_disambiguator(disambiguator).create_dump_file("dot", body)?
|
||||
}
|
||||
|
||||
_ => return Ok(()),
|
||||
}
|
||||
};
|
||||
let mut file = match file {
|
||||
|
|
|
|||
|
|
@ -1294,7 +1294,9 @@ fn create_coroutine_resume_function<'tcx>(
|
|||
|
||||
pm::run_passes_no_validate(tcx, body, &[&abort_unwinding_calls::AbortUnwindingCalls], None);
|
||||
|
||||
dump_mir(tcx, false, "coroutine_resume", &0, body, |_, _| Ok(()));
|
||||
if let Some(dumper) = MirDumper::new(tcx, "coroutine_resume", body) {
|
||||
dumper.dump_mir(body);
|
||||
}
|
||||
}
|
||||
|
||||
/// An operation that can be performed on a coroutine.
|
||||
|
|
@ -1446,7 +1448,9 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
|
|||
|
||||
assert!(body.coroutine_drop().is_none() && body.coroutine_drop_async().is_none());
|
||||
|
||||
dump_mir(tcx, false, "coroutine_before", &0, body, |_, _| Ok(()));
|
||||
if let Some(dumper) = MirDumper::new(tcx, "coroutine_before", body) {
|
||||
dumper.dump_mir(body);
|
||||
}
|
||||
|
||||
// The first argument is the coroutine type passed by value
|
||||
let coroutine_ty = body.local_decls.raw[1].ty;
|
||||
|
|
@ -1506,7 +1510,10 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
|
|||
) {
|
||||
let context_mut_ref = transform_async_context(tcx, body);
|
||||
expand_async_drops(tcx, body, context_mut_ref, coroutine_kind, coroutine_ty);
|
||||
dump_mir(tcx, false, "coroutine_async_drop_expand", &0, body, |_, _| Ok(()));
|
||||
|
||||
if let Some(dumper) = MirDumper::new(tcx, "coroutine_async_drop_expand", body) {
|
||||
dumper.dump_mir(body);
|
||||
}
|
||||
} else {
|
||||
cleanup_async_drops(body);
|
||||
}
|
||||
|
|
@ -1605,14 +1612,18 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
|
|||
// This is expanded to a drop ladder in `elaborate_coroutine_drops`.
|
||||
let drop_clean = insert_clean_drop(tcx, body, has_async_drops);
|
||||
|
||||
dump_mir(tcx, false, "coroutine_pre-elab", &0, body, |_, _| Ok(()));
|
||||
if let Some(dumper) = MirDumper::new(tcx, "coroutine_pre-elab", body) {
|
||||
dumper.dump_mir(body);
|
||||
}
|
||||
|
||||
// Expand `drop(coroutine_struct)` to a drop ladder which destroys upvars.
|
||||
// If any upvars are moved out of, drop elaboration will handle upvar destruction.
|
||||
// However we need to also elaborate the code generated by `insert_clean_drop`.
|
||||
elaborate_coroutine_drops(tcx, body);
|
||||
|
||||
dump_mir(tcx, false, "coroutine_post-transform", &0, body, |_, _| Ok(()));
|
||||
if let Some(dumper) = MirDumper::new(tcx, "coroutine_post-transform", body) {
|
||||
dumper.dump_mir(body);
|
||||
}
|
||||
|
||||
let can_unwind = can_unwind(tcx, body);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ use rustc_hir::definitions::DisambiguatorState;
|
|||
use rustc_middle::bug;
|
||||
use rustc_middle::hir::place::{Projection, ProjectionKind};
|
||||
use rustc_middle::mir::visit::MutVisitor;
|
||||
use rustc_middle::mir::{self, dump_mir};
|
||||
use rustc_middle::mir::{self, MirDumper};
|
||||
use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt, TypeVisitableExt};
|
||||
|
||||
pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
|
||||
|
|
@ -225,7 +225,10 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>(
|
|||
);
|
||||
by_move_body.source =
|
||||
mir::MirSource::from_instance(InstanceKind::Item(body_def.def_id().to_def_id()));
|
||||
dump_mir(tcx, false, "built", &"after", &by_move_body, |_, _| Ok(()));
|
||||
|
||||
if let Some(dumper) = MirDumper::new(tcx, "built", &by_move_body) {
|
||||
dumper.set_disambiguator(&"after").dump_mir(&by_move_body);
|
||||
}
|
||||
|
||||
// Feed HIR because we try to access this body's attrs in the inliner.
|
||||
body_def.feed_hir();
|
||||
|
|
|
|||
|
|
@ -605,7 +605,9 @@ pub(super) fn create_coroutine_drop_shim<'tcx>(
|
|||
// Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible
|
||||
// filename.
|
||||
body.source.instance = coroutine_instance;
|
||||
dump_mir(tcx, false, "coroutine_drop", &0, &body, |_, _| Ok(()));
|
||||
if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop", &body) {
|
||||
dumper.dump_mir(&body);
|
||||
}
|
||||
body.source.instance = drop_instance;
|
||||
|
||||
// Creating a coroutine drop shim happens on `Analysis(PostCleanup) -> Runtime(Initial)`
|
||||
|
|
@ -696,7 +698,9 @@ pub(super) fn create_coroutine_drop_shim_async<'tcx>(
|
|||
None,
|
||||
);
|
||||
|
||||
dump_mir(tcx, false, "coroutine_drop_async", &0, &body, |_, _| Ok(()));
|
||||
if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_async", &body) {
|
||||
dumper.dump_mir(&body);
|
||||
}
|
||||
|
||||
body
|
||||
}
|
||||
|
|
@ -741,7 +745,9 @@ pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>(
|
|||
};
|
||||
body.basic_blocks_mut()[call_bb].terminator = Some(Terminator { source_info, kind });
|
||||
|
||||
dump_mir(tcx, false, "coroutine_drop_proxy_async", &0, &body, |_, _| Ok(()));
|
||||
if let Some(dumper) = MirDumper::new(tcx, "coroutine_drop_proxy_async", &body) {
|
||||
dumper.dump_mir(&body);
|
||||
}
|
||||
|
||||
body
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,8 +137,8 @@ use rustc_index::interval::SparseIntervalMatrix;
|
|||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{
|
||||
Body, HasLocalDecls, InlineAsmOperand, Local, LocalKind, Location, Operand, PassWhere, Place,
|
||||
Rvalue, Statement, StatementKind, TerminatorKind, dump_mir, traversal,
|
||||
Body, HasLocalDecls, InlineAsmOperand, Local, LocalKind, Location, MirDumper, Operand,
|
||||
PassWhere, Place, Rvalue, Statement, StatementKind, TerminatorKind, traversal,
|
||||
};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_mir_dataflow::Analysis;
|
||||
|
|
@ -810,11 +810,15 @@ fn dest_prop_mir_dump<'tcx>(
|
|||
let location = points.point_from_location(location);
|
||||
live.rows().filter(|&r| live.contains(r, location)).collect::<Vec<_>>()
|
||||
};
|
||||
dump_mir(tcx, false, "DestinationPropagation-dataflow", &round, body, |pass_where, w| {
|
||||
if let PassWhere::BeforeLocation(loc) = pass_where {
|
||||
writeln!(w, " // live: {:?}", locals_live_at(loc))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
if let Some(dumper) = MirDumper::new(tcx, "DestinationPropagation-dataflow", body) {
|
||||
let extra_data = &|pass_where, w: &mut dyn std::io::Write| {
|
||||
if let PassWhere::BeforeLocation(loc) = pass_where {
|
||||
writeln!(w, " // live: {:?}", locals_live_at(loc))?;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
dumper.set_disambiguator(&round).set_extra_data(extra_data).dump_mir(body)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ use rustc_index::{IndexSlice, IndexVec};
|
|||
use rustc_macros::{LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::{
|
||||
self, BasicBlock, Body, ClearCrossCrate, Local, Location, Place, StatementKind, TerminatorKind,
|
||||
dump_mir,
|
||||
self, BasicBlock, Body, ClearCrossCrate, Local, Location, MirDumper, Place, StatementKind,
|
||||
TerminatorKind,
|
||||
};
|
||||
use rustc_middle::ty::significant_drop_order::{
|
||||
extract_component_with_significant_dtor, ty_dtor_span,
|
||||
|
|
@ -227,7 +227,10 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
|
|||
return;
|
||||
}
|
||||
|
||||
dump_mir(tcx, false, "lint_tail_expr_drop_order", &0 as _, body, |_, _| Ok(()));
|
||||
if let Some(dumper) = MirDumper::new(tcx, "lint_tail_expr_drop_order", body) {
|
||||
dumper.dump_mir(body);
|
||||
}
|
||||
|
||||
let locals_with_user_names = collect_user_names(body);
|
||||
let is_closure_like = tcx.is_closure_like(def_id.to_def_id());
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::cell::RefCell;
|
|||
use std::collections::hash_map::Entry;
|
||||
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
||||
use rustc_middle::mir::{self, Body, MirPhase, RuntimePhase};
|
||||
use rustc_middle::mir::{Body, MirDumper, MirPhase, RuntimePhase};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Session;
|
||||
use tracing::trace;
|
||||
|
|
@ -281,16 +281,22 @@ fn run_passes_inner<'tcx>(
|
|||
let lint = tcx.sess.opts.unstable_opts.lint_mir;
|
||||
|
||||
for pass in passes {
|
||||
let name = pass.name();
|
||||
let pass_name = pass.name();
|
||||
|
||||
if !should_run_pass(tcx, *pass, optimizations) {
|
||||
continue;
|
||||
};
|
||||
|
||||
let dump_enabled = pass.is_mir_dump_enabled();
|
||||
let dumper = if pass.is_mir_dump_enabled()
|
||||
&& let Some(dumper) = MirDumper::new(tcx, pass_name, body)
|
||||
{
|
||||
Some(dumper.set_show_pass_num().set_disambiguator(&"before"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if dump_enabled {
|
||||
dump_mir_for_pass(tcx, body, name, false);
|
||||
if let Some(dumper) = dumper.as_ref() {
|
||||
dumper.dump_mir(body);
|
||||
}
|
||||
|
||||
if let Some(prof_arg) = &prof_arg {
|
||||
|
|
@ -302,14 +308,15 @@ fn run_passes_inner<'tcx>(
|
|||
pass.run_pass(tcx, body);
|
||||
}
|
||||
|
||||
if dump_enabled {
|
||||
dump_mir_for_pass(tcx, body, name, true);
|
||||
if let Some(dumper) = dumper {
|
||||
dumper.set_disambiguator(&"after").dump_mir(body);
|
||||
}
|
||||
|
||||
if validate {
|
||||
validate_body(tcx, body, format!("after pass {name}"));
|
||||
validate_body(tcx, body, format!("after pass {pass_name}"));
|
||||
}
|
||||
if lint {
|
||||
lint_body(tcx, body, format!("after pass {name}"));
|
||||
lint_body(tcx, body, format!("after pass {pass_name}"));
|
||||
}
|
||||
|
||||
body.pass_count += 1;
|
||||
|
|
@ -345,18 +352,9 @@ pub(super) fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when
|
|||
validate::Validator { when }.run_pass(tcx, body);
|
||||
}
|
||||
|
||||
fn dump_mir_for_pass<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, pass_name: &str, is_after: bool) {
|
||||
mir::dump_mir(
|
||||
tcx,
|
||||
true,
|
||||
pass_name,
|
||||
if is_after { &"after" } else { &"before" },
|
||||
body,
|
||||
|_, _| Ok(()),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn dump_mir_for_phase_change<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
|
||||
assert_eq!(body.pass_count, 0);
|
||||
mir::dump_mir(tcx, true, body.phase.name(), &"after", body, |_, _| Ok(()))
|
||||
if let Some(dumper) = MirDumper::new(tcx, body.phase.name(), body) {
|
||||
dumper.set_show_pass_num().set_disambiguator(&"after").dump_mir(body)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1242,14 +1242,12 @@ fn build_construct_coroutine_by_move_shim<'tcx>(
|
|||
|
||||
let body =
|
||||
new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span);
|
||||
dump_mir(
|
||||
tcx,
|
||||
false,
|
||||
if receiver_by_ref { "coroutine_closure_by_ref" } else { "coroutine_closure_by_move" },
|
||||
&0,
|
||||
&body,
|
||||
|_, _| Ok(()),
|
||||
);
|
||||
|
||||
let pass_name =
|
||||
if receiver_by_ref { "coroutine_closure_by_ref" } else { "coroutine_closure_by_move" };
|
||||
if let Some(dumper) = MirDumper::new(tcx, pass_name, &body) {
|
||||
dumper.dump_mir(&body);
|
||||
}
|
||||
|
||||
body
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue