Line data Source code
1 : /* Generating diagnostics graphs from 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 under
8 : the terms of the GNU General Public License as published by the Free
9 : Software Foundation; either version 3, or (at your option) any later
10 : version.
11 :
12 : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : 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_LIST
22 : #define INCLUDE_MAP
23 : #define INCLUDE_STRING
24 : #define INCLUDE_VECTOR
25 : #include "config.h"
26 : #include "system.h"
27 : #include "coretypes.h"
28 : #include "version.h"
29 : #include "tree.h"
30 : #include "lazily-created.h"
31 : #include "diagnostics/digraphs.h"
32 : #include "diagnostics/dumping.h"
33 : #include "diagnostic.h"
34 : #include "tree-pass.h"
35 : #include "custom-sarif-properties/cfg.h"
36 : #include "tree-diagnostic-sink-extensions.h"
37 : #include "tree-logical-location.h"
38 : #include "tree-pass.h"
39 : #include "function.h"
40 : #include "topics/pass-events.h"
41 : #include "diagnostics/digraphs.h"
42 : #include "diagnostics/sink.h"
43 : #include "context.h"
44 : #include "channels.h"
45 : #include "bitmap.h"
46 : #include "sbitmap.h"
47 : #include "cfghooks.h"
48 : #include "cfganal.h"
49 : #include "cfgloop.h"
50 : #include "graph.h"
51 : #include "basic-block.h"
52 : #include "cfg.h"
53 :
54 :
55 : namespace {
56 : namespace graph_properties = custom_sarif_properties::cfg::graph;
57 : namespace node_properties = custom_sarif_properties::cfg::node;
58 : namespace edge_properties = custom_sarif_properties::cfg::edge;
59 : }
60 :
61 : /* Disable warnings about quoting issues in the pp_xxx calls below
62 : that (intentionally) don't follow GCC diagnostic conventions. */
63 : #if __GNUC__ >= 10
64 : # pragma GCC diagnostic push
65 : # pragma GCC diagnostic ignored "-Wformat-diag"
66 : #endif
67 :
68 180 : class cfg_diagnostic_digraph
69 : : public lazily_created<diagnostics::digraphs::digraph>
70 : {
71 : public:
72 90 : cfg_diagnostic_digraph (function *fun,
73 : opt_pass *pass)
74 90 : : m_fun (fun), m_pass (pass)
75 : {}
76 :
77 : std::unique_ptr<diagnostics::digraphs::digraph>
78 90 : create_object () const final override
79 : {
80 90 : auto g = std::make_unique<diagnostics::digraphs::digraph> ();
81 90 : g->set_graph_kind ("cfg");
82 :
83 90 : pretty_printer pp;
84 90 : pp_printf (&pp, "%s: %s", function_name (m_fun), m_pass->name);
85 90 : g->set_description (pp_formatted_text (&pp));
86 :
87 90 : g->set_property (graph_properties::pass_name, m_pass->name);
88 90 : g->set_property (graph_properties::pass_number, m_pass->static_pass_number);
89 :
90 90 : add_cluster_for_function (*g, m_fun);
91 180 : return g;
92 90 : }
93 :
94 : void
95 90 : add_cluster_for_function (diagnostics::digraphs::digraph &g,
96 : function *fun) const
97 : {
98 90 : const char *funcname = function_name (fun);
99 90 : auto cluster = std::make_unique<diagnostics::digraphs::node> (g, funcname);
100 90 : cluster->set_property (node_properties::kind, "function");
101 :
102 90 : bb_to_node_map node_map;
103 90 : add_cfg_nodes (g, node_map, *cluster, fun);
104 90 : add_cfg_edges (g, node_map, fun);
105 90 : g.add_node (std::move (cluster));
106 90 : }
107 :
108 : private:
109 : typedef std::map<basic_block, diagnostics::digraphs::node *> bb_to_node_map;
110 :
111 : /* Add a basic block BB belonging to the function with FUNCDEF_NO
112 : as its unique number. */
113 : void
114 540 : add_cfg_node (diagnostics::digraphs::digraph &g,
115 : bb_to_node_map &node_map,
116 : diagnostics::digraphs::node &parent_node,
117 : int funcdef_no,
118 : basic_block bb) const
119 : {
120 540 : pretty_printer pp;
121 540 : pp_printf (&pp, "fn_%d_basic_block_%d",
122 : funcdef_no, bb->index);
123 540 : std::string name (pp_formatted_text (&pp));
124 540 : auto bb_node = std::make_unique<diagnostics::digraphs::node> (g, name);
125 540 : node_map.insert ({bb, bb_node.get ()});
126 :
127 540 : bb_node->set_property (node_properties::kind, "basic_block");
128 :
129 540 : dump_bb_as_sarif_properties (nullptr,
130 540 : bb_node->ensure_property_bag (),
131 : bb);
132 :
133 540 : parent_node.add_child (std::move (bb_node));
134 540 : }
135 :
136 : /* Add all successor edges of a basic block BB belonging to the function
137 : with FUNCDEF_NO as its unique number. */
138 : void
139 540 : add_cfg_node_succ_edges (diagnostics::digraphs::digraph &g,
140 : bb_to_node_map &node_map,
141 : int /*funcdef_no*/,
142 : basic_block bb) const
143 : {
144 540 : edge e;
145 540 : edge_iterator ei;
146 1080 : FOR_EACH_EDGE (e, ei, bb->succs)
147 : {
148 540 : auto src_node = node_map.find (e->src);
149 540 : gcc_assert (src_node != node_map.end ());
150 540 : auto dst_node = node_map.find (e->dest);
151 540 : gcc_assert (dst_node != node_map.end ());
152 :
153 540 : auto diag_edge
154 1080 : = std::make_unique<diagnostics::digraphs::edge> (g, nullptr,
155 540 : *(src_node->second),
156 540 : *(dst_node->second));
157 540 : auto flag_arr = std::make_unique<json::array> ();
158 : #define DEF_EDGE_FLAG(NAME,IDX) \
159 : { handle_edge_flag (*flag_arr, #NAME, (e->flags & EDGE_##NAME)); }
160 : #include "cfg-flags.def"
161 : #undef DEF_EDGE_FLAG
162 :
163 540 : auto &bag = diag_edge->ensure_property_bag ();
164 540 : bag.set<json::array> (edge_properties::flags.m_key.get (),
165 : std::move (flag_arr));
166 :
167 540 : if (e->probability.initialized_p ())
168 96 : diag_edge->set_property (edge_properties::probability_pc,
169 96 : (e->probability.to_reg_br_prob_base ()
170 : * 100 / REG_BR_PROB_BASE));
171 :
172 540 : g.add_edge (std::move (diag_edge));
173 540 : }
174 540 : }
175 :
176 : void
177 9720 : handle_edge_flag (json::array &flag_arr,
178 : const char *flag_name,
179 : bool value) const
180 : {
181 920 : if (value)
182 380 : flag_arr.append_string (flag_name);
183 : }
184 :
185 : /* Add all the basic blocks in the CFG in case loops are not available.
186 : First compute a topological order of the blocks to get a good ranking of
187 : the nodes. Then, if any nodes are not reachable from ENTRY, add them at
188 : the end. */
189 : void
190 40 : add_cfg_nodes_no_loops (diagnostics::digraphs::digraph &g,
191 : bb_to_node_map &node_map,
192 : diagnostics::digraphs::node &parent_node,
193 : function *fun) const
194 : {
195 40 : int *rpo = XNEWVEC (int, n_basic_blocks_for_fn (fun));
196 40 : int i, n;
197 :
198 40 : auto_sbitmap visited (last_basic_block_for_fn (fun));
199 40 : bitmap_clear (visited);
200 :
201 40 : n = pre_and_rev_post_order_compute_fn (fun, NULL, rpo, true);
202 280 : for (i = n_basic_blocks_for_fn (fun) - n;
203 280 : i < n_basic_blocks_for_fn (fun); i++)
204 : {
205 240 : basic_block bb = BASIC_BLOCK_FOR_FN (fun, rpo[i]);
206 240 : add_cfg_node (g, node_map, parent_node, fun->funcdef_no, bb);
207 240 : bitmap_set_bit (visited, bb->index);
208 : }
209 40 : free (rpo);
210 :
211 40 : if (n != n_basic_blocks_for_fn (fun))
212 : {
213 : /* Some blocks are unreachable. We still want to dump them. */
214 0 : basic_block bb;
215 0 : FOR_ALL_BB_FN (bb, fun)
216 0 : if (! bitmap_bit_p (visited, bb->index))
217 0 : add_cfg_node (g, node_map, parent_node, fun->funcdef_no, bb);
218 : }
219 40 : }
220 :
221 : /* Add all the basic blocks in LOOP. Add the blocks in breath-first
222 : order to get a good ranking of the nodes. This function is recursive:
223 : It first adds inner loops, then the body of LOOP itself. */
224 :
225 : void
226 50 : add_cfg_nodes_for_loop (diagnostics::digraphs::digraph &g,
227 : bb_to_node_map &node_map,
228 : diagnostics::digraphs::node *parent_node,
229 : int funcdef_no,
230 : class loop *loop) const
231 : {
232 50 : namespace loop_properties = custom_sarif_properties::cfg::loop;
233 :
234 50 : gcc_assert (parent_node);
235 50 : diagnostics::digraphs::node &orig_parent_node = *parent_node;
236 :
237 50 : unsigned int i;
238 50 : std::unique_ptr<diagnostics::digraphs::node> loop_node;
239 :
240 50 : if (loop->header != NULL
241 50 : && loop->latch != EXIT_BLOCK_PTR_FOR_FN (cfun))
242 : {
243 0 : pretty_printer pp;
244 0 : pp_printf (&pp, "fun_%d_loop_%d", funcdef_no, loop->num);
245 0 : std::string name (pp_formatted_text (&pp));
246 0 : loop_node
247 0 : = std::make_unique<diagnostics::digraphs::node> (g, name);
248 0 : parent_node = loop_node.get ();
249 0 : loop_node->set_property (node_properties::kind, "loop");
250 0 : loop_node->set_property (loop_properties::num, loop->num);
251 0 : loop_node->set_property (loop_properties::depth, loop_depth (loop));
252 0 : }
253 :
254 50 : for (class loop *inner = loop->inner; inner; inner = inner->next)
255 0 : add_cfg_nodes_for_loop (g, node_map, parent_node, funcdef_no, inner);
256 :
257 50 : if (loop->header == NULL)
258 0 : return;
259 :
260 50 : basic_block *body;
261 50 : if (loop->latch == EXIT_BLOCK_PTR_FOR_FN (cfun))
262 50 : body = get_loop_body (loop);
263 : else
264 0 : body = get_loop_body_in_bfs_order (loop);
265 :
266 350 : for (i = 0; i < loop->num_nodes; i++)
267 : {
268 300 : basic_block bb = body[i];
269 300 : if (bb->loop_father == loop)
270 300 : add_cfg_node (g, node_map, *parent_node, funcdef_no, bb);
271 : }
272 :
273 50 : free (body);
274 :
275 50 : if (loop->latch != EXIT_BLOCK_PTR_FOR_FN (cfun))
276 : {
277 0 : gcc_assert (loop_node);
278 0 : orig_parent_node.add_child (std::move (loop_node));
279 : }
280 50 : }
281 :
282 : void
283 90 : add_cfg_nodes (diagnostics::digraphs::digraph &g,
284 : bb_to_node_map &node_map,
285 : diagnostics::digraphs::node &parent_node,
286 : function *fun) const
287 : {
288 : /* ??? The loop and dominance APIs are dependent on fun == cfun. */
289 90 : if (fun == cfun && loops_for_fn (fun))
290 50 : add_cfg_nodes_for_loop (g, node_map, &parent_node, fun->funcdef_no,
291 : get_loop (fun, 0));
292 : else
293 40 : add_cfg_nodes_no_loops (g, node_map, parent_node, fun);
294 90 : }
295 :
296 : void
297 90 : add_cfg_edges (diagnostics::digraphs::digraph &g,
298 : bb_to_node_map &node_map,
299 : function *fun) const
300 : {
301 90 : basic_block bb;
302 :
303 : /* Save EDGE_DFS_BACK flag to dfs_back. */
304 90 : auto_bitmap dfs_back;
305 90 : edge e;
306 90 : edge_iterator ei;
307 90 : unsigned int idx = 0;
308 450 : FOR_EACH_BB_FN (bb, fun)
309 810 : FOR_EACH_EDGE (e, ei, bb->succs)
310 : {
311 450 : if (e->flags & EDGE_DFS_BACK)
312 0 : bitmap_set_bit (dfs_back, idx);
313 450 : idx++;
314 : }
315 :
316 90 : mark_dfs_back_edges (fun);
317 630 : FOR_ALL_BB_FN (bb, fun)
318 540 : add_cfg_node_succ_edges (g, node_map, fun->funcdef_no, bb);
319 :
320 : /* Restore EDGE_DFS_BACK flag from dfs_back. */
321 90 : idx = 0;
322 450 : FOR_EACH_BB_FN (bb, fun)
323 810 : FOR_EACH_EDGE (e, ei, bb->succs)
324 : {
325 450 : if (bitmap_bit_p (dfs_back, idx))
326 0 : e->flags |= EDGE_DFS_BACK;
327 : else
328 450 : e->flags &= ~EDGE_DFS_BACK;
329 450 : idx++;
330 : }
331 90 : }
332 :
333 : function *m_fun;
334 : opt_pass *m_pass;
335 : };
336 :
337 : #if __GNUC__ >= 10
338 : # pragma GCC diagnostic pop
339 : #endif
340 :
341 : namespace pass_events = gcc::topics::pass_events;
342 :
343 : /* A diagnostics::sink::extension which subscribes to pass_events
344 : and responds to "after_pass" events by adding a diagnostics digraph
345 : for the CFG for the relevant function. */
346 :
347 : class compiler_capture_cfgs : public diagnostics::sink::extension
348 : {
349 : public:
350 2 : compiler_capture_cfgs (diagnostics::sink &sink)
351 2 : : extension (sink),
352 2 : m_event_subscriber (sink)
353 : {
354 2 : g->get_channels ().pass_events_channel.add_subscriber (m_event_subscriber);
355 2 : }
356 :
357 : void
358 0 : dump (FILE *out, int indent) const
359 : {
360 0 : diagnostics::dumping::emit_heading (out, indent, "compiler_capture_cfgs");
361 0 : }
362 :
363 : private:
364 : class event_subscriber : public pass_events::subscriber
365 : {
366 : public:
367 2 : event_subscriber (diagnostics::sink &sink) : m_sink (sink) {}
368 416 : void on_message (const pass_events::before_pass &) final override
369 : {
370 416 : }
371 156 : void on_message (const pass_events::after_pass &m) final override
372 : {
373 156 : if (m.fun
374 124 : && m.fun->cfg
375 114 : && m.pass->static_pass_number > 0)
376 90 : m_sink.report_digraph_for_logical_location
377 90 : (cfg_diagnostic_digraph (m.fun, m.pass),
378 : tree_logical_location_manager::key_from_tree (m.fun->decl));
379 156 : }
380 :
381 : private:
382 : diagnostics::sink &m_sink;
383 : } m_event_subscriber;
384 : };
385 :
386 : std::unique_ptr<diagnostics::sink::extension>
387 2 : compiler_extension_factory::make_cfg_extension (diagnostics::sink &sink) const
388 : {
389 2 : return std::make_unique<compiler_capture_cfgs> (sink);
390 : }
|