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