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