graph.c 9.65 KB
Newer Older
1
/* Output routines for graphical representation.
2 3
   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004
   Free Software Foundation, Inc.
4 5
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.

6
This file is part of GCC.
7

8 9 10 11
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.
12

13 14 15 16
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.
17

18
You should have received a copy of the GNU General Public License
19 20 21
along with GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */
22 23 24

#include <config.h>
#include "system.h"
25 26
#include "coretypes.h"
#include "tm.h"
27 28 29
#include "rtl.h"
#include "flags.h"
#include "output.h"
30
#include "function.h"
31
#include "hard-reg-set.h"
32
#include "obstack.h"
33 34
#include "basic-block.h"
#include "toplev.h"
35
#include "graph.h"
36

37
static const char *const graph_ext[] =
38 39 40 41 42
{
  /* no_graph */ "",
  /* vcg */      ".vcg",
};

43 44 45 46 47 48
static void start_fct (FILE *);
static void start_bb (FILE *, int);
static void node_data (FILE *, rtx);
static void draw_edge (FILE *, int, int, int, int);
static void end_fct (FILE *);
static void end_bb (FILE *);
49

50 51
/* Output text for new basic block.  */
static void
52
start_fct (FILE *fp)
53 54 55 56 57 58
{
  switch (graph_dump_format)
    {
    case vcg:
      fprintf (fp, "\
graph: { title: \"%s\"\nfolding: 1\nhidden: 2\nnode: { title: \"%s.0\" }\n",
59
	       current_function_name (), current_function_name ());
60 61 62 63 64 65 66
      break;
    case no_graph:
      break;
    }
}

static void
67
start_bb (FILE *fp, int bb)
68
{
69 70 71 72
#if 0
  reg_set_iterator rsi;
#endif

73 74 75 76 77 78
  switch (graph_dump_format)
    {
    case vcg:
      fprintf (fp, "\
graph: {\ntitle: \"%s.BB%d\"\nfolding: 1\ncolor: lightblue\n\
label: \"basic block %d",
79
	       current_function_name (), bb, bb);
80 81 82 83 84 85
      break;
    case no_graph:
      break;
    }

#if 0
86
  /* FIXME Should this be printed?  It makes the graph significantly larger.  */
87 88 89

  /* Print the live-at-start register list.  */
  fputc ('\n', fp);
90 91 92 93 94 95
  EXECUTE_IF_SET_IN_REG_SET (basic_block_live_at_start[bb], 0, i, rsi)
    {
      fprintf (fp, " %d", i);
      if (i < FIRST_PSEUDO_REGISTER)
	fprintf (fp, " [%s]", reg_names[i]);
    }
96 97 98 99 100 101 102 103 104 105 106 107
#endif

  switch (graph_dump_format)
    {
    case vcg:
      fputs ("\"\n\n", fp);
      break;
    case no_graph:
      break;
    }
}

108
static void
109
node_data (FILE *fp, rtx tmp_rtx)
110 111 112 113 114 115 116 117 118 119
{
  if (PREV_INSN (tmp_rtx) == 0)
    {
      /* This is the first instruction.  Add an edge from the starting
	 block.  */
      switch (graph_dump_format)
	{
	case vcg:
	  fprintf (fp, "\
edge: { sourcename: \"%s.0\" targetname: \"%s.%d\" }\n",
120 121
		   current_function_name (),
		   current_function_name (), XINT (tmp_rtx, 0));
122 123 124 125 126 127 128 129 130 131 132
	  break;
	case no_graph:
	  break;
	}
    }

  switch (graph_dump_format)
    {
    case vcg:
      fprintf (fp, "node: {\n  title: \"%s.%d\"\n  color: %s\n  \
label: \"%s %d\n",
133
	       current_function_name (), XINT (tmp_rtx, 0),
134 135 136 137 138
	       NOTE_P (tmp_rtx) ? "lightgrey"
	       : NONJUMP_INSN_P (tmp_rtx) ? "green"
	       : JUMP_P (tmp_rtx) ? "darkgreen"
	       : CALL_P (tmp_rtx) ? "darkgreen"
	       : LABEL_P (tmp_rtx) ?  "\
139 140 141 142 143 144 145 146
darkgrey\n  shape: ellipse" : "white",
	       GET_RTX_NAME (GET_CODE (tmp_rtx)), XINT (tmp_rtx, 0));
      break;
    case no_graph:
      break;
    }

  /* Print the RTL.  */
147
  if (NOTE_P (tmp_rtx))
148
    {
149 150 151 152
      const char *name = "";
      if (NOTE_LINE_NUMBER (tmp_rtx) < 0)
	name =  GET_NOTE_INSN_NAME (NOTE_LINE_NUMBER (tmp_rtx));
      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 160 161 162 163 164 165 166 167 168 169

  switch (graph_dump_format)
    {
    case vcg:
      fputs ("\"\n}\n", fp);
      break;
    case no_graph:
      break;
    }
}

static void
170
draw_edge (FILE *fp, int from, int to, int bb_edge, int class)
171
{
172
  const char * color;
173 174 175
  switch (graph_dump_format)
    {
    case vcg:
176 177 178 179 180 181 182
      color = "";
      if (class == 2)
	color = "color: red ";
      else if (bb_edge)
	color = "color: blue ";
      else if (class == 3)
	color = "color: green ";
183 184
      fprintf (fp,
	       "edge: { sourcename: \"%s.%d\" targetname: \"%s.%d\" %s",
185 186
	       current_function_name (), from,
	       current_function_name (), to, color);
187 188 189 190 191 192 193 194 195 196
      if (class)
	fprintf (fp, "class: %d ", class);
      fputs ("}\n", fp);
      break;
    case no_graph:
      break;
    }
}

static void
197
end_bb (FILE *fp)
198 199 200 201 202 203 204 205 206 207 208 209
{
  switch (graph_dump_format)
    {
    case vcg:
      fputs ("}\n", fp);
      break;
    case no_graph:
      break;
    }
}

static void
210
end_fct (FILE *fp)
211 212 213 214 215
{
  switch (graph_dump_format)
    {
    case vcg:
      fprintf (fp, "node: { title: \"%s.999999\" label: \"END\" }\n}\n",
216
	       current_function_name ());
217 218 219 220 221 222 223 224 225
      break;
    case no_graph:
      break;
    }
}

/* Like print_rtl, but also print out live information for the start of each
   basic block.  */
void
226
print_rtl_graph_with_bb (const char *base, rtx rtx_first)
227
{
228
  rtx tmp_rtx;
229 230
  size_t namelen = strlen (base);
  size_t extlen = strlen (graph_ext[graph_dump_format]) + 1;
231
  char *buf = alloca (namelen + extlen);
232 233
  FILE *fp;

234 235
  if (basic_block_info == NULL)
    return;
236 237

  memcpy (buf, base, namelen);
238
  memcpy (buf + namelen, graph_ext[graph_dump_format], extlen);
239 240 241 242 243 244 245 246 247 248 249

  fp = fopen (buf, "a");
  if (fp == NULL)
    return;

  if (rtx_first == 0)
    fprintf (fp, "(nil)\n");
  else
    {
      enum bb_state { NOT_IN_BB, IN_ONE_BB, IN_MULTIPLE_BB };
      int max_uid = get_max_uid ();
250 251 252
      int *start = xmalloc (max_uid * sizeof (int));
      int *end = xmalloc (max_uid * sizeof (int));
      enum bb_state *in_bb_p = xmalloc (max_uid * sizeof (enum bb_state));
253
      basic_block bb;
254
      int i;
255

256
      for (i = 0; i < max_uid; ++i)
257
	{
258 259
	  start[i] = end[i] = -1;
	  in_bb_p[i] = NOT_IN_BB;
260 261
	}

262
      FOR_EACH_BB_REVERSE (bb)
263 264
	{
	  rtx x;
265 266 267
	  start[INSN_UID (BB_HEAD (bb))] = bb->index;
	  end[INSN_UID (BB_END (bb))] = bb->index;
	  for (x = BB_HEAD (bb); x != NULL_RTX; x = NEXT_INSN (x))
268 269 270 271
	    {
	      in_bb_p[INSN_UID (x)]
		= (in_bb_p[INSN_UID (x)] == NOT_IN_BB)
		 ? IN_ONE_BB : IN_MULTIPLE_BB;
272
	      if (x == BB_END (bb))
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
		break;
	    }
	}

      /* Tell print-rtl that we want graph output.  */
      dump_for_graph = 1;

      /* Start new function.  */
      start_fct (fp);

      for (tmp_rtx = NEXT_INSN (rtx_first); NULL != tmp_rtx;
	   tmp_rtx = NEXT_INSN (tmp_rtx))
	{
	  int edge_printed = 0;
	  rtx next_insn;

	  if (start[INSN_UID (tmp_rtx)] < 0 && end[INSN_UID (tmp_rtx)] < 0)
	    {
291
	      if (BARRIER_P (tmp_rtx))
292
		continue;
293
	      if (NOTE_P (tmp_rtx)
294 295 296 297
		  && (1 || in_bb_p[INSN_UID (tmp_rtx)] == NOT_IN_BB))
		continue;
	    }

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 305 306 307
		draw_edge (fp, 0, INSN_UID (tmp_rtx), 1, 0);
	    }

	  /* Print the data for this node.  */
308
	  node_data (fp, tmp_rtx);
309 310
	  next_insn = next_nonnote_insn (tmp_rtx);

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 322

	      /* Now specify the edges to all the successors of this
		 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 330

		      draw_edge (fp, INSN_UID (tmp_rtx),
				 INSN_UID (block_head),
331 332
				 next_insn != block_head,
				 (e->flags & EDGE_ABNORMAL ? 2 : 0));
333

334
		      if (block_head == next_insn)
335 336
			edge_printed = 1;
		    }
337
		  else
338 339
		    {
		      draw_edge (fp, INSN_UID (tmp_rtx), 999999,
340 341
				 next_insn != 0,
				 (e->flags & EDGE_ABNORMAL ? 2 : 0));
342 343 344 345 346 347 348 349 350 351 352

		      if (next_insn == 0)
			edge_printed = 1;
		    }
		}
	    }

	  if (!edge_printed)
	    {
	      /* Don't print edges to barriers.  */
	      if (next_insn == 0
353
		  || !BARRIER_P (next_insn))
354 355 356 357
		draw_edge (fp, XINT (tmp_rtx, 0),
			   next_insn ? INSN_UID (next_insn) : 999999, 0, 0);
	      else
		{
358 359
		  /* We draw the remaining edges in class 3.  We have
		     to skip over the barrier since these nodes are
360 361 362 363
		     not printed at all.  */
		  do
		    next_insn = NEXT_INSN (next_insn);
		  while (next_insn
364 365
			 && (NOTE_P (next_insn)
			     || 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 374 375
		}
	    }
	}

      dump_for_graph = 0;

      end_fct (fp);
376 377 378 379 380

      /* Clean up.  */
      free (start);
      free (end);
      free (in_bb_p);
381 382 383 384 385 386 387
    }

  fclose (fp);
}


/* Similar as clean_dump_file, but this time for graph output files.  */
388

389
void
390
clean_graph_dump_file (const char *base)
391 392 393
{
  size_t namelen = strlen (base);
  size_t extlen = strlen (graph_ext[graph_dump_format]) + 1;
394
  char *buf = alloca (namelen + extlen);
395 396 397
  FILE *fp;

  memcpy (buf, base, namelen);
398
  memcpy (buf + namelen, graph_ext[graph_dump_format], extlen);
399 400 401 402

  fp = fopen (buf, "w");

  if (fp == NULL)
403
    fatal_error ("can't open %s: %m", buf);
404

405 406
  gcc_assert (graph_dump_format == vcg);
  fputs ("graph: {\nport_sharing: no\n", fp);
407 408 409 410 411 412 413

  fclose (fp);
}


/* Do final work on the graph output file.  */
void
414
finish_graph_dump_file (const char *base)
415 416 417
{
  size_t namelen = strlen (base);
  size_t extlen = strlen (graph_ext[graph_dump_format]) + 1;
418
  char *buf = alloca (namelen + extlen);
419 420 421
  FILE *fp;

  memcpy (buf, base, namelen);
422
  memcpy (buf + namelen, graph_ext[graph_dump_format], extlen);
423 424 425 426

  fp = fopen (buf, "a");
  if (fp != NULL)
    {
427 428
      gcc_assert (graph_dump_format == vcg);
      fputs ("}\n", fp);
429 430 431
      fclose (fp);
    }
}