rust-lang/rust · #163721

Annotate MIR with dataflow results for testing purposes

tmiasko · merged Oct 5, 202615 files · 304 + / 3 −
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() {}