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
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 3, 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 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
36static const char *const graph_ext[] =
37{
38  /* no_graph */ "",
39  /* vcg */      ".vcg",
40};
41
42static void start_fct (FILE *);
43static void start_bb (FILE *, int);
44static void node_data (FILE *, rtx);
45static void draw_edge (FILE *, int, int, int, int);
46static void end_fct (FILE *);
47static void end_bb (FILE *);
48
49/* Output text for new basic block.  */
50static void
51start_fct (FILE *fp)
52{
53  switch (graph_dump_format)
54    {
55    case vcg:
56      fprintf (fp, "\
57graph: { 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
65static void
66start_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, "\
76graph: {\ntitle: \"%s.BB%d\"\nfolding: 1\ncolor: lightblue\n\
77label: \"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
107static void
108node_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, "\
118edge: { 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  \
131label: \"%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) ?  "\
138darkgrey\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
167static void
168draw_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
194static void
195end_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
207static void
208end_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.  */
223void
224print_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
387void
388clean_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.  */
411void
412finish_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}
430