Branch data Line data Source code
1 : : /* Output routines for graphical representation.
2 : : Copyright (C) 1998-2024 Free Software Foundation, Inc.
3 : : Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
4 : : Rewritten for DOT output by Steven Bosscher, 2012.
5 : :
6 : : This file is part of GCC.
7 : :
8 : : GCC is free software; you can redistribute it and/or modify it under
9 : : the terms of the GNU General Public License as published by the Free
10 : : Software Foundation; either version 3, or (at your option) any later
11 : : version.
12 : :
13 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 : : for more details.
17 : :
18 : : You should have received a copy of the GNU General Public License
19 : : along with GCC; see the file COPYING3. If not see
20 : : <http://www.gnu.org/licenses/>. */
21 : :
22 : : #include "config.h"
23 : : #include "system.h"
24 : : #include "coretypes.h"
25 : : #include "backend.h"
26 : : #include "cfghooks.h"
27 : : #include "pretty-print.h"
28 : : #include "diagnostic-core.h" /* for fatal_error */
29 : : #include "cfganal.h"
30 : : #include "cfgloop.h"
31 : : #include "graph.h"
32 : : #include "dumpfile.h"
33 : :
34 : : /* DOT files with the .dot extension are recognized as document templates
35 : : by a well-known piece of word processing software out of Redmond, WA.
36 : : Therefore some recommend using the .gv extension instead. Obstinately
37 : : ignore that recommendation... */
38 : : static const char *const graph_ext = ".dot";
39 : :
40 : : /* Open a file with MODE for dumping our graph to.
41 : : Return the file pointer. */
42 : : static FILE *
43 : 540 : open_graph_file (const char *base, const char *mode)
44 : : {
45 : 540 : size_t namelen = strlen (base);
46 : 540 : size_t extlen = strlen (graph_ext) + 1;
47 : 540 : char *buf = XALLOCAVEC (char, namelen + extlen);
48 : 540 : FILE *fp;
49 : :
50 : 540 : memcpy (buf, base, namelen);
51 : 540 : memcpy (buf + namelen, graph_ext, extlen);
52 : :
53 : 540 : fp = fopen (buf, mode);
54 : 540 : if (fp == NULL)
55 : 0 : fatal_error (input_location, "cannot open %s: %m", buf);
56 : :
57 : 540 : return fp;
58 : : }
59 : :
60 : : /* Disable warnings about quoting issues in the pp_xxx calls below
61 : : that (intentionally) don't follow GCC diagnostic conventions. */
62 : : #if __GNUC__ >= 10
63 : : # pragma GCC diagnostic push
64 : : # pragma GCC diagnostic ignored "-Wformat-diag"
65 : : #endif
66 : :
67 : : /* Draw a basic block BB belonging to the function with FUNCDEF_NO
68 : : as its unique number. */
69 : : static void
70 : 879 : draw_cfg_node (pretty_printer *pp, int funcdef_no, basic_block bb)
71 : : {
72 : 879 : const char *shape;
73 : 879 : const char *fillcolor;
74 : :
75 : 879 : if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK)
76 : : {
77 : : shape = "Mdiamond";
78 : : fillcolor = "white";
79 : : }
80 : : else
81 : : {
82 : 435 : shape = "record";
83 : 345 : fillcolor =
84 : 435 : BB_PARTITION (bb) == BB_HOT_PARTITION ? "lightpink"
85 : : : BB_PARTITION (bb) == BB_COLD_PARTITION ? "lightblue"
86 : : : "lightgrey";
87 : : }
88 : :
89 : 879 : pp_printf (pp,
90 : : "\tfn_%d_basic_block_%d "
91 : : "[shape=%s,style=filled,fillcolor=%s,label=\"",
92 : : funcdef_no, bb->index, shape, fillcolor);
93 : :
94 : 879 : if (bb->index == ENTRY_BLOCK)
95 : 222 : pp_string (pp, "ENTRY");
96 : 657 : else if (bb->index == EXIT_BLOCK)
97 : 222 : pp_string (pp, "EXIT");
98 : : else
99 : : {
100 : 435 : pp_left_brace (pp);
101 : 435 : pp_write_text_to_stream (pp);
102 : 435 : dump_bb_for_graph (pp, bb);
103 : 435 : pp_right_brace (pp);
104 : : }
105 : :
106 : 879 : pp_string (pp, "\"];\n\n");
107 : 879 : pp_flush (pp);
108 : 879 : }
109 : :
110 : : /* Draw all successor edges of a basic block BB belonging to the function
111 : : with FUNCDEF_NO as its unique number. */
112 : : static void
113 : 879 : draw_cfg_node_succ_edges (pretty_printer *pp, int funcdef_no, basic_block bb)
114 : : {
115 : 879 : edge e;
116 : 879 : edge_iterator ei;
117 : 1621 : FOR_EACH_EDGE (e, ei, bb->succs)
118 : : {
119 : 742 : const char *style = "\"solid,bold\"";
120 : 742 : const char *color = "black";
121 : 742 : int weight = 10;
122 : :
123 : 742 : if (e->flags & EDGE_FAKE)
124 : : {
125 : : style = "dotted";
126 : : color = "green";
127 : : weight = 0;
128 : : }
129 : 742 : else if (e->flags & EDGE_DFS_BACK)
130 : : {
131 : : style = "\"dotted,bold\"";
132 : : color = "blue";
133 : : weight = 10;
134 : : }
135 : 738 : else if (e->flags & EDGE_FALLTHRU)
136 : : weight = 100;
137 : 221 : else if (e->flags & EDGE_TRUE_VALUE)
138 : : color = "forestgreen";
139 : 209 : else if (e->flags & EDGE_FALSE_VALUE)
140 : 13 : color = "darkorange";
141 : :
142 : 742 : if (e->flags & EDGE_ABNORMAL)
143 : 0 : color = "red";
144 : :
145 : 1484 : pp_printf (pp,
146 : : "\tfn_%d_basic_block_%d:s -> fn_%d_basic_block_%d:n "
147 : : "[style=%s,color=%s,weight=%d,constraint=%s",
148 : 742 : funcdef_no, e->src->index,
149 : 742 : funcdef_no, e->dest->index,
150 : : style, color, weight,
151 : 742 : (e->flags & (EDGE_FAKE | EDGE_DFS_BACK)) ? "false" : "true");
152 : 742 : if (e->probability.initialized_p ())
153 : 605 : pp_printf (pp, ",label=\"[%i%%]\"",
154 : 605 : e->probability.to_reg_br_prob_base ()
155 : : * 100 / REG_BR_PROB_BASE);
156 : 742 : pp_printf (pp, "];\n");
157 : : }
158 : 879 : pp_flush (pp);
159 : 879 : }
160 : :
161 : : /* Draw all the basic blocks in the CFG in case loops are not available.
162 : : First compute a topological order of the blocks to get a good ranking of
163 : : the nodes. Then, if any nodes are not reachable from ENTRY, add them at
164 : : the end. */
165 : :
166 : : static void
167 : 99 : draw_cfg_nodes_no_loops (pretty_printer *pp, struct function *fun)
168 : : {
169 : 99 : int *rpo = XNEWVEC (int, n_basic_blocks_for_fn (fun));
170 : 99 : int i, n;
171 : :
172 : 99 : auto_sbitmap visited (last_basic_block_for_fn (fun));
173 : 99 : bitmap_clear (visited);
174 : :
175 : 99 : n = pre_and_rev_post_order_compute_fn (fun, NULL, rpo, true);
176 : 482 : for (i = n_basic_blocks_for_fn (fun) - n;
177 : 482 : i < n_basic_blocks_for_fn (fun); i++)
178 : : {
179 : 383 : basic_block bb = BASIC_BLOCK_FOR_FN (fun, rpo[i]);
180 : 383 : draw_cfg_node (pp, fun->funcdef_no, bb);
181 : 383 : bitmap_set_bit (visited, bb->index);
182 : : }
183 : 99 : free (rpo);
184 : :
185 : 99 : if (n != n_basic_blocks_for_fn (fun))
186 : : {
187 : : /* Some blocks are unreachable. We still want to dump them. */
188 : 0 : basic_block bb;
189 : 0 : FOR_ALL_BB_FN (bb, fun)
190 : 0 : if (! bitmap_bit_p (visited, bb->index))
191 : 0 : draw_cfg_node (pp, fun->funcdef_no, bb);
192 : : }
193 : 99 : }
194 : :
195 : : /* Draw all the basic blocks in LOOP. Print the blocks in breath-first
196 : : order to get a good ranking of the nodes. This function is recursive:
197 : : It first prints inner loops, then the body of LOOP itself. */
198 : :
199 : : static void
200 : 127 : draw_cfg_nodes_for_loop (pretty_printer *pp, int funcdef_no,
201 : : class loop *loop)
202 : : {
203 : 127 : basic_block *body;
204 : 127 : unsigned int i;
205 : 127 : const char *fillcolors[3] = { "grey88", "grey77", "grey66" };
206 : :
207 : 127 : if (loop->header != NULL
208 : 127 : && loop->latch != EXIT_BLOCK_PTR_FOR_FN (cfun))
209 : 4 : pp_printf (pp,
210 : : "\tsubgraph cluster_%d_%d {\n"
211 : : "\tstyle=\"filled\";\n"
212 : : "\tcolor=\"darkgreen\";\n"
213 : : "\tfillcolor=\"%s\";\n"
214 : : "\tlabel=\"loop %d\";\n"
215 : : "\tlabeljust=l;\n"
216 : : "\tpenwidth=2;\n",
217 : : funcdef_no, loop->num,
218 : 4 : fillcolors[(loop_depth (loop) - 1) % 3],
219 : : loop->num);
220 : :
221 : 131 : for (class loop *inner = loop->inner; inner; inner = inner->next)
222 : 4 : draw_cfg_nodes_for_loop (pp, funcdef_no, inner);
223 : :
224 : 127 : if (loop->header == NULL)
225 : 0 : return;
226 : :
227 : 127 : if (loop->latch == EXIT_BLOCK_PTR_FOR_FN (cfun))
228 : 123 : body = get_loop_body (loop);
229 : : else
230 : 4 : body = get_loop_body_in_bfs_order (loop);
231 : :
232 : 645 : for (i = 0; i < loop->num_nodes; i++)
233 : : {
234 : 518 : basic_block bb = body[i];
235 : 518 : if (bb->loop_father == loop)
236 : 496 : draw_cfg_node (pp, funcdef_no, bb);
237 : : }
238 : :
239 : 127 : free (body);
240 : :
241 : 127 : if (loop->latch != EXIT_BLOCK_PTR_FOR_FN (cfun))
242 : 4 : pp_printf (pp, "\t}\n");
243 : : }
244 : :
245 : : /* Draw all the basic blocks in the CFG in case the loop tree is available.
246 : : All loop bodys are printed in clusters. */
247 : :
248 : : static void
249 : 222 : draw_cfg_nodes (pretty_printer *pp, struct function *fun)
250 : : {
251 : : /* ??? The loop and dominance APIs are dependent on fun == cfun. */
252 : 222 : if (fun == cfun && loops_for_fn (fun))
253 : 123 : draw_cfg_nodes_for_loop (pp, fun->funcdef_no, get_loop (fun, 0));
254 : : else
255 : 99 : draw_cfg_nodes_no_loops (pp, fun);
256 : 222 : }
257 : :
258 : : /* Draw all edges in the CFG. Retreating edges are drawin as not
259 : : constraining, this makes the layout of the graph better. */
260 : :
261 : : static void
262 : 222 : draw_cfg_edges (pretty_printer *pp, struct function *fun)
263 : : {
264 : 222 : basic_block bb;
265 : :
266 : : /* Save EDGE_DFS_BACK flag to dfs_back. */
267 : 222 : auto_bitmap dfs_back;
268 : 222 : edge e;
269 : 222 : edge_iterator ei;
270 : 222 : unsigned int idx = 0;
271 : 657 : FOR_EACH_BB_FN (bb, fun)
272 : 955 : FOR_EACH_EDGE (e, ei, bb->succs)
273 : : {
274 : 520 : if (e->flags & EDGE_DFS_BACK)
275 : 3 : bitmap_set_bit (dfs_back, idx);
276 : 520 : idx++;
277 : : }
278 : :
279 : 222 : mark_dfs_back_edges (fun);
280 : 1101 : FOR_ALL_BB_FN (bb, fun)
281 : 879 : draw_cfg_node_succ_edges (pp, fun->funcdef_no, bb);
282 : :
283 : : /* Restore EDGE_DFS_BACK flag from dfs_back. */
284 : 222 : idx = 0;
285 : 657 : FOR_EACH_BB_FN (bb, fun)
286 : 955 : FOR_EACH_EDGE (e, ei, bb->succs)
287 : : {
288 : 520 : if (bitmap_bit_p (dfs_back, idx))
289 : 3 : e->flags |= EDGE_DFS_BACK;
290 : : else
291 : 517 : e->flags &= ~EDGE_DFS_BACK;
292 : 520 : idx++;
293 : : }
294 : :
295 : : /* Add an invisible edge from ENTRY to EXIT, to improve the graph layout. */
296 : 222 : pp_printf (pp,
297 : : "\tfn_%d_basic_block_%d:s -> fn_%d_basic_block_%d:n "
298 : : "[style=\"invis\",constraint=true];\n",
299 : : fun->funcdef_no, ENTRY_BLOCK,
300 : : fun->funcdef_no, EXIT_BLOCK);
301 : 222 : pp_flush (pp);
302 : 222 : }
303 : :
304 : : /* Print a graphical representation of the CFG of function FUN.
305 : : First print all basic blocks. Draw all edges at the end to get
306 : : subgraphs right for GraphViz, which requires nodes to be defined
307 : : before edges to cluster nodes properly. */
308 : :
309 : : void DEBUG_FUNCTION
310 : 222 : print_graph_cfg (FILE *fp, struct function *fun)
311 : : {
312 : 222 : pretty_printer graph_slim_pp;
313 : 222 : graph_slim_pp.set_output_stream (fp);
314 : 222 : pretty_printer *const pp = &graph_slim_pp;
315 : 222 : const char *funcname = function_name (fun);
316 : 222 : pp_printf (pp, "subgraph \"cluster_%s\" {\n"
317 : : "\tstyle=\"dashed\";\n"
318 : : "\tcolor=\"black\";\n"
319 : : "\tlabel=\"%s ()\";\n",
320 : : funcname, funcname);
321 : 222 : draw_cfg_nodes (pp, fun);
322 : 222 : draw_cfg_edges (pp, fun);
323 : 222 : pp_printf (pp, "}\n");
324 : 222 : pp_flush (pp);
325 : 222 : }
326 : :
327 : : /* Overload with additional flag argument. */
328 : :
329 : : void DEBUG_FUNCTION
330 : 0 : print_graph_cfg (FILE *fp, struct function *fun, dump_flags_t flags)
331 : : {
332 : 0 : dump_flags_t saved_dump_flags = dump_flags;
333 : 0 : dump_flags = flags;
334 : 0 : print_graph_cfg (fp, fun);
335 : 0 : dump_flags = saved_dump_flags;
336 : 0 : }
337 : :
338 : :
339 : : /* Print a graphical representation of the CFG of function FUN.
340 : : First print all basic blocks. Draw all edges at the end to get
341 : : subgraphs right for GraphViz, which requires nodes to be defined
342 : : before edges to cluster nodes properly. */
343 : :
344 : : void
345 : 222 : print_graph_cfg (const char *base, struct function *fun)
346 : : {
347 : 222 : FILE *fp = open_graph_file (base, "a");
348 : 222 : print_graph_cfg (fp, fun);
349 : 222 : fclose (fp);
350 : 222 : }
351 : :
352 : : /* Start the dump of a graph. */
353 : : static void
354 : 159 : start_graph_dump (FILE *fp, const char *base)
355 : : {
356 : 159 : pretty_printer graph_slim_pp;
357 : 159 : graph_slim_pp.set_output_stream (fp);
358 : 159 : pretty_printer *const pp = &graph_slim_pp;
359 : 159 : pp_string (pp, "digraph \"");
360 : 159 : pp_write_text_to_stream (pp);
361 : 159 : pp_string (pp, base);
362 : 159 : pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
363 : 159 : pp_string (pp, "\" {\n");
364 : 159 : pp_string (pp, "overlap=false;\n");
365 : 159 : pp_flush (pp);
366 : 159 : }
367 : :
368 : : /* End the dump of a graph. */
369 : : static void
370 : 159 : end_graph_dump (FILE *fp)
371 : : {
372 : 0 : fputs ("}\n", fp);
373 : 0 : }
374 : :
375 : : /* Similar as clean_dump_file, but this time for graph output files. */
376 : : void
377 : 159 : clean_graph_dump_file (const char *base)
378 : : {
379 : 159 : FILE *fp = open_graph_file (base, "w");
380 : 159 : start_graph_dump (fp, base);
381 : 159 : fclose (fp);
382 : 159 : }
383 : :
384 : :
385 : : /* Do final work on the graph output file. */
386 : : void
387 : 159 : finish_graph_dump_file (const char *base)
388 : : {
389 : 159 : FILE *fp = open_graph_file (base, "a");
390 : 159 : end_graph_dump (fp);
391 : 159 : fclose (fp);
392 : 159 : }
393 : :
394 : : #if __GNUC__ >= 10
395 : : # pragma GCC diagnostic pop
396 : : #endif
|