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