rustc_middle/mir/
graphviz.rs

1use std::io::{self, Write};
2
3use gsgdt::GraphvizSettings;
4use rustc_graphviz as dot;
5
6use super::generic_graph::mir_fn_to_generic_graph;
7use super::pretty::dump_mir_def_ids;
8use crate::mir::*;
9
10/// Write a graphviz DOT graph of a list of MIRs.
11pub fn write_mir_graphviz<W>(tcx: TyCtxt<'_>, single: Option<DefId>, w: &mut W) -> io::Result<()>
12where
13    W: Write,
14{
15    let def_ids = dump_mir_def_ids(tcx, single);
16
17    let mirs = def_ids
18        .iter()
19        .flat_map(|def_id| {
20            if tcx.is_const_fn(*def_id) {
21                vec![tcx.optimized_mir(*def_id), tcx.mir_for_ctfe(*def_id)]
22            } else {
23                vec![tcx.instance_mir(ty::InstanceKind::Item(*def_id))]
24            }
25        })
26        .collect::<Vec<_>>();
27
28    let use_subgraphs = mirs.len() > 1;
29    if use_subgraphs {
30        writeln!(w, "digraph __crate__ {{")?;
31    }
32
33    for mir in mirs {
34        write_mir_fn_graphviz(tcx, mir, use_subgraphs, w)?;
35    }
36
37    if use_subgraphs {
38        writeln!(w, "}}")?;
39    }
40
41    Ok(())
42}
43
44/// Write a graphviz DOT graph of the MIR.
45pub fn write_mir_fn_graphviz<'tcx, W>(
46    tcx: TyCtxt<'tcx>,
47    body: &Body<'_>,
48    subgraph: bool,
49    w: &mut W,
50) -> io::Result<()>
51where
52    W: Write,
53{
54    // Global graph properties
55    let font = format!(r#"fontname="{}""#, tcx.sess.opts.unstable_opts.graphviz_font);
56    let mut graph_attrs = vec![&font[..]];
57    let mut content_attrs = vec![&font[..]];
58
59    let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode;
60    if dark_mode {
61        graph_attrs.push(r#"bgcolor="black""#);
62        graph_attrs.push(r#"fontcolor="white""#);
63        content_attrs.push(r#"color="white""#);
64        content_attrs.push(r#"fontcolor="white""#);
65    }
66
67    // Graph label
68    let mut label = String::from("");
69    // FIXME: remove this unwrap
70    write_graph_label(tcx, body, &mut label).unwrap();
71    let g = mir_fn_to_generic_graph(tcx, body);
72    let settings = GraphvizSettings {
73        graph_attrs: Some(graph_attrs.join(" ")),
74        node_attrs: Some(content_attrs.join(" ")),
75        edge_attrs: Some(content_attrs.join(" ")),
76        graph_label: Some(label),
77    };
78    g.to_dot(w, &settings, subgraph)
79}
80
81/// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
82/// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
83/// all the variables and temporaries.
84fn write_graph_label<'tcx, W: std::fmt::Write>(
85    tcx: TyCtxt<'tcx>,
86    body: &Body<'_>,
87    w: &mut W,
88) -> std::fmt::Result {
89    let def_id = body.source.def_id();
90
91    write!(w, "fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;
92
93    // fn argument types.
94    for (i, arg) in body.args_iter().enumerate() {
95        if i > 0 {
96            write!(w, ", ")?;
97        }
98        write!(w, "{:?}: {}", Place::from(arg), escape(&body.local_decls[arg].ty))?;
99    }
100
101    write!(w, ") -&gt; {}", escape(&body.return_ty()))?;
102    write!(w, r#"<br align="left"/>"#)?;
103
104    for local in body.vars_and_temps_iter() {
105        let decl = &body.local_decls[local];
106
107        write!(w, "let ")?;
108        if decl.mutability.is_mut() {
109            write!(w, "mut ")?;
110        }
111
112        write!(w, r#"{:?}: {};<br align="left"/>"#, Place::from(local), escape(&decl.ty))?;
113    }
114
115    for var_debug_info in &body.var_debug_info {
116        write!(
117            w,
118            r#"debug {} =&gt; {};<br align="left"/>"#,
119            var_debug_info.name,
120            escape(&var_debug_info.value),
121        )?;
122    }
123
124    Ok(())
125}
126
127fn escape<T: Debug>(t: &T) -> String {
128    dot::escape_html(&format!("{t:?}"))
129}