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 6 This file is part of GCC. 7 8 GCC is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free 10 Software Foundation; either version 3, or (at your option) any later 11 version. 12 13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY 14 WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 for more details. 17 18 You should have received a copy of the GNU General Public License 19 along 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 36 static const char *const graph_ext[] = 37 { 38 /* no_graph */ "", 39 /* vcg */ ".vcg", 40 }; 41 42 static void start_fct (FILE *); 43 static void start_bb (FILE *, int); 44 static void node_data (FILE *, rtx); 45 static void draw_edge (FILE *, int, int, int, int); 46 static void end_fct (FILE *); 47 static void end_bb (FILE *); 48 49 /* Output text for new basic block. */ 50 static void 51 start_fct (FILE *fp) 52 { 53 switch (graph_dump_format) 54 { 55 case vcg: 56 fprintf (fp, "\ 57 graph: { 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 65 static void 66 start_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, "\ 76 graph: {\ntitle: \"%s.BB%d\"\nfolding: 1\ncolor: lightblue\n\ 77 label: \"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 107 static void 108 node_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, "\ 118 edge: { 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 \ 131 label: \"%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) ? "\ 138 darkgrey\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 167 static void 168 draw_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 194 static void 195 end_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 207 static void 208 end_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. */ 223 void 224 print_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 387 void 388 clean_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. */ 411 void 412 finish_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