Skip to main content

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