]>
Commit | Line | Data |
---|---|---|
735a0e33 | 1 | /* Output routines for graphical representation. |
d1e082c2 | 2 | Copyright (C) 1998-2013 Free Software Foundation, Inc. |
735a0e33 | 3 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. |
a27a5de9 | 4 | Rewritten for DOT output by Steven Bosscher, 2012. |
735a0e33 | 5 | |
1322177d | 6 | This file is part of GCC. |
735a0e33 | 7 | |
1322177d LB |
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 | |
9dcd6f09 | 10 | Software Foundation; either version 3, or (at your option) any later |
1322177d | 11 | version. |
735a0e33 | 12 | |
1322177d LB |
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. | |
735a0e33 | 17 | |
400500c4 | 18 | You should have received a copy of the GNU General Public License |
9dcd6f09 NC |
19 | along with GCC; see the file COPYING3. If not see |
20 | <http://www.gnu.org/licenses/>. */ | |
735a0e33 | 21 | |
71f3e391 | 22 | #include "config.h" |
735a0e33 | 23 | #include "system.h" |
4977bab6 | 24 | #include "coretypes.h" |
a27a5de9 | 25 | #include "diagnostic-core.h" /* for fatal_error */ |
13475df1 | 26 | #include "sbitmap.h" |
735a0e33 | 27 | #include "basic-block.h" |
13475df1 | 28 | #include "cfgloop.h" |
6a2cc2ac | 29 | #include "graph.h" |
41222ddf | 30 | #include "dumpfile.h" |
7eba871a | 31 | #include "pretty-print.h" |
735a0e33 | 32 | |
a27a5de9 SB |
33 | /* DOT files with the .dot extension are recognized as document templates |
34 | by a well-known piece of word processing software out of Redmond, WA. | |
35 | Therefore some recommend using the .gv extension instead. Obstinately | |
13475df1 | 36 | ignore that recommendation... */ |
a27a5de9 | 37 | static const char *const graph_ext = ".dot"; |
208d8b55 | 38 | |
a27a5de9 SB |
39 | /* Open a file with MODE for dumping our graph to. |
40 | Return the file pointer. */ | |
41 | static FILE * | |
42 | open_graph_file (const char *base, const char *mode) | |
735a0e33 | 43 | { |
a27a5de9 SB |
44 | size_t namelen = strlen (base); |
45 | size_t extlen = strlen (graph_ext) + 1; | |
46 | char *buf = XALLOCAVEC (char, namelen + extlen); | |
47 | FILE *fp; | |
735a0e33 | 48 | |
a27a5de9 SB |
49 | memcpy (buf, base, namelen); |
50 | memcpy (buf + namelen, graph_ext, extlen); | |
735a0e33 | 51 | |
a27a5de9 SB |
52 | fp = fopen (buf, mode); |
53 | if (fp == NULL) | |
54 | fatal_error ("can%'t open %s: %m", buf); | |
735a0e33 | 55 | |
a27a5de9 | 56 | return fp; |
735a0e33 UD |
57 | } |
58 | ||
7eba871a SB |
59 | /* Return a pretty-print buffer for output to file FP. */ |
60 | ||
61 | static pretty_printer * | |
62 | init_graph_slim_pretty_print (FILE *fp) | |
735a0e33 | 63 | { |
7eba871a SB |
64 | static bool initialized = false; |
65 | static pretty_printer graph_slim_pp; | |
a27a5de9 | 66 | |
7eba871a | 67 | if (! initialized) |
735a0e33 | 68 | { |
7eba871a SB |
69 | pp_construct (&graph_slim_pp, /*prefix=*/NULL, /*linewidth=*/0); |
70 | initialized = true; | |
735a0e33 | 71 | } |
7eba871a SB |
72 | else |
73 | gcc_assert (! pp_last_position_in_text (&graph_slim_pp)); | |
74 | ||
75 | graph_slim_pp.buffer->stream = fp; | |
76 | return &graph_slim_pp; | |
a27a5de9 | 77 | } |
735a0e33 | 78 | |
2c895bd1 | 79 | /* Draw a basic block BB belonging to the function with FUNCDEF_NO |
a27a5de9 SB |
80 | as its unique number. */ |
81 | static void | |
2c895bd1 | 82 | draw_cfg_node (pretty_printer *pp, int funcdef_no, basic_block bb) |
a27a5de9 | 83 | { |
a27a5de9 SB |
84 | const char *shape; |
85 | const char *fillcolor; | |
735a0e33 | 86 | |
a27a5de9 | 87 | if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK) |
735a0e33 | 88 | { |
a27a5de9 SB |
89 | shape = "Mdiamond"; |
90 | fillcolor = "white"; | |
735a0e33 | 91 | } |
735a0e33 | 92 | else |
735a0e33 | 93 | { |
a27a5de9 SB |
94 | shape = "record"; |
95 | fillcolor = | |
96 | BB_PARTITION (bb) == BB_HOT_PARTITION ? "lightpink" | |
97 | : BB_PARTITION (bb) == BB_COLD_PARTITION ? "lightblue" | |
98 | : "lightgrey"; | |
735a0e33 | 99 | } |
735a0e33 | 100 | |
7eba871a SB |
101 | pp_printf (pp, |
102 | "\tfn_%d_basic_block_%d " | |
103 | "[shape=%s,style=filled,fillcolor=%s,label=\"", | |
2c895bd1 | 104 | funcdef_no, bb->index, shape, fillcolor); |
735a0e33 | 105 | |
a27a5de9 | 106 | if (bb->index == ENTRY_BLOCK) |
7eba871a | 107 | pp_string (pp, "ENTRY"); |
a27a5de9 | 108 | else if (bb->index == EXIT_BLOCK) |
7eba871a | 109 | pp_string (pp, "EXIT"); |
a27a5de9 | 110 | else |
735a0e33 | 111 | { |
7eba871a SB |
112 | pp_character (pp, '{'); |
113 | pp_write_text_to_stream (pp); | |
2c895bd1 | 114 | dump_bb_for_graph (pp, bb); |
7eba871a | 115 | pp_character (pp, '}'); |
735a0e33 | 116 | } |
a27a5de9 | 117 | |
7eba871a SB |
118 | pp_string (pp, "\"];\n\n"); |
119 | pp_flush (pp); | |
735a0e33 UD |
120 | } |
121 | ||
a27a5de9 | 122 | /* Draw all successor edges of a basic block BB belonging to the function |
2c895bd1 | 123 | with FUNCDEF_NO as its unique number. */ |
735a0e33 | 124 | static void |
2c895bd1 | 125 | draw_cfg_node_succ_edges (pretty_printer *pp, int funcdef_no, basic_block bb) |
735a0e33 | 126 | { |
a27a5de9 SB |
127 | edge e; |
128 | edge_iterator ei; | |
129 | FOR_EACH_EDGE (e, ei, bb->succs) | |
735a0e33 | 130 | { |
a27a5de9 SB |
131 | const char *style = "\"solid,bold\""; |
132 | const char *color = "black"; | |
133 | int weight = 10; | |
735a0e33 | 134 | |
a27a5de9 | 135 | if (e->flags & EDGE_FAKE) |
735a0e33 | 136 | { |
a27a5de9 SB |
137 | style = "dotted"; |
138 | color = "green"; | |
139 | weight = 0; | |
735a0e33 | 140 | } |
a27a5de9 | 141 | else if (e->flags & EDGE_DFS_BACK) |
735a0e33 | 142 | { |
a27a5de9 SB |
143 | style = "\"dotted,bold\""; |
144 | color = "blue"; | |
145 | weight = 10; | |
735a0e33 | 146 | } |
a27a5de9 | 147 | else if (e->flags & EDGE_FALLTHRU) |
735a0e33 | 148 | { |
a27a5de9 SB |
149 | color = "blue"; |
150 | weight = 100; | |
735a0e33 UD |
151 | } |
152 | ||
a27a5de9 SB |
153 | if (e->flags & EDGE_ABNORMAL) |
154 | color = "red"; | |
735a0e33 | 155 | |
7eba871a SB |
156 | pp_printf (pp, |
157 | "\tfn_%d_basic_block_%d:s -> fn_%d_basic_block_%d:n " | |
158 | "[style=%s,color=%s,weight=%d,constraint=%s];\n", | |
2c895bd1 SB |
159 | funcdef_no, e->src->index, |
160 | funcdef_no, e->dest->index, | |
7eba871a SB |
161 | style, color, weight, |
162 | (e->flags & (EDGE_FAKE | EDGE_DFS_BACK)) ? "false" : "true"); | |
735a0e33 | 163 | } |
7eba871a | 164 | pp_flush (pp); |
a27a5de9 SB |
165 | } |
166 | ||
13475df1 SB |
167 | /* Draw all the basic blocks in the CFG in case loops are not available. |
168 | First compute a topological order of the blocks to get a good ranking of | |
169 | the nodes. Then, if any nodes are not reachable from ENTRY, add them at | |
170 | the end. */ | |
41222ddf | 171 | |
13475df1 SB |
172 | static void |
173 | draw_cfg_nodes_no_loops (pretty_printer *pp, struct function *fun) | |
a27a5de9 | 174 | { |
13475df1 | 175 | int *rpo = XNEWVEC (int, n_basic_blocks_for_function (fun)); |
a27a5de9 | 176 | int i, n; |
13475df1 | 177 | sbitmap visited; |
a27a5de9 | 178 | |
13475df1 SB |
179 | visited = sbitmap_alloc (last_basic_block); |
180 | bitmap_clear (visited); | |
a27a5de9 | 181 | |
13475df1 | 182 | /* FIXME: pre_and_rev_post_order_compute only works if fun == cfun. */ |
a27a5de9 SB |
183 | n = pre_and_rev_post_order_compute (NULL, rpo, true); |
184 | for (i = 0; i < n; i++) | |
13475df1 SB |
185 | { |
186 | basic_block bb = BASIC_BLOCK (rpo[i]); | |
187 | draw_cfg_node (pp, fun->funcdef_no, bb); | |
188 | bitmap_set_bit (visited, bb->index); | |
189 | } | |
190 | free (rpo); | |
a27a5de9 | 191 | |
13475df1 SB |
192 | if (n != n_basic_blocks_for_function (fun)) |
193 | { | |
194 | /* Some blocks are unreachable. We still want to dump them. */ | |
195 | basic_block bb; | |
196 | FOR_ALL_BB_FN (bb, fun) | |
197 | if (! bitmap_bit_p (visited, bb->index)) | |
198 | draw_cfg_node (pp, fun->funcdef_no, bb); | |
199 | } | |
200 | ||
201 | sbitmap_free (visited); | |
202 | } | |
203 | ||
204 | /* Draw all the basic blocks in LOOP. Print the blocks in breath-first | |
205 | order to get a good ranking of the nodes. This function is recursive: | |
206 | It first prints inner loops, then the body of LOOP itself. */ | |
a27a5de9 | 207 | |
13475df1 SB |
208 | static void |
209 | draw_cfg_nodes_for_loop (pretty_printer *pp, int funcdef_no, | |
210 | struct loop *loop) | |
211 | { | |
212 | basic_block *body; | |
213 | unsigned int i; | |
214 | const char *fillcolors[3] = { "grey88", "grey77", "grey66" }; | |
215 | ||
216 | if (loop->latch != EXIT_BLOCK_PTR) | |
217 | pp_printf (pp, | |
218 | "\tsubgraph cluster_%d_%d {\n" | |
219 | "\tstyle=\"filled\";\n" | |
220 | "\tcolor=\"darkgreen\";\n" | |
221 | "\tfillcolor=\"%s\";\n" | |
222 | "\tlabel=\"loop %d\";\n" | |
223 | "\tlabeljust=l;\n" | |
224 | "\tpenwidth=2;\n", | |
225 | funcdef_no, loop->num, | |
226 | fillcolors[(loop_depth (loop) - 1) % 3], | |
227 | loop->num); | |
228 | ||
229 | for (struct loop *inner = loop->inner; inner; inner = inner->next) | |
230 | draw_cfg_nodes_for_loop (pp, funcdef_no, inner); | |
231 | ||
232 | if (loop->latch == EXIT_BLOCK_PTR) | |
233 | body = get_loop_body (loop); | |
234 | else | |
235 | body = get_loop_body_in_bfs_order (loop); | |
236 | ||
237 | for (i = 0; i < loop->num_nodes; i++) | |
238 | { | |
239 | basic_block bb = body[i]; | |
240 | if (bb->loop_father == loop) | |
241 | draw_cfg_node (pp, funcdef_no, bb); | |
242 | } | |
243 | ||
244 | free (body); | |
245 | ||
246 | if (loop->latch != EXIT_BLOCK_PTR) | |
247 | pp_printf (pp, "\t}\n"); | |
248 | } | |
249 | ||
250 | /* Draw all the basic blocks in the CFG in case the loop tree is available. | |
251 | All loop bodys are printed in clusters. */ | |
252 | ||
253 | static void | |
254 | draw_cfg_nodes (pretty_printer *pp, struct function *fun) | |
255 | { | |
256 | /* ??? This x_current_loops should be enapsulated. */ | |
257 | if (fun->x_current_loops) | |
258 | draw_cfg_nodes_for_loop (pp, fun->funcdef_no, | |
259 | fun->x_current_loops->tree_root); | |
260 | else | |
261 | draw_cfg_nodes_no_loops (pp, fun); | |
262 | } | |
263 | ||
264 | /* Draw all edges in the CFG. Retreating edges are drawin as not | |
265 | constraining, this makes the layout of the graph better. | |
266 | (??? Calling mark_dfs_back may change the compiler's behavior when | |
267 | dumping, but computing back edges here for ourselves is also not | |
268 | desirable.) */ | |
269 | ||
270 | static void | |
271 | draw_cfg_edges (pretty_printer *pp, struct function *fun) | |
272 | { | |
273 | basic_block bb; | |
a27a5de9 SB |
274 | mark_dfs_back_edges (); |
275 | FOR_ALL_BB (bb) | |
13475df1 SB |
276 | draw_cfg_node_succ_edges (pp, fun->funcdef_no, bb); |
277 | ||
278 | /* Add an invisible edge from ENTRY to EXIT, to improve the graph layout. */ | |
279 | pp_printf (pp, | |
280 | "\tfn_%d_basic_block_%d:s -> fn_%d_basic_block_%d:n " | |
281 | "[style=\"invis\",constraint=true];\n", | |
282 | fun->funcdef_no, ENTRY_BLOCK, | |
283 | fun->funcdef_no, EXIT_BLOCK); | |
284 | pp_flush (pp); | |
285 | } | |
735a0e33 | 286 | |
13475df1 SB |
287 | /* Print a graphical representation of the CFG of function FUN. |
288 | First print all basic blocks. Draw all edges at the end to get | |
289 | subgraphs right for GraphViz, which requires nodes to be defined | |
290 | before edges to cluster nodes properly. */ | |
291 | ||
292 | void | |
293 | print_graph_cfg (const char *base, struct function *fun) | |
294 | { | |
295 | const char *funcname = function_name (fun); | |
296 | FILE *fp = open_graph_file (base, "a"); | |
297 | pretty_printer *pp = init_graph_slim_pretty_print (fp); | |
298 | pp_printf (pp, "subgraph \"%s\" {\n" | |
299 | "\tcolor=\"black\";\n" | |
300 | "\tlabel=\"%s\";\n", | |
301 | funcname, funcname); | |
302 | draw_cfg_nodes (pp, fun); | |
303 | draw_cfg_edges (pp, fun); | |
304 | pp_printf (pp, "}\n"); | |
7eba871a | 305 | pp_flush (pp); |
735a0e33 UD |
306 | fclose (fp); |
307 | } | |
308 | ||
a27a5de9 SB |
309 | /* Start the dump of a graph. */ |
310 | static void | |
311 | start_graph_dump (FILE *fp) | |
312 | { | |
313 | fputs ("digraph \"\" {\n" | |
314 | "overlap=false;\n", | |
315 | fp); | |
316 | } | |
317 | ||
318 | /* End the dump of a graph. */ | |
319 | static void | |
320 | end_graph_dump (FILE *fp) | |
321 | { | |
322 | fputs ("}\n", fp); | |
323 | } | |
735a0e33 UD |
324 | |
325 | /* Similar as clean_dump_file, but this time for graph output files. */ | |
326 | void | |
9f8628ba | 327 | clean_graph_dump_file (const char *base) |
735a0e33 | 328 | { |
a27a5de9 SB |
329 | FILE *fp = open_graph_file (base, "w"); |
330 | start_graph_dump (fp); | |
735a0e33 UD |
331 | fclose (fp); |
332 | } | |
333 | ||
334 | ||
335 | /* Do final work on the graph output file. */ | |
336 | void | |
9f8628ba | 337 | finish_graph_dump_file (const char *base) |
735a0e33 | 338 | { |
a27a5de9 SB |
339 | FILE *fp = open_graph_file (base, "a"); |
340 | end_graph_dump (fp); | |
341 | fclose (fp); | |
735a0e33 | 342 | } |