Branch data Line data Source code
1 : : /* "Supergraph" classes that combine CFGs and callgraph into one digraph.
2 : : Copyright (C) 2019-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 : : #include "analyzer/common.h"
22 : :
23 : : #include "timevar.h"
24 : : #include "gimple-pretty-print.h"
25 : : #include "ordered-hash-map.h"
26 : : #include "options.h"
27 : : #include "cgraph.h"
28 : : #include "cfg.h"
29 : : #include "digraph.h"
30 : : #include "tree-cfg.h"
31 : : #include "tree-dfa.h"
32 : : #include "cfganal.h"
33 : : #include "except.h"
34 : :
35 : : #include "analyzer/supergraph.h"
36 : : #include "analyzer/analyzer-logging.h"
37 : : #include "analyzer/region-model.h"
38 : :
39 : : #if ENABLE_ANALYZER
40 : :
41 : : namespace ana {
42 : :
43 : : /* Get the function of the ultimate alias target being called at EDGE,
44 : : if any. */
45 : :
46 : : function *
47 : 120465 : get_ultimate_function_for_cgraph_edge (cgraph_edge *edge)
48 : : {
49 : 120465 : cgraph_node *ultimate_node = edge->callee->ultimate_alias_target ();
50 : 120465 : if (!ultimate_node)
51 : : return NULL;
52 : 120465 : return ultimate_node->get_fun ();
53 : : }
54 : :
55 : : /* Get the cgraph_edge, but only if there's an underlying function body. */
56 : :
57 : : cgraph_edge *
58 : 372705 : supergraph_call_edge (function *fun, const gimple *stmt)
59 : : {
60 : 461239 : const gcall *call = dyn_cast<const gcall *> (stmt);
61 : 104914 : if (!call)
62 : : return NULL;
63 : 104914 : cgraph_edge *edge
64 : 104914 : = cgraph_node::get (fun->decl)->get_edge (const_cast <gimple *> (stmt));
65 : 104914 : if (!edge)
66 : : return NULL;
67 : 102591 : if (!edge->callee)
68 : : return NULL; /* e.g. for a function pointer. */
69 : 101127 : if (!get_ultimate_function_for_cgraph_edge (edge))
70 : : return NULL;
71 : : return edge;
72 : : }
73 : :
74 : : /* class saved_uids.
75 : :
76 : : In order to ensure consistent results without relying on the ordering
77 : : of pointer values we assign a uid to each gimple stmt, globally unique
78 : : across all functions.
79 : :
80 : : Normally, the stmt uids are a scratch space that each pass can freely
81 : : assign its own values to. However, in the case of LTO, the uids are
82 : : used to associate call stmts with callgraph edges between the WPA phase
83 : : (where the analyzer runs in LTO mode) and the LTRANS phase; if the
84 : : analyzer changes them in the WPA phase, it leads to errors when
85 : : streaming the code back in at LTRANS.
86 : : lto_prepare_function_for_streaming has code to renumber the stmt UIDs
87 : : when the code is streamed back out, but for some reason this isn't
88 : : called for clones.
89 : :
90 : : Hence, as a workaround, this class has responsibility for tracking
91 : : the original uids and restoring them once the pass is complete
92 : : (in the supergraph dtor). */
93 : :
94 : : /* Give STMT a globally unique uid, storing its original uid so it can
95 : : later be restored. */
96 : :
97 : : void
98 : 132568 : saved_uids::make_uid_unique (gimple *stmt)
99 : : {
100 : 132568 : unsigned next_uid = m_old_stmt_uids.length ();
101 : 132568 : unsigned old_stmt_uid = stmt->uid;
102 : 132568 : stmt->uid = next_uid;
103 : 132568 : m_old_stmt_uids.safe_push
104 : 132568 : (std::pair<gimple *, unsigned> (stmt, old_stmt_uid));
105 : 132568 : }
106 : :
107 : : /* Restore the saved uids of all stmts. */
108 : :
109 : : void
110 : 3313 : saved_uids::restore_uids () const
111 : : {
112 : 3313 : unsigned i;
113 : 3313 : std::pair<gimple *, unsigned> *pair;
114 : 135881 : FOR_EACH_VEC_ELT (m_old_stmt_uids, i, pair)
115 : 132568 : pair->first->uid = pair->second;
116 : 3313 : }
117 : :
118 : : /* supergraph's ctor. Walk the callgraph, building supernodes for each
119 : : CFG basic block, splitting the basic blocks at callsites. Join
120 : : together the supernodes with interprocedural and intraprocedural
121 : : superedges as appropriate.
122 : : Assign UIDs to the gimple stmts. */
123 : :
124 : 3313 : supergraph::supergraph (logger *logger)
125 : : {
126 : 3313 : auto_timevar tv (TV_ANALYZER_SUPERGRAPH);
127 : :
128 : 3313 : LOG_FUNC (logger);
129 : :
130 : : /* First pass: make supernodes (and assign UIDs to the gimple stmts). */
131 : 3313 : {
132 : : /* Sort the cgraph_nodes? */
133 : 3313 : cgraph_node *node;
134 : 13415 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
135 : : {
136 : 10102 : function *fun = node->get_fun ();
137 : :
138 : : /* Ensure that EDGE_DFS_BACK is correct for every CFG edge in
139 : : the supergraph (by doing it per-function). */
140 : 10102 : auto_cfun sentinel (fun);
141 : 10102 : mark_dfs_back_edges ();
142 : :
143 : 10102 : const int start_idx = m_nodes.length ();
144 : :
145 : 10102 : basic_block bb;
146 : 67493 : FOR_ALL_BB_FN (bb, fun)
147 : : {
148 : : /* The initial supernode for the BB gets the phi nodes (if any). */
149 : 57391 : supernode *node_for_stmts = add_node (fun, bb, NULL, phi_nodes (bb));
150 : 57391 : m_bb_to_initial_node.put (bb, node_for_stmts);
151 : 66606 : for (gphi_iterator gpi = gsi_start_phis (bb); !gsi_end_p (gpi);
152 : 9215 : gsi_next (&gpi))
153 : : {
154 : 9215 : gimple *stmt = gsi_stmt (gpi);
155 : 9215 : m_stmt_to_node_t.put (stmt, node_for_stmts);
156 : 9215 : m_stmt_uids.make_uid_unique (stmt);
157 : : }
158 : :
159 : : /* Append statements from BB to the current supernode, splitting
160 : : them into a new supernode at each call site; such call statements
161 : : appear in both supernodes (representing call and return). */
162 : 57391 : gimple_stmt_iterator gsi;
163 : 239920 : for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
164 : : {
165 : 125138 : gimple *stmt = gsi_stmt (gsi);
166 : : /* Discard debug stmts here, so we don't have to check for
167 : : them anywhere within the analyzer. */
168 : 125138 : if (is_gimple_debug (stmt))
169 : 1785 : continue;
170 : 123353 : node_for_stmts->m_stmts.safe_push (stmt);
171 : 123353 : m_stmt_to_node_t.put (stmt, node_for_stmts);
172 : 123353 : m_stmt_uids.make_uid_unique (stmt);
173 : 123353 : if (cgraph_edge *edge = supergraph_call_edge (fun, stmt))
174 : : {
175 : 3598 : m_cgraph_edge_to_caller_prev_node.put(edge, node_for_stmts);
176 : 3598 : node_for_stmts = add_node (fun, bb, as_a <gcall *> (stmt),
177 : : NULL);
178 : 3598 : m_cgraph_edge_to_caller_next_node.put (edge, node_for_stmts);
179 : : }
180 : : else
181 : : {
182 : : // maybe call is via a function pointer
183 : 159336 : if (gcall *call = dyn_cast<gcall *> (stmt))
184 : : {
185 : 35983 : cgraph_edge *edge
186 : 35983 : = cgraph_node::get (fun->decl)->get_edge (stmt);
187 : 35983 : if (!edge || !edge->callee)
188 : : {
189 : 745 : supernode *old_node_for_stmts = node_for_stmts;
190 : 745 : node_for_stmts = add_node (fun, bb, call, NULL);
191 : :
192 : 745 : superedge *sedge
193 : : = new callgraph_superedge (old_node_for_stmts,
194 : : node_for_stmts,
195 : : SUPEREDGE_INTRAPROCEDURAL_CALL,
196 : 745 : NULL);
197 : 745 : add_edge (sedge);
198 : : }
199 : : }
200 : : }
201 : : }
202 : :
203 : 57391 : m_bb_to_final_node.put (bb, node_for_stmts);
204 : : }
205 : :
206 : 10102 : const unsigned num_snodes = m_nodes.length () - start_idx;
207 : 10102 : m_function_to_num_snodes.put (fun, num_snodes);
208 : :
209 : 10102 : if (logger)
210 : : {
211 : 2 : const int end_idx = m_nodes.length () - 1;
212 : 2 : logger->log ("SN: %i...%i: function %qD",
213 : : start_idx, end_idx, fun->decl);
214 : : }
215 : : }
216 : : }
217 : :
218 : : /* Second pass: make superedges. */
219 : 3313 : {
220 : : /* Make superedges for CFG edges. */
221 : 3313 : for (bb_to_node_t::iterator iter = m_bb_to_final_node.begin ();
222 : 121404 : iter != m_bb_to_final_node.end ();
223 : 57391 : ++iter)
224 : : {
225 : 57391 : basic_block bb = (*iter).first;
226 : 57391 : supernode *src_supernode = (*iter).second;
227 : :
228 : 57391 : ::edge cfg_edge;
229 : 57391 : int idx;
230 : 57391 : if (bb->succs)
231 : 115661 : FOR_EACH_VEC_ELT (*bb->succs, idx, cfg_edge)
232 : : {
233 : 58270 : basic_block dest_cfg_block = cfg_edge->dest;
234 : 58270 : supernode *dest_supernode
235 : 58270 : = *m_bb_to_initial_node.get (dest_cfg_block);
236 : 58270 : cfg_superedge *cfg_sedge
237 : 58270 : = add_cfg_edge (src_supernode, dest_supernode, cfg_edge);
238 : 58270 : m_cfg_edge_to_cfg_superedge.put (cfg_edge, cfg_sedge);
239 : : }
240 : : }
241 : :
242 : : /* Make interprocedural superedges for calls. */
243 : 3313 : {
244 : 3313 : for (cgraph_edge_to_node_t::iterator iter
245 : 3313 : = m_cgraph_edge_to_caller_prev_node.begin ();
246 : 11492 : iter != m_cgraph_edge_to_caller_prev_node.end ();
247 : 3598 : ++iter)
248 : : {
249 : 3598 : cgraph_edge *edge = (*iter).first;
250 : 3598 : supernode *caller_prev_supernode = (*iter).second;
251 : 3598 : function* callee_fn = get_ultimate_function_for_cgraph_edge (edge);
252 : 3598 : if (!callee_fn || !callee_fn->cfg)
253 : 0 : continue;
254 : 3598 : basic_block callee_cfg_block = ENTRY_BLOCK_PTR_FOR_FN (callee_fn);
255 : 3598 : supernode *callee_supernode
256 : 3598 : = *m_bb_to_initial_node.get (callee_cfg_block);
257 : 3598 : call_superedge *sedge
258 : 3598 : = add_call_superedge (caller_prev_supernode,
259 : : callee_supernode,
260 : 3598 : edge);
261 : 3598 : m_cgraph_edge_to_call_superedge.put (edge, sedge);
262 : : }
263 : : }
264 : :
265 : : /* Make interprocedural superedges for returns. */
266 : 3313 : {
267 : 3313 : for (cgraph_edge_to_node_t::iterator iter
268 : 3313 : = m_cgraph_edge_to_caller_next_node.begin ();
269 : 11492 : iter != m_cgraph_edge_to_caller_next_node.end ();
270 : 3598 : ++iter)
271 : : {
272 : 3598 : cgraph_edge *edge = (*iter).first;
273 : 3598 : supernode *caller_next_supernode = (*iter).second;
274 : 3598 : function* callee_fn = get_ultimate_function_for_cgraph_edge (edge);
275 : 3598 : if (!callee_fn || !callee_fn->cfg)
276 : 0 : continue;
277 : 3598 : basic_block callee_cfg_block = EXIT_BLOCK_PTR_FOR_FN (callee_fn);
278 : 3598 : supernode *callee_supernode
279 : 3598 : = *m_bb_to_initial_node.get (callee_cfg_block);
280 : 3598 : return_superedge *sedge
281 : 3598 : = add_return_superedge (callee_supernode,
282 : : caller_next_supernode,
283 : 3598 : edge);
284 : 3598 : m_cgraph_edge_to_return_superedge.put (edge, sedge);
285 : : }
286 : : }
287 : :
288 : : /* Make intraprocedural superedges linking the two halves of a call. */
289 : 3313 : {
290 : 3313 : for (cgraph_edge_to_node_t::iterator iter
291 : 3313 : = m_cgraph_edge_to_caller_prev_node.begin ();
292 : 11492 : iter != m_cgraph_edge_to_caller_prev_node.end ();
293 : 3598 : ++iter)
294 : : {
295 : 3598 : cgraph_edge *edge = (*iter).first;
296 : 3598 : supernode *caller_prev_supernode = (*iter).second;
297 : 3598 : supernode *caller_next_supernode
298 : 3598 : = *m_cgraph_edge_to_caller_next_node.get (edge);
299 : 3598 : superedge *sedge
300 : : = new callgraph_superedge (caller_prev_supernode,
301 : : caller_next_supernode,
302 : : SUPEREDGE_INTRAPROCEDURAL_CALL,
303 : 3598 : edge);
304 : 3598 : add_edge (sedge);
305 : 3598 : m_cgraph_edge_to_intraproc_superedge.put (edge, sedge);
306 : : }
307 : :
308 : : }
309 : : }
310 : 3313 : }
311 : :
312 : : /* supergraph's dtor. Reset stmt uids. */
313 : :
314 : 3313 : supergraph::~supergraph ()
315 : : {
316 : 3313 : m_stmt_uids.restore_uids ();
317 : 3313 : }
318 : :
319 : : /* Dump this graph in .dot format to PP, using DUMP_ARGS.
320 : : Cluster the supernodes by function, then by BB from original CFG. */
321 : :
322 : : void
323 : 12 : supergraph::dump_dot_to_pp (pretty_printer *pp,
324 : : const dump_args_t &dump_args) const
325 : : {
326 : 12 : graphviz_out gv (pp);
327 : :
328 : 12 : pp_string (pp, "digraph \"");
329 : 12 : pp_write_text_to_stream (pp);
330 : 12 : pp_string (pp, "supergraph");
331 : 12 : pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
332 : 12 : pp_string (pp, "\" {\n");
333 : 12 : gv.indent ();
334 : :
335 : 12 : gv.println ("overlap=false;");
336 : 12 : gv.println ("compound=true;");
337 : :
338 : : /* TODO: maybe (optionally) sub-subdivide by TU, for LTO; see also:
339 : : https://gcc-python-plugin.readthedocs.io/en/latest/_images/sample-supergraph.png
340 : : */
341 : :
342 : : /* Break out the supernodes into clusters by function. */
343 : 12 : {
344 : 12 : cgraph_node *node;
345 : 48 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
346 : : {
347 : 36 : function *fun = node->get_fun ();
348 : 36 : gcc_assert (fun);
349 : 36 : const char *funcname = function_name (fun);
350 : 36 : gv.println ("subgraph \"cluster_%s\" {",
351 : : funcname);
352 : 36 : gv.indent ();
353 : 36 : pp_printf (pp,
354 : : ("style=\"dashed\";"
355 : : " color=\"black\";"
356 : : " label=\"%s\";\n"),
357 : : funcname);
358 : :
359 : : /* Break out the nodes into clusters by BB from original CFG. */
360 : 36 : {
361 : 36 : basic_block bb;
362 : 252 : FOR_ALL_BB_FN (bb, fun)
363 : : {
364 : 216 : if (dump_args.m_flags & SUPERGRAPH_DOT_SHOW_BBS)
365 : : {
366 : 0 : gv.println ("subgraph \"cluster_%s_bb_%i\" {",
367 : : funcname, bb->index);
368 : 0 : gv.indent ();
369 : 0 : pp_printf (pp,
370 : : ("style=\"dashed\";"
371 : : " color=\"black\";"
372 : : " label=\"bb: %i\";\n"),
373 : : bb->index);
374 : : }
375 : :
376 : : // TODO: maybe keep an index per-function/per-bb to speed this up???
377 : : int i;
378 : : supernode *n;
379 : 4320 : FOR_EACH_VEC_ELT (m_nodes, i, n)
380 : 4104 : if (n->m_fun == fun && n->m_bb == bb)
381 : 228 : n->dump_dot (&gv, dump_args);
382 : :
383 : 216 : if (dump_args.m_flags & SUPERGRAPH_DOT_SHOW_BBS)
384 : : {
385 : : /* Terminate per-bb "subgraph" */
386 : 0 : gv.outdent ();
387 : 0 : gv.println ("}");
388 : : }
389 : : }
390 : : }
391 : :
392 : : /* Add an invisible edge from ENTRY to EXIT, to improve the graph layout. */
393 : 36 : pp_string (pp, "\t");
394 : 36 : get_node_for_function_entry (*fun)->dump_dot_id (pp);
395 : 36 : pp_string (pp, ":s -> ");
396 : 36 : get_node_for_function_exit (*fun)->dump_dot_id (pp);
397 : 36 : pp_string (pp, ":n [style=\"invis\",constraint=true];\n");
398 : :
399 : : /* Terminate per-function "subgraph" */
400 : 36 : gv.outdent ();
401 : 36 : gv.println ("}");
402 : : }
403 : : }
404 : :
405 : : /* Superedges. */
406 : : int i;
407 : : superedge *e;
408 : 264 : FOR_EACH_VEC_ELT (m_edges, i, e)
409 : 252 : e->dump_dot (&gv, dump_args);
410 : :
411 : : /* Terminate "digraph" */
412 : 12 : gv.outdent ();
413 : 12 : gv.println ("}");
414 : 12 : }
415 : :
416 : : /* Dump this graph in .dot format to FP, using DUMP_ARGS. */
417 : :
418 : : void
419 : 12 : supergraph::dump_dot_to_file (FILE *fp, const dump_args_t &dump_args) const
420 : : {
421 : 12 : std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
422 : 12 : pp_show_color (pp.get ()) = 0;
423 : : /* %qE in logs for SSA_NAMEs should show the ssa names, rather than
424 : : trying to prettify things by showing the underlying var. */
425 : 12 : pp_format_decoder (pp.get ()) = default_tree_printer;
426 : :
427 : 12 : pp->set_output_stream (fp);
428 : 12 : dump_dot_to_pp (pp.get (), dump_args);
429 : 12 : pp_flush (pp.get ());
430 : 12 : }
431 : :
432 : : /* Dump this graph in .dot format to PATH, using DUMP_ARGS. */
433 : :
434 : : void
435 : 12 : supergraph::dump_dot (const char *path, const dump_args_t &dump_args) const
436 : : {
437 : 12 : FILE *fp = fopen (path, "w");
438 : 12 : dump_dot_to_file (fp, dump_args);
439 : 12 : fclose (fp);
440 : 12 : }
441 : :
442 : : /* Return a new json::object of the form
443 : : {"nodes" : [objs for snodes],
444 : : "edges" : [objs for sedges]}. */
445 : :
446 : : std::unique_ptr<json::object>
447 : 0 : supergraph::to_json () const
448 : : {
449 : 0 : auto sgraph_obj = std::make_unique<json::object> ();
450 : :
451 : : /* Nodes. */
452 : 0 : {
453 : 0 : auto nodes_arr = std::make_unique<json::array> ();
454 : 0 : unsigned i;
455 : 0 : supernode *n;
456 : 0 : FOR_EACH_VEC_ELT (m_nodes, i, n)
457 : 0 : nodes_arr->append (n->to_json ());
458 : 0 : sgraph_obj->set ("nodes", std::move (nodes_arr));
459 : 0 : }
460 : :
461 : : /* Edges. */
462 : 0 : {
463 : 0 : auto edges_arr = std::make_unique<json::array> ();
464 : 0 : unsigned i;
465 : 0 : superedge *n;
466 : 0 : FOR_EACH_VEC_ELT (m_edges, i, n)
467 : 0 : edges_arr->append (n->to_json ());
468 : 0 : sgraph_obj->set ("edges", std::move (edges_arr));
469 : 0 : }
470 : :
471 : 0 : return sgraph_obj;
472 : : }
473 : :
474 : : /* Create a supernode for BB within FUN and add it to this supergraph.
475 : :
476 : : If RETURNING_CALL is non-NULL, the supernode represents the resumption
477 : : of the basic block after returning from that call.
478 : :
479 : : If PHI_NODES is non-NULL, this is the initial supernode for the basic
480 : : block, and is responsible for any handling of the phi nodes. */
481 : :
482 : : supernode *
483 : 61734 : supergraph::add_node (function *fun, basic_block bb, gcall *returning_call,
484 : : gimple_seq phi_nodes)
485 : : {
486 : 61734 : supernode *n = new supernode (fun, bb, returning_call, phi_nodes,
487 : 120159 : m_nodes.length ());
488 : 61734 : m_nodes.safe_push (n);
489 : 61734 : return n;
490 : : }
491 : :
492 : : /* Create a new cfg_superedge from SRC to DEST for the underlying CFG edge E,
493 : : adding it to this supergraph.
494 : :
495 : : If the edge is for a switch or eh_dispatch statement, create a
496 : : switch_cfg_superedge or eh_dispatch_cfg_superedge subclass,
497 : : respectively */
498 : :
499 : : cfg_superedge *
500 : 58270 : supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e)
501 : : {
502 : : /* Special-case switch and eh_dispatch edges. */
503 : 58270 : gimple *stmt = src->get_last_stmt ();
504 : 58270 : std::unique_ptr<cfg_superedge> new_edge;
505 : 58270 : if (stmt && stmt->code == GIMPLE_SWITCH)
506 : 3031 : new_edge = std::make_unique<switch_cfg_superedge> (src, dest, e);
507 : 43315 : else if (stmt && stmt->code == GIMPLE_EH_DISPATCH)
508 : 384 : new_edge = eh_dispatch_cfg_superedge::make (src, dest, e,
509 : 384 : as_a <geh_dispatch *> (stmt));
510 : : else
511 : 55047 : new_edge = std::make_unique<cfg_superedge> (src, dest, e);
512 : 58270 : add_edge (new_edge.get ());
513 : 58270 : return new_edge.release ();
514 : 58270 : }
515 : :
516 : : /* Create and add a call_superedge representing an interprocedural call
517 : : from SRC to DEST, using CEDGE. */
518 : :
519 : : call_superedge *
520 : 3598 : supergraph::add_call_superedge (supernode *src, supernode *dest,
521 : : cgraph_edge *cedge)
522 : : {
523 : 3598 : call_superedge *new_edge = new call_superedge (src, dest, cedge);
524 : 3598 : add_edge (new_edge);
525 : 3598 : return new_edge;
526 : : }
527 : :
528 : : /* Create and add a return_superedge representing returning from an
529 : : interprocedural call, returning from SRC to DEST, using CEDGE. */
530 : :
531 : : return_superedge *
532 : 3598 : supergraph::add_return_superedge (supernode *src, supernode *dest,
533 : : cgraph_edge *cedge)
534 : : {
535 : 3598 : return_superedge *new_edge = new return_superedge (src, dest, cedge);
536 : 3598 : add_edge (new_edge);
537 : 3598 : return new_edge;
538 : : }
539 : :
540 : : /* Implementation of dnode::dump_dot vfunc for supernodes.
541 : :
542 : : Write a cluster for the node, and within it a .dot node showing
543 : : the phi nodes and stmts. Call into any node annotator from ARGS to
544 : : potentially add other records to the cluster. */
545 : :
546 : : void
547 : 228 : supernode::dump_dot (graphviz_out *gv, const dump_args_t &args) const
548 : : {
549 : 228 : gv->println ("subgraph cluster_node_%i {",
550 : 228 : m_index);
551 : 228 : gv->indent ();
552 : :
553 : 228 : gv->println("style=\"solid\";");
554 : 228 : gv->println("color=\"black\";");
555 : 228 : gv->println("fillcolor=\"lightgrey\";");
556 : 228 : gv->println("label=\"sn: %i (bb: %i)\";", m_index, m_bb->index);
557 : :
558 : 228 : pretty_printer *pp = gv->get_pp ();
559 : :
560 : 228 : if (args.m_node_annotator)
561 : 152 : args.m_node_annotator->add_node_annotations (gv, *this, false);
562 : :
563 : 228 : gv->write_indent ();
564 : 228 : dump_dot_id (pp);
565 : 228 : pp_printf (pp,
566 : : " [shape=none,margin=0,style=filled,fillcolor=%s,label=<",
567 : : "lightgrey");
568 : 228 : pp_string (pp, "<TABLE BORDER=\"0\">");
569 : 228 : pp_write_text_to_stream (pp);
570 : :
571 : 228 : bool had_row = false;
572 : :
573 : : /* Give any annotator the chance to add its own per-node TR elements. */
574 : 228 : if (args.m_node_annotator)
575 : 152 : if (args.m_node_annotator->add_node_annotations (gv, *this, true))
576 : 228 : had_row = true;
577 : :
578 : 228 : if (m_returning_call)
579 : : {
580 : 12 : gv->begin_trtd ();
581 : 12 : pp_string (pp, "returning call: ");
582 : 12 : gv->end_tdtr ();
583 : :
584 : 12 : gv->begin_tr ();
585 : 12 : gv->begin_td ();
586 : 12 : pp_gimple_stmt_1 (pp, m_returning_call, 0, (dump_flags_t)0);
587 : 12 : pp_write_text_as_html_like_dot_to_stream (pp);
588 : 12 : gv->end_td ();
589 : : /* Give any annotator the chance to add per-stmt TD elements to
590 : : this row. */
591 : 12 : if (args.m_node_annotator)
592 : 8 : args.m_node_annotator->add_stmt_annotations (gv, m_returning_call,
593 : : true);
594 : 12 : gv->end_tr ();
595 : :
596 : : /* Give any annotator the chance to add per-stmt TR elements. */
597 : 12 : if (args.m_node_annotator)
598 : 8 : args.m_node_annotator->add_stmt_annotations (gv, m_returning_call,
599 : : false);
600 : 12 : pp_newline (pp);
601 : :
602 : 12 : had_row = true;
603 : : }
604 : :
605 : 228 : if (entry_p ())
606 : : {
607 : 36 : pp_string (pp, "<TR><TD>ENTRY</TD></TR>");
608 : 36 : pp_newline (pp);
609 : 36 : had_row = true;
610 : : }
611 : :
612 : 228 : if (return_p ())
613 : : {
614 : 36 : pp_string (pp, "<TR><TD>EXIT</TD></TR>");
615 : 36 : pp_newline (pp);
616 : 36 : had_row = true;
617 : : }
618 : :
619 : : /* Phi nodes. */
620 : 228 : for (gphi_iterator gpi = const_cast<supernode *> (this)->start_phis ();
621 : 264 : !gsi_end_p (gpi); gsi_next (&gpi))
622 : : {
623 : 36 : const gimple *stmt = gsi_stmt (gpi);
624 : 36 : gv->begin_tr ();
625 : 36 : gv->begin_td ();
626 : 36 : pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
627 : 36 : pp_write_text_as_html_like_dot_to_stream (pp);
628 : 36 : gv->end_td ();
629 : : /* Give any annotator the chance to add per-phi TD elements to
630 : : this row. */
631 : 36 : if (args.m_node_annotator)
632 : 24 : args.m_node_annotator->add_stmt_annotations (gv, stmt, true);
633 : 36 : gv->end_tr ();
634 : :
635 : : /* Give any annotator the chance to add per-phi TR elements. */
636 : 36 : if (args.m_node_annotator)
637 : 24 : args.m_node_annotator->add_stmt_annotations (gv, stmt, false);
638 : :
639 : 36 : pp_newline (pp);
640 : 36 : had_row = true;
641 : : }
642 : :
643 : : /* Statements. */
644 : : int i;
645 : : gimple *stmt;
646 : 585 : FOR_EACH_VEC_ELT (m_stmts, i, stmt)
647 : : {
648 : 357 : gv->begin_tr ();
649 : 357 : gv->begin_td ();
650 : 357 : pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
651 : 357 : pp_write_text_as_html_like_dot_to_stream (pp);
652 : 357 : gv->end_td ();
653 : : /* Give any annotator the chance to add per-stmt TD elements to
654 : : this row. */
655 : 357 : if (args.m_node_annotator)
656 : 238 : args.m_node_annotator->add_stmt_annotations (gv, stmt, true);
657 : 357 : gv->end_tr ();
658 : :
659 : : /* Give any annotator the chance to add per-stmt TR elements. */
660 : 357 : if (args.m_node_annotator)
661 : 238 : args.m_node_annotator->add_stmt_annotations (gv, stmt, false);
662 : :
663 : 357 : pp_newline (pp);
664 : 357 : had_row = true;
665 : : }
666 : :
667 : : /* Give any annotator the chance to add additional per-node TR elements
668 : : to the end of the TABLE. */
669 : 228 : if (args.m_node_annotator)
670 : 152 : if (args.m_node_annotator->add_after_node_annotations (gv, *this))
671 : : had_row = true;
672 : :
673 : : /* Graphviz requires a TABLE element to have at least one TR
674 : : (and each TR to have at least one TD). */
675 : 152 : if (!had_row)
676 : : {
677 : 8 : pp_string (pp, "<TR><TD>(empty)</TD></TR>");
678 : 8 : pp_newline (pp);
679 : : }
680 : :
681 : 228 : pp_string (pp, "</TABLE>>];\n\n");
682 : 228 : pp_flush (pp);
683 : :
684 : : /* Terminate "subgraph" */
685 : 228 : gv->outdent ();
686 : 228 : gv->println ("}");
687 : 228 : }
688 : :
689 : : /* Write an ID for this node to PP, for use in .dot output. */
690 : :
691 : : void
692 : 804 : supernode::dump_dot_id (pretty_printer *pp) const
693 : : {
694 : 804 : pp_printf (pp, "node_%i", m_index);
695 : 804 : }
696 : :
697 : : /* Return a new json::object of the form
698 : : {"idx": int,
699 : : "fun": optional str
700 : : "bb_idx": int,
701 : : "returning_call": optional str,
702 : : "phis": [str],
703 : : "stmts" : [str]}. */
704 : :
705 : : std::unique_ptr<json::object>
706 : 0 : supernode::to_json () const
707 : : {
708 : 0 : auto snode_obj = std::make_unique<json::object> ();
709 : :
710 : 0 : snode_obj->set_integer ("idx", m_index);
711 : 0 : snode_obj->set_integer ("bb_idx", m_bb->index);
712 : 0 : if (function *fun = get_function ())
713 : 0 : snode_obj->set_string ("fun", function_name (fun));
714 : :
715 : 0 : if (m_returning_call)
716 : : {
717 : 0 : pretty_printer pp;
718 : 0 : pp_format_decoder (&pp) = default_tree_printer;
719 : 0 : pp_gimple_stmt_1 (&pp, m_returning_call, 0, (dump_flags_t)0);
720 : 0 : snode_obj->set_string ("returning_call", pp_formatted_text (&pp));
721 : 0 : }
722 : :
723 : : /* Phi nodes. */
724 : 0 : {
725 : 0 : auto phi_arr = std::make_unique<json::array> ();
726 : 0 : for (gphi_iterator gpi = const_cast<supernode *> (this)->start_phis ();
727 : 0 : !gsi_end_p (gpi); gsi_next (&gpi))
728 : : {
729 : 0 : const gimple *stmt = gsi_stmt (gpi);
730 : 0 : pretty_printer pp;
731 : 0 : pp_format_decoder (&pp) = default_tree_printer;
732 : 0 : pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
733 : 0 : phi_arr->append_string (pp_formatted_text (&pp));
734 : 0 : }
735 : 0 : snode_obj->set ("phis", std::move (phi_arr));
736 : 0 : }
737 : :
738 : : /* Statements. */
739 : 0 : {
740 : 0 : auto stmt_arr = std::make_unique<json::array> ();
741 : 0 : int i;
742 : 0 : gimple *stmt;
743 : 0 : FOR_EACH_VEC_ELT (m_stmts, i, stmt)
744 : : {
745 : 0 : pretty_printer pp;
746 : 0 : pp_format_decoder (&pp) = default_tree_printer;
747 : 0 : pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
748 : 0 : stmt_arr->append_string (pp_formatted_text (&pp));
749 : 0 : }
750 : 0 : snode_obj->set ("stmts", std::move (stmt_arr));
751 : 0 : }
752 : :
753 : 0 : return snode_obj;
754 : : }
755 : :
756 : : /* Get a location_t for the start of this supernode. */
757 : :
758 : : location_t
759 : 21432 : supernode::get_start_location () const
760 : : {
761 : 21432 : if (m_returning_call
762 : 21432 : && get_pure_location (m_returning_call->location) != UNKNOWN_LOCATION)
763 : 0 : return m_returning_call->location;
764 : :
765 : : int i;
766 : : gimple *stmt;
767 : 22699 : FOR_EACH_VEC_ELT (m_stmts, i, stmt)
768 : 15860 : if (get_pure_location (stmt->location) != UNKNOWN_LOCATION)
769 : 14593 : return stmt->location;
770 : :
771 : 6839 : if (entry_p ())
772 : : {
773 : : // TWEAK: show the decl instead; this leads to more readable output:
774 : 5319 : return DECL_SOURCE_LOCATION (m_fun->decl);
775 : :
776 : : return m_fun->function_start_locus;
777 : : }
778 : 1520 : if (return_p ())
779 : 1087 : return m_fun->function_end_locus;
780 : :
781 : : /* We have no locations for stmts. If we have a single out-edge that's
782 : : a CFG edge, the goto_locus of that edge is a better location for this
783 : : than UNKNOWN_LOCATION. */
784 : 433 : if (m_succs.length () == 1)
785 : 394 : if (const cfg_superedge *cfg_sedge = m_succs[0]->dyn_cast_cfg_superedge ())
786 : 394 : return cfg_sedge->get_goto_locus ();
787 : :
788 : : return UNKNOWN_LOCATION;
789 : : }
790 : :
791 : : /* Get a location_t for the end of this supernode. */
792 : :
793 : : location_t
794 : 91 : supernode::get_end_location () const
795 : : {
796 : 91 : int i;
797 : 91 : gimple *stmt;
798 : 91 : FOR_EACH_VEC_ELT_REVERSE (m_stmts, i, stmt)
799 : 0 : if (get_pure_location (stmt->location) != UNKNOWN_LOCATION)
800 : 0 : return stmt->location;
801 : :
802 : 91 : if (m_returning_call
803 : 91 : && get_pure_location (m_returning_call->location) != UNKNOWN_LOCATION)
804 : 33 : return m_returning_call->location;
805 : :
806 : 58 : if (entry_p ())
807 : 0 : return m_fun->function_start_locus;
808 : 58 : if (return_p ())
809 : 5 : return m_fun->function_end_locus;
810 : :
811 : : /* If we have a single out-edge that's a CFG edge, use the goto_locus of
812 : : that edge. */
813 : 53 : if (m_succs.length () == 1)
814 : 53 : if (const cfg_superedge *cfg_sedge = m_succs[0]->dyn_cast_cfg_superedge ())
815 : 53 : return cfg_sedge->get_goto_locus ();
816 : :
817 : : return UNKNOWN_LOCATION;
818 : : }
819 : :
820 : : /* Given STMT within this supernode, return its index within m_stmts. */
821 : :
822 : : unsigned int
823 : 82563 : supernode::get_stmt_index (const gimple *stmt) const
824 : : {
825 : 82563 : unsigned i;
826 : 82563 : gimple *iter_stmt;
827 : 12444681 : FOR_EACH_VEC_ELT (m_stmts, i, iter_stmt)
828 : 12444681 : if (iter_stmt == stmt)
829 : 82563 : return i;
830 : 0 : gcc_unreachable ();
831 : : }
832 : :
833 : : /* Get any label_decl for this supernode, or NULL_TREE if there isn't one. */
834 : :
835 : : tree
836 : 271 : supernode::get_label () const
837 : : {
838 : 271 : if (m_stmts.length () == 0)
839 : : return NULL_TREE;
840 : 271 : const glabel *label_stmt = dyn_cast<const glabel *> (m_stmts[0]);
841 : 208 : if (!label_stmt)
842 : : return NULL_TREE;
843 : 208 : return gimple_label_label (label_stmt);
844 : : }
845 : :
846 : : /* Get a string for PK. */
847 : :
848 : : static const char *
849 : 8 : edge_kind_to_string (enum edge_kind kind)
850 : : {
851 : 8 : switch (kind)
852 : : {
853 : 0 : default:
854 : 0 : gcc_unreachable ();
855 : : case SUPEREDGE_CFG_EDGE:
856 : : return "SUPEREDGE_CFG_EDGE";
857 : 0 : case SUPEREDGE_CALL:
858 : 0 : return "SUPEREDGE_CALL";
859 : 0 : case SUPEREDGE_RETURN:
860 : 0 : return "SUPEREDGE_RETURN";
861 : 0 : case SUPEREDGE_INTRAPROCEDURAL_CALL:
862 : 0 : return "SUPEREDGE_INTRAPROCEDURAL_CALL";
863 : : }
864 : : };
865 : :
866 : : /* Dump this superedge to PP. */
867 : :
868 : : void
869 : 0 : superedge::dump (pretty_printer *pp) const
870 : : {
871 : 0 : pp_printf (pp, "edge: SN: %i -> SN: %i", m_src->m_index, m_dest->m_index);
872 : 0 : label_text desc (get_description (false));
873 : 0 : if (strlen (desc.get ()) > 0)
874 : : {
875 : 0 : pp_space (pp);
876 : 0 : pp_string (pp, desc.get ());
877 : : }
878 : 0 : }
879 : :
880 : : /* Dump this superedge to stderr. */
881 : :
882 : : DEBUG_FUNCTION void
883 : 0 : superedge::dump () const
884 : : {
885 : 0 : tree_dump_pretty_printer pp (stderr);
886 : 0 : dump (&pp);
887 : 0 : pp_newline (&pp);
888 : 0 : }
889 : :
890 : : /* Implementation of dedge::dump_dot for superedges.
891 : : Write a .dot edge to GV representing this superedge. */
892 : :
893 : : void
894 : 252 : superedge::dump_dot (graphviz_out *gv, const dump_args_t &) const
895 : : {
896 : 252 : const char *style = "\"solid,bold\"";
897 : 252 : const char *color = "black";
898 : 252 : int weight = 10;
899 : 252 : const char *constraint = "true";
900 : :
901 : 252 : switch (m_kind)
902 : : {
903 : 0 : default:
904 : 0 : gcc_unreachable ();
905 : : case SUPEREDGE_CFG_EDGE:
906 : : break;
907 : 12 : case SUPEREDGE_CALL:
908 : 12 : color = "red";
909 : 12 : break;
910 : 12 : case SUPEREDGE_RETURN:
911 : 12 : color = "green";
912 : 12 : break;
913 : 12 : case SUPEREDGE_INTRAPROCEDURAL_CALL:
914 : 12 : style = "\"dotted\"";
915 : 12 : break;
916 : : }
917 : :
918 : : /* Adapted from graph.cc:draw_cfg_node_succ_edges. */
919 : 252 : if (::edge cfg_edge = get_any_cfg_edge ())
920 : : {
921 : 216 : if (cfg_edge->flags & EDGE_FAKE)
922 : : {
923 : : style = "dotted";
924 : : color = "green";
925 : : weight = 0;
926 : : }
927 : 216 : else if (cfg_edge->flags & EDGE_DFS_BACK)
928 : : {
929 : : style = "\"dotted,bold\"";
930 : : color = "blue";
931 : : weight = 10;
932 : : }
933 : 192 : else if (cfg_edge->flags & EDGE_FALLTHRU)
934 : : {
935 : 96 : color = "blue";
936 : 96 : weight = 100;
937 : : }
938 : :
939 : 216 : if (cfg_edge->flags & EDGE_ABNORMAL)
940 : 0 : color = "red";
941 : : }
942 : :
943 : 252 : gv->write_indent ();
944 : :
945 : 252 : pretty_printer *pp = gv->get_pp ();
946 : :
947 : 252 : m_src->dump_dot_id (pp);
948 : 252 : pp_string (pp, " -> ");
949 : 252 : m_dest->dump_dot_id (pp);
950 : 252 : pp_printf (pp,
951 : : (" [style=%s, color=%s, weight=%d, constraint=%s,"
952 : : " ltail=\"cluster_node_%i\", lhead=\"cluster_node_%i\""
953 : : " headlabel=\""),
954 : : style, color, weight, constraint,
955 : 252 : m_src->m_index, m_dest->m_index);
956 : :
957 : 252 : dump_label_to_pp (pp, false);
958 : :
959 : 252 : pp_printf (pp, "\"];\n");
960 : 252 : }
961 : :
962 : : /* Return a new json::object of the form
963 : : {"kind" : str,
964 : : "src_idx": int, the index of the source supernode,
965 : : "dst_idx": int, the index of the destination supernode,
966 : : "desc" : str. */
967 : :
968 : : std::unique_ptr<json::object>
969 : 8 : superedge::to_json () const
970 : : {
971 : 8 : auto sedge_obj = std::make_unique<json::object> ();
972 : 8 : sedge_obj->set_string ("kind", edge_kind_to_string (m_kind));
973 : 8 : sedge_obj->set_integer ("src_idx", m_src->m_index);
974 : 8 : sedge_obj->set_integer ("dst_idx", m_dest->m_index);
975 : :
976 : 8 : {
977 : 8 : pretty_printer pp;
978 : 8 : pp_format_decoder (&pp) = default_tree_printer;
979 : 8 : dump_label_to_pp (&pp, false);
980 : 8 : sedge_obj->set_string ("desc", pp_formatted_text (&pp));
981 : 8 : }
982 : :
983 : 8 : return sedge_obj;
984 : : }
985 : :
986 : : /* If this is an intraprocedural superedge, return the associated
987 : : CFG edge. Otherwise, return NULL. */
988 : :
989 : : ::edge
990 : 8137 : superedge::get_any_cfg_edge () const
991 : : {
992 : 8137 : if (const cfg_superedge *sub = dyn_cast_cfg_superedge ())
993 : 7768 : return sub->get_cfg_edge ();
994 : : return NULL;
995 : : }
996 : :
997 : : /* If this is an interprocedural superedge, return the associated
998 : : cgraph_edge *. Otherwise, return NULL. */
999 : :
1000 : : cgraph_edge *
1001 : 9023 : superedge::get_any_callgraph_edge () const
1002 : : {
1003 : 9023 : if (const callgraph_superedge *sub = dyn_cast_callgraph_superedge ())
1004 : 9023 : return sub->m_cedge;
1005 : : return NULL;
1006 : : }
1007 : :
1008 : : /* Build a description of this superedge (e.g. "true" for the true
1009 : : edge of a conditional, or "case 42:" for a switch case).
1010 : :
1011 : : If USER_FACING is false, the result also contains any underlying
1012 : : CFG edge flags. e.g. " (flags FALLTHRU | DFS_BACK)". */
1013 : :
1014 : : label_text
1015 : 15436 : superedge::get_description (bool user_facing) const
1016 : : {
1017 : 15436 : pretty_printer pp;
1018 : 15436 : pp_format_decoder (&pp) = default_tree_printer;
1019 : 15436 : dump_label_to_pp (&pp, user_facing);
1020 : 15436 : return label_text::take (xstrdup (pp_formatted_text (&pp)));
1021 : 15436 : }
1022 : :
1023 : : /* Implementation of superedge::dump_label_to_pp for non-switch CFG
1024 : : superedges.
1025 : :
1026 : : For true/false edges, print "true" or "false" to PP.
1027 : :
1028 : : If USER_FACING is false, also print flags on the underlying CFG edge to
1029 : : PP. */
1030 : :
1031 : : void
1032 : 15426 : cfg_superedge::dump_label_to_pp (pretty_printer *pp,
1033 : : bool user_facing) const
1034 : : {
1035 : 15426 : if (true_value_p ())
1036 : 3869 : pp_printf (pp, "true");
1037 : 11557 : else if (false_value_p ())
1038 : 3315 : pp_printf (pp, "false");
1039 : :
1040 : 15426 : if (user_facing)
1041 : : return;
1042 : :
1043 : : /* Express edge flags as a string with " | " separator.
1044 : : e.g. " (flags FALLTHRU | DFS_BACK)". */
1045 : 451 : if (get_flags ())
1046 : : {
1047 : 390 : pp_string (pp, " (flags ");
1048 : 390 : bool seen_flag = false;
1049 : : #define DEF_EDGE_FLAG(NAME,IDX) \
1050 : : do { \
1051 : : if (get_flags () & EDGE_##NAME) \
1052 : : { \
1053 : : if (seen_flag) \
1054 : : pp_string (pp, " | "); \
1055 : : pp_printf (pp, "%s", (#NAME)); \
1056 : : seen_flag = true; \
1057 : : } \
1058 : : } while (0);
1059 : : #include "cfg-flags.def"
1060 : : #undef DEF_EDGE_FLAG
1061 : 390 : pp_string (pp, ")");
1062 : : }
1063 : :
1064 : 451 : if (m_cfg_edge->goto_locus > BUILTINS_LOCATION)
1065 : 338 : pp_string (pp, " (has goto_locus)");
1066 : :
1067 : : /* Otherwise, no label. */
1068 : : }
1069 : :
1070 : : /* Get the index number for this edge for use in phi stmts
1071 : : in its destination. */
1072 : :
1073 : : size_t
1074 : 86422 : cfg_superedge::get_phi_arg_idx () const
1075 : : {
1076 : 86422 : return m_cfg_edge->dest_idx;
1077 : : }
1078 : :
1079 : : /* Get the phi argument for PHI for this CFG edge. */
1080 : :
1081 : : tree
1082 : 75379 : cfg_superedge::get_phi_arg (const gphi *phi) const
1083 : : {
1084 : 75379 : size_t index = get_phi_arg_idx ();
1085 : 75379 : return gimple_phi_arg_def (phi, index);
1086 : : }
1087 : :
1088 : : /* class switch_cfg_superedge : public cfg_superedge. */
1089 : :
1090 : 3031 : switch_cfg_superedge::switch_cfg_superedge (supernode *src,
1091 : : supernode *dst,
1092 : 3031 : ::edge e)
1093 : 3031 : : cfg_superedge (src, dst, e)
1094 : : {
1095 : : /* Populate m_case_labels with all cases which go to DST. */
1096 : 3031 : const gswitch *gswitch = get_switch_stmt ();
1097 : 542778 : for (unsigned i = 0; i < gimple_switch_num_labels (gswitch); i++)
1098 : : {
1099 : 539747 : tree case_ = gimple_switch_label (gswitch, i);
1100 : 539747 : basic_block bb = label_to_block (src->get_function (),
1101 : 539747 : CASE_LABEL (case_));
1102 : 539747 : if (bb == dst->m_bb)
1103 : 3599 : m_case_labels.safe_push (case_);
1104 : : }
1105 : 3031 : }
1106 : :
1107 : : /* Implementation of superedge::dump_label_to_pp for CFG superedges for
1108 : : "switch" statements.
1109 : :
1110 : : Print "case VAL:", "case LOWER ... UPPER:", or "default:" to PP. */
1111 : :
1112 : : void
1113 : 411 : switch_cfg_superedge::dump_label_to_pp (pretty_printer *pp,
1114 : : bool user_facing ATTRIBUTE_UNUSED) const
1115 : : {
1116 : 411 : if (user_facing)
1117 : : {
1118 : 828 : for (unsigned i = 0; i < m_case_labels.length (); ++i)
1119 : : {
1120 : 417 : if (i > 0)
1121 : 6 : pp_string (pp, ", ");
1122 : 417 : tree case_label = m_case_labels[i];
1123 : 417 : gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
1124 : 417 : tree lower_bound = CASE_LOW (case_label);
1125 : 417 : tree upper_bound = CASE_HIGH (case_label);
1126 : 417 : if (lower_bound)
1127 : : {
1128 : 162 : pp_printf (pp, "case ");
1129 : 162 : dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false);
1130 : 162 : if (upper_bound)
1131 : : {
1132 : 12 : pp_printf (pp, " ... ");
1133 : 12 : dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0,
1134 : : false);
1135 : : }
1136 : 162 : pp_printf (pp, ":");
1137 : : }
1138 : : else
1139 : 255 : pp_printf (pp, "default:");
1140 : : }
1141 : : }
1142 : : else
1143 : : {
1144 : 0 : pp_character (pp, '{');
1145 : 0 : for (unsigned i = 0; i < m_case_labels.length (); ++i)
1146 : : {
1147 : 0 : if (i > 0)
1148 : 0 : pp_string (pp, ", ");
1149 : 0 : tree case_label = m_case_labels[i];
1150 : 0 : gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
1151 : 0 : tree lower_bound = CASE_LOW (case_label);
1152 : 0 : tree upper_bound = CASE_HIGH (case_label);
1153 : 0 : if (lower_bound)
1154 : : {
1155 : 0 : if (upper_bound)
1156 : : {
1157 : 0 : pp_character (pp, '[');
1158 : 0 : dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0,
1159 : : false);
1160 : 0 : pp_string (pp, ", ");
1161 : 0 : dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0,
1162 : : false);
1163 : 0 : pp_character (pp, ']');
1164 : : }
1165 : : else
1166 : 0 : dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false);
1167 : : }
1168 : : else
1169 : 0 : pp_printf (pp, "default");
1170 : : }
1171 : 0 : pp_character (pp, '}');
1172 : 0 : if (implicitly_created_default_p ())
1173 : : {
1174 : 0 : pp_string (pp, " IMPLICITLY CREATED");
1175 : : }
1176 : : }
1177 : 411 : }
1178 : :
1179 : : /* Return true iff this edge is purely for an implicitly-created "default". */
1180 : :
1181 : : bool
1182 : 614 : switch_cfg_superedge::implicitly_created_default_p () const
1183 : : {
1184 : 614 : if (m_case_labels.length () != 1)
1185 : : return false;
1186 : :
1187 : 554 : tree case_label = m_case_labels[0];
1188 : 554 : gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
1189 : 554 : if (CASE_LOW (case_label))
1190 : : return false;
1191 : :
1192 : : /* We have a single "default" case.
1193 : : Assume that it was implicitly created if it has UNKNOWN_LOCATION. */
1194 : 163 : return EXPR_LOCATION (case_label) == UNKNOWN_LOCATION;
1195 : : }
1196 : :
1197 : : /* class eh_dispatch_cfg_superedge : public cfg_superedge. */
1198 : :
1199 : : /* Given an ERT_TRY region, get the eh_catch corresponding to
1200 : : the label of DST_SNODE, if any. */
1201 : :
1202 : : static eh_catch
1203 : 180 : get_catch (eh_region eh_reg, supernode *dst_snode)
1204 : : {
1205 : 180 : gcc_assert (eh_reg->type == ERT_TRY);
1206 : :
1207 : 180 : tree dst_snode_label = dst_snode->get_label ();
1208 : 180 : if (!dst_snode_label)
1209 : : return nullptr;
1210 : :
1211 : 123 : for (eh_catch iter = eh_reg->u.eh_try.first_catch;
1212 : 183 : iter;
1213 : 60 : iter = iter->next_catch)
1214 : 183 : if (iter->label == dst_snode_label)
1215 : : return iter;
1216 : :
1217 : : return nullptr;
1218 : : }
1219 : :
1220 : : std::unique_ptr<eh_dispatch_cfg_superedge>
1221 : 192 : eh_dispatch_cfg_superedge::make (supernode *src_snode,
1222 : : supernode *dst_snode,
1223 : : ::edge e,
1224 : : const geh_dispatch *eh_dispatch_stmt)
1225 : : {
1226 : 192 : const eh_status *eh = src_snode->get_function ()->eh;
1227 : 192 : gcc_assert (eh);
1228 : 192 : int region_idx = gimple_eh_dispatch_region (eh_dispatch_stmt);
1229 : 192 : gcc_assert (region_idx > 0);
1230 : 192 : gcc_assert ((*eh->region_array)[region_idx]);
1231 : 192 : eh_region eh_reg = (*eh->region_array)[region_idx];
1232 : 192 : gcc_assert (eh_reg);
1233 : 192 : switch (eh_reg->type)
1234 : : {
1235 : 0 : default:
1236 : 0 : gcc_unreachable ();
1237 : 0 : case ERT_CLEANUP:
1238 : : // TODO
1239 : 0 : gcc_unreachable ();
1240 : 180 : break;
1241 : 180 : case ERT_TRY:
1242 : 180 : {
1243 : 180 : eh_catch ehc = get_catch (eh_reg, dst_snode);
1244 : 180 : return std::make_unique<eh_dispatch_try_cfg_superedge>
1245 : 180 : (src_snode, dst_snode,
1246 : : e, eh_dispatch_stmt,
1247 : 180 : eh_reg, ehc);
1248 : : }
1249 : 12 : break;
1250 : 12 : case ERT_ALLOWED_EXCEPTIONS:
1251 : 12 : return std::make_unique<eh_dispatch_allowed_cfg_superedge>
1252 : 12 : (src_snode, dst_snode,
1253 : : e, eh_dispatch_stmt,
1254 : 12 : eh_reg);
1255 : 0 : break;
1256 : 0 : case ERT_MUST_NOT_THROW:
1257 : : // TODO
1258 : 0 : gcc_unreachable ();
1259 : : break;
1260 : : }
1261 : : }
1262 : :
1263 : 192 : eh_dispatch_cfg_superedge::
1264 : : eh_dispatch_cfg_superedge (supernode *src,
1265 : : supernode *dst,
1266 : : ::edge e,
1267 : : const geh_dispatch *eh_dispatch_stmt,
1268 : 192 : eh_region eh_reg)
1269 : : : cfg_superedge (src, dst, e),
1270 : 192 : m_eh_dispatch_stmt (eh_dispatch_stmt),
1271 : 192 : m_eh_region (eh_reg)
1272 : : {
1273 : 192 : gcc_assert (m_eh_region);
1274 : 192 : }
1275 : :
1276 : : const eh_status &
1277 : 0 : eh_dispatch_cfg_superedge::get_eh_status () const
1278 : : {
1279 : 0 : const eh_status *eh = m_src->get_function ()->eh;
1280 : 0 : gcc_assert (eh);
1281 : 0 : return *eh;
1282 : : }
1283 : :
1284 : : // class eh_dispatch_try_cfg_superedge : public eh_dispatch_cfg_superedge
1285 : :
1286 : : /* Implementation of superedge::dump_label_to_pp for CFG superedges for
1287 : : "eh_dispatch" statements for ERT_TRY regions. */
1288 : :
1289 : : void
1290 : 0 : eh_dispatch_try_cfg_superedge::dump_label_to_pp (pretty_printer *pp,
1291 : : bool user_facing) const
1292 : : {
1293 : 0 : if (!user_facing)
1294 : 0 : pp_string (pp, "ERT_TRY: ");
1295 : 0 : if (m_eh_catch)
1296 : : {
1297 : 0 : bool first = true;
1298 : 0 : for (tree iter = m_eh_catch->type_list; iter; iter = TREE_CHAIN (iter))
1299 : : {
1300 : 0 : if (!first)
1301 : 0 : pp_string (pp, ", ");
1302 : 0 : pp_printf (pp, "on catch %qT", TREE_VALUE (iter));
1303 : 0 : first = false;
1304 : : }
1305 : : }
1306 : : else
1307 : 0 : pp_string (pp, "on uncaught exception");
1308 : 0 : }
1309 : :
1310 : : bool
1311 : 240 : eh_dispatch_try_cfg_superedge::
1312 : : apply_constraints (region_model *model,
1313 : : region_model_context *ctxt,
1314 : : tree exception_type,
1315 : : std::unique_ptr<rejected_constraint> *out) const
1316 : : {
1317 : 240 : return model->apply_constraints_for_eh_dispatch_try
1318 : 240 : (*this, ctxt, exception_type, out);
1319 : : }
1320 : :
1321 : : // class eh_dispatch_allowed_cfg_superedge : public eh_dispatch_cfg_superedge
1322 : :
1323 : 12 : eh_dispatch_allowed_cfg_superedge::
1324 : : eh_dispatch_allowed_cfg_superedge (supernode *src, supernode *dst, ::edge e,
1325 : : const geh_dispatch *eh_dispatch_stmt,
1326 : 12 : eh_region eh_reg)
1327 : 12 : : eh_dispatch_cfg_superedge (src, dst, e, eh_dispatch_stmt, eh_reg)
1328 : : {
1329 : 12 : gcc_assert (eh_reg->type == ERT_ALLOWED_EXCEPTIONS);
1330 : :
1331 : : /* We expect two sibling out-edges at an eh_dispatch from such a region:
1332 : :
1333 : : - one to a bb without a gimple label, with a resx,
1334 : : for exceptions of expected types
1335 : :
1336 : : - one to a bb with a gimple label, with a call to __cxa_unexpected,
1337 : : for exceptions of unexpected types.
1338 : :
1339 : : Set m_kind for this edge accordingly. */
1340 : 12 : gcc_assert (e->src->succs->length () == 2);
1341 : 12 : tree label_for_unexpected_exceptions = eh_reg->u.allowed.label;
1342 : 12 : tree label_for_dest_enode = dst->get_label ();
1343 : 12 : if (label_for_dest_enode == label_for_unexpected_exceptions)
1344 : 6 : m_kind = eh_kind::unexpected;
1345 : : else
1346 : : {
1347 : 6 : gcc_assert (label_for_dest_enode == nullptr);
1348 : 6 : m_kind = eh_kind::expected;
1349 : : }
1350 : 12 : }
1351 : :
1352 : : void
1353 : 3 : eh_dispatch_allowed_cfg_superedge::dump_label_to_pp (pretty_printer *pp,
1354 : : bool user_facing) const
1355 : : {
1356 : 3 : if (!user_facing)
1357 : : {
1358 : 0 : switch (m_kind)
1359 : : {
1360 : 0 : default:
1361 : 0 : gcc_unreachable ();
1362 : 0 : case eh_dispatch_allowed_cfg_superedge::eh_kind::expected:
1363 : 0 : pp_string (pp, "expected: ");
1364 : 0 : break;
1365 : 0 : case eh_dispatch_allowed_cfg_superedge::eh_kind::unexpected:
1366 : 0 : pp_string (pp, "unexpected: ");
1367 : 0 : break;
1368 : : }
1369 : 0 : pp_string (pp, "ERT_ALLOWED_EXCEPTIONS: ");
1370 : 0 : eh_region eh_reg = get_eh_region ();
1371 : 0 : bool first = true;
1372 : 0 : for (tree iter = eh_reg->u.allowed.type_list; iter;
1373 : 0 : iter = TREE_CHAIN (iter))
1374 : : {
1375 : 0 : if (!first)
1376 : 0 : pp_string (pp, ", ");
1377 : 0 : pp_printf (pp, "%qT", TREE_VALUE (iter));
1378 : 0 : first = false;
1379 : : }
1380 : : }
1381 : 3 : }
1382 : :
1383 : : bool
1384 : 13 : eh_dispatch_allowed_cfg_superedge::
1385 : : apply_constraints (region_model *model,
1386 : : region_model_context *ctxt,
1387 : : tree exception_type,
1388 : : std::unique_ptr<rejected_constraint> *out) const
1389 : : {
1390 : 13 : return model->apply_constraints_for_eh_dispatch_allowed
1391 : 13 : (*this, ctxt, exception_type, out);
1392 : : }
1393 : :
1394 : : /* Implementation of superedge::dump_label_to_pp for interprocedural
1395 : : superedges. */
1396 : :
1397 : : void
1398 : 52 : callgraph_superedge::dump_label_to_pp (pretty_printer *pp,
1399 : : bool user_facing ATTRIBUTE_UNUSED) const
1400 : : {
1401 : 52 : switch (m_kind)
1402 : : {
1403 : 0 : default:
1404 : 0 : case SUPEREDGE_CFG_EDGE:
1405 : 0 : gcc_unreachable ();
1406 : :
1407 : 20 : case SUPEREDGE_CALL:
1408 : 20 : pp_printf (pp, "call");
1409 : 20 : break;
1410 : :
1411 : 20 : case SUPEREDGE_RETURN:
1412 : 20 : pp_printf (pp, "return");
1413 : 20 : break;
1414 : :
1415 : 12 : case SUPEREDGE_INTRAPROCEDURAL_CALL:
1416 : 12 : pp_printf (pp, "intraproc link");
1417 : 12 : break;
1418 : : }
1419 : 52 : }
1420 : :
1421 : : /* Get the function that was called at this interprocedural call/return
1422 : : edge. */
1423 : :
1424 : : function *
1425 : 11147 : callgraph_superedge::get_callee_function () const
1426 : : {
1427 : 11147 : return get_ultimate_function_for_cgraph_edge (m_cedge);
1428 : : }
1429 : :
1430 : : /* Get the calling function at this interprocedural call/return edge. */
1431 : :
1432 : : function *
1433 : 0 : callgraph_superedge::get_caller_function () const
1434 : : {
1435 : 0 : return m_cedge->caller->get_fun ();
1436 : : }
1437 : :
1438 : : /* Get the fndecl that was called at this interprocedural call/return
1439 : : edge. */
1440 : :
1441 : : tree
1442 : 980 : callgraph_superedge::get_callee_decl () const
1443 : : {
1444 : 980 : return get_callee_function ()->decl;
1445 : : }
1446 : :
1447 : : /* Get the gcall * of this interprocedural call/return edge. */
1448 : :
1449 : : const gcall &
1450 : 19604 : callgraph_superedge::get_call_stmt () const
1451 : : {
1452 : 19604 : if (m_cedge)
1453 : 19604 : return *m_cedge->call_stmt;
1454 : :
1455 : 0 : return *m_src->get_final_call ();
1456 : : }
1457 : :
1458 : : /* Get the calling fndecl at this interprocedural call/return edge. */
1459 : :
1460 : : tree
1461 : 0 : callgraph_superedge::get_caller_decl () const
1462 : : {
1463 : 0 : return get_caller_function ()->decl;
1464 : : }
1465 : :
1466 : : /* Given PARM_TO_FIND, a PARM_DECL, identify its index (writing it
1467 : : to *OUT if OUT is non-NULL), and return the corresponding argument
1468 : : at the callsite. */
1469 : :
1470 : : tree
1471 : 230 : callgraph_superedge::get_arg_for_parm (tree parm_to_find,
1472 : : callsite_expr *out) const
1473 : : {
1474 : 230 : gcc_assert (TREE_CODE (parm_to_find) == PARM_DECL);
1475 : :
1476 : 230 : tree callee = get_callee_decl ();
1477 : 230 : const gcall &call_stmt = get_call_stmt ();
1478 : :
1479 : 230 : unsigned i = 0;
1480 : 252 : for (tree iter_parm = DECL_ARGUMENTS (callee); iter_parm;
1481 : 22 : iter_parm = DECL_CHAIN (iter_parm), ++i)
1482 : : {
1483 : 219 : if (i >= gimple_call_num_args (&call_stmt))
1484 : : return NULL_TREE;
1485 : 219 : if (iter_parm == parm_to_find)
1486 : : {
1487 : 197 : if (out)
1488 : 197 : *out = callsite_expr::from_zero_based_param (i);
1489 : 197 : return gimple_call_arg (&call_stmt, i);
1490 : : }
1491 : : }
1492 : :
1493 : : /* Not found. */
1494 : : return NULL_TREE;
1495 : : }
1496 : :
1497 : : /* Look for a use of ARG_TO_FIND as an argument at this callsite.
1498 : : If found, return the default SSA def of the corresponding parm within
1499 : : the callee, and if OUT is non-NULL, write the index to *OUT.
1500 : : Only the first match is handled. */
1501 : :
1502 : : tree
1503 : 469 : callgraph_superedge::get_parm_for_arg (tree arg_to_find,
1504 : : callsite_expr *out) const
1505 : : {
1506 : 469 : tree callee = get_callee_decl ();
1507 : 469 : const gcall &call_stmt = get_call_stmt ();
1508 : :
1509 : 469 : unsigned i = 0;
1510 : 811 : for (tree iter_parm = DECL_ARGUMENTS (callee); iter_parm;
1511 : 342 : iter_parm = DECL_CHAIN (iter_parm), ++i)
1512 : : {
1513 : 426 : if (i >= gimple_call_num_args (&call_stmt))
1514 : : return NULL_TREE;
1515 : 425 : tree param = gimple_call_arg (&call_stmt, i);
1516 : 425 : if (arg_to_find == param)
1517 : : {
1518 : 83 : if (out)
1519 : 83 : *out = callsite_expr::from_zero_based_param (i);
1520 : 83 : return ssa_default_def (get_callee_function (), iter_parm);
1521 : : }
1522 : : }
1523 : :
1524 : : /* Not found. */
1525 : : return NULL_TREE;
1526 : : }
1527 : :
1528 : : /* Map caller_expr back to an expr within the callee, or return NULL_TREE.
1529 : : If non-NULL is returned, populate OUT. */
1530 : :
1531 : : tree
1532 : 469 : callgraph_superedge::map_expr_from_caller_to_callee (tree caller_expr,
1533 : : callsite_expr *out) const
1534 : : {
1535 : : /* Is it an argument (actual param)? If so, convert to
1536 : : parameter (formal param). */
1537 : 469 : tree parm = get_parm_for_arg (caller_expr, out);
1538 : 469 : if (parm)
1539 : : return parm;
1540 : : /* Otherwise try return value. */
1541 : 390 : if (caller_expr == gimple_call_lhs (&get_call_stmt ()))
1542 : : {
1543 : 69 : if (out)
1544 : 69 : *out = callsite_expr::from_return_value ();
1545 : 69 : return DECL_RESULT (get_callee_decl ());
1546 : : }
1547 : :
1548 : : return NULL_TREE;
1549 : : }
1550 : :
1551 : : /* Map callee_expr back to an expr within the caller, or return NULL_TREE.
1552 : : If non-NULL is returned, populate OUT. */
1553 : :
1554 : : tree
1555 : 1177 : callgraph_superedge::map_expr_from_callee_to_caller (tree callee_expr,
1556 : : callsite_expr *out) const
1557 : : {
1558 : 1177 : if (callee_expr == NULL_TREE)
1559 : : return NULL_TREE;
1560 : :
1561 : : /* If it's a parameter (formal param), get the argument (actual param). */
1562 : 442 : if (TREE_CODE (callee_expr) == PARM_DECL)
1563 : 10 : return get_arg_for_parm (callee_expr, out);
1564 : :
1565 : : /* Similar for the default SSA name of the PARM_DECL. */
1566 : 432 : if (TREE_CODE (callee_expr) == SSA_NAME
1567 : 345 : && SSA_NAME_IS_DEFAULT_DEF (callee_expr)
1568 : 652 : && TREE_CODE (SSA_NAME_VAR (callee_expr)) == PARM_DECL)
1569 : 220 : return get_arg_for_parm (SSA_NAME_VAR (callee_expr), out);
1570 : :
1571 : : /* Otherwise try return value. */
1572 : 212 : if (callee_expr == DECL_RESULT (get_callee_decl ()))
1573 : : {
1574 : 0 : if (out)
1575 : 0 : *out = callsite_expr::from_return_value ();
1576 : 0 : return gimple_call_lhs (&get_call_stmt ());
1577 : : }
1578 : :
1579 : : return NULL_TREE;
1580 : : }
1581 : :
1582 : : } // namespace ana
1583 : :
1584 : : #endif /* #if ENABLE_ANALYZER */
|