Skip to content

Commit 4031f7b

Browse files
committed
Auto merge of #78399 - vn-ki:gsgdt-graphviz, r=oli-obk
make MIR graphviz generation use gsgdt gsgdt [https://crates.io/crates/gsgdt] is a crate which provides an interface for stringly typed graphs. It also provides generation of graphviz dot format from said graph. This is the first in a series of PRs on moving graphviz code out of rustc into normal crates and then implementating graph diffing on top of these crates. r? `@oli-obk`
2 parents f76ecd0 + 6fe31e7 commit 4031f7b

File tree

7 files changed

+103
-136
lines changed

7 files changed

+103
-136
lines changed

Cargo.lock

+10
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,15 @@ dependencies = [
13451345
"regex",
13461346
]
13471347

1348+
[[package]]
1349+
name = "gsgdt"
1350+
version = "0.1.2"
1351+
source = "registry+https://github.com/rust-lang/crates.io-index"
1352+
checksum = "a0d876ce7262df96262a2a19531da6ff9a86048224d49580a585fc5c04617825"
1353+
dependencies = [
1354+
"serde",
1355+
]
1356+
13481357
[[package]]
13491358
name = "handlebars"
13501359
version = "3.4.0"
@@ -3940,6 +3949,7 @@ version = "0.0.0"
39403949
dependencies = [
39413950
"coverage_test_macros",
39423951
"either",
3952+
"gsgdt",
39433953
"itertools 0.9.0",
39443954
"polonius-engine",
39453955
"regex",

compiler/rustc_mir/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ doctest = false
1010
[dependencies]
1111
either = "1.5.0"
1212
rustc_graphviz = { path = "../rustc_graphviz" }
13+
gsgdt = "0.1.2"
1314
itertools = "0.9"
1415
tracing = "0.1"
1516
polonius-engine = "0.12.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use gsgdt::{Edge, Graph, Node, NodeStyle};
2+
use rustc_hir::def_id::DefId;
3+
use rustc_index::vec::Idx;
4+
use rustc_middle::mir::*;
5+
use rustc_middle::ty::TyCtxt;
6+
7+
/// Convert an MIR function into a gsgdt Graph
8+
pub fn mir_fn_to_generic_graph<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Graph {
9+
let def_id = body.source.def_id();
10+
let def_name = graphviz_safe_def_name(def_id);
11+
let graph_name = format!("Mir_{}", def_name);
12+
let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
13+
14+
// Nodes
15+
let nodes: Vec<Node> = body
16+
.basic_blocks()
17+
.iter_enumerated()
18+
.map(|(block, _)| bb_to_graph_node(block, body, dark_mode))
19+
.collect();
20+
21+
// Edges
22+
let mut edges = Vec::new();
23+
for (source, _) in body.basic_blocks().iter_enumerated() {
24+
let def_id = body.source.def_id();
25+
let terminator = body[source].terminator();
26+
let labels = terminator.kind.fmt_successor_labels();
27+
28+
for (&target, label) in terminator.successors().zip(labels) {
29+
let src = node(def_id, source);
30+
let trg = node(def_id, target);
31+
edges.push(Edge::new(src, trg, label.to_string()));
32+
}
33+
}
34+
35+
Graph::new(graph_name, nodes, edges)
36+
}
37+
38+
fn bb_to_graph_node(block: BasicBlock, body: &Body<'_>, dark_mode: bool) -> Node {
39+
let def_id = body.source.def_id();
40+
let data = &body[block];
41+
let label = node(def_id, block);
42+
43+
let (title, bgcolor) = if data.is_cleanup {
44+
let color = if dark_mode { "royalblue" } else { "lightblue" };
45+
(format!("{} (cleanup)", block.index()), color)
46+
} else {
47+
let color = if dark_mode { "dimgray" } else { "gray" };
48+
(format!("{}", block.index()), color)
49+
};
50+
51+
let style = NodeStyle { title_bg: Some(bgcolor.to_owned()), ..Default::default() };
52+
let mut stmts: Vec<String> = data.statements.iter().map(|x| format!("{:?}", x)).collect();
53+
54+
// add the terminator to the stmts, gsgdt can print it out seperately
55+
let mut terminator_head = String::new();
56+
data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
57+
stmts.push(terminator_head);
58+
59+
Node::new(stmts, label, title, style)
60+
}
61+
62+
// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
63+
// it does not have to be user friendly.
64+
pub fn graphviz_safe_def_name(def_id: DefId) -> String {
65+
format!("{}_{}", def_id.krate.index(), def_id.index.index(),)
66+
}
67+
68+
fn node(def_id: DefId, block: BasicBlock) -> String {
69+
format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
70+
}

compiler/rustc_mir/src/util/graphviz.rs

+16-131
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use gsgdt::GraphvizSettings;
12
use rustc_graphviz as dot;
23
use rustc_hir::def_id::DefId;
3-
use rustc_index::vec::Idx;
44
use rustc_middle::mir::*;
55
use rustc_middle::ty::TyCtxt;
66
use std::fmt::Debug;
77
use std::io::{self, Write};
88

9+
use super::generic_graph::mir_fn_to_generic_graph;
910
use super::pretty::dump_mir_def_ids;
1011

1112
/// Write a graphviz DOT graph of a list of MIRs.
@@ -32,12 +33,6 @@ where
3233
Ok(())
3334
}
3435

35-
// Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
36-
// it does not have to be user friendly.
37-
pub fn graphviz_safe_def_name(def_id: DefId) -> String {
38-
format!("{}_{}", def_id.krate.index(), def_id.index.index(),)
39-
}
40-
4136
/// Write a graphviz DOT graph of the MIR.
4237
pub fn write_mir_fn_graphviz<'tcx, W>(
4338
tcx: TyCtxt<'tcx>,
@@ -48,12 +43,6 @@ pub fn write_mir_fn_graphviz<'tcx, W>(
4843
where
4944
W: Write,
5045
{
51-
let def_id = body.source.def_id();
52-
let kind = if subgraph { "subgraph" } else { "digraph" };
53-
let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR
54-
let def_name = graphviz_safe_def_name(def_id);
55-
writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?;
56-
5746
// Global graph properties
5847
let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font);
5948
let mut graph_attrs = vec![&font[..]];
@@ -67,131 +56,31 @@ where
6756
content_attrs.push(r#"fontcolor="white""#);
6857
}
6958

70-
writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?;
71-
let content_attrs_str = content_attrs.join(" ");
72-
writeln!(w, r#" node [{}];"#, content_attrs_str)?;
73-
writeln!(w, r#" edge [{}];"#, content_attrs_str)?;
74-
7559
// Graph label
76-
write_graph_label(tcx, body, w)?;
77-
78-
// Nodes
79-
for (block, _) in body.basic_blocks().iter_enumerated() {
80-
write_node(block, body, dark_mode, w)?;
81-
}
82-
83-
// Edges
84-
for (source, _) in body.basic_blocks().iter_enumerated() {
85-
write_edges(source, body, w)?;
86-
}
87-
writeln!(w, "}}")
88-
}
89-
90-
/// Write a graphviz HTML-styled label for the given basic block, with
91-
/// all necessary escaping already performed. (This is suitable for
92-
/// emitting directly, as is done in this module, or for use with the
93-
/// LabelText::HtmlStr from librustc_graphviz.)
94-
///
95-
/// `init` and `fini` are callbacks for emitting additional rows of
96-
/// data (using HTML enclosed with `<tr>` in the emitted text).
97-
pub fn write_node_label<W: Write, INIT, FINI>(
98-
block: BasicBlock,
99-
body: &Body<'_>,
100-
dark_mode: bool,
101-
w: &mut W,
102-
num_cols: u32,
103-
init: INIT,
104-
fini: FINI,
105-
) -> io::Result<()>
106-
where
107-
INIT: Fn(&mut W) -> io::Result<()>,
108-
FINI: Fn(&mut W) -> io::Result<()>,
109-
{
110-
let data = &body[block];
111-
112-
write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
113-
114-
// Basic block number at the top.
115-
let (blk, bgcolor) = if data.is_cleanup {
116-
let color = if dark_mode { "royalblue" } else { "lightblue" };
117-
(format!("{} (cleanup)", block.index()), color)
118-
} else {
119-
let color = if dark_mode { "dimgray" } else { "gray" };
120-
(format!("{}", block.index()), color)
60+
let mut label = String::from("");
61+
// FIXME: remove this unwrap
62+
write_graph_label(tcx, body, &mut label).unwrap();
63+
let g = mir_fn_to_generic_graph(tcx, body);
64+
let settings = GraphvizSettings {
65+
graph_attrs: Some(graph_attrs.join(" ")),
66+
node_attrs: Some(content_attrs.join(" ")),
67+
edge_attrs: Some(content_attrs.join(" ")),
68+
graph_label: Some(label),
12169
};
122-
write!(
123-
w,
124-
r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
125-
attrs = r#"align="center""#,
126-
colspan = num_cols,
127-
blk = blk,
128-
bgcolor = bgcolor
129-
)?;
130-
131-
init(w)?;
132-
133-
// List of statements in the middle.
134-
if !data.statements.is_empty() {
135-
write!(w, r#"<tr><td align="left" balign="left">"#)?;
136-
for statement in &data.statements {
137-
write!(w, "{}<br/>", escape(statement))?;
138-
}
139-
write!(w, "</td></tr>")?;
140-
}
141-
142-
// Terminator head at the bottom, not including the list of successor blocks. Those will be
143-
// displayed as labels on the edges between blocks.
144-
let mut terminator_head = String::new();
145-
data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
146-
write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
147-
148-
fini(w)?;
149-
150-
// Close the table
151-
write!(w, "</table>")
152-
}
153-
154-
/// Write a graphviz DOT node for the given basic block.
155-
fn write_node<W: Write>(
156-
block: BasicBlock,
157-
body: &Body<'_>,
158-
dark_mode: bool,
159-
w: &mut W,
160-
) -> io::Result<()> {
161-
let def_id = body.source.def_id();
162-
// Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
163-
write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?;
164-
write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?;
165-
// Close the node label and the node itself.
166-
writeln!(w, ">];")
167-
}
168-
169-
/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
170-
fn write_edges<W: Write>(source: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> {
171-
let def_id = body.source.def_id();
172-
let terminator = body[source].terminator();
173-
let labels = terminator.kind.fmt_successor_labels();
174-
175-
for (&target, label) in terminator.successors().zip(labels) {
176-
let src = node(def_id, source);
177-
let trg = node(def_id, target);
178-
writeln!(w, r#" {} -> {} [label="{}"];"#, src, trg, label)?;
179-
}
180-
181-
Ok(())
70+
g.to_dot(w, &settings, subgraph)
18271
}
18372

18473
/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
18574
/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
18675
/// all the variables and temporaries.
187-
fn write_graph_label<'tcx, W: Write>(
76+
fn write_graph_label<'tcx, W: std::fmt::Write>(
18877
tcx: TyCtxt<'tcx>,
18978
body: &Body<'_>,
19079
w: &mut W,
191-
) -> io::Result<()> {
80+
) -> std::fmt::Result {
19281
let def_id = body.source.def_id();
19382

194-
write!(w, " label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;
83+
write!(w, "fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;
19584

19685
// fn argument types.
19786
for (i, arg) in body.args_iter().enumerate() {
@@ -224,11 +113,7 @@ fn write_graph_label<'tcx, W: Write>(
224113
)?;
225114
}
226115

227-
writeln!(w, ">;")
228-
}
229-
230-
fn node(def_id: DefId, block: BasicBlock) -> String {
231-
format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
116+
Ok(())
232117
}
233118

234119
fn escape<T: Debug>(t: &T) -> String {

compiler/rustc_mir/src/util/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod storage;
77
mod alignment;
88
pub mod collect_writes;
99
mod find_self_call;
10+
mod generic_graph;
1011
pub(crate) mod generic_graphviz;
1112
mod graphviz;
1213
pub(crate) mod pretty;
@@ -15,6 +16,6 @@ pub(crate) mod spanview;
1516
pub use self::aggregate::expand_aggregate;
1617
pub use self::alignment::is_disaligned;
1718
pub use self::find_self_call::find_self_call;
18-
pub use self::graphviz::write_node_label as write_graphviz_node_label;
19-
pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz};
19+
pub use self::generic_graph::graphviz_safe_def_name;
20+
pub use self::graphviz::write_mir_graphviz;
2021
pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere};

src/tools/clippy/tests/ui/crashes/used_underscore_binding_macro.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
#![allow(clippy::useless_attribute)] //issue #2910
1+
// edition:2018
22

3-
#[macro_use]
4-
extern crate serde_derive;
3+
use serde::Deserialize;
54

65
/// Tests that we do not lint for unused underscores in a `MacroAttribute`
76
/// expansion

src/tools/tidy/src/deps.rs

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ const PERMITTED_DEPENDENCIES: &[&str] = &[
104104
"getopts",
105105
"getrandom",
106106
"gimli",
107+
"gsgdt",
107108
"hashbrown",
108109
"hermit-abi",
109110
"humantime",

0 commit comments

Comments
 (0)