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