1/* Output routines for graphical representation.
2   Copyright (C) 1998, 1999 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
32static const char *graph_ext[] =
33{
34  /* no_graph */ "",
35  /* vcg */      ".vcg",
36};
37
38/* Output text for new basic block.  */
39static void
40start_fct (fp)
41     FILE *fp;
42{
43
44  switch (graph_dump_format)
45    {
46    case vcg:
47      fprintf (fp, "\
48graph: { 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
56static void
57start_bb (fp, bb)
58     FILE *fp;
59     int bb;
60{
61  switch (graph_dump_format)
62    {
63    case vcg:
64      fprintf (fp, "\
65graph: {\ntitle: \"%s.BB%d\"\nfolding: 1\ncolor: lightblue\n\
66label: \"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
97static void
98node_data (fp, tmp_rtx)
99     FILE *fp;
100     rtx tmp_rtx;
101{
102
103  if (PREV_INSN (tmp_rtx) == 0)
104    {
105      /* This is the first instruction.  Add an edge from the starting
106	 block.  */
107      switch (graph_dump_format)
108	{
109	case vcg:
110	  fprintf (fp, "\
111edge: { sourcename: \"%s.0\" targetname: \"%s.%d\" }\n",
112		   current_function_name,
113		   current_function_name, XINT (tmp_rtx, 0));
114	  break;
115	case no_graph:
116	  break;
117	}
118    }
119
120  switch (graph_dump_format)
121    {
122    case vcg:
123      fprintf (fp, "node: {\n  title: \"%s.%d\"\n  color: %s\n  \
124label: \"%s %d\n",
125	       current_function_name, XINT (tmp_rtx, 0),
126	       GET_CODE (tmp_rtx) == NOTE ? "lightgrey"
127	       : GET_CODE (tmp_rtx) == INSN ? "green"
128	       : GET_CODE (tmp_rtx) == JUMP_INSN ? "darkgreen"
129	       : GET_CODE (tmp_rtx) == CALL_INSN ? "darkgreen"
130	       : GET_CODE (tmp_rtx) == CODE_LABEL ?  "\
131darkgrey\n  shape: ellipse" : "white",
132	       GET_RTX_NAME (GET_CODE (tmp_rtx)), XINT (tmp_rtx, 0));
133      break;
134    case no_graph:
135      break;
136    }
137
138  /* Print the RTL.  */
139  if (GET_CODE (tmp_rtx) == NOTE)
140    {
141      static const char *note_names[] =
142      {
143	NULL,
144	"deleted",
145	"block_beg",
146	"block_end",
147	"loop_beg",
148	"loop_end",
149	"function_end",
150	"setjmp",
151	"loop_cont",
152	"loop_vtop",
153	"prologue_end",
154	"epilogue_beg",
155	"deleted_label",
156	"function_beg",
157	"eh_region_beg",
158	"eh_region_end",
159	"repeated_line_number",
160	"range_start",
161	"range_end",
162	"live",
163	"basic_block"
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    print_rtl_single (fp, PATTERN (tmp_rtx));
171  else
172    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
184static void
185draw_edge (fp, from, to, bb_edge, class)
186     FILE *fp;
187     int from;
188     int to;
189     int bb_edge;
190     int class;
191{
192  char * color;
193  switch (graph_dump_format)
194    {
195    case vcg:
196      color = "";
197      if (class == 2)
198	color = "color: red ";
199      else if (bb_edge)
200	color = "color: blue ";
201      else if (class == 3)
202	color = "color: green ";
203      fprintf (fp,
204	       "edge: { sourcename: \"%s.%d\" targetname: \"%s.%d\" %s",
205	       current_function_name, from,
206	       current_function_name, to, color);
207      if (class)
208	fprintf (fp, "class: %d ", class);
209      fputs ("}\n", fp);
210      break;
211    case no_graph:
212      break;
213    }
214}
215
216static void
217end_bb (fp, bb)
218     FILE *fp;
219     int bb ATTRIBUTE_UNUSED;
220{
221  switch (graph_dump_format)
222    {
223    case vcg:
224      fputs ("}\n", fp);
225      break;
226    case no_graph:
227      break;
228    }
229}
230
231static void
232end_fct (fp)
233     FILE *fp;
234{
235  switch (graph_dump_format)
236    {
237    case vcg:
238      fprintf (fp, "node: { title: \"%s.999999\" label: \"END\" }\n}\n",
239	       current_function_name);
240      break;
241    case no_graph:
242      break;
243    }
244}
245
246/* Like print_rtl, but also print out live information for the start of each
247   basic block.  */
248void
249print_rtl_graph_with_bb (base, suffix, rtx_first)
250     const char *base;
251     const char *suffix;
252     rtx rtx_first;
253{
254  register rtx tmp_rtx;
255  size_t namelen = strlen (base);
256  size_t suffixlen = strlen (suffix);
257  size_t extlen = strlen (graph_ext[graph_dump_format]) + 1;
258  char *buf = (char *) alloca (namelen + suffixlen + extlen);
259  FILE *fp;
260
261  if (basic_block_info == NULL)
262    return;
263
264  memcpy (buf, base, namelen);
265  memcpy (buf + namelen, suffix, suffixlen);
266  memcpy (buf + namelen + suffixlen, graph_ext[graph_dump_format], extlen);
267
268  fp = fopen (buf, "a");
269  if (fp == NULL)
270    return;
271
272  if (rtx_first == 0)
273    fprintf (fp, "(nil)\n");
274  else
275    {
276      int i;
277      enum bb_state { NOT_IN_BB, IN_ONE_BB, IN_MULTIPLE_BB };
278      int max_uid = get_max_uid ();
279      int *start = (int *) alloca (max_uid * sizeof (int));
280      int *end = (int *) alloca (max_uid * sizeof (int));
281      enum bb_state *in_bb_p = (enum bb_state *)
282	alloca (max_uid * sizeof (enum bb_state));
283      basic_block bb;
284
285      for (i = 0; i < max_uid; ++i)
286	{
287	  start[i] = end[i] = -1;
288	  in_bb_p[i] = NOT_IN_BB;
289	}
290
291      for (i = n_basic_blocks - 1; i >= 0; --i)
292	{
293	  rtx x;
294	  bb = BASIC_BLOCK (i);
295	  start[INSN_UID (bb->head)] = i;
296	  end[INSN_UID (bb->end)] = i;
297	  for (x = bb->head; x != NULL_RTX; x = NEXT_INSN (x))
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;
302	      if (x == bb->end)
303		break;
304	    }
305	}
306
307      /* Tell print-rtl that we want graph output.  */
308      dump_for_graph = 1;
309
310      /* Start new function.  */
311      start_fct (fp);
312
313      for (tmp_rtx = NEXT_INSN (rtx_first); NULL != tmp_rtx;
314	   tmp_rtx = NEXT_INSN (tmp_rtx))
315	{
316	  int edge_printed = 0;
317	  rtx next_insn;
318
319	  if (start[INSN_UID (tmp_rtx)] < 0 && end[INSN_UID (tmp_rtx)] < 0)
320	    {
321	      if (GET_CODE (tmp_rtx) == BARRIER)
322		continue;
323	      if (GET_CODE (tmp_rtx) == NOTE
324		  && (1 || in_bb_p[INSN_UID (tmp_rtx)] == NOT_IN_BB))
325		continue;
326	    }
327
328	  if ((i = start[INSN_UID (tmp_rtx)]) >= 0)
329	    {
330	      /* We start a subgraph for each basic block.  */
331	      start_bb (fp, i);
332
333	      if (i == 0)
334		draw_edge (fp, 0, INSN_UID (tmp_rtx), 1, 0);
335	    }
336
337	  /* Print the data for this node.  */
338	  node_data (fp, tmp_rtx);
339	  next_insn = next_nonnote_insn (tmp_rtx);
340
341	  if ((i = end[INSN_UID (tmp_rtx)]) >= 0)
342	    {
343	      edge e;
344
345	      bb = BASIC_BLOCK (i);
346
347	      /* End of the basic block.  */
348	      end_bb (fp, bb);
349
350	      /* Now specify the edges to all the successors of this
351		 basic block.  */
352	      for (e = bb->succ; e ; e = e->succ_next)
353		{
354		  if (e->dest != EXIT_BLOCK_PTR)
355		    {
356		      rtx block_head = e->dest->head;
357
358		      draw_edge (fp, INSN_UID (tmp_rtx),
359				 INSN_UID (block_head),
360				 next_insn != block_head,
361				 (e->flags & EDGE_ABNORMAL ? 2 : 0));
362
363		      if (block_head == next_insn)
364			edge_printed = 1;
365		    }
366		  else
367		    {
368		      draw_edge (fp, INSN_UID (tmp_rtx), 999999,
369				 next_insn != 0,
370				 (e->flags & EDGE_ABNORMAL ? 2 : 0));
371
372		      if (next_insn == 0)
373			edge_printed = 1;
374		    }
375		}
376	    }
377
378	  if (!edge_printed)
379	    {
380	      /* Don't print edges to barriers.  */
381	      if (next_insn == 0
382		  || GET_CODE (next_insn) != BARRIER)
383		draw_edge (fp, XINT (tmp_rtx, 0),
384			   next_insn ? INSN_UID (next_insn) : 999999, 0, 0);
385	      else
386		{
387		  /* We draw the remaining edges in class 3.  We have
388		     to skip over the barrier since these nodes are
389		     not printed at all.  */
390		  do
391		    next_insn = NEXT_INSN (next_insn);
392		  while (next_insn
393			 && (GET_CODE (next_insn) == NOTE
394			     || GET_CODE (next_insn) == BARRIER));
395
396		  draw_edge (fp, XINT (tmp_rtx, 0),
397			     next_insn ? INSN_UID (next_insn) : 999999, 0, 3);
398		}
399	    }
400	}
401
402      dump_for_graph = 0;
403
404      end_fct (fp);
405    }
406
407  fclose (fp);
408}
409
410
411/* Similar as clean_dump_file, but this time for graph output files.  */
412void
413clean_graph_dump_file (base, suffix)
414     const char *base;
415     const char *suffix;
416{
417  size_t namelen = strlen (base);
418  size_t suffixlen = strlen (suffix);
419  size_t extlen = strlen (graph_ext[graph_dump_format]) + 1;
420  char *buf = (char *) alloca (namelen + extlen + suffixlen);
421  FILE *fp;
422
423  memcpy (buf, base, namelen);
424  memcpy (buf + namelen, suffix, suffixlen);
425  memcpy (buf + namelen + suffixlen, graph_ext[graph_dump_format], extlen);
426
427  fp = fopen (buf, "w");
428
429  if (fp == NULL)
430    pfatal_with_name (buf);
431
432  switch (graph_dump_format)
433    {
434    case vcg:
435      fputs ("graph: {\nport_sharing: no\n", fp);
436      break;
437    case no_graph:
438      abort ();
439    }
440
441  fclose (fp);
442}
443
444
445/* Do final work on the graph output file.  */
446void
447finish_graph_dump_file (base, suffix)
448     const char *base;
449     const char *suffix;
450{
451  size_t namelen = strlen (base);
452  size_t suffixlen = strlen (suffix);
453  size_t extlen = strlen (graph_ext[graph_dump_format]) + 1;
454  char *buf = (char *) alloca (namelen + suffixlen + extlen);
455  FILE *fp;
456
457  memcpy (buf, base, namelen);
458  memcpy (buf + namelen, suffix, suffixlen);
459  memcpy (buf + namelen + suffixlen, graph_ext[graph_dump_format], extlen);
460
461  fp = fopen (buf, "a");
462  if (fp != NULL)
463    {
464      switch (graph_dump_format)
465	{
466	case vcg:
467	  fputs ("}\n", fp);
468	  break;
469	case no_graph:
470	  abort ();
471	}
472
473      fclose (fp);
474    }
475}
476