]>
Commit | Line | Data |
---|---|---|
757bf1df DM |
1 | /* "Supergraph" classes that combine CFGs and callgraph into one digraph. |
2 | Copyright (C) 2019-2020 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 "config.h" | |
22 | #include "system.h" | |
23 | #include "coretypes.h" | |
24 | #include "tree.h" | |
25 | #include "tm.h" | |
26 | #include "toplev.h" | |
27 | #include "hash-table.h" | |
28 | #include "vec.h" | |
29 | #include "ggc.h" | |
30 | #include "basic-block.h" | |
31 | #include "function.h" | |
32 | #include "gimple-fold.h" | |
33 | #include "tree-eh.h" | |
34 | #include "gimple-expr.h" | |
35 | #include "is-a.h" | |
36 | #include "timevar.h" | |
37 | #include "gimple.h" | |
38 | #include "gimple-iterator.h" | |
39 | #include "gimple-pretty-print.h" | |
40 | #include "tree-pretty-print.h" | |
41 | #include "graphviz.h" | |
42 | #include "cgraph.h" | |
43 | #include "tree-dfa.h" | |
44 | #include "cfganal.h" | |
45 | #include "function.h" | |
46 | #include "analyzer/analyzer.h" | |
47 | #include "ordered-hash-map.h" | |
48 | #include "options.h" | |
49 | #include "cgraph.h" | |
50 | #include "cfg.h" | |
51 | #include "digraph.h" | |
52 | #include "analyzer/supergraph.h" | |
53 | #include "analyzer/analyzer-logging.h" | |
54 | ||
55 | #if ENABLE_ANALYZER | |
56 | ||
75038aa6 DM |
57 | namespace ana { |
58 | ||
757bf1df DM |
59 | /* Get the cgraph_edge, but only if there's an underlying function body. */ |
60 | ||
61 | cgraph_edge * | |
62 | supergraph_call_edge (function *fun, gimple *stmt) | |
63 | { | |
64 | gcall *call = dyn_cast<gcall *> (stmt); | |
65 | if (!call) | |
66 | return NULL; | |
67 | cgraph_edge *edge = cgraph_node::get (fun->decl)->get_edge (stmt); | |
68 | if (!edge) | |
69 | return NULL; | |
70 | if (!edge->callee) | |
71 | return NULL; /* e.g. for a function pointer. */ | |
72 | if (!edge->callee->get_fun ()) | |
73 | return NULL; | |
74 | return edge; | |
75 | } | |
76 | ||
77 | /* supergraph's ctor. Walk the callgraph, building supernodes for each | |
78 | CFG basic block, splitting the basic blocks at callsites. Join | |
79 | together the supernodes with interprocedural and intraprocedural | |
80 | superedges as appropriate. */ | |
81 | ||
82 | supergraph::supergraph (logger *logger) | |
83 | { | |
84 | auto_timevar tv (TV_ANALYZER_SUPERGRAPH); | |
85 | ||
86 | LOG_FUNC (logger); | |
87 | ||
88 | /* First pass: make supernodes. */ | |
89 | { | |
90 | /* Sort the cgraph_nodes? */ | |
91 | cgraph_node *node; | |
92 | FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) | |
93 | { | |
94 | function *fun = node->get_fun (); | |
95 | ||
96 | /* Ensure that EDGE_DFS_BACK is correct for every CFG edge in | |
97 | the supergraph (by doing it per-function). */ | |
98 | auto_cfun sentinel (fun); | |
99 | mark_dfs_back_edges (); | |
100 | ||
101 | const int start_idx = m_nodes.length (); | |
102 | ||
103 | basic_block bb; | |
104 | FOR_ALL_BB_FN (bb, fun) | |
105 | { | |
106 | /* The initial supernode for the BB gets the phi nodes (if any). */ | |
107 | supernode *node_for_stmts = add_node (fun, bb, NULL, phi_nodes (bb)); | |
108 | m_bb_to_initial_node.put (bb, node_for_stmts); | |
109 | for (gphi_iterator gpi = gsi_start_phis (bb); !gsi_end_p (gpi); | |
110 | gsi_next (&gpi)) | |
111 | { | |
112 | gimple *stmt = gsi_stmt (gpi); | |
113 | m_stmt_to_node_t.put (stmt, node_for_stmts); | |
114 | } | |
115 | ||
116 | /* Append statements from BB to the current supernode, splitting | |
117 | them into a new supernode at each call site; such call statements | |
118 | appear in both supernodes (representing call and return). */ | |
119 | gimple_stmt_iterator gsi; | |
120 | for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) | |
121 | { | |
122 | gimple *stmt = gsi_stmt (gsi); | |
123 | node_for_stmts->m_stmts.safe_push (stmt); | |
124 | m_stmt_to_node_t.put (stmt, node_for_stmts); | |
125 | if (cgraph_edge *edge = supergraph_call_edge (fun, stmt)) | |
126 | { | |
127 | m_cgraph_edge_to_caller_prev_node.put(edge, node_for_stmts); | |
128 | node_for_stmts = add_node (fun, bb, as_a <gcall *> (stmt), NULL); | |
129 | m_cgraph_edge_to_caller_next_node.put (edge, node_for_stmts); | |
130 | } | |
131 | } | |
132 | ||
133 | m_bb_to_final_node.put (bb, node_for_stmts); | |
134 | } | |
135 | ||
136 | const unsigned num_snodes = m_nodes.length () - start_idx; | |
137 | m_function_to_num_snodes.put (fun, num_snodes); | |
138 | ||
139 | if (logger) | |
140 | { | |
141 | const int end_idx = m_nodes.length () - 1; | |
142 | logger->log ("SN: %i...%i: function %qD", | |
143 | start_idx, end_idx, fun->decl); | |
144 | } | |
145 | } | |
146 | } | |
147 | ||
148 | /* Second pass: make superedges. */ | |
149 | { | |
150 | /* Make superedges for CFG edges. */ | |
151 | for (bb_to_node_t::iterator iter = m_bb_to_final_node.begin (); | |
152 | iter != m_bb_to_final_node.end (); | |
153 | ++iter) | |
154 | { | |
155 | basic_block bb = (*iter).first; | |
156 | supernode *src_supernode = (*iter).second; | |
157 | ||
158 | ::edge cfg_edge; | |
159 | int idx; | |
160 | if (bb->succs) | |
161 | FOR_EACH_VEC_ELT (*bb->succs, idx, cfg_edge) | |
162 | { | |
163 | basic_block dest_cfg_block = cfg_edge->dest; | |
164 | supernode *dest_supernode | |
165 | = *m_bb_to_initial_node.get (dest_cfg_block); | |
166 | cfg_superedge *cfg_sedge | |
167 | = add_cfg_edge (src_supernode, dest_supernode, cfg_edge, idx); | |
168 | m_cfg_edge_to_cfg_superedge.put (cfg_edge, cfg_sedge); | |
169 | } | |
170 | } | |
171 | ||
172 | /* Make interprocedural superedges for calls. */ | |
173 | { | |
174 | for (cgraph_edge_to_node_t::iterator iter | |
175 | = m_cgraph_edge_to_caller_prev_node.begin (); | |
176 | iter != m_cgraph_edge_to_caller_prev_node.end (); | |
177 | ++iter) | |
178 | { | |
179 | cgraph_edge *edge = (*iter).first; | |
180 | supernode *caller_prev_supernode = (*iter).second; | |
181 | basic_block callee_cfg_block | |
182 | = ENTRY_BLOCK_PTR_FOR_FN (edge->callee->get_fun ()); | |
183 | supernode *callee_supernode | |
184 | = *m_bb_to_initial_node.get (callee_cfg_block); | |
185 | call_superedge *sedge | |
186 | = add_call_superedge (caller_prev_supernode, | |
187 | callee_supernode, | |
188 | edge); | |
189 | m_cgraph_edge_to_call_superedge.put (edge, sedge); | |
190 | } | |
191 | } | |
192 | ||
193 | /* Make interprocedural superedges for returns. */ | |
194 | { | |
195 | for (cgraph_edge_to_node_t::iterator iter | |
196 | = m_cgraph_edge_to_caller_next_node.begin (); | |
197 | iter != m_cgraph_edge_to_caller_next_node.end (); | |
198 | ++iter) | |
199 | { | |
200 | cgraph_edge *edge = (*iter).first; | |
201 | supernode *caller_next_supernode = (*iter).second; | |
202 | basic_block callee_cfg_block | |
203 | = EXIT_BLOCK_PTR_FOR_FN (edge->callee->get_fun ()); | |
204 | supernode *callee_supernode | |
205 | = *m_bb_to_initial_node.get (callee_cfg_block); | |
206 | return_superedge *sedge | |
207 | = add_return_superedge (callee_supernode, | |
208 | caller_next_supernode, | |
209 | edge); | |
210 | m_cgraph_edge_to_return_superedge.put (edge, sedge); | |
211 | } | |
212 | } | |
213 | ||
214 | /* Make intraprocedural superedges linking the two halves of a call. */ | |
215 | { | |
216 | for (cgraph_edge_to_node_t::iterator iter | |
217 | = m_cgraph_edge_to_caller_prev_node.begin (); | |
218 | iter != m_cgraph_edge_to_caller_prev_node.end (); | |
219 | ++iter) | |
220 | { | |
221 | cgraph_edge *edge = (*iter).first; | |
222 | supernode *caller_prev_supernode = (*iter).second; | |
223 | supernode *caller_next_supernode | |
224 | = *m_cgraph_edge_to_caller_next_node.get (edge); | |
225 | superedge *sedge | |
226 | = new callgraph_superedge (caller_prev_supernode, | |
227 | caller_next_supernode, | |
228 | SUPEREDGE_INTRAPROCEDURAL_CALL, | |
229 | edge); | |
230 | add_edge (sedge); | |
231 | m_cgraph_edge_to_intraproc_superedge.put (edge, sedge); | |
232 | } | |
233 | ||
234 | } | |
235 | } | |
236 | } | |
237 | ||
238 | /* Dump this graph in .dot format to PP, using DUMP_ARGS. | |
239 | Cluster the supernodes by function, then by BB from original CFG. */ | |
240 | ||
241 | void | |
242 | supergraph::dump_dot_to_pp (pretty_printer *pp, | |
243 | const dump_args_t &dump_args) const | |
244 | { | |
245 | graphviz_out gv (pp); | |
246 | ||
247 | pp_string (pp, "digraph \""); | |
248 | pp_write_text_to_stream (pp); | |
249 | pp_string (pp, "supergraph"); | |
250 | pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false); | |
251 | pp_string (pp, "\" {\n"); | |
252 | gv.indent (); | |
253 | ||
254 | gv.println ("overlap=false;"); | |
255 | gv.println ("compound=true;"); | |
256 | ||
257 | /* TODO: maybe (optionally) sub-subdivide by TU, for LTO; see also: | |
258 | https://gcc-python-plugin.readthedocs.io/en/latest/_images/sample-supergraph.png | |
259 | */ | |
260 | ||
261 | /* Break out the supernodes into clusters by function. */ | |
262 | { | |
263 | cgraph_node *node; | |
264 | FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) | |
265 | { | |
266 | function *fun = node->get_fun (); | |
267 | const char *funcname = function_name (fun); | |
268 | gv.println ("subgraph \"cluster_%s\" {", | |
269 | funcname); | |
270 | gv.indent (); | |
271 | pp_printf (pp, | |
272 | ("style=\"dashed\";" | |
273 | " color=\"black\";" | |
274 | " label=\"%s\";\n"), | |
275 | funcname); | |
276 | ||
277 | /* Break out the nodes into clusters by BB from original CFG. */ | |
278 | { | |
279 | basic_block bb; | |
280 | FOR_ALL_BB_FN (bb, fun) | |
281 | { | |
282 | if (dump_args.m_flags & SUPERGRAPH_DOT_SHOW_BBS) | |
283 | { | |
284 | gv.println ("subgraph \"cluster_%s_bb_%i\" {", | |
285 | funcname, bb->index); | |
286 | gv.indent (); | |
287 | pp_printf (pp, | |
288 | ("style=\"dashed\";" | |
289 | " color=\"black\";" | |
290 | " label=\"bb: %i\";\n"), | |
291 | bb->index); | |
292 | } | |
293 | ||
294 | // TODO: maybe keep an index per-function/per-bb to speed this up??? | |
295 | int i; | |
296 | supernode *n; | |
297 | FOR_EACH_VEC_ELT (m_nodes, i, n) | |
298 | if (n->m_fun == fun && n->m_bb == bb) | |
299 | n->dump_dot (&gv, dump_args); | |
300 | ||
301 | if (dump_args.m_flags & SUPERGRAPH_DOT_SHOW_BBS) | |
302 | { | |
303 | /* Terminate per-bb "subgraph" */ | |
304 | gv.outdent (); | |
305 | gv.println ("}"); | |
306 | } | |
307 | } | |
308 | } | |
309 | ||
310 | /* Add an invisible edge from ENTRY to EXIT, to improve the graph layout. */ | |
311 | pp_string (pp, "\t"); | |
312 | get_node_for_function_entry (fun)->dump_dot_id (pp); | |
313 | pp_string (pp, ":s -> "); | |
314 | get_node_for_function_exit (fun)->dump_dot_id (pp); | |
315 | pp_string (pp, ":n [style=\"invis\",constraint=true];\n"); | |
316 | ||
317 | /* Terminate per-function "subgraph" */ | |
318 | gv.outdent (); | |
319 | gv.println ("}"); | |
320 | } | |
321 | } | |
322 | ||
323 | /* Superedges. */ | |
324 | int i; | |
325 | superedge *e; | |
326 | FOR_EACH_VEC_ELT (m_edges, i, e) | |
327 | e->dump_dot (&gv, dump_args); | |
328 | ||
329 | /* Terminate "digraph" */ | |
330 | gv.outdent (); | |
331 | gv.println ("}"); | |
332 | } | |
333 | ||
334 | /* Dump this graph in .dot format to FP, using DUMP_ARGS. */ | |
335 | ||
336 | void | |
337 | supergraph::dump_dot_to_file (FILE *fp, const dump_args_t &dump_args) const | |
338 | { | |
339 | pretty_printer *pp = global_dc->printer->clone (); | |
340 | pp_show_color (pp) = 0; | |
341 | /* %qE in logs for SSA_NAMEs should show the ssa names, rather than | |
342 | trying to prettify things by showing the underlying var. */ | |
343 | pp_format_decoder (pp) = default_tree_printer; | |
344 | ||
345 | pp->buffer->stream = fp; | |
346 | dump_dot_to_pp (pp, dump_args); | |
347 | pp_flush (pp); | |
348 | delete pp; | |
349 | } | |
350 | ||
351 | /* Dump this graph in .dot format to PATH, using DUMP_ARGS. */ | |
352 | ||
353 | void | |
354 | supergraph::dump_dot (const char *path, const dump_args_t &dump_args) const | |
355 | { | |
356 | FILE *fp = fopen (path, "w"); | |
357 | dump_dot_to_file (fp, dump_args); | |
358 | fclose (fp); | |
359 | } | |
360 | ||
361 | /* Create a supernode for BB within FUN and add it to this supergraph. | |
362 | ||
363 | If RETURNING_CALL is non-NULL, the supernode represents the resumption | |
364 | of the basic block after returning from that call. | |
365 | ||
366 | If PHI_NODES is non-NULL, this is the initial supernode for the basic | |
367 | block, and is responsible for any handling of the phi nodes. */ | |
368 | ||
369 | supernode * | |
370 | supergraph::add_node (function *fun, basic_block bb, gcall *returning_call, | |
371 | gimple_seq phi_nodes) | |
372 | { | |
373 | supernode *n = new supernode (fun, bb, returning_call, phi_nodes, | |
374 | m_nodes.length ()); | |
375 | m_nodes.safe_push (n); | |
376 | return n; | |
377 | } | |
378 | ||
379 | /* Create a new cfg_superedge from SRC to DEST for the underlying CFG edge E, | |
380 | adding it to this supergraph. | |
381 | ||
382 | If the edge is for a switch statement, create a switch_cfg_superedge | |
383 | subclass using IDX (the index of E within the out-edges from SRC's | |
384 | underlying basic block). */ | |
385 | ||
386 | cfg_superedge * | |
387 | supergraph::add_cfg_edge (supernode *src, supernode *dest, ::edge e, int idx) | |
388 | { | |
389 | /* Special-case switch edges. */ | |
390 | gimple *stmt = src->get_last_stmt (); | |
391 | cfg_superedge *new_edge; | |
392 | if (stmt && stmt->code == GIMPLE_SWITCH) | |
393 | new_edge = new switch_cfg_superedge (src, dest, e, idx); | |
394 | else | |
395 | new_edge = new cfg_superedge (src, dest, e); | |
396 | add_edge (new_edge); | |
397 | return new_edge; | |
398 | } | |
399 | ||
400 | /* Create and add a call_superedge representing an interprocedural call | |
401 | from SRC to DEST, using CEDGE. */ | |
402 | ||
403 | call_superedge * | |
404 | supergraph::add_call_superedge (supernode *src, supernode *dest, | |
405 | cgraph_edge *cedge) | |
406 | { | |
407 | call_superedge *new_edge = new call_superedge (src, dest, cedge); | |
408 | add_edge (new_edge); | |
409 | return new_edge; | |
410 | } | |
411 | ||
412 | /* Create and add a return_superedge representing returning from an | |
413 | interprocedural call, returning from SRC to DEST, using CEDGE. */ | |
414 | ||
415 | return_superedge * | |
416 | supergraph::add_return_superedge (supernode *src, supernode *dest, | |
417 | cgraph_edge *cedge) | |
418 | { | |
419 | return_superedge *new_edge = new return_superedge (src, dest, cedge); | |
420 | add_edge (new_edge); | |
421 | return new_edge; | |
422 | } | |
423 | ||
424 | /* Implementation of dnode::dump_dot vfunc for supernodes. | |
425 | ||
426 | Write a cluster for the node, and within it a .dot node showing | |
427 | the phi nodes and stmts. Call into any node annotator from ARGS to | |
428 | potentially add other records to the cluster. */ | |
429 | ||
430 | void | |
431 | supernode::dump_dot (graphviz_out *gv, const dump_args_t &args) const | |
432 | { | |
433 | gv->println ("subgraph cluster_node_%i {", | |
434 | m_index); | |
435 | gv->indent (); | |
436 | ||
437 | gv->println("style=\"solid\";"); | |
438 | gv->println("color=\"black\";"); | |
439 | gv->println("fillcolor=\"lightgrey\";"); | |
440 | gv->println("label=\"sn: %i\";", m_index); | |
441 | ||
442 | pretty_printer *pp = gv->get_pp (); | |
443 | ||
444 | if (args.m_node_annotator) | |
445 | args.m_node_annotator->add_node_annotations (gv, *this); | |
446 | ||
447 | gv->write_indent (); | |
448 | dump_dot_id (pp); | |
449 | pp_printf (pp, | |
450 | " [shape=none,margin=0,style=filled,fillcolor=%s,label=<", | |
451 | "lightgrey"); | |
452 | pp_string (pp, "<TABLE BORDER=\"0\">"); | |
453 | pp_write_text_to_stream (pp); | |
454 | ||
718930c0 DM |
455 | bool had_row = false; |
456 | ||
757bf1df DM |
457 | if (m_returning_call) |
458 | { | |
459 | gv->begin_tr (); | |
460 | pp_string (pp, "returning call: "); | |
461 | gv->end_tr (); | |
462 | ||
463 | gv->begin_tr (); | |
464 | pp_gimple_stmt_1 (pp, m_returning_call, 0, (dump_flags_t)0); | |
465 | pp_write_text_as_html_like_dot_to_stream (pp); | |
466 | gv->end_tr (); | |
467 | ||
468 | if (args.m_node_annotator) | |
469 | args.m_node_annotator->add_stmt_annotations (gv, m_returning_call); | |
470 | pp_newline (pp); | |
718930c0 DM |
471 | |
472 | had_row = true; | |
757bf1df DM |
473 | } |
474 | ||
475 | if (entry_p ()) | |
476 | { | |
477 | pp_string (pp, "<TR><TD>ENTRY</TD></TR>"); | |
478 | pp_newline (pp); | |
718930c0 | 479 | had_row = true; |
757bf1df DM |
480 | } |
481 | ||
482 | if (return_p ()) | |
483 | { | |
484 | pp_string (pp, "<TR><TD>EXIT</TD></TR>"); | |
485 | pp_newline (pp); | |
718930c0 | 486 | had_row = true; |
757bf1df DM |
487 | } |
488 | ||
489 | /* Phi nodes. */ | |
490 | for (gphi_iterator gpi = const_cast<supernode *> (this)->start_phis (); | |
491 | !gsi_end_p (gpi); gsi_next (&gpi)) | |
492 | { | |
493 | const gimple *stmt = gsi_stmt (gpi); | |
494 | gv->begin_tr (); | |
495 | pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0); | |
496 | pp_write_text_as_html_like_dot_to_stream (pp); | |
497 | gv->end_tr (); | |
498 | ||
499 | if (args.m_node_annotator) | |
500 | args.m_node_annotator->add_stmt_annotations (gv, stmt); | |
501 | ||
502 | pp_newline (pp); | |
718930c0 | 503 | had_row = true; |
757bf1df DM |
504 | } |
505 | ||
506 | /* Statements. */ | |
507 | int i; | |
508 | gimple *stmt; | |
509 | FOR_EACH_VEC_ELT (m_stmts, i, stmt) | |
510 | { | |
511 | gv->begin_tr (); | |
512 | pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0); | |
513 | pp_write_text_as_html_like_dot_to_stream (pp); | |
514 | gv->end_tr (); | |
515 | ||
516 | if (args.m_node_annotator) | |
517 | args.m_node_annotator->add_stmt_annotations (gv, stmt); | |
518 | ||
519 | pp_newline (pp); | |
718930c0 DM |
520 | had_row = true; |
521 | } | |
522 | ||
523 | /* Graphviz requires a TABLE element to have at least one TR | |
524 | (and each TR to have at least one TD). */ | |
525 | if (!had_row) | |
526 | { | |
527 | pp_string (pp, "<TR><TD>(empty)</TD></TR>"); | |
528 | pp_newline (pp); | |
757bf1df DM |
529 | } |
530 | ||
531 | pp_string (pp, "</TABLE>>];\n\n"); | |
532 | pp_flush (pp); | |
533 | ||
534 | /* Terminate "subgraph" */ | |
535 | gv->outdent (); | |
536 | gv->println ("}"); | |
537 | } | |
538 | ||
539 | /* Write an ID for this node to PP, for use in .dot output. */ | |
540 | ||
541 | void | |
542 | supernode::dump_dot_id (pretty_printer *pp) const | |
543 | { | |
544 | pp_printf (pp, "node_%i", m_index); | |
545 | } | |
546 | ||
547 | /* Get a location_t for the start of this supernode. */ | |
548 | ||
549 | location_t | |
550 | supernode::get_start_location () const | |
551 | { | |
8397af8e DM |
552 | if (m_returning_call |
553 | && get_pure_location (m_returning_call->location) != UNKNOWN_LOCATION) | |
757bf1df DM |
554 | return m_returning_call->location; |
555 | ||
556 | int i; | |
557 | gimple *stmt; | |
558 | FOR_EACH_VEC_ELT (m_stmts, i, stmt) | |
8397af8e | 559 | if (get_pure_location (stmt->location) != UNKNOWN_LOCATION) |
757bf1df DM |
560 | return stmt->location; |
561 | ||
562 | if (entry_p ()) | |
563 | { | |
564 | // TWEAK: show the decl instead; this leads to more readable output: | |
565 | return DECL_SOURCE_LOCATION (m_fun->decl); | |
566 | ||
567 | return m_fun->function_start_locus; | |
568 | } | |
569 | if (return_p ()) | |
570 | return m_fun->function_end_locus; | |
571 | ||
572 | return UNKNOWN_LOCATION; | |
573 | } | |
574 | ||
575 | /* Get a location_t for the end of this supernode. */ | |
576 | ||
577 | location_t | |
578 | supernode::get_end_location () const | |
579 | { | |
580 | int i; | |
581 | gimple *stmt; | |
582 | FOR_EACH_VEC_ELT_REVERSE (m_stmts, i, stmt) | |
8397af8e | 583 | if (get_pure_location (stmt->location) != UNKNOWN_LOCATION) |
757bf1df DM |
584 | return stmt->location; |
585 | ||
8397af8e DM |
586 | if (m_returning_call |
587 | && get_pure_location (m_returning_call->location) != UNKNOWN_LOCATION) | |
757bf1df DM |
588 | return m_returning_call->location; |
589 | ||
590 | if (entry_p ()) | |
591 | return m_fun->function_start_locus; | |
592 | if (return_p ()) | |
593 | return m_fun->function_end_locus; | |
594 | ||
595 | return UNKNOWN_LOCATION; | |
596 | } | |
597 | ||
598 | /* Given STMT within this supernode, return its index within m_stmts. */ | |
599 | ||
600 | unsigned int | |
601 | supernode::get_stmt_index (const gimple *stmt) const | |
602 | { | |
603 | unsigned i; | |
604 | gimple *iter_stmt; | |
605 | FOR_EACH_VEC_ELT (m_stmts, i, iter_stmt) | |
606 | if (iter_stmt == stmt) | |
607 | return i; | |
608 | gcc_unreachable (); | |
609 | } | |
610 | ||
611 | /* Implementation of dedge::dump_dot for superedges. | |
612 | Write a .dot edge to GV representing this superedge. */ | |
613 | ||
614 | void | |
615 | superedge::dump_dot (graphviz_out *gv, const dump_args_t &) const | |
616 | { | |
617 | const char *style = "\"solid,bold\""; | |
618 | const char *color = "black"; | |
619 | int weight = 10; | |
620 | const char *constraint = "true"; | |
621 | ||
622 | switch (m_kind) | |
623 | { | |
624 | default: | |
625 | gcc_unreachable (); | |
626 | case SUPEREDGE_CFG_EDGE: | |
627 | break; | |
628 | case SUPEREDGE_CALL: | |
629 | color = "red"; | |
630 | break; | |
631 | case SUPEREDGE_RETURN: | |
632 | color = "green"; | |
633 | break; | |
634 | case SUPEREDGE_INTRAPROCEDURAL_CALL: | |
635 | style = "\"dotted\""; | |
636 | break; | |
637 | } | |
638 | ||
639 | /* Adapted from graph.c:draw_cfg_node_succ_edges. */ | |
640 | if (::edge cfg_edge = get_any_cfg_edge ()) | |
641 | { | |
642 | if (cfg_edge->flags & EDGE_FAKE) | |
643 | { | |
644 | style = "dotted"; | |
645 | color = "green"; | |
646 | weight = 0; | |
647 | } | |
648 | else if (cfg_edge->flags & EDGE_DFS_BACK) | |
649 | { | |
650 | style = "\"dotted,bold\""; | |
651 | color = "blue"; | |
652 | weight = 10; | |
653 | } | |
654 | else if (cfg_edge->flags & EDGE_FALLTHRU) | |
655 | { | |
656 | color = "blue"; | |
657 | weight = 100; | |
658 | } | |
659 | ||
660 | if (cfg_edge->flags & EDGE_ABNORMAL) | |
661 | color = "red"; | |
662 | } | |
663 | ||
664 | gv->write_indent (); | |
665 | ||
666 | pretty_printer *pp = gv->get_pp (); | |
667 | ||
668 | m_src->dump_dot_id (pp); | |
669 | pp_string (pp, " -> "); | |
670 | m_dest->dump_dot_id (pp); | |
671 | pp_printf (pp, | |
672 | (" [style=%s, color=%s, weight=%d, constraint=%s," | |
673 | " ltail=\"cluster_node_%i\", lhead=\"cluster_node_%i\"" | |
674 | " headlabel=\""), | |
675 | style, color, weight, constraint, | |
676 | m_src->m_index, m_dest->m_index); | |
677 | ||
678 | dump_label_to_pp (pp, false); | |
679 | ||
680 | pp_printf (pp, "\"];\n"); | |
681 | } | |
682 | ||
683 | /* If this is an intraprocedural superedge, return the associated | |
684 | CFG edge. Otherwise, return NULL. */ | |
685 | ||
686 | ::edge | |
687 | superedge::get_any_cfg_edge () const | |
688 | { | |
689 | if (const cfg_superedge *sub = dyn_cast_cfg_superedge ()) | |
690 | return sub->get_cfg_edge (); | |
691 | return NULL; | |
692 | } | |
693 | ||
694 | /* If this is an interprocedural superedge, return the associated | |
695 | cgraph_edge *. Otherwise, return NULL. */ | |
696 | ||
697 | cgraph_edge * | |
698 | superedge::get_any_callgraph_edge () const | |
699 | { | |
700 | if (const callgraph_superedge *sub = dyn_cast_callgraph_superedge ()) | |
701 | return sub->m_cedge; | |
702 | return NULL; | |
703 | } | |
704 | ||
705 | /* Build a description of this superedge (e.g. "true" for the true | |
706 | edge of a conditional, or "case 42:" for a switch case). | |
707 | ||
708 | The caller is responsible for freeing the result. | |
709 | ||
710 | If USER_FACING is false, the result also contains any underlying | |
711 | CFG edge flags. e.g. " (flags FALLTHRU | DFS_BACK)". */ | |
712 | ||
713 | char * | |
714 | superedge::get_description (bool user_facing) const | |
715 | { | |
716 | pretty_printer pp; | |
717 | dump_label_to_pp (&pp, user_facing); | |
718 | return xstrdup (pp_formatted_text (&pp)); | |
719 | } | |
720 | ||
721 | /* Implementation of superedge::dump_label_to_pp for non-switch CFG | |
722 | superedges. | |
723 | ||
724 | For true/false edges, print "true" or "false" to PP. | |
725 | ||
726 | If USER_FACING is false, also print flags on the underlying CFG edge to | |
727 | PP. */ | |
728 | ||
729 | void | |
730 | cfg_superedge::dump_label_to_pp (pretty_printer *pp, | |
731 | bool user_facing) const | |
732 | { | |
733 | if (true_value_p ()) | |
734 | pp_printf (pp, "true"); | |
735 | else if (false_value_p ()) | |
736 | pp_printf (pp, "false"); | |
737 | ||
738 | if (user_facing) | |
739 | return; | |
740 | ||
741 | /* Express edge flags as a string with " | " separator. | |
742 | e.g. " (flags FALLTHRU | DFS_BACK)". */ | |
743 | if (get_flags ()) | |
744 | { | |
745 | pp_string (pp, " (flags "); | |
746 | bool seen_flag = false; | |
747 | #define DEF_EDGE_FLAG(NAME,IDX) \ | |
748 | do { \ | |
749 | if (get_flags () & EDGE_##NAME) \ | |
750 | { \ | |
751 | if (seen_flag) \ | |
752 | pp_string (pp, " | "); \ | |
753 | pp_printf (pp, "%s", (#NAME)); \ | |
754 | seen_flag = true; \ | |
755 | } \ | |
756 | } while (0); | |
757 | #include "cfg-flags.def" | |
758 | #undef DEF_EDGE_FLAG | |
759 | pp_string (pp, ")"); | |
760 | } | |
761 | ||
762 | /* Otherwise, no label. */ | |
763 | } | |
764 | ||
765 | /* Get the phi argument for PHI for this CFG edge. */ | |
766 | ||
767 | tree | |
768 | cfg_superedge::get_phi_arg (const gphi *phi) const | |
769 | { | |
770 | size_t index = m_cfg_edge->dest_idx; | |
771 | return gimple_phi_arg_def (phi, index); | |
772 | } | |
773 | ||
774 | /* Implementation of superedge::dump_label_to_pp for CFG superedges for | |
775 | "switch" statements. | |
776 | ||
777 | Print "case VAL:", "case LOWER ... UPPER:", or "default:" to PP. */ | |
778 | ||
779 | void | |
780 | switch_cfg_superedge::dump_label_to_pp (pretty_printer *pp, | |
781 | bool user_facing ATTRIBUTE_UNUSED) const | |
782 | { | |
783 | tree case_label = get_case_label (); | |
784 | gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR); | |
785 | tree lower_bound = CASE_LOW (case_label); | |
786 | tree upper_bound = CASE_HIGH (case_label); | |
787 | if (lower_bound) | |
788 | { | |
789 | pp_printf (pp, "case "); | |
790 | dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false); | |
791 | if (upper_bound) | |
792 | { | |
793 | pp_printf (pp, " ... "); | |
794 | dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0, false); | |
795 | } | |
796 | pp_printf (pp, ":"); | |
797 | } | |
798 | else | |
799 | pp_printf (pp, "default:"); | |
800 | } | |
801 | ||
802 | /* Get the case label for this "switch" superedge. */ | |
803 | ||
804 | tree | |
805 | switch_cfg_superedge::get_case_label () const | |
806 | { | |
807 | return gimple_switch_label (get_switch_stmt (), m_idx); | |
808 | } | |
809 | ||
810 | /* Implementation of superedge::dump_label_to_pp for interprocedural | |
811 | superedges. */ | |
812 | ||
813 | void | |
814 | callgraph_superedge::dump_label_to_pp (pretty_printer *pp, | |
815 | bool user_facing ATTRIBUTE_UNUSED) const | |
816 | { | |
817 | switch (m_kind) | |
818 | { | |
819 | default: | |
820 | case SUPEREDGE_CFG_EDGE: | |
821 | gcc_unreachable (); | |
822 | ||
823 | case SUPEREDGE_CALL: | |
824 | pp_printf (pp, "call"); | |
825 | break; | |
826 | ||
827 | case SUPEREDGE_RETURN: | |
828 | pp_printf (pp, "return"); | |
829 | break; | |
830 | ||
831 | case SUPEREDGE_INTRAPROCEDURAL_CALL: | |
832 | pp_printf (pp, "intraproc link"); | |
833 | break; | |
834 | } | |
835 | } | |
836 | ||
837 | /* Get the function that was called at this interprocedural call/return | |
838 | edge. */ | |
839 | ||
840 | function * | |
841 | callgraph_superedge::get_callee_function () const | |
842 | { | |
843 | return m_cedge->callee->get_fun (); | |
844 | } | |
845 | ||
846 | /* Get the calling function at this interprocedural call/return edge. */ | |
847 | ||
848 | function * | |
849 | callgraph_superedge::get_caller_function () const | |
850 | { | |
851 | return m_cedge->caller->get_fun (); | |
852 | } | |
853 | ||
854 | /* Get the fndecl that was called at this interprocedural call/return | |
855 | edge. */ | |
856 | ||
857 | tree | |
858 | callgraph_superedge::get_callee_decl () const | |
859 | { | |
860 | return get_callee_function ()->decl; | |
861 | } | |
862 | ||
863 | /* Get the calling fndecl at this interprocedural call/return edge. */ | |
864 | ||
865 | tree | |
866 | callgraph_superedge::get_caller_decl () const | |
867 | { | |
868 | return get_caller_function ()->decl; | |
869 | } | |
870 | ||
871 | /* Given PARM_TO_FIND, a PARM_DECL, identify its index (writing it | |
872 | to *OUT if OUT is non-NULL), and return the corresponding argument | |
873 | at the callsite. */ | |
874 | ||
875 | tree | |
876 | callgraph_superedge::get_arg_for_parm (tree parm_to_find, | |
877 | callsite_expr *out) const | |
878 | { | |
879 | gcc_assert (TREE_CODE (parm_to_find) == PARM_DECL); | |
880 | ||
881 | tree callee = get_callee_decl (); | |
882 | ||
883 | int i = 0; | |
884 | for (tree iter_parm = DECL_ARGUMENTS (callee); iter_parm; | |
885 | iter_parm = DECL_CHAIN (iter_parm), ++i) | |
886 | { | |
887 | if (iter_parm == parm_to_find) | |
888 | { | |
889 | if (out) | |
890 | *out = callsite_expr::from_zero_based_param (i); | |
891 | return gimple_call_arg (get_call_stmt (), i); | |
892 | } | |
893 | } | |
894 | ||
895 | /* Not found. */ | |
896 | return NULL_TREE; | |
897 | } | |
898 | ||
899 | /* Look for a use of ARG_TO_FIND as an argument at this callsite. | |
900 | If found, return the default SSA def of the corresponding parm within | |
901 | the callee, and if OUT is non-NULL, write the index to *OUT. | |
902 | Only the first match is handled. */ | |
903 | ||
904 | tree | |
905 | callgraph_superedge::get_parm_for_arg (tree arg_to_find, | |
906 | callsite_expr *out) const | |
907 | { | |
908 | tree callee = get_callee_decl (); | |
909 | ||
910 | int i = 0; | |
911 | for (tree iter_parm = DECL_ARGUMENTS (callee); iter_parm; | |
912 | iter_parm = DECL_CHAIN (iter_parm), ++i) | |
913 | { | |
914 | tree param = gimple_call_arg (get_call_stmt (), i); | |
915 | if (arg_to_find == param) | |
916 | { | |
917 | if (out) | |
918 | *out = callsite_expr::from_zero_based_param (i); | |
919 | return ssa_default_def (get_callee_function (), iter_parm); | |
920 | } | |
921 | } | |
922 | ||
923 | /* Not found. */ | |
924 | return NULL_TREE; | |
925 | } | |
926 | ||
927 | /* Map caller_expr back to an expr within the callee, or return NULL_TREE. | |
928 | If non-NULL is returned, populate OUT. */ | |
929 | ||
930 | tree | |
931 | callgraph_superedge::map_expr_from_caller_to_callee (tree caller_expr, | |
932 | callsite_expr *out) const | |
933 | { | |
934 | /* Is it an argument (actual param)? If so, convert to | |
935 | parameter (formal param). */ | |
936 | tree parm = get_parm_for_arg (caller_expr, out); | |
937 | if (parm) | |
938 | return parm; | |
939 | /* Otherwise try return value. */ | |
940 | if (caller_expr == gimple_call_lhs (get_call_stmt ())) | |
941 | { | |
942 | if (out) | |
943 | *out = callsite_expr::from_return_value (); | |
944 | return DECL_RESULT (get_callee_decl ()); | |
945 | } | |
946 | ||
947 | return NULL_TREE; | |
948 | } | |
949 | ||
950 | /* Map callee_expr back to an expr within the caller, or return NULL_TREE. | |
951 | If non-NULL is returned, populate OUT. */ | |
952 | ||
953 | tree | |
954 | callgraph_superedge::map_expr_from_callee_to_caller (tree callee_expr, | |
955 | callsite_expr *out) const | |
956 | { | |
957 | if (callee_expr == NULL_TREE) | |
958 | return NULL_TREE; | |
959 | ||
960 | /* If it's a parameter (formal param), get the argument (actual param). */ | |
961 | if (TREE_CODE (callee_expr) == PARM_DECL) | |
962 | return get_arg_for_parm (callee_expr, out); | |
963 | ||
964 | /* Similar for the default SSA name of the PARM_DECL. */ | |
965 | if (TREE_CODE (callee_expr) == SSA_NAME | |
966 | && SSA_NAME_IS_DEFAULT_DEF (callee_expr) | |
967 | && TREE_CODE (SSA_NAME_VAR (callee_expr)) == PARM_DECL) | |
968 | return get_arg_for_parm (SSA_NAME_VAR (callee_expr), out); | |
969 | ||
970 | /* Otherwise try return value. */ | |
971 | if (callee_expr == DECL_RESULT (get_callee_decl ())) | |
972 | { | |
973 | if (out) | |
974 | *out = callsite_expr::from_return_value (); | |
975 | return gimple_call_lhs (get_call_stmt ()); | |
976 | } | |
977 | ||
978 | return NULL_TREE; | |
979 | } | |
980 | ||
75038aa6 DM |
981 | } // namespace ana |
982 | ||
757bf1df | 983 | #endif /* #if ENABLE_ANALYZER */ |