1 | /* Instruction scheduling pass. Log dumping infrastructure. |
2 | Copyright (C) 2006-2023 Free Software Foundation, Inc. |
3 | |
4 | This file is part of GCC. |
5 | |
6 | GCC is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free |
8 | Software Foundation; either version 3, or (at your option) any later |
9 | version. |
10 | |
11 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | for more details. |
15 | |
16 | You should have received a copy of the GNU General Public License |
17 | along with GCC; see the file COPYING3. If not see |
18 | <http://www.gnu.org/licenses/>. */ |
19 | |
20 | #include "config.h" |
21 | #include "system.h" |
22 | #include "coretypes.h" |
23 | #include "backend.h" |
24 | #include "rtl.h" |
25 | #include "df.h" |
26 | #include "insn-attr.h" |
27 | #include "cselib.h" |
28 | |
29 | #ifdef INSN_SCHEDULING |
30 | #include "regset.h" |
31 | #include "sched-int.h" |
32 | #include "cfgloop.h" |
33 | #include "sel-sched-ir.h" |
34 | #include "sel-sched-dump.h" |
35 | #include "print-rtl.h" |
36 | |
37 | |
38 | /* These variables control high-level pretty printing. */ |
39 | static int sel_dump_cfg_flags = SEL_DUMP_CFG_FLAGS; |
40 | static int sel_debug_cfg_flags = SEL_DUMP_CFG_FLAGS; |
41 | |
42 | /* True when a cfg should be dumped. */ |
43 | static bool sel_dump_cfg_p; |
44 | |
45 | /* Variables that are used to build the cfg dump file name. */ |
46 | static const char * const sel_debug_cfg_root = "./" ; |
47 | static const char * const sel_debug_cfg_root_postfix_default = "" ; |
48 | static const char *sel_debug_cfg_root_postfix = "" ; |
49 | static int sel_dump_cfg_fileno = -1; |
50 | static int sel_debug_cfg_fileno = -1; |
51 | |
52 | /* When this flag is on, we are dumping to the .dot file. |
53 | When it is off, we are dumping to log. |
54 | This is useful to differentiate formatting between log and .dot |
55 | files. */ |
56 | bool sched_dump_to_dot_p = false; |
57 | |
58 | /* Controls how insns from a fence list should be dumped. */ |
59 | static int dump_flist_insn_flags = (DUMP_INSN_UID | DUMP_INSN_BBN |
60 | | DUMP_INSN_SEQNO); |
61 | |
62 | |
63 | /* The variable used to hold the value of sched_dump when temporarily |
64 | switching dump output to the other source, e.g. the .dot file. */ |
65 | static FILE *saved_sched_dump = NULL; |
66 | |
67 | /* Switch sched_dump to TO. It must not be called twice. */ |
68 | static void |
69 | switch_dump (FILE *to) |
70 | { |
71 | gcc_assert (saved_sched_dump == NULL); |
72 | |
73 | saved_sched_dump = sched_dump; |
74 | sched_dump = to; |
75 | } |
76 | |
77 | /* Restore previously switched dump. */ |
78 | static void |
79 | restore_dump (void) |
80 | { |
81 | sched_dump = saved_sched_dump; |
82 | saved_sched_dump = NULL; |
83 | } |
84 | |
85 | |
86 | /* Functions for dumping instructions, av sets, and exprs. */ |
87 | |
88 | /* Default flags for dumping insns. */ |
89 | static int dump_insn_rtx_flags = DUMP_INSN_RTX_UID | DUMP_INSN_RTX_PATTERN; |
90 | |
91 | /* Default flags for dumping vinsns. */ |
92 | static int dump_vinsn_flags = (DUMP_VINSN_INSN_RTX | DUMP_VINSN_TYPE |
93 | | DUMP_VINSN_COUNT); |
94 | |
95 | /* Default flags for dumping expressions. */ |
96 | static int dump_expr_flags = DUMP_EXPR_ALL; |
97 | |
98 | /* Default flags for dumping insns when debugging. */ |
99 | static int debug_insn_rtx_flags = DUMP_INSN_RTX_ALL; |
100 | |
101 | /* Default flags for dumping vinsns when debugging. */ |
102 | static int debug_vinsn_flags = DUMP_VINSN_ALL; |
103 | |
104 | /* Default flags for dumping expressions when debugging. */ |
105 | static int debug_expr_flags = DUMP_EXPR_ALL; |
106 | |
107 | /* Controls how an insn from stream should be dumped when debugging. */ |
108 | static int debug_insn_flags = DUMP_INSN_ALL; |
109 | |
110 | /* Print an rtx X. */ |
111 | void |
112 | sel_print_rtl (rtx x) |
113 | { |
114 | print_rtl_single (sched_dump, x); |
115 | } |
116 | |
117 | /* Dump insn INSN honoring FLAGS. */ |
118 | void |
119 | dump_insn_rtx_1 (rtx insn, int flags) |
120 | { |
121 | int all; |
122 | |
123 | /* flags == -1 also means dumping all. */ |
124 | all = (flags & 1); |
125 | if (all) |
126 | flags |= DUMP_INSN_RTX_ALL; |
127 | |
128 | sel_print (fmt: "(" ); |
129 | |
130 | if (flags & DUMP_INSN_RTX_UID) |
131 | sel_print (fmt: "%d;" , INSN_UID (insn)); |
132 | |
133 | if (flags & DUMP_INSN_RTX_PATTERN) |
134 | sel_print (fmt: "%s;" , str_pattern_slim (PATTERN (insn))); |
135 | |
136 | if (flags & DUMP_INSN_RTX_BBN) |
137 | { |
138 | basic_block bb = BLOCK_FOR_INSN (insn); |
139 | |
140 | sel_print (fmt: "bb:%d;" , bb != NULL ? bb->index : -1); |
141 | } |
142 | |
143 | sel_print (fmt: ")" ); |
144 | } |
145 | |
146 | |
147 | /* Dump INSN with default flags. */ |
148 | void |
149 | dump_insn_rtx (rtx insn) |
150 | { |
151 | dump_insn_rtx_1 (insn, flags: dump_insn_rtx_flags); |
152 | } |
153 | |
154 | |
155 | /* Dump INSN to stderr. */ |
156 | DEBUG_FUNCTION void |
157 | debug_insn_rtx (rtx insn) |
158 | { |
159 | switch_dump (stderr); |
160 | dump_insn_rtx_1 (insn, flags: debug_insn_rtx_flags); |
161 | sel_print (fmt: "\n" ); |
162 | restore_dump (); |
163 | } |
164 | |
165 | /* Dump vinsn VI honoring flags. */ |
166 | void |
167 | dump_vinsn_1 (vinsn_t vi, int flags) |
168 | { |
169 | int all; |
170 | |
171 | /* flags == -1 also means dumping all. */ |
172 | all = flags & 1; |
173 | if (all) |
174 | flags |= DUMP_VINSN_ALL; |
175 | |
176 | sel_print (fmt: "(" ); |
177 | |
178 | if (flags & DUMP_VINSN_INSN_RTX) |
179 | dump_insn_rtx_1 (VINSN_INSN_RTX (vi), flags: dump_insn_rtx_flags | all); |
180 | |
181 | if (flags & DUMP_VINSN_TYPE) |
182 | sel_print (fmt: "type:%s;" , GET_RTX_NAME (VINSN_TYPE (vi))); |
183 | |
184 | if (flags & DUMP_VINSN_COUNT) |
185 | sel_print (fmt: "count:%d;" , VINSN_COUNT (vi)); |
186 | |
187 | if (flags & DUMP_VINSN_COST) |
188 | { |
189 | int cost = vi->cost; |
190 | |
191 | if (cost != -1) |
192 | sel_print (fmt: "cost:%d;" , cost); |
193 | } |
194 | |
195 | sel_print (fmt: ")" ); |
196 | } |
197 | |
198 | /* Dump vinsn VI with default flags. */ |
199 | void |
200 | dump_vinsn (vinsn_t vi) |
201 | { |
202 | dump_vinsn_1 (vi, flags: dump_vinsn_flags); |
203 | } |
204 | |
205 | DEBUG_FUNCTION void |
206 | debug (vinsn_def &ref) |
207 | { |
208 | switch_dump (stderr); |
209 | dump_vinsn_1 (vi: &ref, flags: dump_vinsn_flags); |
210 | sel_print (fmt: "\n" ); |
211 | restore_dump (); |
212 | } |
213 | |
214 | DEBUG_FUNCTION void |
215 | debug (vinsn_def *ptr) |
216 | { |
217 | if (ptr) |
218 | debug (ref&: *ptr); |
219 | else |
220 | fprintf (stderr, format: "<nil>\n" ); |
221 | } |
222 | |
223 | DEBUG_FUNCTION void |
224 | debug_verbose (vinsn_def &ref) |
225 | { |
226 | switch_dump (stderr); |
227 | dump_vinsn_1 (vi: &ref, flags: debug_vinsn_flags); |
228 | sel_print (fmt: "\n" ); |
229 | restore_dump (); |
230 | } |
231 | |
232 | DEBUG_FUNCTION void |
233 | debug_verbose (vinsn_def *ptr) |
234 | { |
235 | if (ptr) |
236 | debug (ref&: *ptr); |
237 | else |
238 | fprintf (stderr, format: "<nil>\n" ); |
239 | } |
240 | |
241 | /* Dump vinsn VI to stderr. */ |
242 | DEBUG_FUNCTION void |
243 | debug_vinsn (vinsn_t vi) |
244 | { |
245 | switch_dump (stderr); |
246 | dump_vinsn_1 (vi, flags: debug_vinsn_flags); |
247 | sel_print (fmt: "\n" ); |
248 | restore_dump (); |
249 | } |
250 | |
251 | /* Dump EXPR honoring flags. */ |
252 | void |
253 | dump_expr_1 (expr_t expr, int flags) |
254 | { |
255 | int all; |
256 | |
257 | /* flags == -1 also means dumping all. */ |
258 | all = flags & 1; |
259 | if (all) |
260 | flags |= DUMP_EXPR_ALL; |
261 | |
262 | sel_print (fmt: "[" ); |
263 | |
264 | if (flags & DUMP_EXPR_VINSN) |
265 | dump_vinsn_1 (EXPR_VINSN (expr), flags: dump_vinsn_flags | all); |
266 | |
267 | if (flags & DUMP_EXPR_SPEC) |
268 | { |
269 | int spec = EXPR_SPEC (expr); |
270 | |
271 | if (spec != 0) |
272 | sel_print (fmt: "spec:%d;" , spec); |
273 | } |
274 | |
275 | if (flags & DUMP_EXPR_USEFULNESS) |
276 | { |
277 | int use = EXPR_USEFULNESS (expr); |
278 | |
279 | if (use != REG_BR_PROB_BASE) |
280 | sel_print (fmt: "use:%d;" , use); |
281 | } |
282 | |
283 | if (flags & DUMP_EXPR_PRIORITY) |
284 | sel_print (fmt: "prio:%d;" , EXPR_PRIORITY (expr)); |
285 | |
286 | if (flags & DUMP_EXPR_SCHED_TIMES) |
287 | { |
288 | int times = EXPR_SCHED_TIMES (expr); |
289 | |
290 | if (times != 0) |
291 | sel_print (fmt: "times:%d;" , times); |
292 | } |
293 | |
294 | if (flags & DUMP_EXPR_SPEC_DONE_DS) |
295 | { |
296 | ds_t spec_done_ds = EXPR_SPEC_DONE_DS (expr); |
297 | |
298 | if (spec_done_ds != 0) |
299 | sel_print (fmt: "ds:%d;" , spec_done_ds); |
300 | } |
301 | |
302 | if (flags & DUMP_EXPR_ORIG_BB) |
303 | { |
304 | int orig_bb = EXPR_ORIG_BB_INDEX (expr); |
305 | |
306 | if (orig_bb != 0) |
307 | sel_print (fmt: "orig_bb:%d;" , orig_bb); |
308 | } |
309 | |
310 | if (EXPR_TARGET_AVAILABLE (expr) < 1) |
311 | sel_print (fmt: "target:%d;" , EXPR_TARGET_AVAILABLE (expr)); |
312 | sel_print (fmt: "]" ); |
313 | } |
314 | |
315 | /* Dump expression EXPR with default flags. */ |
316 | void |
317 | dump_expr (expr_t expr) |
318 | { |
319 | dump_expr_1 (expr, flags: dump_expr_flags); |
320 | } |
321 | |
322 | /* Dump expression EXPR to stderr. */ |
323 | DEBUG_FUNCTION void |
324 | debug_expr (expr_t expr) |
325 | { |
326 | switch_dump (stderr); |
327 | dump_expr_1 (expr, flags: debug_expr_flags); |
328 | sel_print (fmt: "\n" ); |
329 | restore_dump (); |
330 | } |
331 | |
332 | /* Dump expression REF. */ |
333 | |
334 | DEBUG_FUNCTION void |
335 | debug (expr_def &ref) |
336 | { |
337 | switch_dump (stderr); |
338 | dump_expr_1 (expr: &ref, flags: 0); |
339 | sel_print (fmt: "\n" ); |
340 | restore_dump (); |
341 | } |
342 | |
343 | DEBUG_FUNCTION void |
344 | debug (expr_def *ptr) |
345 | { |
346 | if (ptr) |
347 | debug (ref&: *ptr); |
348 | else |
349 | fprintf (stderr, format: "<nil>\n" ); |
350 | } |
351 | |
352 | /* Dump expression REF verbosely. */ |
353 | |
354 | DEBUG_FUNCTION void |
355 | debug_verbose (expr_def &ref) |
356 | { |
357 | switch_dump (stderr); |
358 | dump_expr_1 (expr: &ref, flags: DUMP_EXPR_ALL); |
359 | sel_print (fmt: "\n" ); |
360 | restore_dump (); |
361 | } |
362 | |
363 | DEBUG_FUNCTION void |
364 | debug_verbose (expr_def *ptr) |
365 | { |
366 | if (ptr) |
367 | debug_verbose (ref&: *ptr); |
368 | else |
369 | fprintf (stderr, format: "<nil>\n" ); |
370 | } |
371 | |
372 | /* Dump insn I honoring FLAGS. */ |
373 | void |
374 | dump_insn_1 (insn_t i, int flags) |
375 | { |
376 | int all; |
377 | |
378 | all = flags & 1; |
379 | if (all) |
380 | flags |= DUMP_INSN_ALL; |
381 | |
382 | if (!sched_dump_to_dot_p) |
383 | sel_print (fmt: "(" ); |
384 | |
385 | if (flags & DUMP_INSN_EXPR) |
386 | { |
387 | dump_expr_1 (INSN_EXPR (i), flags: dump_expr_flags | all); |
388 | sel_print (fmt: ";" ); |
389 | } |
390 | else if (flags & DUMP_INSN_PATTERN) |
391 | { |
392 | dump_insn_rtx_1 (insn: i, flags: DUMP_INSN_RTX_PATTERN | all); |
393 | sel_print (fmt: ";" ); |
394 | } |
395 | else if (flags & DUMP_INSN_UID) |
396 | sel_print (fmt: "uid:%d;" , INSN_UID (insn: i)); |
397 | |
398 | if (flags & DUMP_INSN_SEQNO) |
399 | sel_print (fmt: "seqno:%d;" , INSN_SEQNO (i)); |
400 | |
401 | if (flags & DUMP_INSN_SCHED_CYCLE) |
402 | { |
403 | int cycle = INSN_SCHED_CYCLE (i); |
404 | |
405 | if (cycle != 0) |
406 | sel_print (fmt: "cycle:%d;" , cycle); |
407 | } |
408 | |
409 | if (!sched_dump_to_dot_p) |
410 | sel_print (fmt: ")" ); |
411 | } |
412 | |
413 | /* Dump insn I with default flags. */ |
414 | void |
415 | dump_insn (insn_t i) |
416 | { |
417 | dump_insn_1 (i, flags: DUMP_INSN_EXPR | DUMP_INSN_SCHED_CYCLE); |
418 | } |
419 | |
420 | /* Dump INSN to stderr. */ |
421 | DEBUG_FUNCTION void |
422 | debug_insn (insn_t insn) |
423 | { |
424 | switch_dump (stderr); |
425 | dump_insn_1 (i: insn, flags: debug_insn_flags); |
426 | sel_print (fmt: "\n" ); |
427 | restore_dump (); |
428 | } |
429 | |
430 | /* Dumps av_set AV. */ |
431 | void |
432 | dump_av_set (av_set_t av) |
433 | { |
434 | av_set_iterator i; |
435 | expr_t expr; |
436 | |
437 | if (!sched_dump_to_dot_p) |
438 | sel_print (fmt: "{" ); |
439 | |
440 | FOR_EACH_EXPR (expr, i, av) |
441 | { |
442 | dump_expr (expr); |
443 | if (!sched_dump_to_dot_p) |
444 | sel_print (fmt: " " ); |
445 | else |
446 | sel_print (fmt: "\n" ); |
447 | } |
448 | |
449 | if (!sched_dump_to_dot_p) |
450 | sel_print (fmt: "}" ); |
451 | } |
452 | |
453 | /* Dumps lvset LV. */ |
454 | void |
455 | dump_lv_set (regset lv) |
456 | { |
457 | sel_print (fmt: "{" ); |
458 | |
459 | /* This code was adapted from cfg.cc: dump_regset (). */ |
460 | if (lv == NULL) |
461 | sel_print (fmt: "nil" ); |
462 | else |
463 | { |
464 | unsigned i; |
465 | reg_set_iterator rsi; |
466 | int count = 0; |
467 | |
468 | EXECUTE_IF_SET_IN_REG_SET (lv, 0, i, rsi) |
469 | { |
470 | sel_print (fmt: " %d" , i); |
471 | if (i < FIRST_PSEUDO_REGISTER) |
472 | { |
473 | sel_print (fmt: " [%s]" , reg_names[i]); |
474 | ++count; |
475 | } |
476 | |
477 | ++count; |
478 | |
479 | if (sched_dump_to_dot_p && count == 12) |
480 | { |
481 | count = 0; |
482 | sel_print (fmt: "\n" ); |
483 | } |
484 | } |
485 | } |
486 | |
487 | sel_print (fmt: "}\n" ); |
488 | } |
489 | |
490 | /* Dumps a list of instructions pointed to by P. */ |
491 | static void |
492 | dump_ilist (ilist_t p) |
493 | { |
494 | while (p) |
495 | { |
496 | dump_insn (ILIST_INSN (p)); |
497 | p = ILIST_NEXT (p); |
498 | } |
499 | } |
500 | |
501 | /* Dumps a list of boundaries pointed to by BNDS. */ |
502 | void |
503 | dump_blist (blist_t bnds) |
504 | { |
505 | for (; bnds; bnds = BLIST_NEXT (bnds)) |
506 | { |
507 | bnd_t bnd = BLIST_BND (bnds); |
508 | |
509 | sel_print (fmt: "[to: %d; ptr: " , INSN_UID (BND_TO (bnd))); |
510 | dump_ilist (BND_PTR (bnd)); |
511 | sel_print (fmt: "] " ); |
512 | } |
513 | } |
514 | |
515 | /* Dumps a list of fences pointed to by L. */ |
516 | void |
517 | dump_flist (flist_t l) |
518 | { |
519 | while (l) |
520 | { |
521 | dump_insn_1 (FENCE_INSN (FLIST_FENCE (l)), flags: dump_flist_insn_flags); |
522 | sel_print (fmt: " " ); |
523 | l = FLIST_NEXT (l); |
524 | } |
525 | } |
526 | |
527 | /* Dumps an insn vector SUCCS. */ |
528 | void |
529 | dump_insn_vector (rtx_vec_t succs) |
530 | { |
531 | for (rtx_insn *succ : succs) |
532 | if (succ) |
533 | dump_insn (i: succ); |
534 | else |
535 | sel_print (fmt: "NULL " ); |
536 | } |
537 | |
538 | /* Dumps a hard reg set SET to FILE using PREFIX. */ |
539 | static void |
540 | print_hard_reg_set (FILE *file, const char *prefix, HARD_REG_SET set) |
541 | { |
542 | int i; |
543 | |
544 | fprintf (stream: file, format: "%s{ " , prefix); |
545 | for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
546 | { |
547 | if (TEST_HARD_REG_BIT (set, bit: i)) |
548 | fprintf (stream: file, format: "%d " , i); |
549 | } |
550 | fprintf (stream: file, format: "}\n" ); |
551 | } |
552 | |
553 | /* Dumps a hard reg set SET using PREFIX. */ |
554 | void |
555 | dump_hard_reg_set (const char *prefix, HARD_REG_SET set) |
556 | { |
557 | print_hard_reg_set (file: sched_dump, prefix, set); |
558 | } |
559 | |
560 | /* Pretty print INSN. This is used as a hook. */ |
561 | const char * |
562 | sel_print_insn (const rtx_insn *insn, int aligned ATTRIBUTE_UNUSED) |
563 | { |
564 | static char buf[80]; |
565 | |
566 | /* '+' before insn means it is a new cycle start and it's not been |
567 | scheduled yet. '>' - has been scheduled. */ |
568 | if (s_i_d.exists () && INSN_LUID (insn) > 0) |
569 | if (GET_MODE (insn) == TImode) |
570 | sprintf (s: buf, format: "%s %4d" , |
571 | INSN_SCHED_TIMES (insn) > 0 ? "> " : "< " , |
572 | INSN_UID (insn)); |
573 | else |
574 | sprintf (s: buf, format: "%s %4d" , |
575 | INSN_SCHED_TIMES (insn) > 0 ? "! " : " " , |
576 | INSN_UID (insn)); |
577 | else |
578 | if (GET_MODE (insn) == TImode) |
579 | sprintf (s: buf, format: "+ %4d" , INSN_UID (insn)); |
580 | else |
581 | sprintf (s: buf, format: " %4d" , INSN_UID (insn)); |
582 | |
583 | return buf; |
584 | } |
585 | |
586 | |
587 | /* Functions for pretty printing of CFG. */ |
588 | /* FIXME: Using pretty-print here could simplify this stuff. */ |
589 | |
590 | /* Replace all occurencies of STR1 to STR2 in BUF. |
591 | The BUF must be large enough to hold the result. */ |
592 | static void |
593 | replace_str_in_buf (char *buf, const char *str1, const char *str2) |
594 | { |
595 | int buf_len = strlen (s: buf); |
596 | int str1_len = strlen (s: str1); |
597 | int str2_len = strlen (s: str2); |
598 | int diff = str2_len - str1_len; |
599 | |
600 | char *p = buf; |
601 | do |
602 | { |
603 | p = strstr (haystack: p, needle: str1); |
604 | if (p) |
605 | { |
606 | char *p1 = p + str1_len; |
607 | /* Copy the rest of buf and '\0'. */ |
608 | int n = buf + buf_len - p1; |
609 | int i; |
610 | |
611 | /* Shift str by DIFF chars. */ |
612 | if (diff > 0) |
613 | for (i = n; i >= 0; i--) |
614 | p1[i + diff] = p1[i]; |
615 | else |
616 | for (i = 0; i <= n; i++) |
617 | p1[i + diff] = p1[i]; |
618 | |
619 | /* Copy str2. */ |
620 | for (i = 0; i < str2_len; i++) |
621 | p[i] = str2[i]; |
622 | |
623 | p += str2_len; |
624 | buf_len += diff; |
625 | } |
626 | |
627 | } |
628 | while (p); |
629 | } |
630 | |
631 | /* Replace characters in BUF that have special meaning in .dot file. |
632 | Similar to pp_write_text_as_dot_label_to_stream. */ |
633 | static void |
634 | sel_prepare_string_for_dot_label (char *buf) |
635 | { |
636 | static char specials_from[7][2] = { "<" , ">" , "{" , "|" , "}" , "\"" , |
637 | "\n" }; |
638 | static char specials_to[7][3] = { "\\<" , "\\>" , "\\{" , "\\|" , "\\}" , |
639 | "\\\"" , "\\l" }; |
640 | unsigned i; |
641 | |
642 | for (i = 0; i < 7; i++) |
643 | replace_str_in_buf (buf, str1: specials_from[i], str2: specials_to[i]); |
644 | } |
645 | |
646 | /* This function acts like printf but dumps to the sched_dump file. */ |
647 | void |
648 | sel_print (const char *fmt, ...) |
649 | { |
650 | va_list ap; |
651 | va_start (ap, fmt); |
652 | if (sched_dump_to_dot_p) |
653 | { |
654 | char *message; |
655 | if (vasprintf (ptr: &message, f: fmt, arg: ap) >= 0 && message != NULL) |
656 | { |
657 | message = (char *) xrealloc (message, 2 * strlen (s: message) + 1); |
658 | sel_prepare_string_for_dot_label (buf: message); |
659 | fprintf (stream: sched_dump, format: "%s" , message); |
660 | free (ptr: message); |
661 | } |
662 | } |
663 | else |
664 | vfprintf (s: sched_dump, format: fmt, arg: ap); |
665 | va_end (ap); |
666 | } |
667 | |
668 | /* Dump INSN with FLAGS. */ |
669 | static void |
670 | sel_dump_cfg_insn (insn_t insn, int flags) |
671 | { |
672 | int insn_flags = DUMP_INSN_UID | DUMP_INSN_PATTERN; |
673 | |
674 | if (sched_luids.exists () && INSN_LUID (insn) > 0) |
675 | { |
676 | if (flags & SEL_DUMP_CFG_INSN_SEQNO) |
677 | insn_flags |= DUMP_INSN_SEQNO | DUMP_INSN_SCHED_CYCLE | DUMP_INSN_EXPR; |
678 | } |
679 | |
680 | dump_insn_1 (i: insn, flags: insn_flags); |
681 | } |
682 | |
683 | /* Dump E to the dot file F. */ |
684 | static void |
685 | sel_dump_cfg_edge (FILE *f, edge e) |
686 | { |
687 | int w; |
688 | const char *color; |
689 | |
690 | if (e->flags & EDGE_FALLTHRU) |
691 | { |
692 | w = 10; |
693 | color = ", color = red" ; |
694 | } |
695 | else if (e->src->next_bb == e->dest) |
696 | { |
697 | w = 3; |
698 | color = ", color = blue" ; |
699 | } |
700 | else |
701 | { |
702 | w = 1; |
703 | color = "" ; |
704 | } |
705 | |
706 | fprintf (stream: f, format: "\tbb%d -> bb%d [weight = %d%s];\n" , |
707 | e->src->index, e->dest->index, w, color); |
708 | } |
709 | |
710 | |
711 | /* Return true if BB has a predesessor from current region. |
712 | TODO: Either make this function to trace back through empty block |
713 | or just remove those empty blocks. */ |
714 | static bool |
715 | has_preds_in_current_region_p (basic_block bb) |
716 | { |
717 | edge e; |
718 | edge_iterator ei; |
719 | |
720 | gcc_assert (!in_current_region_p (bb)); |
721 | |
722 | FOR_EACH_EDGE (e, ei, bb->preds) |
723 | if (in_current_region_p (e->src)) |
724 | return true; |
725 | |
726 | return false; |
727 | } |
728 | |
729 | /* Dump a cfg region to the dot file F honoring FLAGS. */ |
730 | static void |
731 | sel_dump_cfg_2 (FILE *f, int flags) |
732 | { |
733 | basic_block bb; |
734 | |
735 | sched_dump_to_dot_p = true; |
736 | switch_dump (to: f); |
737 | |
738 | fprintf (stream: f, format: "digraph G {\n" |
739 | "\tratio = 2.25;\n" |
740 | "\tnode [shape = record, fontsize = 9];\n" ); |
741 | |
742 | if (flags & SEL_DUMP_CFG_FUNCTION_NAME) |
743 | fprintf (stream: f, format: "function [label = \"%s\"];\n" , current_function_name ()); |
744 | |
745 | FOR_EACH_BB_FN (bb, cfun) |
746 | { |
747 | insn_t insn = BB_HEAD (bb); |
748 | insn_t next_tail = NEXT_INSN (BB_END (bb)); |
749 | edge e; |
750 | edge_iterator ei; |
751 | bool in_region_p = ((flags & SEL_DUMP_CFG_CURRENT_REGION) |
752 | && in_current_region_p (bb)); |
753 | bool full_p = (!(flags & SEL_DUMP_CFG_CURRENT_REGION) |
754 | || in_region_p); |
755 | bool some_p = full_p || has_preds_in_current_region_p (bb); |
756 | const char *color; |
757 | const char *style; |
758 | |
759 | if (!some_p) |
760 | continue; |
761 | |
762 | if ((flags & SEL_DUMP_CFG_CURRENT_REGION) |
763 | && in_current_region_p (bb) |
764 | && BLOCK_TO_BB (bb->index) == 0) |
765 | color = "color = green, " ; |
766 | else |
767 | color = "" ; |
768 | |
769 | if ((flags & SEL_DUMP_CFG_FENCES) |
770 | && in_region_p) |
771 | { |
772 | style = "" ; |
773 | |
774 | if (!sel_bb_empty_p (bb)) |
775 | { |
776 | bool first_p = true; |
777 | insn_t tail = BB_END (bb); |
778 | insn_t cur_insn; |
779 | |
780 | cur_insn = bb_note (bb); |
781 | |
782 | do |
783 | { |
784 | fence_t fence; |
785 | |
786 | cur_insn = NEXT_INSN (insn: cur_insn); |
787 | fence = flist_lookup (fences, cur_insn); |
788 | |
789 | if (fence != NULL) |
790 | { |
791 | if (!FENCE_SCHEDULED_P (fence)) |
792 | { |
793 | if (first_p) |
794 | color = "color = red, " ; |
795 | else |
796 | color = "color = yellow, " ; |
797 | } |
798 | else |
799 | color = "color = blue, " ; |
800 | } |
801 | |
802 | first_p = false; |
803 | } |
804 | while (cur_insn != tail); |
805 | } |
806 | } |
807 | else if (!full_p) |
808 | style = "style = dashed, " ; |
809 | else |
810 | style = "" ; |
811 | |
812 | fprintf (stream: f, format: "\tbb%d [%s%slabel = \"{Basic block %d" , bb->index, |
813 | style, color, bb->index); |
814 | |
815 | if ((flags & SEL_DUMP_CFG_BB_LOOP) |
816 | && bb->loop_father != NULL) |
817 | fprintf (stream: f, format: ", loop %d" , bb->loop_father->num); |
818 | |
819 | if (full_p |
820 | && (flags & SEL_DUMP_CFG_BB_NOTES_LIST)) |
821 | { |
822 | insn_t notes = BB_NOTE_LIST (bb); |
823 | |
824 | if (notes != NULL_RTX) |
825 | { |
826 | fprintf (stream: f, format: "|" ); |
827 | |
828 | /* For simplicity, we dump notes from note_list in reversed order |
829 | to that what they will appear in the code. */ |
830 | while (notes != NULL_RTX) |
831 | { |
832 | sel_dump_cfg_insn (insn: notes, flags); |
833 | fprintf (stream: f, format: "\\l" ); |
834 | |
835 | notes = PREV_INSN (insn: notes); |
836 | } |
837 | } |
838 | } |
839 | |
840 | if (full_p |
841 | && (flags & SEL_DUMP_CFG_AV_SET) |
842 | && in_current_region_p (bb) |
843 | && !sel_bb_empty_p (bb)) |
844 | { |
845 | fprintf (stream: f, format: "|" ); |
846 | |
847 | if (BB_AV_SET_VALID_P (bb)) |
848 | dump_av_set (BB_AV_SET (bb)); |
849 | else if (BB_AV_LEVEL (bb) == -1) |
850 | fprintf (stream: f, format: "AV_SET needs update" ); |
851 | } |
852 | |
853 | if ((flags & SEL_DUMP_CFG_LV_SET) |
854 | && !sel_bb_empty_p (bb)) |
855 | { |
856 | fprintf (stream: f, format: "|" ); |
857 | |
858 | if (BB_LV_SET_VALID_P (bb)) |
859 | dump_lv_set (BB_LV_SET (bb)); |
860 | else |
861 | fprintf (stream: f, format: "LV_SET needs update" ); |
862 | } |
863 | |
864 | if (full_p |
865 | && (flags & SEL_DUMP_CFG_BB_INSNS)) |
866 | { |
867 | fprintf (stream: f, format: "|" ); |
868 | while (insn != next_tail) |
869 | { |
870 | sel_dump_cfg_insn (insn, flags); |
871 | fprintf (stream: f, format: "\\l" ); |
872 | |
873 | insn = NEXT_INSN (insn); |
874 | } |
875 | } |
876 | |
877 | fprintf (stream: f, format: "}\"];\n" ); |
878 | |
879 | FOR_EACH_EDGE (e, ei, bb->succs) |
880 | if (full_p || in_current_region_p (e->dest)) |
881 | sel_dump_cfg_edge (f, e); |
882 | } |
883 | |
884 | fprintf (stream: f, format: "}" ); |
885 | |
886 | restore_dump (); |
887 | sched_dump_to_dot_p = false; |
888 | } |
889 | |
890 | /* Dump a cfg region to the file specified by TAG honoring flags. |
891 | The file is created by the function. */ |
892 | static void |
893 | sel_dump_cfg_1 (const char *tag, int flags) |
894 | { |
895 | char *buf; |
896 | int i; |
897 | FILE *f; |
898 | |
899 | ++sel_dump_cfg_fileno; |
900 | |
901 | if (!sel_dump_cfg_p) |
902 | return; |
903 | |
904 | i = 1 + snprintf (NULL, maxlen: 0, format: "%s/%s%05d-%s.dot" , sel_debug_cfg_root, |
905 | sel_debug_cfg_root_postfix, sel_dump_cfg_fileno, tag); |
906 | buf = XNEWVEC (char, i); |
907 | snprintf (s: buf, maxlen: i, format: "%s/%s%05d-%s.dot" , sel_debug_cfg_root, |
908 | sel_debug_cfg_root_postfix, sel_dump_cfg_fileno, tag); |
909 | |
910 | f = fopen (filename: buf, modes: "w" ); |
911 | |
912 | if (f == NULL) |
913 | fprintf (stderr, format: "Can't create file: %s.\n" , buf); |
914 | else |
915 | { |
916 | sel_dump_cfg_2 (f, flags); |
917 | |
918 | fclose (stream: f); |
919 | } |
920 | |
921 | free (ptr: buf); |
922 | } |
923 | |
924 | /* Setup cfg dumping flags. Used for debugging. */ |
925 | void |
926 | setup_dump_cfg_params (void) |
927 | { |
928 | sel_dump_cfg_flags = SEL_DUMP_CFG_FLAGS; |
929 | sel_dump_cfg_p = 0; |
930 | sel_debug_cfg_root_postfix = sel_debug_cfg_root_postfix_default; |
931 | } |
932 | |
933 | /* Debug a cfg region with FLAGS. */ |
934 | void |
935 | sel_debug_cfg_1 (int flags) |
936 | { |
937 | bool t1 = sel_dump_cfg_p; |
938 | int t2 = sel_dump_cfg_fileno; |
939 | |
940 | sel_dump_cfg_p = true; |
941 | sel_dump_cfg_fileno = ++sel_debug_cfg_fileno; |
942 | |
943 | sel_dump_cfg_1 (tag: "sel-debug-cfg" , flags); |
944 | |
945 | sel_dump_cfg_fileno = t2; |
946 | sel_dump_cfg_p = t1; |
947 | } |
948 | |
949 | /* Dumps av_set AV to stderr. */ |
950 | DEBUG_FUNCTION void |
951 | debug_av_set (av_set_t av) |
952 | { |
953 | switch_dump (stderr); |
954 | dump_av_set (av); |
955 | sel_print (fmt: "\n" ); |
956 | restore_dump (); |
957 | } |
958 | |
959 | /* Dump LV to stderr. */ |
960 | DEBUG_FUNCTION void |
961 | debug_lv_set (regset lv) |
962 | { |
963 | switch_dump (stderr); |
964 | dump_lv_set (lv); |
965 | sel_print (fmt: "\n" ); |
966 | restore_dump (); |
967 | } |
968 | |
969 | /* Dump an instruction list P to stderr. */ |
970 | DEBUG_FUNCTION void |
971 | debug_ilist (ilist_t p) |
972 | { |
973 | switch_dump (stderr); |
974 | dump_ilist (p); |
975 | sel_print (fmt: "\n" ); |
976 | restore_dump (); |
977 | } |
978 | |
979 | /* Dump a boundary list BNDS to stderr. */ |
980 | DEBUG_FUNCTION void |
981 | debug_blist (blist_t bnds) |
982 | { |
983 | switch_dump (stderr); |
984 | dump_blist (bnds); |
985 | sel_print (fmt: "\n" ); |
986 | restore_dump (); |
987 | } |
988 | |
989 | /* Debug a cfg region with default flags. */ |
990 | void |
991 | sel_debug_cfg (void) |
992 | { |
993 | sel_debug_cfg_1 (flags: sel_debug_cfg_flags); |
994 | } |
995 | |
996 | /* Print a current cselib value for X's address to stderr. */ |
997 | DEBUG_FUNCTION rtx |
998 | debug_mem_addr_value (rtx x) |
999 | { |
1000 | rtx t, addr; |
1001 | machine_mode address_mode; |
1002 | |
1003 | gcc_assert (MEM_P (x)); |
1004 | address_mode = get_address_mode (mem: x); |
1005 | |
1006 | t = shallow_copy_rtx (x); |
1007 | if (cselib_lookup (XEXP (t, 0), address_mode, 0, GET_MODE (t))) |
1008 | XEXP (t, 0) = cselib_subst_to_values (XEXP (t, 0), GET_MODE (t)); |
1009 | |
1010 | t = canon_rtx (t); |
1011 | addr = get_addr (XEXP (t, 0)); |
1012 | debug_rtx (t); |
1013 | debug_rtx (addr); |
1014 | return t; |
1015 | } |
1016 | #endif |
1017 | |
1018 | |