diff options
Diffstat (limited to 'compiler/rustc_middle/src/mir/pretty.rs')
| -rw-r--r-- | compiler/rustc_middle/src/mir/pretty.rs | 282 |
1 files changed, 143 insertions, 139 deletions
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 14f504bb2a9..a7d99f513a1 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -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,6 +58,79 @@ impl PrettyPrintMirOptions { } } +/// 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>, +} + +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), + }) + } + + pub fn tcx(&self) -> TyCtxt<'tcx> { + self.writer.tcx + } + + #[must_use] + pub fn set_show_pass_num(mut self) -> Self { + self.show_pass_num = true; + self + } + + #[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: /// @@ -82,125 +155,49 @@ impl PrettyPrintMirOptions { /// 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>( - tcx: TyCtxt<'tcx>, - pass_num: bool, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - ) { - dump_mir_with_options( - tcx, - pass_num, - pass_name, - disambiguator, - body, - extra_data, - PrettyPrintMirOptions::from_cli(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>( - tcx: TyCtxt<'tcx>, - pass_num: bool, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - options: PrettyPrintMirOptions, - ) { - if !dump_enabled(tcx, pass_name, body.source.def_id()) { - return; - } - + pub fn dump_mir(&self, body: &Body<'tcx>) { 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)?; + let mut file = self.create_dump_file("mir", body)?; + self.dump_mir_to_writer(body, &mut file)?; }; - if tcx.sess.opts.unstable_opts.dump_mir_graphviz { + if self.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)?; + let mut file = self.create_dump_file("dot", body)?; + write_mir_fn_graphviz(self.tcx(), body, false, &mut file)?; }; } } - 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>( - tcx: TyCtxt<'tcx>, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - w: &mut dyn io::Write, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - options: PrettyPrintMirOptions, - ) -> io::Result<()> { + // #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!(tcx.def_path_str(body.source.def_id())); + 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, " {disambiguator} {pass_name}")?; + writeln!(w, " {} {}", self.disambiguator, self.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, extra_data, w, options)?; - extra_data(PassWhere::AfterCFG, 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<'tcx>( - tcx: TyCtxt<'tcx>, - extension: &str, - pass_num: bool, - pass_name: &str, - disambiguator: &dyn Display, - body: &Body<'tcx>, - ) -> PathBuf { + 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:?}"), @@ -209,7 +206,7 @@ impl PrettyPrintMirOptions { let pass_num = if tcx.sess.opts.unstable_opts.dump_mir_exclude_pass_number { String::new() - } else if pass_num { + } else if self.show_pass_num { let (dialect_index, phase_index) = body.phase.index(); format!(".{}-{}-{:03}", dialect_index, phase_index, body.pass_count) } else { @@ -222,8 +219,8 @@ impl PrettyPrintMirOptions { // 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. + // 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, @@ -275,6 +272,8 @@ impl PrettyPrintMirOptions { 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}", ); @@ -288,15 +287,12 @@ impl PrettyPrintMirOptions { /// 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>, + pub fn create_dump_file( + &self, 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); + 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( @@ -309,6 +305,7 @@ impl PrettyPrintMirOptions { io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}")) }) } +} /////////////////////////////////////////////////////////////////////////// // Whole MIR bodies @@ -320,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.")?; @@ -336,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, &|_, _| Ok(()), w, options)?; + writer.write_mir_fn(body, w)?; for body in tcx.promoted_mir(def_id) { writeln!(w)?; - write_mir_fn(tcx, body, &|_, _| Ok(()), w, options)?; + writer.write_mir_fn(body, w)?; } Ok(()) }; @@ -352,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), &|_, _| 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)?; @@ -361,18 +358,24 @@ pub fn write_mir_pretty<'tcx>( Ok(()) } +/// Does the writing of MIR to output, e.g. a file. +pub struct MirWriter<'de, 'tcx> { + tcx: TyCtxt<'tcx>, + extra_data: &'de dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, + options: PrettyPrintMirOptions, +} + +impl<'de, 'tcx> MirWriter<'de, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + MirWriter { tcx, extra_data: &|_, _| Ok(()), options: PrettyPrintMirOptions::from_cli(tcx) } + } + /// Write out a human-readable textual representation for the given function. - pub fn write_mir_fn<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, - w: &mut dyn io::Write, - options: PrettyPrintMirOptions, - ) -> io::Result<()> { - write_mir_intro(tcx, body, w, options)?; + 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() { - extra_data(PassWhere::BeforeBlock(block), w)?; - write_basic_block(tcx, block, body, extra_data, w, options)?; + (self.extra_data)(PassWhere::BeforeBlock(block), w)?; + self.write_basic_block(block, body, w)?; if block.index() + 1 != body.basic_blocks.len() { writeln!(w)?; } @@ -380,10 +383,11 @@ pub fn write_mir_pretty<'tcx>( writeln!(w, "}}")?; - write_allocations(tcx, body, w)?; + write_allocations(self.tcx, body, w)?; Ok(()) } +} /// Prints local variables in a scope tree. fn write_scope_tree( @@ -695,14 +699,13 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> { /////////////////////////////////////////////////////////////////////////// // Basic blocks and their parts (statements, terminators, ...) +impl<'de, 'tcx> MirWriter<'de, 'tcx> { /// Write out a human-readable textual representation for the given basic block. - fn write_basic_block<'tcx>( - tcx: TyCtxt<'tcx>, + fn write_basic_block( + &self, block: BasicBlock, body: &Body<'tcx>, - extra_data: &dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, w: &mut dyn io::Write, - options: PrettyPrintMirOptions, ) -> io::Result<()> { let data = &body[block]; @@ -713,19 +716,19 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> { // 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)?; + (self.extra_data)(PassWhere::BeforeLocation(current_location), w)?; let indented_body = format!("{INDENT}{INDENT}{statement:?};"); - if options.include_extra_comments { + if self.options.include_extra_comments { writeln!( w, "{:A$} // {}{}", indented_body, - if tcx.sess.verbose_internals() { + if self.tcx.sess.verbose_internals() { format!("{current_location:?}: ") } else { String::new() }, - comment(tcx, statement.source_info), + comment(self.tcx, statement.source_info), A = ALIGN, )?; } else { @@ -733,32 +736,32 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> { } write_extra( - tcx, + self.tcx, w, &|visitor| visitor.visit_statement(statement, current_location), - options, + self.options, )?; - extra_data(PassWhere::AfterLocation(current_location), w)?; + (self.extra_data)(PassWhere::AfterLocation(current_location), w)?; current_location.statement_index += 1; } // Terminator at the bottom. - extra_data(PassWhere::BeforeLocation(current_location), w)?; + (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 options.include_extra_comments { + if self.options.include_extra_comments { writeln!( w, "{:A$} // {}{}", indented_terminator, - if tcx.sess.verbose_internals() { + if self.tcx.sess.verbose_internals() { format!("{current_location:?}: ") } else { String::new() }, - comment(tcx, data.terminator().source_info), + comment(self.tcx, data.terminator().source_info), A = ALIGN, )?; } else { @@ -766,18 +769,19 @@ pub fn dump_mir_def_ids(tcx: TyCtxt<'_>, single: Option<DefId>) -> Vec<DefId> { } write_extra( - tcx, + self.tcx, w, &|visitor| visitor.visit_terminator(data.terminator(), current_location), - options, + self.options, )?; } - extra_data(PassWhere::AfterLocation(current_location), w)?; - extra_data(PassWhere::AfterTerminator(block), w)?; + (self.extra_data)(PassWhere::AfterLocation(current_location), w)?; + (self.extra_data)(PassWhere::AfterTerminator(block), w)?; writeln!(w, "{INDENT}}}") } +} impl Debug for Statement<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { |
