1 /* Instruction scheduling pass. Log dumping infrastructure.
2 Copyright (C) 2006-2024 Free Software Foundation, Inc.
4 This file is part of GCC.
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
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
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/>. */
22 #include "coretypes.h"
26 #include "insn-attr.h"
29 #ifdef INSN_SCHEDULING
31 #include "sched-int.h"
33 #include "sel-sched-ir.h"
34 #include "sel-sched-dump.h"
35 #include "print-rtl.h"
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
;
42 /* True when a cfg should be dumped. */
43 static bool sel_dump_cfg_p
;
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;
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
56 bool sched_dump_to_dot_p
= false;
58 /* Controls how insns from a fence list should be dumped. */
59 static int dump_flist_insn_flags
= (DUMP_INSN_UID
| DUMP_INSN_BBN
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
;
67 /* Switch sched_dump to TO. It must not be called twice. */
69 switch_dump (FILE *to
)
71 gcc_assert (saved_sched_dump
== NULL
);
73 saved_sched_dump
= sched_dump
;
77 /* Restore previously switched dump. */
81 sched_dump
= saved_sched_dump
;
82 saved_sched_dump
= NULL
;
86 /* Functions for dumping instructions, av sets, and exprs. */
88 /* Default flags for dumping insns. */
89 static int dump_insn_rtx_flags
= DUMP_INSN_RTX_UID
| DUMP_INSN_RTX_PATTERN
;
91 /* Default flags for dumping vinsns. */
92 static int dump_vinsn_flags
= (DUMP_VINSN_INSN_RTX
| DUMP_VINSN_TYPE
95 /* Default flags for dumping expressions. */
96 static int dump_expr_flags
= DUMP_EXPR_ALL
;
98 /* Default flags for dumping insns when debugging. */
99 static int debug_insn_rtx_flags
= DUMP_INSN_RTX_ALL
;
101 /* Default flags for dumping vinsns when debugging. */
102 static int debug_vinsn_flags
= DUMP_VINSN_ALL
;
104 /* Default flags for dumping expressions when debugging. */
105 static int debug_expr_flags
= DUMP_EXPR_ALL
;
107 /* Controls how an insn from stream should be dumped when debugging. */
108 static int debug_insn_flags
= DUMP_INSN_ALL
;
110 /* Print an rtx X. */
112 sel_print_rtl (rtx x
)
114 print_rtl_single (sched_dump
, x
);
117 /* Dump insn INSN honoring FLAGS. */
119 dump_insn_rtx_1 (rtx insn
, int flags
)
123 /* flags == -1 also means dumping all. */
126 flags
|= DUMP_INSN_RTX_ALL
;
130 if (flags
& DUMP_INSN_RTX_UID
)
131 sel_print ("%d;", INSN_UID (insn
));
133 if (flags
& DUMP_INSN_RTX_PATTERN
)
134 sel_print ("%s;", str_pattern_slim (PATTERN (insn
)));
136 if (flags
& DUMP_INSN_RTX_BBN
)
138 basic_block bb
= BLOCK_FOR_INSN (insn
);
140 sel_print ("bb:%d;", bb
!= NULL
? bb
->index
: -1);
147 /* Dump INSN with default flags. */
149 dump_insn_rtx (rtx insn
)
151 dump_insn_rtx_1 (insn
, dump_insn_rtx_flags
);
155 /* Dump INSN to stderr. */
157 debug_insn_rtx (rtx insn
)
159 switch_dump (stderr
);
160 dump_insn_rtx_1 (insn
, debug_insn_rtx_flags
);
165 /* Dump vinsn VI honoring flags. */
167 dump_vinsn_1 (vinsn_t vi
, int flags
)
171 /* flags == -1 also means dumping all. */
174 flags
|= DUMP_VINSN_ALL
;
178 if (flags
& DUMP_VINSN_INSN_RTX
)
179 dump_insn_rtx_1 (VINSN_INSN_RTX (vi
), dump_insn_rtx_flags
| all
);
181 if (flags
& DUMP_VINSN_TYPE
)
182 sel_print ("type:%s;", GET_RTX_NAME (VINSN_TYPE (vi
)));
184 if (flags
& DUMP_VINSN_COUNT
)
185 sel_print ("count:%d;", VINSN_COUNT (vi
));
187 if (flags
& DUMP_VINSN_COST
)
192 sel_print ("cost:%d;", cost
);
198 /* Dump vinsn VI with default flags. */
200 dump_vinsn (vinsn_t vi
)
202 dump_vinsn_1 (vi
, dump_vinsn_flags
);
206 debug (vinsn_def
&ref
)
208 switch_dump (stderr
);
209 dump_vinsn_1 (&ref
, dump_vinsn_flags
);
215 debug (vinsn_def
*ptr
)
220 fprintf (stderr
, "<nil>\n");
224 debug_verbose (vinsn_def
&ref
)
226 switch_dump (stderr
);
227 dump_vinsn_1 (&ref
, debug_vinsn_flags
);
233 debug_verbose (vinsn_def
*ptr
)
238 fprintf (stderr
, "<nil>\n");
241 /* Dump vinsn VI to stderr. */
243 debug_vinsn (vinsn_t vi
)
245 switch_dump (stderr
);
246 dump_vinsn_1 (vi
, debug_vinsn_flags
);
251 /* Dump EXPR honoring flags. */
253 dump_expr_1 (expr_t expr
, int flags
)
257 /* flags == -1 also means dumping all. */
260 flags
|= DUMP_EXPR_ALL
;
264 if (flags
& DUMP_EXPR_VINSN
)
265 dump_vinsn_1 (EXPR_VINSN (expr
), dump_vinsn_flags
| all
);
267 if (flags
& DUMP_EXPR_SPEC
)
269 int spec
= EXPR_SPEC (expr
);
272 sel_print ("spec:%d;", spec
);
275 if (flags
& DUMP_EXPR_USEFULNESS
)
277 int use
= EXPR_USEFULNESS (expr
);
279 if (use
!= REG_BR_PROB_BASE
)
280 sel_print ("use:%d;", use
);
283 if (flags
& DUMP_EXPR_PRIORITY
)
284 sel_print ("prio:%d;", EXPR_PRIORITY (expr
));
286 if (flags
& DUMP_EXPR_SCHED_TIMES
)
288 int times
= EXPR_SCHED_TIMES (expr
);
291 sel_print ("times:%d;", times
);
294 if (flags
& DUMP_EXPR_SPEC_DONE_DS
)
296 ds_t spec_done_ds
= EXPR_SPEC_DONE_DS (expr
);
298 if (spec_done_ds
!= 0)
299 sel_print ("ds:%d;", spec_done_ds
);
302 if (flags
& DUMP_EXPR_ORIG_BB
)
304 int orig_bb
= EXPR_ORIG_BB_INDEX (expr
);
307 sel_print ("orig_bb:%d;", orig_bb
);
310 if (EXPR_TARGET_AVAILABLE (expr
) < 1)
311 sel_print ("target:%d;", EXPR_TARGET_AVAILABLE (expr
));
315 /* Dump expression EXPR with default flags. */
317 dump_expr (expr_t expr
)
319 dump_expr_1 (expr
, dump_expr_flags
);
322 /* Dump expression EXPR to stderr. */
324 debug_expr (expr_t expr
)
326 switch_dump (stderr
);
327 dump_expr_1 (expr
, debug_expr_flags
);
332 /* Dump expression REF. */
335 debug (expr_def
&ref
)
337 switch_dump (stderr
);
338 dump_expr_1 (&ref
, 0);
344 debug (expr_def
*ptr
)
349 fprintf (stderr
, "<nil>\n");
352 /* Dump expression REF verbosely. */
355 debug_verbose (expr_def
&ref
)
357 switch_dump (stderr
);
358 dump_expr_1 (&ref
, DUMP_EXPR_ALL
);
364 debug_verbose (expr_def
*ptr
)
367 debug_verbose (*ptr
);
369 fprintf (stderr
, "<nil>\n");
372 /* Dump insn I honoring FLAGS. */
374 dump_insn_1 (insn_t i
, int flags
)
380 flags
|= DUMP_INSN_ALL
;
382 if (!sched_dump_to_dot_p
)
385 if (flags
& DUMP_INSN_EXPR
)
387 dump_expr_1 (INSN_EXPR (i
), dump_expr_flags
| all
);
390 else if (flags
& DUMP_INSN_PATTERN
)
392 dump_insn_rtx_1 (i
, DUMP_INSN_RTX_PATTERN
| all
);
395 else if (flags
& DUMP_INSN_UID
)
396 sel_print ("uid:%d;", INSN_UID (i
));
398 if (flags
& DUMP_INSN_SEQNO
)
399 sel_print ("seqno:%d;", INSN_SEQNO (i
));
401 if (flags
& DUMP_INSN_SCHED_CYCLE
)
403 int cycle
= INSN_SCHED_CYCLE (i
);
406 sel_print ("cycle:%d;", cycle
);
409 if (!sched_dump_to_dot_p
)
413 /* Dump insn I with default flags. */
417 dump_insn_1 (i
, DUMP_INSN_EXPR
| DUMP_INSN_SCHED_CYCLE
);
420 /* Dump INSN to stderr. */
422 debug_insn (insn_t insn
)
424 switch_dump (stderr
);
425 dump_insn_1 (insn
, debug_insn_flags
);
430 /* Dumps av_set AV. */
432 dump_av_set (av_set_t av
)
437 if (!sched_dump_to_dot_p
)
440 FOR_EACH_EXPR (expr
, i
, av
)
443 if (!sched_dump_to_dot_p
)
449 if (!sched_dump_to_dot_p
)
453 /* Dumps lvset LV. */
455 dump_lv_set (regset lv
)
459 /* This code was adapted from cfg.cc: dump_regset (). */
465 reg_set_iterator rsi
;
468 EXECUTE_IF_SET_IN_REG_SET (lv
, 0, i
, rsi
)
470 sel_print (" %d", i
);
471 if (i
< FIRST_PSEUDO_REGISTER
)
473 sel_print (" [%s]", reg_names
[i
]);
479 if (sched_dump_to_dot_p
&& count
== 12)
490 /* Dumps a list of instructions pointed to by P. */
492 dump_ilist (ilist_t p
)
496 dump_insn (ILIST_INSN (p
));
501 /* Dumps a list of boundaries pointed to by BNDS. */
503 dump_blist (blist_t bnds
)
505 for (; bnds
; bnds
= BLIST_NEXT (bnds
))
507 bnd_t bnd
= BLIST_BND (bnds
);
509 sel_print ("[to: %d; ptr: ", INSN_UID (BND_TO (bnd
)));
510 dump_ilist (BND_PTR (bnd
));
515 /* Dumps a list of fences pointed to by L. */
517 dump_flist (flist_t l
)
521 dump_insn_1 (FENCE_INSN (FLIST_FENCE (l
)), dump_flist_insn_flags
);
527 /* Dumps an insn vector SUCCS. */
529 dump_insn_vector (rtx_vec_t succs
)
531 for (rtx_insn
*succ
: succs
)
538 /* Dumps a hard reg set SET to FILE using PREFIX. */
540 print_hard_reg_set (FILE *file
, const char *prefix
, HARD_REG_SET set
)
544 fprintf (file
, "%s{ ", prefix
);
545 for (i
= 0; i
< FIRST_PSEUDO_REGISTER
; i
++)
547 if (TEST_HARD_REG_BIT (set
, i
))
548 fprintf (file
, "%d ", i
);
550 fprintf (file
, "}\n");
553 /* Dumps a hard reg set SET using PREFIX. */
555 dump_hard_reg_set (const char *prefix
, HARD_REG_SET set
)
557 print_hard_reg_set (sched_dump
, prefix
, set
);
560 /* Pretty print INSN. This is used as a hook. */
562 sel_print_insn (const rtx_insn
*insn
, int aligned ATTRIBUTE_UNUSED
)
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 (buf
, "%s %4d",
571 INSN_SCHED_TIMES (insn
) > 0 ? "> " : "< ",
574 sprintf (buf
, "%s %4d",
575 INSN_SCHED_TIMES (insn
) > 0 ? "! " : " ",
578 if (GET_MODE (insn
) == TImode
)
579 sprintf (buf
, "+ %4d", INSN_UID (insn
));
581 sprintf (buf
, " %4d", INSN_UID (insn
));
587 /* Functions for pretty printing of CFG. */
588 /* FIXME: Using pretty-print here could simplify this stuff. */
590 /* Replace all occurencies of STR1 to STR2 in BUF.
591 The BUF must be large enough to hold the result. */
593 replace_str_in_buf (char *buf
, const char *str1
, const char *str2
)
595 int buf_len
= strlen (buf
);
596 int str1_len
= strlen (str1
);
597 int str2_len
= strlen (str2
);
598 int diff
= str2_len
- str1_len
;
603 p
= strstr (p
, str1
);
606 char *p1
= p
+ str1_len
;
607 /* Copy the rest of buf and '\0'. */
608 int n
= buf
+ buf_len
- p1
;
611 /* Shift str by DIFF chars. */
613 for (i
= n
; i
>= 0; i
--)
614 p1
[i
+ diff
] = p1
[i
];
616 for (i
= 0; i
<= n
; i
++)
617 p1
[i
+ diff
] = p1
[i
];
620 for (i
= 0; i
< str2_len
; i
++)
631 /* Replace characters in BUF that have special meaning in .dot file.
632 Similar to pp_write_text_as_dot_label_to_stream. */
634 sel_prepare_string_for_dot_label (char *buf
)
636 static char specials_from
[7][2] = { "<", ">", "{", "|", "}", "\"",
638 static char specials_to
[7][3] = { "\\<", "\\>", "\\{", "\\|", "\\}",
642 for (i
= 0; i
< 7; i
++)
643 replace_str_in_buf (buf
, specials_from
[i
], specials_to
[i
]);
646 /* This function acts like printf but dumps to the sched_dump file. */
648 sel_print (const char *fmt
, ...)
652 if (sched_dump_to_dot_p
)
655 if (vasprintf (&message
, fmt
, ap
) >= 0 && message
!= NULL
)
657 message
= (char *) xrealloc (message
, 2 * strlen (message
) + 1);
658 sel_prepare_string_for_dot_label (message
);
659 fprintf (sched_dump
, "%s", message
);
664 vfprintf (sched_dump
, fmt
, ap
);
668 /* Dump INSN with FLAGS. */
670 sel_dump_cfg_insn (insn_t insn
, int flags
)
672 int insn_flags
= DUMP_INSN_UID
| DUMP_INSN_PATTERN
;
674 if (sched_luids
.exists () && INSN_LUID (insn
) > 0)
676 if (flags
& SEL_DUMP_CFG_INSN_SEQNO
)
677 insn_flags
|= DUMP_INSN_SEQNO
| DUMP_INSN_SCHED_CYCLE
| DUMP_INSN_EXPR
;
680 dump_insn_1 (insn
, insn_flags
);
683 /* Dump E to the dot file F. */
685 sel_dump_cfg_edge (FILE *f
, edge e
)
690 if (e
->flags
& EDGE_FALLTHRU
)
693 color
= ", color = red";
695 else if (e
->src
->next_bb
== e
->dest
)
698 color
= ", color = blue";
706 fprintf (f
, "\tbb%d -> bb%d [weight = %d%s];\n",
707 e
->src
->index
, e
->dest
->index
, w
, color
);
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. */
715 has_preds_in_current_region_p (basic_block bb
)
720 gcc_assert (!in_current_region_p (bb
));
722 FOR_EACH_EDGE (e
, ei
, bb
->preds
)
723 if (in_current_region_p (e
->src
))
729 /* Dump a cfg region to the dot file F honoring FLAGS. */
731 sel_dump_cfg_2 (FILE *f
, int flags
)
735 sched_dump_to_dot_p
= true;
738 fprintf (f
, "digraph G {\n"
740 "\tnode [shape = record, fontsize = 9];\n");
742 if (flags
& SEL_DUMP_CFG_FUNCTION_NAME
)
743 fprintf (f
, "function [label = \"%s\"];\n", current_function_name ());
745 FOR_EACH_BB_FN (bb
, cfun
)
747 insn_t insn
= BB_HEAD (bb
);
748 insn_t next_tail
= NEXT_INSN (BB_END (bb
));
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
)
755 bool some_p
= full_p
|| has_preds_in_current_region_p (bb
);
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, ";
769 if ((flags
& SEL_DUMP_CFG_FENCES
)
774 if (!sel_bb_empty_p (bb
))
777 insn_t tail
= BB_END (bb
);
780 cur_insn
= bb_note (bb
);
786 cur_insn
= NEXT_INSN (cur_insn
);
787 fence
= flist_lookup (fences
, cur_insn
);
791 if (!FENCE_SCHEDULED_P (fence
))
794 color
= "color = red, ";
796 color
= "color = yellow, ";
799 color
= "color = blue, ";
804 while (cur_insn
!= tail
);
808 style
= "style = dashed, ";
812 fprintf (f
, "\tbb%d [%s%slabel = \"{Basic block %d", bb
->index
,
813 style
, color
, bb
->index
);
815 if ((flags
& SEL_DUMP_CFG_BB_LOOP
)
816 && bb
->loop_father
!= NULL
)
817 fprintf (f
, ", loop %d", bb
->loop_father
->num
);
820 && (flags
& SEL_DUMP_CFG_BB_NOTES_LIST
))
822 insn_t notes
= BB_NOTE_LIST (bb
);
824 if (notes
!= NULL_RTX
)
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
)
832 sel_dump_cfg_insn (notes
, flags
);
835 notes
= PREV_INSN (notes
);
841 && (flags
& SEL_DUMP_CFG_AV_SET
)
842 && in_current_region_p (bb
)
843 && !sel_bb_empty_p (bb
))
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 (f
, "AV_SET needs update");
853 if ((flags
& SEL_DUMP_CFG_LV_SET
)
854 && !sel_bb_empty_p (bb
))
858 if (BB_LV_SET_VALID_P (bb
))
859 dump_lv_set (BB_LV_SET (bb
));
861 fprintf (f
, "LV_SET needs update");
865 && (flags
& SEL_DUMP_CFG_BB_INSNS
))
868 while (insn
!= next_tail
)
870 sel_dump_cfg_insn (insn
, flags
);
873 insn
= NEXT_INSN (insn
);
877 fprintf (f
, "}\"];\n");
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
);
887 sched_dump_to_dot_p
= false;
890 /* Dump a cfg region to the file specified by TAG honoring flags.
891 The file is created by the function. */
893 sel_dump_cfg_1 (const char *tag
, int flags
)
899 ++sel_dump_cfg_fileno
;
904 i
= 1 + snprintf (NULL
, 0, "%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 (buf
, i
, "%s/%s%05d-%s.dot", sel_debug_cfg_root
,
908 sel_debug_cfg_root_postfix
, sel_dump_cfg_fileno
, tag
);
910 f
= fopen (buf
, "w");
913 fprintf (stderr
, "Can't create file: %s.\n", buf
);
916 sel_dump_cfg_2 (f
, flags
);
924 /* Setup cfg dumping flags. Used for debugging. */
926 setup_dump_cfg_params (void)
928 sel_dump_cfg_flags
= SEL_DUMP_CFG_FLAGS
;
930 sel_debug_cfg_root_postfix
= sel_debug_cfg_root_postfix_default
;
933 /* Debug a cfg region with FLAGS. */
935 sel_debug_cfg_1 (int flags
)
937 bool t1
= sel_dump_cfg_p
;
938 int t2
= sel_dump_cfg_fileno
;
940 sel_dump_cfg_p
= true;
941 sel_dump_cfg_fileno
= ++sel_debug_cfg_fileno
;
943 sel_dump_cfg_1 ("sel-debug-cfg", flags
);
945 sel_dump_cfg_fileno
= t2
;
949 /* Dumps av_set AV to stderr. */
951 debug_av_set (av_set_t av
)
953 switch_dump (stderr
);
959 /* Dump LV to stderr. */
961 debug_lv_set (regset lv
)
963 switch_dump (stderr
);
969 /* Dump an instruction list P to stderr. */
971 debug_ilist (ilist_t p
)
973 switch_dump (stderr
);
979 /* Dump a boundary list BNDS to stderr. */
981 debug_blist (blist_t bnds
)
983 switch_dump (stderr
);
989 /* Debug a cfg region with default flags. */
993 sel_debug_cfg_1 (sel_debug_cfg_flags
);
996 /* Print a current cselib value for X's address to stderr. */
998 debug_mem_addr_value (rtx x
)
1001 machine_mode address_mode
;
1003 gcc_assert (MEM_P (x
));
1004 address_mode
= get_address_mode (x
);
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
));
1011 addr
= get_addr (XEXP (t
, 0));