rust-lang/rust · #163721
Annotate MIR with dataflow results for testing purposes
Cargo.lock1 + / 0 −
@@ -4295,6 +4295,7 @@ dependencies = [ "rustc_metadata", "rustc_middle", "rustc_mir_build",+ "rustc_mir_dataflow", "rustc_mir_transform", "rustc_monomorphize", "rustc_parse",compiler/rustc_attr_ir/src/data_structures.rs2 + / 0 −
@@ -591,6 +591,8 @@ pub enum RustcDumpLayoutKind { #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq, Eq)] pub enum RustcMirKind {+ PrettyLiveLocals,+ PrettyTransitiveLiveLocals, PeekMaybeInit, PeekMaybeUninit, PeekLiveness,compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs8 + / 0 −
@@ -626,6 +626,14 @@ impl CombineAttributeParser for RustcMirParser { .filter_map(|mi| { if let Some(ident) = mi.ident() { match ident.name {+ sym::rustc_pretty_live_locals => {+ cx.expect_no_args(mi.args())?;+ Some(RustcMirKind::PrettyLiveLocals)+ }+ sym::rustc_pretty_transitive_live_locals => {+ cx.expect_no_args(mi.args())?;+ Some(RustcMirKind::PrettyTransitiveLiveLocals)+ } sym::rustc_peek_maybe_init => Some(RustcMirKind::PeekMaybeInit), sym::rustc_peek_maybe_uninit => Some(RustcMirKind::PeekMaybeUninit), sym::rustc_peek_liveness => Some(RustcMirKind::PeekLiveness),compiler/rustc_interface/Cargo.toml1 + / 0 −
@@ -34,6 +34,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } rustc_mir_build = { path = "../rustc_mir_build" }+rustc_mir_dataflow = { path = "../rustc_mir_dataflow" } rustc_mir_transform = { path = "../rustc_mir_transform" } rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_parse = { path = "../rustc_parse" }compiler/rustc_interface/src/passes.rs1 + / 0 −
@@ -906,6 +906,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| { rustc_borrowck::provide(&mut providers.queries); rustc_incremental::provide(providers); rustc_mir_build::provide(providers);+ rustc_mir_dataflow::provide(providers); rustc_mir_transform::provide(providers); rustc_monomorphize::provide(providers); rustc_privacy::provide(&mut providers.queries);compiler/rustc_middle/src/hooks.rs8 + / 0 −
@@ -3,6 +3,8 @@ //! similar to queries, but queries come with a lot of machinery for caching and incremental //! compilation, whereas hooks are just plain function pointers without any of the query magic. +use std::io;+ use rustc_hir::def_id::{DefId, DefPathHash}; use rustc_index::bit_set::DenseBitSet; use rustc_session::StableCrateId;@@ -123,6 +125,12 @@ declare_hooks! { instance: ty::Instance<'tcx>, typing_env: ty::TypingEnv<'tcx>, ) -> DenseBitSet<mir::BasicBlock>;++ /// Configures extra data to be included when pretty printing MIR+ hook mir_pretty_extra_data(+ body: &mir::Body<'tcx>,+ ) -> Option<Box<dyn Fn(mir::PassWhere, &mut dyn io::Write) -> io::Result<()>>>;+ } #[cold]compiler/rustc_middle/src/mir/pretty.rs8 + / 2 −
@@ -26,7 +26,7 @@ pub(crate) const ALIGN: usize = 40; /// An indication of where we are in the control flow graph. Used for printing /// extra information in `dump_mir`-#[derive(Clone, Copy)]+#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub enum PassWhere { /// We have not started dumping the control flow graph, but we are about to. BeforeCFG,@@ -332,7 +332,13 @@ pub fn write_mir_pretty<'tcx>(tcx: TyCtxt<'tcx>, w: &mut dyn io::Write) -> io::R } let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> {- writer.write_mir_fn(body, w)?;+ if let Some(mut extra_data) = tcx.mir_pretty_extra_data(body) {+ let mut writer = MirWriter::new(tcx);+ writer.extra_data = &mut *extra_data;+ writer.write_mir_fn(body, w)?;+ } else {+ writer.write_mir_fn(body, w)?;+ } for body in tcx.promoted_mir(def_id) { writeln!(w)?;compiler/rustc_mir_dataflow/src/lib.rs6 + / 0 −
@@ -6,6 +6,7 @@ // tidy-alphabetical-end use rustc_middle::ty;+use rustc_middle::util::Providers; // Please change the public `use` directives cautiously, as they might be used by external tools. // See issue #120130.@@ -27,6 +28,7 @@ mod framework; pub mod impls; pub mod move_paths; pub mod points;+mod pretty; pub mod rustc_peek; mod un_derefer; pub mod value_analysis;@@ -35,3 +37,7 @@ pub struct MoveDataTypingEnv<'tcx> { pub move_data: MoveData<'tcx>, pub typing_env: ty::TypingEnv<'tcx>, }++pub fn provide(providers: &mut Providers) {+ providers.hooks.mir_pretty_extra_data = pretty::mir_pretty_extra_data;+}compiler/rustc_mir_dataflow/src/pretty.rsadded132 + / 0 −
@@ -0,0 +1,132 @@+use std::io;++use rustc_attr_ir::{RustcMirKind, find_attr};+use rustc_data_structures::fx::FxIndexMap;+use rustc_middle::mir::{self, Body, Local, MirDumper, PassWhere, traversal};+use rustc_middle::ty::TyCtxt;++use crate::debuginfo::debuginfo_locals;+use crate::framework::Analysis;+use crate::impls::{MaybeLiveLocals, MaybeTransitiveLiveLocals, borrowed_locals};+use crate::{ResultsVisitor, visit_results};++type ExtraDataFn = dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>;++pub(crate) fn mir_pretty_extra_data<'tcx>(+ tcx: TyCtxt<'tcx>,+ body: &Body<'tcx>,+) -> Option<Box<ExtraDataFn>> {+ let def_id = body.source.def_id();+ let Some(kinds) = find_attr!(tcx, def_id, RustcMir(kind) => kind) else {+ return None;+ };+ let mut annotations = Annotations::default();+ for kind in kinds {+ match kind {+ RustcMirKind::PrettyLiveLocals => {+ let results = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None);+ let blocks = traversal::reachable(body).map(|(bb, _)| bb);+ visit_results(+ body,+ blocks,+ &results,+ &mut Annotator { annotations: &mut annotations },+ );+ }+ RustcMirKind::PrettyTransitiveLiveLocals => {+ let borrowed_locals = borrowed_locals(body);+ let debuginfo_locals = debuginfo_locals(body);+ let results = MaybeTransitiveLiveLocals::new(&borrowed_locals, &debuginfo_locals)+ .iterate_to_fixpoint(tcx, body, None);+ let blocks = traversal::reachable(body).map(|(bb, _)| bb);+ visit_results(+ body,+ blocks,+ &results,+ &mut Annotator { annotations: &mut annotations },+ );+ }+ _ => {}+ }+ }+ let extra_data = annotations.into_extra_data()?;+ if let Some(dumper) = MirDumper::new(tcx, "dataflow", body) {+ dumper.set_extra_data(&*extra_data).dump_mir(body);+ }+ Some(extra_data)+}++#[derive(Default)]+struct Annotations {+ entries: FxIndexMap<PassWhere, Vec<String>>,+}++impl Annotations {+ fn add(&mut self, pass_where: PassWhere, s: String) {+ self.entries.entry(pass_where).or_default().push(s);+ }++ fn into_extra_data(self) -> Option<Box<ExtraDataFn>> {+ if self.entries.is_empty() {+ return None;+ }+ Some(Box::new(move |pass_where: PassWhere, out: &mut dyn io::Write| {+ let Some(notes) = self.entries.get(&pass_where) else {+ return Ok(());+ };+ for s in notes {+ writeln!(out, " // {s}")?;+ }+ Ok(())+ }))+ }+}++/// Examines dataflow results and collects extra annotations for inclusion in MIR pretty printing.+struct Annotator<'a> {+ annotations: &'a mut Annotations,+}++impl<'tcx> ResultsVisitor<'tcx, MaybeLiveLocals> for Annotator<'_> {+ fn visit_after_primary_statement_effect(+ &mut self,+ state: &<MaybeLiveLocals as Analysis<'tcx>>::Domain,+ _statement: &mir::Statement<'tcx>,+ location: mir::Location,+ ) {+ let live: Vec<Local> = state.iter().collect();+ self.annotations.add(PassWhere::BeforeLocation(location), format!("live: {live:?}"));+ }++ fn visit_after_primary_terminator_effect(+ &mut self,+ state: &<MaybeLiveLocals as Analysis<'tcx>>::Domain,+ _terminator: &mir::Terminator<'tcx>,+ location: mir::Location,+ ) {+ let live: Vec<Local> = state.iter().collect();+ self.annotations.add(PassWhere::BeforeLocation(location), format!("live: {live:?}"));+ }+}++impl<'tcx> ResultsVisitor<'tcx, MaybeTransitiveLiveLocals<'_>> for Annotator<'_> {+ fn visit_after_primary_statement_effect(+ &mut self,+ state: &<MaybeLiveLocals as Analysis<'tcx>>::Domain,+ _statement: &mir::Statement<'tcx>,+ location: mir::Location,+ ) {+ let live: Vec<Local> = state.iter().collect();+ self.annotations.add(PassWhere::BeforeLocation(location), format!("live: {live:?}"));+ }++ fn visit_after_primary_terminator_effect(+ &mut self,+ state: &<MaybeLiveLocals as Analysis<'tcx>>::Domain,+ _terminator: &mir::Terminator<'tcx>,+ location: mir::Location,+ ) {+ let live: Vec<Local> = state.iter().collect();+ self.annotations.add(PassWhere::BeforeLocation(location), format!("live: {live:?}"));+ }+}compiler/rustc_span/src/symbol.rs2 + / 0 −
@@ -1896,6 +1896,8 @@ symbols! { rustc_peek_maybe_init, rustc_peek_maybe_uninit, rustc_preserve_ub_checks,+ rustc_pretty_live_locals,+ rustc_pretty_transitive_live_locals, rustc_private, rustc_proc_macro_decls, rustc_promotable,tests/mir-opt/dataflow/liveness.deref.dataflow.0.miradded25 + / 0 −
@@ -0,0 +1,25 @@+// MIR for `deref` 0 dataflow++fn deref() -> () {+ let mut _0: ();+ let mut _1: usize;+ let mut _2: &mut usize;++ bb0: {+ // live: []+ // live: []+ _1 = const 42_usize;+ // live: [_1]+ // live: [_1]+ _2 = &mut _1;+ // live: [_2]+ // live: [_2]+ (*_2) = const 24_usize;+ // live: []+ // live: []+ _0 = ();+ // live: [_0]+ // live: [_0]+ return;+ }+}tests/mir-opt/dataflow/liveness.fields.dataflow.0.miradded27 + / 0 −
@@ -0,0 +1,27 @@+// MIR for `fields` 0 dataflow++fn fields() -> usize {+ let mut _0: usize;+ let mut _1: (usize, usize);++ bb0: {+ // live: []+ // live: []+ _1 = (const 1_usize, const 0_usize);+ // live: []+ // live: []+ (_1.1: usize) = const 2_usize;+ // live: []+ // live: []+ _1 = (const 0_usize, const 3_usize);+ // live: [_1]+ // live: [_1]+ (_1.1: usize) = const 4_usize;+ // live: [_1]+ // live: [_1]+ _0 = copy (_1.1: usize);+ // live: [_0]+ // live: [_0]+ return;+ }+}tests/mir-opt/dataflow/liveness.rsadded82 + / 0 −
@@ -0,0 +1,82 @@+// Tests for MaybeLiveLocals and MaybeTransitiveLiveLocals dataflows.+#![feature(core_intrinsics, custom_mir, rustc_attrs)]+#![crate_type = "lib"]+use std::intrinsics::mir::*;++// EMIT_MIR liveness.fields.dataflow.0.mir+#[custom_mir(dialect = "runtime", phase = "optimized")]+#[rustc_mir(rustc_pretty_live_locals)]+#[rustc_mir(rustc_pretty_transitive_live_locals)]+pub fn fields() -> usize {+ mir! {+ let _1: (usize, usize);+ {+ // CHECK-LABEL: fn fields(+ // CHECK: bb0: {+ // CHECK-NEXT: live: []+ // CHECK-NEXT: live: []+ // CHECK-NEXT: _1 = (const 1_usize, const 0_usize);+ _1 = (1, 0);+ // write to _1.1 doesn't make _1 live+ // CHECK-NEXT: live: []+ // CHECK-NEXT: live: []+ // CHECK-NEXT: (_1.1: usize) = const 2_usize;+ _1.1 = 2;+ // write to _1 makes _1 dead+ // CHECK-NEXT: live: []+ // CHECK-NEXT: live: []+ // CHECK-NEXT: (const 0_usize, const 3_usize);+ _1 = (0, 3);+ // write to _1.1 doesn't make _1 dead+ // CHECK-NEXT: live: [_1]+ // CHECK-NEXT: live: [_1]+ // CHECK-NEXT: (_1.1: usize) = const 4_usize;+ _1.1 = 4;+ // read from _1.1 makes _1 live+ // CHECK-NEXT: live: [_1]+ // CHECK-NEXT: live: [_1]+ // CHECK-NEXT: _0 = copy (_1.1: usize);+ RET = _1.1;+ // CHECK-NEXT: live: [_0]+ // CHECK-NEXT: live: [_0]+ // CHECK-NEXT: return;+ Return()+ }+ }+}++// EMIT_MIR liveness.deref.dataflow.0.mir+#[custom_mir(dialect = "runtime", phase = "optimized")]+#[rustc_mir(rustc_pretty_live_locals)]+#[rustc_mir(rustc_pretty_transitive_live_locals)]+pub fn deref() {+ mir! {+ let _1: usize;+ let _2: &mut usize;+ {+ // CHECK-LABEL: fn deref(+ // CHECK: bb0: {+ // CHECK-NEXT: live: []+ // CHECK-NEXT: live: []+ // CHECK-NEXT: _1 = const 42_usize;+ _1 = 42;+ // CHECK-NEXT: live: [_1]+ // CHECK-NEXT: live: [_1]+ // CHECK-NEXT: _2 = &mut _1;+ _2 = &mut _1;+ // deref of _2 makes it live even on LHS of an assignment+ // CHECK-NEXT: live: [_2]+ // CHECK-NEXT: live: [_2]+ // CHECK-NEXT: (*_2) = const 24_usize;+ *_2 = 24;+ // CHECK-NEXT: live: []+ // CHECK-NEXT: live: []+ // CHECK-NEXT: _0 = ();+ RET = ();+ // CHECK-NEXT: live: [_0]+ // CHECK-NEXT: live: [_0]+ // CHECK-NEXT: return;+ Return()+ }+ }+}tests/mir-opt/dump_dataflow.main.maybe_uninit.borrowck.dotrenamed0 + / 0 −
GitHub publishes no diff for this file. View it on GitHub ↗
tests/mir-opt/dump_dataflow.rsrenamed1 + / 1 −
@@ -2,5 +2,5 @@ // Test graphviz dataflow output //@ compile-flags: -Z dump-mir=main -Z dump-mir-dataflow -// EMIT_MIR dataflow.main.maybe_uninit.borrowck.dot+// EMIT_MIR dump_dataflow.main.maybe_uninit.borrowck.dot fn main() {}