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