]>
Commit | Line | Data |
---|---|---|
735a0e33 | 1 | /* Output routines for graphical representation. |
d9221e01 KH |
2 | Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004 |
3 | Free Software Foundation, Inc. | |
735a0e33 UD |
4 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. |
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 | |
10 | Software Foundation; either version 2, or (at your option) any later | |
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 |
1322177d LB |
19 | along with GCC; see the file COPYING. If not, write to the Free |
20 | Software Foundation, 59 Temple Place - Suite 330, Boston, MA | |
21 | 02111-1307, USA. */ | |
735a0e33 UD |
22 | |
23 | #include <config.h> | |
24 | #include "system.h" | |
4977bab6 ZW |
25 | #include "coretypes.h" |
26 | #include "tm.h" | |
735a0e33 UD |
27 | |
28 | #include "rtl.h" | |
29 | #include "flags.h" | |
30 | #include "output.h" | |
49ad7cfa | 31 | #include "function.h" |
735a0e33 UD |
32 | #include "hard-reg-set.h" |
33 | #include "basic-block.h" | |
34 | #include "toplev.h" | |
6a2cc2ac | 35 | #include "graph.h" |
735a0e33 | 36 | |
27c38fbe | 37 | static const char *const graph_ext[] = |
735a0e33 UD |
38 | { |
39 | /* no_graph */ "", | |
40 | /* vcg */ ".vcg", | |
41 | }; | |
42 | ||
1d088dee AJ |
43 | static void start_fct (FILE *); |
44 | static void start_bb (FILE *, int); | |
45 | static void node_data (FILE *, rtx); | |
46 | static void draw_edge (FILE *, int, int, int, int); | |
47 | static void end_fct (FILE *); | |
48 | static void end_bb (FILE *); | |
5f06c983 | 49 | |
735a0e33 UD |
50 | /* Output text for new basic block. */ |
51 | static void | |
1d088dee | 52 | start_fct (FILE *fp) |
735a0e33 | 53 | { |
735a0e33 UD |
54 | switch (graph_dump_format) |
55 | { | |
56 | case vcg: | |
57 | fprintf (fp, "\ | |
58 | graph: { title: \"%s\"\nfolding: 1\nhidden: 2\nnode: { title: \"%s.0\" }\n", | |
faed5cc3 | 59 | current_function_name (), current_function_name ()); |
735a0e33 UD |
60 | break; |
61 | case no_graph: | |
62 | break; | |
63 | } | |
64 | } | |
65 | ||
66 | static void | |
1d088dee | 67 | start_bb (FILE *fp, int bb) |
735a0e33 UD |
68 | { |
69 | switch (graph_dump_format) | |
70 | { | |
71 | case vcg: | |
72 | fprintf (fp, "\ | |
73 | graph: {\ntitle: \"%s.BB%d\"\nfolding: 1\ncolor: lightblue\n\ | |
74 | label: \"basic block %d", | |
faed5cc3 | 75 | current_function_name (), bb, bb); |
735a0e33 UD |
76 | break; |
77 | case no_graph: | |
78 | break; | |
79 | } | |
80 | ||
81 | #if 0 | |
dc297297 | 82 | /* FIXME Should this be printed? It makes the graph significantly larger. */ |
735a0e33 UD |
83 | |
84 | /* Print the live-at-start register list. */ | |
85 | fputc ('\n', fp); | |
86 | EXECUTE_IF_SET_IN_REG_SET (basic_block_live_at_start[bb], 0, i, | |
87 | { | |
88 | fprintf (fp, " %d", i); | |
89 | if (i < FIRST_PSEUDO_REGISTER) | |
90 | fprintf (fp, " [%s]", | |
91 | reg_names[i]); | |
92 | }); | |
93 | #endif | |
94 | ||
95 | switch (graph_dump_format) | |
96 | { | |
97 | case vcg: | |
98 | fputs ("\"\n\n", fp); | |
99 | break; | |
100 | case no_graph: | |
101 | break; | |
102 | } | |
103 | } | |
104 | ||
a30d557c | 105 | static void |
1d088dee | 106 | node_data (FILE *fp, rtx tmp_rtx) |
735a0e33 | 107 | { |
735a0e33 UD |
108 | if (PREV_INSN (tmp_rtx) == 0) |
109 | { | |
110 | /* This is the first instruction. Add an edge from the starting | |
111 | block. */ | |
112 | switch (graph_dump_format) | |
113 | { | |
114 | case vcg: | |
115 | fprintf (fp, "\ | |
116 | edge: { sourcename: \"%s.0\" targetname: \"%s.%d\" }\n", | |
faed5cc3 SB |
117 | current_function_name (), |
118 | current_function_name (), XINT (tmp_rtx, 0)); | |
735a0e33 UD |
119 | break; |
120 | case no_graph: | |
121 | break; | |
122 | } | |
123 | } | |
124 | ||
125 | switch (graph_dump_format) | |
126 | { | |
127 | case vcg: | |
128 | fprintf (fp, "node: {\n title: \"%s.%d\"\n color: %s\n \ | |
129 | label: \"%s %d\n", | |
faed5cc3 | 130 | current_function_name (), XINT (tmp_rtx, 0), |
735a0e33 UD |
131 | GET_CODE (tmp_rtx) == NOTE ? "lightgrey" |
132 | : GET_CODE (tmp_rtx) == INSN ? "green" | |
133 | : GET_CODE (tmp_rtx) == JUMP_INSN ? "darkgreen" | |
134 | : GET_CODE (tmp_rtx) == CALL_INSN ? "darkgreen" | |
135 | : GET_CODE (tmp_rtx) == CODE_LABEL ? "\ | |
136 | darkgrey\n shape: ellipse" : "white", | |
137 | GET_RTX_NAME (GET_CODE (tmp_rtx)), XINT (tmp_rtx, 0)); | |
138 | break; | |
139 | case no_graph: | |
140 | break; | |
141 | } | |
142 | ||
143 | /* Print the RTL. */ | |
144 | if (GET_CODE (tmp_rtx) == NOTE) | |
145 | { | |
0a2287bf RH |
146 | const char *name = ""; |
147 | if (NOTE_LINE_NUMBER (tmp_rtx) < 0) | |
148 | name = GET_NOTE_INSN_NAME (NOTE_LINE_NUMBER (tmp_rtx)); | |
149 | fprintf (fp, " %s", name); | |
735a0e33 | 150 | } |
2c3c49de | 151 | else if (INSN_P (tmp_rtx)) |
a30d557c | 152 | print_rtl_single (fp, PATTERN (tmp_rtx)); |
735a0e33 | 153 | else |
a30d557c | 154 | print_rtl_single (fp, tmp_rtx); |
735a0e33 UD |
155 | |
156 | switch (graph_dump_format) | |
157 | { | |
158 | case vcg: | |
159 | fputs ("\"\n}\n", fp); | |
160 | break; | |
161 | case no_graph: | |
162 | break; | |
163 | } | |
735a0e33 UD |
164 | } |
165 | ||
166 | static void | |
1d088dee | 167 | draw_edge (FILE *fp, int from, int to, int bb_edge, int class) |
735a0e33 | 168 | { |
5f06c983 | 169 | const char * color; |
735a0e33 UD |
170 | switch (graph_dump_format) |
171 | { | |
172 | case vcg: | |
e881bb1b RH |
173 | color = ""; |
174 | if (class == 2) | |
175 | color = "color: red "; | |
176 | else if (bb_edge) | |
177 | color = "color: blue "; | |
178 | else if (class == 3) | |
179 | color = "color: green "; | |
735a0e33 UD |
180 | fprintf (fp, |
181 | "edge: { sourcename: \"%s.%d\" targetname: \"%s.%d\" %s", | |
faed5cc3 SB |
182 | current_function_name (), from, |
183 | current_function_name (), to, color); | |
735a0e33 UD |
184 | if (class) |
185 | fprintf (fp, "class: %d ", class); | |
186 | fputs ("}\n", fp); | |
187 | break; | |
188 | case no_graph: | |
189 | break; | |
190 | } | |
191 | } | |
192 | ||
193 | static void | |
1d088dee | 194 | end_bb (FILE *fp) |
735a0e33 UD |
195 | { |
196 | switch (graph_dump_format) | |
197 | { | |
198 | case vcg: | |
199 | fputs ("}\n", fp); | |
200 | break; | |
201 | case no_graph: | |
202 | break; | |
203 | } | |
204 | } | |
205 | ||
206 | static void | |
1d088dee | 207 | end_fct (FILE *fp) |
735a0e33 UD |
208 | { |
209 | switch (graph_dump_format) | |
210 | { | |
211 | case vcg: | |
212 | fprintf (fp, "node: { title: \"%s.999999\" label: \"END\" }\n}\n", | |
faed5cc3 | 213 | current_function_name ()); |
735a0e33 UD |
214 | break; |
215 | case no_graph: | |
216 | break; | |
217 | } | |
218 | } | |
219 | \f | |
220 | /* Like print_rtl, but also print out live information for the start of each | |
221 | basic block. */ | |
222 | void | |
1d088dee | 223 | print_rtl_graph_with_bb (const char *base, const char *suffix, rtx rtx_first) |
735a0e33 | 224 | { |
b3694847 | 225 | rtx tmp_rtx; |
735a0e33 UD |
226 | size_t namelen = strlen (base); |
227 | size_t suffixlen = strlen (suffix); | |
228 | size_t extlen = strlen (graph_ext[graph_dump_format]) + 1; | |
703ad42b | 229 | char *buf = alloca (namelen + suffixlen + extlen); |
735a0e33 UD |
230 | FILE *fp; |
231 | ||
e881bb1b RH |
232 | if (basic_block_info == NULL) |
233 | return; | |
735a0e33 UD |
234 | |
235 | memcpy (buf, base, namelen); | |
236 | memcpy (buf + namelen, suffix, suffixlen); | |
237 | memcpy (buf + namelen + suffixlen, graph_ext[graph_dump_format], extlen); | |
238 | ||
239 | fp = fopen (buf, "a"); | |
240 | if (fp == NULL) | |
241 | return; | |
242 | ||
243 | if (rtx_first == 0) | |
244 | fprintf (fp, "(nil)\n"); | |
245 | else | |
246 | { | |
735a0e33 UD |
247 | enum bb_state { NOT_IN_BB, IN_ONE_BB, IN_MULTIPLE_BB }; |
248 | int max_uid = get_max_uid (); | |
703ad42b KG |
249 | int *start = xmalloc (max_uid * sizeof (int)); |
250 | int *end = xmalloc (max_uid * sizeof (int)); | |
251 | enum bb_state *in_bb_p = xmalloc (max_uid * sizeof (enum bb_state)); | |
e881bb1b | 252 | basic_block bb; |
e0082a72 | 253 | int i; |
735a0e33 | 254 | |
0b17ab2f | 255 | for (i = 0; i < max_uid; ++i) |
735a0e33 | 256 | { |
0b17ab2f RH |
257 | start[i] = end[i] = -1; |
258 | in_bb_p[i] = NOT_IN_BB; | |
735a0e33 UD |
259 | } |
260 | ||
e0082a72 | 261 | FOR_EACH_BB_REVERSE (bb) |
735a0e33 UD |
262 | { |
263 | rtx x; | |
a813c111 SB |
264 | start[INSN_UID (BB_HEAD (bb))] = bb->index; |
265 | end[INSN_UID (BB_END (bb))] = bb->index; | |
266 | for (x = BB_HEAD (bb); x != NULL_RTX; x = NEXT_INSN (x)) | |
735a0e33 UD |
267 | { |
268 | in_bb_p[INSN_UID (x)] | |
269 | = (in_bb_p[INSN_UID (x)] == NOT_IN_BB) | |
270 | ? IN_ONE_BB : IN_MULTIPLE_BB; | |
a813c111 | 271 | if (x == BB_END (bb)) |
735a0e33 UD |
272 | break; |
273 | } | |
274 | } | |
275 | ||
735a0e33 UD |
276 | /* Tell print-rtl that we want graph output. */ |
277 | dump_for_graph = 1; | |
278 | ||
279 | /* Start new function. */ | |
280 | start_fct (fp); | |
281 | ||
282 | for (tmp_rtx = NEXT_INSN (rtx_first); NULL != tmp_rtx; | |
283 | tmp_rtx = NEXT_INSN (tmp_rtx)) | |
284 | { | |
735a0e33 UD |
285 | int edge_printed = 0; |
286 | rtx next_insn; | |
287 | ||
288 | if (start[INSN_UID (tmp_rtx)] < 0 && end[INSN_UID (tmp_rtx)] < 0) | |
289 | { | |
290 | if (GET_CODE (tmp_rtx) == BARRIER) | |
291 | continue; | |
292 | if (GET_CODE (tmp_rtx) == NOTE | |
293 | && (1 || in_bb_p[INSN_UID (tmp_rtx)] == NOT_IN_BB)) | |
294 | continue; | |
295 | } | |
296 | ||
0b17ab2f | 297 | if ((i = start[INSN_UID (tmp_rtx)]) >= 0) |
735a0e33 UD |
298 | { |
299 | /* We start a subgraph for each basic block. */ | |
0b17ab2f | 300 | start_bb (fp, i); |
735a0e33 | 301 | |
0b17ab2f | 302 | if (i == 0) |
735a0e33 UD |
303 | draw_edge (fp, 0, INSN_UID (tmp_rtx), 1, 0); |
304 | } | |
305 | ||
306 | /* Print the data for this node. */ | |
a30d557c | 307 | node_data (fp, tmp_rtx); |
735a0e33 UD |
308 | next_insn = next_nonnote_insn (tmp_rtx); |
309 | ||
0b17ab2f | 310 | if ((i = end[INSN_UID (tmp_rtx)]) >= 0) |
735a0e33 | 311 | { |
e881bb1b RH |
312 | edge e; |
313 | ||
0b17ab2f | 314 | bb = BASIC_BLOCK (i); |
735a0e33 UD |
315 | |
316 | /* End of the basic block. */ | |
5f06c983 | 317 | end_bb (fp); |
735a0e33 UD |
318 | |
319 | /* Now specify the edges to all the successors of this | |
320 | basic block. */ | |
e881bb1b | 321 | for (e = bb->succ; e ; e = e->succ_next) |
735a0e33 | 322 | { |
e881bb1b | 323 | if (e->dest != EXIT_BLOCK_PTR) |
735a0e33 | 324 | { |
a813c111 | 325 | rtx block_head = BB_HEAD (e->dest); |
735a0e33 UD |
326 | |
327 | draw_edge (fp, INSN_UID (tmp_rtx), | |
328 | INSN_UID (block_head), | |
e881bb1b RH |
329 | next_insn != block_head, |
330 | (e->flags & EDGE_ABNORMAL ? 2 : 0)); | |
735a0e33 | 331 | |
e881bb1b | 332 | if (block_head == next_insn) |
735a0e33 UD |
333 | edge_printed = 1; |
334 | } | |
e881bb1b | 335 | else |
735a0e33 UD |
336 | { |
337 | draw_edge (fp, INSN_UID (tmp_rtx), 999999, | |
e881bb1b RH |
338 | next_insn != 0, |
339 | (e->flags & EDGE_ABNORMAL ? 2 : 0)); | |
735a0e33 UD |
340 | |
341 | if (next_insn == 0) | |
342 | edge_printed = 1; | |
343 | } | |
735a0e33 UD |
344 | } |
345 | } | |
346 | ||
347 | if (!edge_printed) | |
348 | { | |
349 | /* Don't print edges to barriers. */ | |
350 | if (next_insn == 0 | |
351 | || GET_CODE (next_insn) != BARRIER) | |
352 | draw_edge (fp, XINT (tmp_rtx, 0), | |
353 | next_insn ? INSN_UID (next_insn) : 999999, 0, 0); | |
354 | else | |
355 | { | |
e881bb1b RH |
356 | /* We draw the remaining edges in class 3. We have |
357 | to skip over the barrier since these nodes are | |
735a0e33 UD |
358 | not printed at all. */ |
359 | do | |
360 | next_insn = NEXT_INSN (next_insn); | |
361 | while (next_insn | |
362 | && (GET_CODE (next_insn) == NOTE | |
363 | || GET_CODE (next_insn) == BARRIER)); | |
364 | ||
365 | draw_edge (fp, XINT (tmp_rtx, 0), | |
e881bb1b | 366 | next_insn ? INSN_UID (next_insn) : 999999, 0, 3); |
735a0e33 UD |
367 | } |
368 | } | |
369 | } | |
370 | ||
371 | dump_for_graph = 0; | |
372 | ||
373 | end_fct (fp); | |
4da896b2 MM |
374 | |
375 | /* Clean up. */ | |
376 | free (start); | |
377 | free (end); | |
378 | free (in_bb_p); | |
735a0e33 UD |
379 | } |
380 | ||
381 | fclose (fp); | |
382 | } | |
383 | ||
384 | ||
385 | /* Similar as clean_dump_file, but this time for graph output files. */ | |
400500c4 | 386 | |
735a0e33 | 387 | void |
1d088dee | 388 | clean_graph_dump_file (const char *base, const char *suffix) |
735a0e33 UD |
389 | { |
390 | size_t namelen = strlen (base); | |
391 | size_t suffixlen = strlen (suffix); | |
392 | size_t extlen = strlen (graph_ext[graph_dump_format]) + 1; | |
703ad42b | 393 | char *buf = alloca (namelen + extlen + suffixlen); |
735a0e33 UD |
394 | FILE *fp; |
395 | ||
396 | memcpy (buf, base, namelen); | |
397 | memcpy (buf + namelen, suffix, suffixlen); | |
398 | memcpy (buf + namelen + suffixlen, graph_ext[graph_dump_format], extlen); | |
399 | ||
400 | fp = fopen (buf, "w"); | |
401 | ||
402 | if (fp == NULL) | |
fa6ef813 | 403 | fatal_error ("can't open %s: %m", buf); |
735a0e33 UD |
404 | |
405 | switch (graph_dump_format) | |
406 | { | |
407 | case vcg: | |
408 | fputs ("graph: {\nport_sharing: no\n", fp); | |
409 | break; | |
410 | case no_graph: | |
411 | abort (); | |
412 | } | |
413 | ||
414 | fclose (fp); | |
415 | } | |
416 | ||
417 | ||
418 | /* Do final work on the graph output file. */ | |
419 | void | |
1d088dee | 420 | finish_graph_dump_file (const char *base, const char *suffix) |
735a0e33 UD |
421 | { |
422 | size_t namelen = strlen (base); | |
423 | size_t suffixlen = strlen (suffix); | |
424 | size_t extlen = strlen (graph_ext[graph_dump_format]) + 1; | |
703ad42b | 425 | char *buf = alloca (namelen + suffixlen + extlen); |
735a0e33 UD |
426 | FILE *fp; |
427 | ||
428 | memcpy (buf, base, namelen); | |
429 | memcpy (buf + namelen, suffix, suffixlen); | |
430 | memcpy (buf + namelen + suffixlen, graph_ext[graph_dump_format], extlen); | |
431 | ||
432 | fp = fopen (buf, "a"); | |
433 | if (fp != NULL) | |
434 | { | |
435 | switch (graph_dump_format) | |
436 | { | |
437 | case vcg: | |
438 | fputs ("}\n", fp); | |
439 | break; | |
440 | case no_graph: | |
441 | abort (); | |
442 | } | |
443 | ||
444 | fclose (fp); | |
445 | } | |
446 | } |