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