]>
Commit | Line | Data |
---|---|---|
735a0e33 UD |
1 | /* Output routines for graphical representation. |
2 | Copyright (C) 1998 Free Software Foundation, Inc. | |
3 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. | |
4 | ||
5 | This file is part of GNU CC. | |
6 | ||
7 | GNU CC is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2, or (at your option) | |
10 | any later version. | |
11 | ||
12 | GNU CC is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with GNU CC; see the file COPYING. If not, write to | |
19 | the Free Software Foundation, 59 Temple Place - Suite 330, | |
20 | Boston, MA 02111-1307, USA. */ | |
21 | ||
22 | #include <config.h> | |
23 | #include "system.h" | |
24 | ||
25 | #include "rtl.h" | |
26 | #include "flags.h" | |
27 | #include "output.h" | |
28 | #include "hard-reg-set.h" | |
29 | #include "basic-block.h" | |
30 | #include "toplev.h" | |
31 | ||
32 | static const char *graph_ext[] = | |
33 | { | |
34 | /* no_graph */ "", | |
35 | /* vcg */ ".vcg", | |
36 | }; | |
37 | ||
38 | /* Output text for new basic block. */ | |
39 | static void | |
40 | start_fct (fp) | |
41 | FILE *fp; | |
42 | { | |
43 | ||
44 | switch (graph_dump_format) | |
45 | { | |
46 | case vcg: | |
47 | fprintf (fp, "\ | |
48 | graph: { title: \"%s\"\nfolding: 1\nhidden: 2\nnode: { title: \"%s.0\" }\n", | |
49 | current_function_name, current_function_name); | |
50 | break; | |
51 | case no_graph: | |
52 | break; | |
53 | } | |
54 | } | |
55 | ||
56 | static void | |
57 | start_bb (fp, bb) | |
58 | FILE *fp; | |
59 | int bb; | |
60 | { | |
61 | switch (graph_dump_format) | |
62 | { | |
63 | case vcg: | |
64 | fprintf (fp, "\ | |
65 | graph: {\ntitle: \"%s.BB%d\"\nfolding: 1\ncolor: lightblue\n\ | |
66 | label: \"basic block %d", | |
67 | current_function_name, bb, bb); | |
68 | break; | |
69 | case no_graph: | |
70 | break; | |
71 | } | |
72 | ||
73 | #if 0 | |
74 | /* FIXME Should this be printed? It makes the graph significantly larger. */ | |
75 | ||
76 | /* Print the live-at-start register list. */ | |
77 | fputc ('\n', fp); | |
78 | EXECUTE_IF_SET_IN_REG_SET (basic_block_live_at_start[bb], 0, i, | |
79 | { | |
80 | fprintf (fp, " %d", i); | |
81 | if (i < FIRST_PSEUDO_REGISTER) | |
82 | fprintf (fp, " [%s]", | |
83 | reg_names[i]); | |
84 | }); | |
85 | #endif | |
86 | ||
87 | switch (graph_dump_format) | |
88 | { | |
89 | case vcg: | |
90 | fputs ("\"\n\n", fp); | |
91 | break; | |
92 | case no_graph: | |
93 | break; | |
94 | } | |
95 | } | |
96 | ||
97 | static int | |
98 | node_data (fp, tmp_rtx) | |
99 | FILE *fp; | |
100 | rtx tmp_rtx; | |
101 | { | |
102 | int result; | |
103 | ||
104 | if (PREV_INSN (tmp_rtx) == 0) | |
105 | { | |
106 | /* This is the first instruction. Add an edge from the starting | |
107 | block. */ | |
108 | switch (graph_dump_format) | |
109 | { | |
110 | case vcg: | |
111 | fprintf (fp, "\ | |
112 | edge: { sourcename: \"%s.0\" targetname: \"%s.%d\" }\n", | |
113 | current_function_name, | |
114 | current_function_name, XINT (tmp_rtx, 0)); | |
115 | break; | |
116 | case no_graph: | |
117 | break; | |
118 | } | |
119 | } | |
120 | ||
121 | switch (graph_dump_format) | |
122 | { | |
123 | case vcg: | |
124 | fprintf (fp, "node: {\n title: \"%s.%d\"\n color: %s\n \ | |
125 | label: \"%s %d\n", | |
126 | current_function_name, XINT (tmp_rtx, 0), | |
127 | GET_CODE (tmp_rtx) == NOTE ? "lightgrey" | |
128 | : GET_CODE (tmp_rtx) == INSN ? "green" | |
129 | : GET_CODE (tmp_rtx) == JUMP_INSN ? "darkgreen" | |
130 | : GET_CODE (tmp_rtx) == CALL_INSN ? "darkgreen" | |
131 | : GET_CODE (tmp_rtx) == CODE_LABEL ? "\ | |
132 | darkgrey\n shape: ellipse" : "white", | |
133 | GET_RTX_NAME (GET_CODE (tmp_rtx)), XINT (tmp_rtx, 0)); | |
134 | break; | |
135 | case no_graph: | |
136 | break; | |
137 | } | |
138 | ||
139 | /* Print the RTL. */ | |
140 | if (GET_CODE (tmp_rtx) == NOTE) | |
141 | { | |
142 | static const char *note_names[] = | |
143 | { | |
144 | NULL, | |
145 | "deleted", | |
146 | "block_beg", | |
147 | "block_end", | |
148 | "loop_beg", | |
149 | "loop_end", | |
150 | "function_end", | |
151 | "setjmp", | |
152 | "loop_cont", | |
153 | "loop_vtop", | |
154 | "prologue_end", | |
155 | "epilogue_beg", | |
156 | "deleted_label", | |
157 | "function_beg", | |
158 | "eh_region_beg", | |
159 | "eh_region_end", | |
160 | "repeated_line_number", | |
161 | "range_start", | |
162 | "range_end", | |
163 | "live" | |
164 | }; | |
165 | ||
166 | fprintf (fp, " %s", | |
167 | XINT (tmp_rtx, 4) < 0 ? note_names[-XINT (tmp_rtx, 4)] : ""); | |
168 | } | |
169 | else if (GET_RTX_CLASS (GET_CODE (tmp_rtx)) == 'i') | |
170 | result = print_rtl_single (fp, PATTERN (tmp_rtx)); | |
171 | else | |
172 | result = print_rtl_single (fp, tmp_rtx); | |
173 | ||
174 | switch (graph_dump_format) | |
175 | { | |
176 | case vcg: | |
177 | fputs ("\"\n}\n", fp); | |
178 | break; | |
179 | case no_graph: | |
180 | break; | |
181 | } | |
182 | ||
183 | return result; | |
184 | } | |
185 | ||
186 | static void | |
187 | draw_edge (fp, from, to, bb_edge, class) | |
188 | FILE *fp; | |
189 | int from; | |
190 | int to; | |
191 | int bb_edge; | |
192 | int class; | |
193 | { | |
194 | switch (graph_dump_format) | |
195 | { | |
196 | case vcg: | |
197 | fprintf (fp, | |
198 | "edge: { sourcename: \"%s.%d\" targetname: \"%s.%d\" %s", | |
199 | current_function_name, from, | |
200 | current_function_name, to, | |
201 | bb_edge ? "color: blue " : class ? "color: red " : ""); | |
202 | if (class) | |
203 | fprintf (fp, "class: %d ", class); | |
204 | fputs ("}\n", fp); | |
205 | break; | |
206 | case no_graph: | |
207 | break; | |
208 | } | |
209 | } | |
210 | ||
211 | static void | |
212 | end_bb (fp, bb) | |
213 | FILE *fp; | |
214 | int bb ATTRIBUTE_UNUSED; | |
215 | { | |
216 | switch (graph_dump_format) | |
217 | { | |
218 | case vcg: | |
219 | fputs ("}\n", fp); | |
220 | break; | |
221 | case no_graph: | |
222 | break; | |
223 | } | |
224 | } | |
225 | ||
226 | static void | |
227 | end_fct (fp) | |
228 | FILE *fp; | |
229 | { | |
230 | switch (graph_dump_format) | |
231 | { | |
232 | case vcg: | |
233 | fprintf (fp, "node: { title: \"%s.999999\" label: \"END\" }\n}\n", | |
234 | current_function_name); | |
235 | break; | |
236 | case no_graph: | |
237 | break; | |
238 | } | |
239 | } | |
240 | \f | |
241 | /* Like print_rtl, but also print out live information for the start of each | |
242 | basic block. */ | |
243 | void | |
244 | print_rtl_graph_with_bb (base, suffix, rtx_first) | |
245 | const char *base; | |
246 | const char *suffix; | |
247 | rtx rtx_first; | |
248 | { | |
249 | register rtx tmp_rtx; | |
250 | size_t namelen = strlen (base); | |
251 | size_t suffixlen = strlen (suffix); | |
252 | size_t extlen = strlen (graph_ext[graph_dump_format]) + 1; | |
253 | char *buf = (char *) alloca (namelen + suffixlen + extlen); | |
254 | FILE *fp; | |
255 | ||
256 | /* Regenerate the basic block information. */ | |
257 | find_basic_blocks (rtx_first, max_reg_num (), NULL); | |
258 | ||
259 | memcpy (buf, base, namelen); | |
260 | memcpy (buf + namelen, suffix, suffixlen); | |
261 | memcpy (buf + namelen + suffixlen, graph_ext[graph_dump_format], extlen); | |
262 | ||
263 | fp = fopen (buf, "a"); | |
264 | if (fp == NULL) | |
265 | return; | |
266 | ||
267 | if (rtx_first == 0) | |
268 | fprintf (fp, "(nil)\n"); | |
269 | else | |
270 | { | |
271 | int i, bb; | |
272 | enum bb_state { NOT_IN_BB, IN_ONE_BB, IN_MULTIPLE_BB }; | |
273 | int max_uid = get_max_uid (); | |
274 | int *start = (int *) alloca (max_uid * sizeof (int)); | |
275 | int *end = (int *) alloca (max_uid * sizeof (int)); | |
276 | enum bb_state *in_bb_p = (enum bb_state *) | |
277 | alloca (max_uid * sizeof (enum bb_state)); | |
278 | /* Element I is a list of I's predecessors/successors. */ | |
279 | int_list_ptr *s_preds; | |
280 | int_list_ptr *s_succs; | |
281 | /* Element I is the number of predecessors/successors of basic | |
282 | block I. */ | |
283 | int *num_preds; | |
284 | int *num_succs; | |
285 | ||
286 | for (i = 0; i < max_uid; ++i) | |
287 | { | |
288 | start[i] = end[i] = -1; | |
289 | in_bb_p[i] = NOT_IN_BB; | |
290 | } | |
291 | ||
292 | for (i = n_basic_blocks - 1; i >= 0; --i) | |
293 | { | |
294 | rtx x; | |
3b413743 RH |
295 | start[INSN_UID (BLOCK_HEAD (i))] = i; |
296 | end[INSN_UID (BLOCK_END (i))] = i; | |
297 | for (x = BLOCK_HEAD (i); x != NULL_RTX; x = NEXT_INSN (x)) | |
735a0e33 UD |
298 | { |
299 | in_bb_p[INSN_UID (x)] | |
300 | = (in_bb_p[INSN_UID (x)] == NOT_IN_BB) | |
301 | ? IN_ONE_BB : IN_MULTIPLE_BB; | |
3b413743 | 302 | if (x == BLOCK_END (i)) |
735a0e33 UD |
303 | break; |
304 | } | |
305 | } | |
306 | ||
307 | /* Get the information about the basic blocks predecessors and | |
308 | successors. */ | |
309 | s_preds = (int_list_ptr *) alloca (n_basic_blocks | |
310 | * sizeof (int_list_ptr)); | |
311 | s_succs = (int_list_ptr *) alloca (n_basic_blocks | |
312 | * sizeof (int_list_ptr)); | |
313 | num_preds = (int *) alloca (n_basic_blocks * sizeof (int)); | |
314 | num_succs = (int *) alloca (n_basic_blocks * sizeof (int)); | |
315 | compute_preds_succs (s_preds, s_succs, num_preds, num_succs); | |
316 | ||
317 | /* Tell print-rtl that we want graph output. */ | |
318 | dump_for_graph = 1; | |
319 | ||
320 | /* Start new function. */ | |
321 | start_fct (fp); | |
322 | ||
323 | for (tmp_rtx = NEXT_INSN (rtx_first); NULL != tmp_rtx; | |
324 | tmp_rtx = NEXT_INSN (tmp_rtx)) | |
325 | { | |
326 | int did_output; | |
327 | int edge_printed = 0; | |
328 | rtx next_insn; | |
329 | ||
330 | if (start[INSN_UID (tmp_rtx)] < 0 && end[INSN_UID (tmp_rtx)] < 0) | |
331 | { | |
332 | if (GET_CODE (tmp_rtx) == BARRIER) | |
333 | continue; | |
334 | if (GET_CODE (tmp_rtx) == NOTE | |
335 | && (1 || in_bb_p[INSN_UID (tmp_rtx)] == NOT_IN_BB)) | |
336 | continue; | |
337 | } | |
338 | ||
339 | if ((bb = start[INSN_UID (tmp_rtx)]) >= 0) | |
340 | { | |
341 | /* We start a subgraph for each basic block. */ | |
342 | start_bb (fp, bb); | |
343 | ||
344 | if (bb == 0) | |
345 | draw_edge (fp, 0, INSN_UID (tmp_rtx), 1, 0); | |
346 | } | |
347 | ||
348 | /* Print the data for this node. */ | |
349 | did_output = node_data (fp, tmp_rtx); | |
350 | next_insn = next_nonnote_insn (tmp_rtx); | |
351 | ||
352 | if ((bb = end[INSN_UID (tmp_rtx)]) >= 0) | |
353 | { | |
354 | int_list_ptr p; | |
355 | ||
356 | /* End of the basic block. */ | |
357 | end_bb (fp, bb); | |
358 | ||
359 | /* Now specify the edges to all the successors of this | |
360 | basic block. */ | |
361 | for (p = s_succs[bb]; p != NULL; p = p->next) | |
362 | { | |
363 | int bb_succ = INT_LIST_VAL (p); | |
364 | ||
365 | if (bb_succ >= 0) | |
366 | { | |
367 | rtx block_head = BLOCK_HEAD (bb_succ); | |
368 | ||
369 | draw_edge (fp, INSN_UID (tmp_rtx), | |
370 | INSN_UID (block_head), | |
371 | next_insn != block_head, 0); | |
372 | ||
373 | if (BLOCK_HEAD (bb_succ) == next_insn) | |
374 | edge_printed = 1; | |
375 | } | |
376 | else if (bb_succ == EXIT_BLOCK) | |
377 | { | |
378 | draw_edge (fp, INSN_UID (tmp_rtx), 999999, | |
379 | next_insn != 0, 0); | |
380 | ||
381 | if (next_insn == 0) | |
382 | edge_printed = 1; | |
383 | } | |
384 | else | |
385 | abort (); | |
386 | } | |
387 | } | |
388 | ||
389 | if (!edge_printed) | |
390 | { | |
391 | /* Don't print edges to barriers. */ | |
392 | if (next_insn == 0 | |
393 | || GET_CODE (next_insn) != BARRIER) | |
394 | draw_edge (fp, XINT (tmp_rtx, 0), | |
395 | next_insn ? INSN_UID (next_insn) : 999999, 0, 0); | |
396 | else | |
397 | { | |
398 | /* We draw the remaining edges in class 2. We have | |
399 | to skip oevr the barrier since these nodes are | |
400 | not printed at all. */ | |
401 | do | |
402 | next_insn = NEXT_INSN (next_insn); | |
403 | while (next_insn | |
404 | && (GET_CODE (next_insn) == NOTE | |
405 | || GET_CODE (next_insn) == BARRIER)); | |
406 | ||
407 | draw_edge (fp, XINT (tmp_rtx, 0), | |
408 | next_insn ? INSN_UID (next_insn) : 999999, 0, 2); | |
409 | } | |
410 | } | |
411 | } | |
412 | ||
413 | dump_for_graph = 0; | |
414 | ||
415 | end_fct (fp); | |
416 | } | |
417 | ||
418 | fclose (fp); | |
419 | } | |
420 | ||
421 | ||
422 | /* Similar as clean_dump_file, but this time for graph output files. */ | |
423 | void | |
424 | clean_graph_dump_file (base, suffix) | |
425 | const char *base; | |
426 | const char *suffix; | |
427 | { | |
428 | size_t namelen = strlen (base); | |
429 | size_t suffixlen = strlen (suffix); | |
430 | size_t extlen = strlen (graph_ext[graph_dump_format]) + 1; | |
431 | char *buf = (char *) alloca (namelen + extlen + suffixlen); | |
432 | FILE *fp; | |
433 | ||
434 | memcpy (buf, base, namelen); | |
435 | memcpy (buf + namelen, suffix, suffixlen); | |
436 | memcpy (buf + namelen + suffixlen, graph_ext[graph_dump_format], extlen); | |
437 | ||
438 | fp = fopen (buf, "w"); | |
439 | ||
440 | if (fp == NULL) | |
441 | pfatal_with_name (buf); | |
442 | ||
443 | switch (graph_dump_format) | |
444 | { | |
445 | case vcg: | |
446 | fputs ("graph: {\nport_sharing: no\n", fp); | |
447 | break; | |
448 | case no_graph: | |
449 | abort (); | |
450 | } | |
451 | ||
452 | fclose (fp); | |
453 | } | |
454 | ||
455 | ||
456 | /* Do final work on the graph output file. */ | |
457 | void | |
458 | finish_graph_dump_file (base, suffix) | |
459 | const char *base; | |
460 | const char *suffix; | |
461 | { | |
462 | size_t namelen = strlen (base); | |
463 | size_t suffixlen = strlen (suffix); | |
464 | size_t extlen = strlen (graph_ext[graph_dump_format]) + 1; | |
465 | char *buf = (char *) alloca (namelen + suffixlen + extlen); | |
466 | FILE *fp; | |
467 | ||
468 | memcpy (buf, base, namelen); | |
469 | memcpy (buf + namelen, suffix, suffixlen); | |
470 | memcpy (buf + namelen + suffixlen, graph_ext[graph_dump_format], extlen); | |
471 | ||
472 | fp = fopen (buf, "a"); | |
473 | if (fp != NULL) | |
474 | { | |
475 | switch (graph_dump_format) | |
476 | { | |
477 | case vcg: | |
478 | fputs ("}\n", fp); | |
479 | break; | |
480 | case no_graph: | |
481 | abort (); | |
482 | } | |
483 | ||
484 | fclose (fp); | |
485 | } | |
486 | } |