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