Line data Source code
1 : /* Presentation tweaks for generating .dot from digraphs for GCC CFGs.
2 : Copyright (C) 2025 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3, or (at your option)
10 : any later version.
11 :
12 : GCC is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : #define INCLUDE_ALGORITHM
22 : #define INCLUDE_MAP
23 : #define INCLUDE_SET
24 : #define INCLUDE_STRING
25 : #define INCLUDE_VECTOR
26 : #include "config.h"
27 : #include "system.h"
28 : #include "coretypes.h"
29 :
30 : #include "graphviz.h"
31 : #include "xml.h"
32 : #include "xml-printer.h"
33 : #include "diagnostics/digraphs.h"
34 : #include "diagnostics/digraphs-to-dot.h"
35 : #include "diagnostics/sarif-sink.h"
36 : #include "custom-sarif-properties/cfg.h"
37 :
38 : #include "selftest.h"
39 :
40 : namespace diagnostics {
41 : namespace digraphs {
42 : namespace to_dot {
43 :
44 : namespace {
45 : namespace node_properties = custom_sarif_properties::cfg::node;
46 : namespace edge_properties = custom_sarif_properties::cfg::edge;
47 : }
48 :
49 0 : class converter_from_cfg : public converter
50 : {
51 : public:
52 : std::unique_ptr<dot::graph>
53 45 : make_dot_graph_from_diagnostic_graph (const digraph &dg) final override
54 : {
55 45 : auto dot_graph
56 45 : = converter::make_dot_graph_from_diagnostic_graph (dg);
57 :
58 : /* Add an invisible edge from ENTRY to EXIT, to improve the
59 : graph layout. */
60 45 : if (const digraphs::node *entry_node = get_entry_node (dg))
61 45 : if (const digraphs::node *exit_node = get_exit_node (dg))
62 : {
63 45 : auto extra_edge_stmt
64 : = std::make_unique<dot::edge_stmt>
65 90 : (get_node_id_for_node (*entry_node, "s"),
66 135 : get_node_id_for_node (*exit_node, "n"));
67 45 : extra_edge_stmt->set_attr (dot::id ("style"), dot::id ("invis"));
68 45 : extra_edge_stmt->set_attr (dot::id ("constraint"), dot::id ("true"));
69 45 : dot_graph->m_stmt_list.add_stmt (std::move (extra_edge_stmt));
70 45 : }
71 :
72 45 : return dot_graph;
73 : }
74 :
75 : void
76 45 : add_any_subgraph_attrs (const digraph_node &input_node,
77 : dot::subgraph &output_subgraph) final override
78 : {
79 45 : if (const char *kind = input_node.get_property (node_properties::kind))
80 : {
81 45 : if (strcmp (kind, "function") == 0)
82 : {
83 : }
84 0 : else if (strcmp (kind, "loop") == 0)
85 : {
86 0 : namespace loop_property_names
87 : = custom_sarif_properties::cfg::loop;
88 0 : long num;
89 0 : if (input_node.maybe_get_property (loop_property_names::num, num))
90 : {
91 0 : pretty_printer pp;
92 0 : pp_printf (&pp, "loop %li", num);
93 0 : output_subgraph.add_attr (dot::id ("label"),
94 0 : dot::id (pp_formatted_text (&pp)));
95 0 : }
96 0 : long depth;
97 0 : if (input_node.maybe_get_property (loop_property_names::depth,
98 : depth))
99 : {
100 0 : const char *fillcolors[3] = { "grey88", "grey77", "grey66" };
101 0 : output_subgraph.add_attr
102 0 : (dot::id ("fillcolor"),
103 0 : dot::id (fillcolors[(depth - 1) % 3]));
104 : }
105 0 : output_subgraph.add_attr (dot::id ("style"), dot::id ("filled"));
106 0 : output_subgraph.add_attr (dot::id ("color"), dot::id ("darkgreen"));
107 0 : output_subgraph.add_attr (dot::id ("labeljust"), dot::id ("l"));
108 0 : output_subgraph.add_attr (dot::id ("penwidth"), dot::id ("2"));
109 : }
110 : }
111 45 : }
112 :
113 : void
114 270 : add_any_node_attrs (const digraph_node &input_node,
115 : dot::node_stmt &output_node) final override
116 : {
117 270 : if (const char *node_kind = input_node.get_property (node_properties::kind))
118 270 : if (strcmp (node_kind, "basic_block") == 0)
119 : {
120 270 : namespace bb_property_names
121 : = custom_sarif_properties::cfg::basic_block;
122 270 : const char *shape = nullptr;
123 270 : const char *fillcolor = "lightgrey";
124 270 : const char *label = nullptr;
125 540 : if (const char *bb_kind
126 270 : = input_node.get_property (bb_property_names::kind))
127 : {
128 90 : if (strcmp (bb_kind, "entry") == 0)
129 : {
130 : shape = "Mdiamond";
131 : fillcolor = "white";
132 : label = "ENTRY";
133 : }
134 45 : else if (strcmp (bb_kind, "exit") == 0)
135 : {
136 : shape = "Mdiamond";
137 : fillcolor = "white";
138 : label = "EXIT";
139 : }
140 0 : else if (strcmp (bb_kind, "hot") == 0)
141 : fillcolor = "lightpink";
142 0 : else if (strcmp (bb_kind, "cold") == 0)
143 : fillcolor = "lightblue";
144 : }
145 :
146 : if (shape)
147 90 : output_node.set_attr (dot::id ("shape"), dot::id (shape));
148 : else
149 : {
150 180 : output_node.set_attr (dot::id ("shape"), dot::id ("none"));
151 180 : output_node.set_attr (dot::id ("margin"), dot::id ("0"));
152 : }
153 270 : output_node.set_attr (dot::id ("fillcolor"), dot::id (fillcolor));
154 270 : if (label)
155 90 : output_node.set_label (dot::id (label));
156 : else
157 : {
158 : // Create node with table
159 180 : xml::element table ("table", false);
160 180 : xml::printer xp (table);
161 180 : xp.set_attr ("border", "0");
162 180 : xp.set_attr ("cellborder", "1");
163 180 : xp.set_attr ("cellspacing", "0");
164 :
165 180 : long bb_index;
166 180 : if (input_node.maybe_get_property (bb_property_names::index,
167 : bb_index))
168 : {
169 80 : xp.push_tag ("tr", true);
170 80 : xp.push_tag ("td", true);
171 80 : xp.set_attr ("align", "left");
172 80 : pretty_printer pp;
173 80 : pp_printf (&pp, "<bb %li>:", bb_index);
174 80 : xp.add_text_from_pp (pp);
175 80 : xp.pop_tag ("td");
176 80 : xp.pop_tag ("tr");
177 80 : }
178 :
179 360 : if (json::array *arr
180 180 : = input_node.get_property (bb_property_names::gimple::phis))
181 80 : print_rows_for_strings (*arr, xp);
182 :
183 360 : if (json::array *arr
184 : = input_node.get_property
185 180 : (bb_property_names::gimple::stmts))
186 80 : print_rows_for_strings (*arr, xp);
187 :
188 360 : if (json::array *arr
189 180 : = input_node.get_property (bb_property_names::rtl::insns))
190 100 : print_rows_for_strings (*arr, xp);
191 :
192 : // xml must be done by now
193 :
194 180 : output_node.m_attrs.add (dot::id ("label"),
195 360 : dot::id (table));
196 180 : }
197 : }
198 270 : }
199 :
200 : virtual void
201 270 : add_any_edge_attrs (const digraph_edge &input_edge,
202 : dot::edge_stmt &output_edge) final override
203 : {
204 270 : namespace edge_properties = custom_sarif_properties::cfg::edge;
205 :
206 270 : const char *style = "solid,bold";
207 270 : const char *color = "black";
208 270 : int weight = 10;
209 :
210 270 : if (edge_has_flag (input_edge, "FAKE"))
211 : {
212 0 : style = "dotted";
213 0 : color = "green";
214 0 : weight = 0;
215 : }
216 270 : if (edge_has_flag (input_edge, "DFS_BACK"))
217 : {
218 : style = "dotted,bold";
219 : color = "blue";
220 : weight = 10;
221 : }
222 270 : else if (edge_has_flag (input_edge, "FALLTHRU"))
223 : weight = 100;
224 120 : else if (edge_has_flag (input_edge, "TRUE_VALUE"))
225 : color = "forestgreen";
226 100 : else if (edge_has_flag (input_edge, "FALSE_VALUE"))
227 20 : color = "darkorange";
228 :
229 270 : if (edge_has_flag (input_edge, "ABNORMAL"))
230 0 : color = "red";
231 :
232 270 : output_edge.set_attr (dot::id ("style"), dot::id (style));
233 270 : output_edge.set_attr (dot::id ("color"), dot::id (color));
234 270 : output_edge.set_attr (dot::id ("weight"),
235 540 : dot::id (std::to_string (weight)));
236 540 : output_edge.set_attr (dot::id ("constraint"),
237 270 : dot::id ((edge_has_flag (input_edge, "FAKE")
238 270 : || edge_has_flag (input_edge, "DFS_BACK"))
239 540 : ? "false" : "true"));
240 :
241 270 : long probability_pc;
242 270 : if (input_edge.maybe_get_property (edge_properties::probability_pc,
243 : probability_pc))
244 : {
245 48 : pretty_printer pp;
246 48 : pp_printf (&pp, "[%li%%]", probability_pc);
247 48 : output_edge.set_label (dot::id (pp_formatted_text (&pp)));
248 48 : }
249 270 : }
250 :
251 : private:
252 : bool
253 1840 : edge_has_flag (const digraph_edge &input_edge,
254 : const char *flag_name) const
255 : {
256 1840 : auto flags = input_edge.get_property (edge_properties::flags);
257 5250 : for (auto iter : *flags)
258 1200 : if (auto str = iter->dyn_cast_string ())
259 1200 : if (!strcmp (flag_name, str->get_string ()))
260 : return true;
261 : return false;
262 : }
263 :
264 : void
265 260 : print_rows_for_strings (json::array &arr,
266 : xml::printer &xp)
267 : {
268 1433 : for (auto iter : arr)
269 779 : if (auto js_str = iter->dyn_cast_string ())
270 : {
271 779 : xp.push_tag ("tr", true);
272 779 : xp.push_tag ("td", true);
273 779 : xp.set_attr ("align", "left");
274 779 : xp.add_text (js_str->get_string ());
275 779 : xp.pop_tag ("td");
276 779 : xp.pop_tag ("tr");
277 : }
278 260 : }
279 :
280 : const node *
281 90 : get_bb_node_by_kind (const digraph &dg, const char *kind) const
282 : {
283 135 : for (auto &iter : dg.get_all_nodes ())
284 : {
285 135 : const node &input_node = *iter.second;
286 135 : if (const char *node_kind = input_node.get_property (node_properties::kind))
287 135 : if (strcmp (node_kind, "basic_block") == 0)
288 : {
289 135 : namespace bb_property_names
290 : = custom_sarif_properties::cfg::basic_block;
291 270 : if (const char *bb_kind
292 135 : = input_node.get_property (bb_property_names::kind))
293 : {
294 135 : if (strcmp (bb_kind, kind) == 0)
295 90 : return &input_node;
296 : }
297 : }
298 : }
299 : return nullptr;
300 : }
301 :
302 : const node *
303 45 : get_entry_node (const digraph &dg) const
304 : {
305 45 : return get_bb_node_by_kind (dg, "entry");
306 : }
307 :
308 : const node *
309 45 : get_exit_node (const digraph &dg) const
310 : {
311 45 : return get_bb_node_by_kind (dg, "exit");
312 : }
313 : };
314 :
315 : std::unique_ptr<converter>
316 45 : make_converter_from_cfg ()
317 : {
318 45 : return std::make_unique<converter_from_cfg> ();
319 : }
320 :
321 : } // namespace to_dot
322 : } // namespace digraphs
323 : } // namespace diagnostics
|