Skip to content

Commit 06107a2

Browse files
authored
Rollup merge of rust-lang#134065 - nnethercote:mv-write_graphviz_results, r=tmiasko
Move `write_graphviz_results` r? ``@tmiasko``
2 parents 599ff4d + b59c4dc commit 06107a2

File tree

4 files changed

+182
-191
lines changed

4 files changed

+182
-191
lines changed

Diff for: compiler/rustc_mir_dataflow/src/framework/graphviz.rs

+175-8
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,186 @@
22
33
use std::borrow::Cow;
44
use std::cell::RefCell;
5+
use std::ffi::OsString;
6+
use std::path::PathBuf;
57
use std::sync::OnceLock;
68
use std::{io, ops, str};
79

810
use regex::Regex;
9-
use rustc_graphviz as dot;
11+
use rustc_hir::def_id::DefId;
1012
use rustc_index::bit_set::BitSet;
11-
use rustc_middle::mir::{self, BasicBlock, Body, Location, graphviz_safe_def_name};
13+
use rustc_middle::mir::{
14+
self, BasicBlock, Body, Location, create_dump_file, dump_enabled, graphviz_safe_def_name,
15+
traversal,
16+
};
17+
use rustc_middle::ty::TyCtxt;
18+
use rustc_middle::ty::print::with_no_trimmed_paths;
19+
use rustc_span::symbol::{Symbol, sym};
20+
use tracing::debug;
21+
use {rustc_ast as ast, rustc_graphviz as dot};
1222

1323
use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
1424
use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor};
25+
use crate::errors::{
26+
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
27+
};
28+
29+
/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
30+
/// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are
31+
/// the same.
32+
pub(super) fn write_graphviz_results<'tcx, A>(
33+
tcx: TyCtxt<'tcx>,
34+
body: &Body<'tcx>,
35+
results: &mut Results<'tcx, A>,
36+
pass_name: Option<&'static str>,
37+
) -> std::io::Result<()>
38+
where
39+
A: Analysis<'tcx>,
40+
A::Domain: DebugWithContext<A>,
41+
{
42+
use std::fs;
43+
use std::io::Write;
44+
45+
let def_id = body.source.def_id();
46+
let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else {
47+
// Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
48+
return Ok(());
49+
};
50+
51+
let file = try {
52+
match attrs.output_path(A::NAME) {
53+
Some(path) => {
54+
debug!("printing dataflow results for {:?} to {}", def_id, path.display());
55+
if let Some(parent) = path.parent() {
56+
fs::create_dir_all(parent)?;
57+
}
58+
fs::File::create_buffered(&path)?
59+
}
60+
61+
None if dump_enabled(tcx, A::NAME, def_id) => {
62+
create_dump_file(tcx, "dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
63+
}
64+
65+
_ => return Ok(()),
66+
}
67+
};
68+
let mut file = match file {
69+
Ok(f) => f,
70+
Err(e) => return Err(e),
71+
};
72+
73+
let style = match attrs.formatter {
74+
Some(sym::two_phase) => OutputStyle::BeforeAndAfter,
75+
_ => OutputStyle::AfterOnly,
76+
};
77+
78+
let mut buf = Vec::new();
79+
80+
let graphviz = Formatter::new(body, results, style);
81+
let mut render_opts =
82+
vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())];
83+
if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
84+
render_opts.push(dot::RenderOption::DarkTheme);
85+
}
86+
let r = with_no_trimmed_paths!(dot::render_opts(&graphviz, &mut buf, &render_opts));
87+
88+
let lhs = try {
89+
r?;
90+
file.write_all(&buf)?;
91+
};
92+
93+
lhs
94+
}
95+
96+
#[derive(Default)]
97+
struct RustcMirAttrs {
98+
basename_and_suffix: Option<PathBuf>,
99+
formatter: Option<Symbol>,
100+
}
101+
102+
impl RustcMirAttrs {
103+
fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result<Self, ()> {
104+
let mut result = Ok(());
105+
let mut ret = RustcMirAttrs::default();
106+
107+
let rustc_mir_attrs = tcx
108+
.get_attrs(def_id, sym::rustc_mir)
109+
.flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
110+
111+
for attr in rustc_mir_attrs {
112+
let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) {
113+
Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| {
114+
let path = PathBuf::from(s.to_string());
115+
match path.file_name() {
116+
Some(_) => Ok(path),
117+
None => {
118+
tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() });
119+
Err(())
120+
}
121+
}
122+
})
123+
} else if attr.has_name(sym::borrowck_graphviz_format) {
124+
Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
125+
sym::gen_kill | sym::two_phase => Ok(s),
126+
_ => {
127+
tcx.dcx().emit_err(UnknownFormatter { span: attr.span() });
128+
Err(())
129+
}
130+
})
131+
} else {
132+
Ok(())
133+
};
134+
135+
result = result.and(attr_result);
136+
}
137+
138+
result.map(|()| ret)
139+
}
140+
141+
fn set_field<T>(
142+
field: &mut Option<T>,
143+
tcx: TyCtxt<'_>,
144+
attr: &ast::MetaItemInner,
145+
mapper: impl FnOnce(Symbol) -> Result<T, ()>,
146+
) -> Result<(), ()> {
147+
if field.is_some() {
148+
tcx.dcx()
149+
.emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() });
150+
151+
return Err(());
152+
}
153+
154+
if let Some(s) = attr.value_str() {
155+
*field = Some(mapper(s)?);
156+
Ok(())
157+
} else {
158+
tcx.dcx()
159+
.emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() });
160+
Err(())
161+
}
162+
}
163+
164+
/// Returns the path where dataflow results should be written, or `None`
165+
/// `borrowck_graphviz_postflow` was not specified.
166+
///
167+
/// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
168+
///
169+
/// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
170+
fn output_path(&self, analysis_name: &str) -> Option<PathBuf> {
171+
let mut ret = self.basename_and_suffix.as_ref().cloned()?;
172+
let suffix = ret.file_name().unwrap(); // Checked when parsing attrs
173+
174+
let mut file_name: OsString = analysis_name.into();
175+
file_name.push("_");
176+
file_name.push(suffix);
177+
ret.set_file_name(file_name);
178+
179+
Some(ret)
180+
}
181+
}
15182

16183
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17-
pub(crate) enum OutputStyle {
184+
enum OutputStyle {
18185
AfterOnly,
19186
BeforeAndAfter,
20187
}
@@ -28,7 +195,7 @@ impl OutputStyle {
28195
}
29196
}
30197

31-
pub(crate) struct Formatter<'mir, 'tcx, A>
198+
struct Formatter<'mir, 'tcx, A>
32199
where
33200
A: Analysis<'tcx>,
34201
{
@@ -45,12 +212,12 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A>
45212
where
46213
A: Analysis<'tcx>,
47214
{
48-
pub(crate) fn new(
215+
fn new(
49216
body: &'mir Body<'tcx>,
50217
results: &'mir mut Results<'tcx, A>,
51218
style: OutputStyle,
52219
) -> Self {
53-
let reachable = mir::traversal::reachable_as_bitset(body);
220+
let reachable = traversal::reachable_as_bitset(body);
54221
Formatter { cursor: results.as_results_cursor(body).into(), style, reachable }
55222
}
56223

@@ -61,7 +228,7 @@ where
61228

62229
/// A pair of a basic block and an index into that basic blocks `successors`.
63230
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
64-
pub(crate) struct CfgEdge {
231+
struct CfgEdge {
65232
source: BasicBlock,
66233
index: usize,
67234
}
@@ -520,7 +687,7 @@ struct StateDiffCollector<D> {
520687

521688
impl<D> StateDiffCollector<D> {
522689
fn run<'tcx, A>(
523-
body: &mir::Body<'tcx>,
690+
body: &Body<'tcx>,
524691
block: BasicBlock,
525692
results: &mut Results<'tcx, A>,
526693
style: OutputStyle,

Diff for: compiler/rustc_mir_dataflow/src/framework/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, Terminator
4242
use rustc_middle::ty::TyCtxt;
4343
use tracing::error;
4444

45-
use self::results::write_graphviz_results;
45+
use self::graphviz::write_graphviz_results;
4646
use super::fmt::DebugWithContext;
4747

4848
mod cursor;

0 commit comments

Comments
 (0)