]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/mir/generic_graphviz.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / compiler / rustc_middle / src / mir / generic_graphviz.rs
CommitLineData
29967ef6
XL
1use rustc_data_structures::graph::{self, iterate};
2use rustc_graphviz as dot;
3use rustc_middle::ty::TyCtxt;
4use std::io::{self, Write};
5
6pub struct GraphvizWriter<
7 'a,
8 G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes,
6a06907d
XL
9 NodeContentFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
10 EdgeLabelsFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
29967ef6
XL
11> {
12 graph: &'a G,
13 is_subgraph: bool,
14 graphviz_name: String,
15 graph_label: Option<String>,
16 node_content_fn: NodeContentFn,
17 edge_labels_fn: EdgeLabelsFn,
18}
19
20impl<
21 'a,
22 G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes,
6a06907d
XL
23 NodeContentFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
24 EdgeLabelsFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
29967ef6
XL
25> GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn>
26{
27 pub fn new(
28 graph: &'a G,
29 graphviz_name: &str,
30 node_content_fn: NodeContentFn,
31 edge_labels_fn: EdgeLabelsFn,
32 ) -> Self {
33 Self {
34 graph,
35 is_subgraph: false,
36 graphviz_name: graphviz_name.to_owned(),
37 graph_label: None,
38 node_content_fn,
39 edge_labels_fn,
40 }
41 }
42
29967ef6
XL
43 pub fn set_graph_label(&mut self, graph_label: &str) {
44 self.graph_label = Some(graph_label.to_owned());
45 }
46
47 /// Write a graphviz DOT of the graph
48 pub fn write_graphviz<'tcx, W>(&self, tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()>
49 where
50 W: Write,
51 {
52 let kind = if self.is_subgraph { "subgraph" } else { "digraph" };
53 let cluster = if self.is_subgraph { "cluster_" } else { "" }; // Print border around graph
54 // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation,
55 // prepend "Mir_" to the graphviz_safe_def_name(def_id)
56 writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?;
57
58 // Global graph properties
064997fb 59 let font = format!(r#"fontname="{}""#, tcx.sess.opts.unstable_opts.graphviz_font);
29967ef6
XL
60 let mut graph_attrs = vec![&font[..]];
61 let mut content_attrs = vec![&font[..]];
62
064997fb 63 let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode;
29967ef6
XL
64 if dark_mode {
65 graph_attrs.push(r#"bgcolor="black""#);
66 graph_attrs.push(r#"fontcolor="white""#);
67 content_attrs.push(r#"color="white""#);
68 content_attrs.push(r#"fontcolor="white""#);
69 }
70
71 writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?;
72 let content_attrs_str = content_attrs.join(" ");
73 writeln!(w, r#" node [{}];"#, content_attrs_str)?;
74 writeln!(w, r#" edge [{}];"#, content_attrs_str)?;
75
76 // Graph label
77 if let Some(graph_label) = &self.graph_label {
78 self.write_graph_label(graph_label, w)?;
79 }
80
81 // Nodes
82 for node in iterate::post_order_from(self.graph, self.graph.start_node()) {
83 self.write_node(node, dark_mode, w)?;
84 }
85
86 // Edges
87 for source in iterate::post_order_from(self.graph, self.graph.start_node()) {
88 self.write_edges(source, w)?;
89 }
90 writeln!(w, "}}")
91 }
92
93 /// Write a graphviz DOT node for the given node.
94 pub fn write_node<W>(&self, node: G::Node, dark_mode: bool, w: &mut W) -> io::Result<()>
95 where
96 W: Write,
97 {
98 // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
99 write!(w, r#" {} [shape="none", label=<"#, self.node(node))?;
100
101 write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
102
fc512014
XL
103 // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation,
104 // we need generic way to know if node header should have a different color. For example,
105 // for MIR:
106 //
29967ef6 107 // let (blk, bgcolor) = if data.is_cleanup {
fc512014
XL
108 // let color = if dark_mode { "royalblue" } else { "lightblue" };
109 // (format!("{:?} (cleanup)", node), color)
29967ef6
XL
110 // } else {
111 // let color = if dark_mode { "dimgray" } else { "gray" };
112 // (format!("{:?}", node), color)
113 // };
114 let color = if dark_mode { "dimgray" } else { "gray" };
115 let (blk, bgcolor) = (format!("{:?}", node), color);
116 write!(
117 w,
118 r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
119 attrs = r#"align="center""#,
120 colspan = 1,
121 blk = blk,
122 bgcolor = bgcolor
123 )?;
124
125 for section in (self.node_content_fn)(node) {
126 write!(
127 w,
128 r#"<tr><td align="left" balign="left">{}</td></tr>"#,
487cf647 129 dot::escape_html(&section)
29967ef6
XL
130 )?;
131 }
132
133 // Close the table
134 write!(w, "</table>")?;
135
136 // Close the node label and the node itself.
137 writeln!(w, ">];")
138 }
139
140 /// Write graphviz DOT edges with labels between the given node and all of its successors.
141 fn write_edges<W>(&self, source: G::Node, w: &mut W) -> io::Result<()>
142 where
143 W: Write,
144 {
145 let edge_labels = (self.edge_labels_fn)(source);
146 for (index, target) in self.graph.successors(source).enumerate() {
147 let src = self.node(source);
148 let trg = self.node(target);
149 let escaped_edge_label = if let Some(edge_label) = edge_labels.get(index) {
487cf647 150 dot::escape_html(edge_label)
29967ef6
XL
151 } else {
152 "".to_owned()
153 };
154 writeln!(w, r#" {} -> {} [label=<{}>];"#, src, trg, escaped_edge_label)?;
155 }
156 Ok(())
157 }
158
159 /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
160 /// will appear below the graph.
161 fn write_graph_label<W>(&self, label: &str, w: &mut W) -> io::Result<()>
162 where
163 W: Write,
164 {
487cf647 165 let escaped_label = dot::escape_html(label);
29967ef6
XL
166 writeln!(w, r#" label=<<br/><br/>{}<br align="left"/><br/><br/><br/>>;"#, escaped_label)
167 }
168
169 fn node(&self, node: G::Node) -> String {
170 format!("{:?}__{}", node, self.graphviz_name)
171 }
172}