| /* Instruction scheduling pass. Log dumping infrastructure. |
| Copyright (C) 2006-2017 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "rtl.h" |
| #include "df.h" |
| #include "insn-attr.h" |
| #include "cselib.h" |
| |
| #ifdef INSN_SCHEDULING |
| #include "regset.h" |
| #include "sched-int.h" |
| #include "cfgloop.h" |
| #include "sel-sched-ir.h" |
| #include "sel-sched-dump.h" |
| #include "print-rtl.h" |
| |
| |
| /* These variables control high-level pretty printing. */ |
| static int sel_dump_cfg_flags = SEL_DUMP_CFG_FLAGS; |
| static int sel_debug_cfg_flags = SEL_DUMP_CFG_FLAGS; |
| |
| /* True when a cfg should be dumped. */ |
| static bool sel_dump_cfg_p; |
| |
| /* Variables that are used to build the cfg dump file name. */ |
| static const char * const sel_debug_cfg_root = "./"; |
| static const char * const sel_debug_cfg_root_postfix_default = ""; |
| static const char *sel_debug_cfg_root_postfix = ""; |
| static int sel_dump_cfg_fileno = -1; |
| static int sel_debug_cfg_fileno = -1; |
| |
| /* When this flag is on, we are dumping to the .dot file. |
| When it is off, we are dumping to log. |
| This is useful to differentiate formatting between log and .dot |
| files. */ |
| bool sched_dump_to_dot_p = false; |
| |
| /* Controls how insns from a fence list should be dumped. */ |
| static int dump_flist_insn_flags = (DUMP_INSN_UID | DUMP_INSN_BBN |
| | DUMP_INSN_SEQNO); |
| |
| |
| /* The variable used to hold the value of sched_dump when temporarily |
| switching dump output to the other source, e.g. the .dot file. */ |
| static FILE *saved_sched_dump = NULL; |
| |
| /* Switch sched_dump to TO. It must not be called twice. */ |
| static void |
| switch_dump (FILE *to) |
| { |
| gcc_assert (saved_sched_dump == NULL); |
| |
| saved_sched_dump = sched_dump; |
| sched_dump = to; |
| } |
| |
| /* Restore previously switched dump. */ |
| static void |
| restore_dump (void) |
| { |
| sched_dump = saved_sched_dump; |
| saved_sched_dump = NULL; |
| } |
| |
| |
| /* Functions for dumping instructions, av sets, and exprs. */ |
| |
| /* Default flags for dumping insns. */ |
| static int dump_insn_rtx_flags = DUMP_INSN_RTX_UID | DUMP_INSN_RTX_PATTERN; |
| |
| /* Default flags for dumping vinsns. */ |
| static int dump_vinsn_flags = (DUMP_VINSN_INSN_RTX | DUMP_VINSN_TYPE |
| | DUMP_VINSN_COUNT); |
| |
| /* Default flags for dumping expressions. */ |
| static int dump_expr_flags = DUMP_EXPR_ALL; |
| |
| /* Default flags for dumping insns when debugging. */ |
| static int debug_insn_rtx_flags = DUMP_INSN_RTX_ALL; |
| |
| /* Default flags for dumping vinsns when debugging. */ |
| static int debug_vinsn_flags = DUMP_VINSN_ALL; |
| |
| /* Default flags for dumping expressions when debugging. */ |
| static int debug_expr_flags = DUMP_EXPR_ALL; |
| |
| /* Controls how an insn from stream should be dumped when debugging. */ |
| static int debug_insn_flags = DUMP_INSN_ALL; |
| |
| /* Print an rtx X. */ |
| void |
| sel_print_rtl (rtx x) |
| { |
| print_rtl_single (sched_dump, x); |
| } |
| |
| /* Dump insn INSN honoring FLAGS. */ |
| void |
| dump_insn_rtx_1 (rtx insn, int flags) |
| { |
| int all; |
| |
| /* flags == -1 also means dumping all. */ |
| all = (flags & 1);; |
| if (all) |
| flags |= DUMP_INSN_RTX_ALL; |
| |
| sel_print ("("); |
| |
| if (flags & DUMP_INSN_RTX_UID) |
| sel_print ("%d;", INSN_UID (insn)); |
| |
| if (flags & DUMP_INSN_RTX_PATTERN) |
| sel_print ("%s;", str_pattern_slim (PATTERN (insn))); |
| |
| if (flags & DUMP_INSN_RTX_BBN) |
| { |
| basic_block bb = BLOCK_FOR_INSN (insn); |
| |
| sel_print ("bb:%d;", bb != NULL ? bb->index : -1); |
| } |
| |
| sel_print (")"); |
| } |
| |
| |
| /* Dump INSN with default flags. */ |
| void |
| dump_insn_rtx (rtx insn) |
| { |
| dump_insn_rtx_1 (insn, dump_insn_rtx_flags); |
| } |
| |
| |
| /* Dump INSN to stderr. */ |
| DEBUG_FUNCTION void |
| debug_insn_rtx (rtx insn) |
| { |
| switch_dump (stderr); |
| dump_insn_rtx_1 (insn, debug_insn_rtx_flags); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| /* Dump vinsn VI honoring flags. */ |
| void |
| dump_vinsn_1 (vinsn_t vi, int flags) |
| { |
| int all; |
| |
| /* flags == -1 also means dumping all. */ |
| all = flags & 1; |
| if (all) |
| flags |= DUMP_VINSN_ALL; |
| |
| sel_print ("("); |
| |
| if (flags & DUMP_VINSN_INSN_RTX) |
| dump_insn_rtx_1 (VINSN_INSN_RTX (vi), dump_insn_rtx_flags | all); |
| |
| if (flags & DUMP_VINSN_TYPE) |
| sel_print ("type:%s;", GET_RTX_NAME (VINSN_TYPE (vi))); |
| |
| if (flags & DUMP_VINSN_COUNT) |
| sel_print ("count:%d;", VINSN_COUNT (vi)); |
| |
| if (flags & DUMP_VINSN_COST) |
| { |
| int cost = vi->cost; |
| |
| if (cost != -1) |
| sel_print ("cost:%d;", cost); |
| } |
| |
| sel_print (")"); |
| } |
| |
| /* Dump vinsn VI with default flags. */ |
| void |
| dump_vinsn (vinsn_t vi) |
| { |
| dump_vinsn_1 (vi, dump_vinsn_flags); |
| } |
| |
| DEBUG_FUNCTION void |
| debug (vinsn_def &ref) |
| { |
| switch_dump (stderr); |
| dump_vinsn_1 (&ref, dump_vinsn_flags); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| DEBUG_FUNCTION void |
| debug (vinsn_def *ptr) |
| { |
| if (ptr) |
| debug (*ptr); |
| else |
| fprintf (stderr, "<nil>\n"); |
| } |
| |
| DEBUG_FUNCTION void |
| debug_verbose (vinsn_def &ref) |
| { |
| switch_dump (stderr); |
| dump_vinsn_1 (&ref, debug_vinsn_flags); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| DEBUG_FUNCTION void |
| debug_verbose (vinsn_def *ptr) |
| { |
| if (ptr) |
| debug (*ptr); |
| else |
| fprintf (stderr, "<nil>\n"); |
| } |
| |
| /* Dump vinsn VI to stderr. */ |
| DEBUG_FUNCTION void |
| debug_vinsn (vinsn_t vi) |
| { |
| switch_dump (stderr); |
| dump_vinsn_1 (vi, debug_vinsn_flags); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| /* Dump EXPR honoring flags. */ |
| void |
| dump_expr_1 (expr_t expr, int flags) |
| { |
| int all; |
| |
| /* flags == -1 also means dumping all. */ |
| all = flags & 1; |
| if (all) |
| flags |= DUMP_EXPR_ALL; |
| |
| sel_print ("["); |
| |
| if (flags & DUMP_EXPR_VINSN) |
| dump_vinsn_1 (EXPR_VINSN (expr), dump_vinsn_flags | all); |
| |
| if (flags & DUMP_EXPR_SPEC) |
| { |
| int spec = EXPR_SPEC (expr); |
| |
| if (spec != 0) |
| sel_print ("spec:%d;", spec); |
| } |
| |
| if (flags & DUMP_EXPR_USEFULNESS) |
| { |
| int use = EXPR_USEFULNESS (expr); |
| |
| if (use != REG_BR_PROB_BASE) |
| sel_print ("use:%d;", use); |
| } |
| |
| if (flags & DUMP_EXPR_PRIORITY) |
| sel_print ("prio:%d;", EXPR_PRIORITY (expr)); |
| |
| if (flags & DUMP_EXPR_SCHED_TIMES) |
| { |
| int times = EXPR_SCHED_TIMES (expr); |
| |
| if (times != 0) |
| sel_print ("times:%d;", times); |
| } |
| |
| if (flags & DUMP_EXPR_SPEC_DONE_DS) |
| { |
| ds_t spec_done_ds = EXPR_SPEC_DONE_DS (expr); |
| |
| if (spec_done_ds != 0) |
| sel_print ("ds:%d;", spec_done_ds); |
| } |
| |
| if (flags & DUMP_EXPR_ORIG_BB) |
| { |
| int orig_bb = EXPR_ORIG_BB_INDEX (expr); |
| |
| if (orig_bb != 0) |
| sel_print ("orig_bb:%d;", orig_bb); |
| } |
| |
| if (EXPR_TARGET_AVAILABLE (expr) < 1) |
| sel_print ("target:%d;", EXPR_TARGET_AVAILABLE (expr)); |
| sel_print ("]"); |
| } |
| |
| /* Dump expression EXPR with default flags. */ |
| void |
| dump_expr (expr_t expr) |
| { |
| dump_expr_1 (expr, dump_expr_flags); |
| } |
| |
| /* Dump expression EXPR to stderr. */ |
| DEBUG_FUNCTION void |
| debug_expr (expr_t expr) |
| { |
| switch_dump (stderr); |
| dump_expr_1 (expr, debug_expr_flags); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| /* Dump expression REF. */ |
| |
| DEBUG_FUNCTION void |
| debug (expr_def &ref) |
| { |
| switch_dump (stderr); |
| dump_expr_1 (&ref, 0); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| DEBUG_FUNCTION void |
| debug (expr_def *ptr) |
| { |
| if (ptr) |
| debug (*ptr); |
| else |
| fprintf (stderr, "<nil>\n"); |
| } |
| |
| /* Dump expression REF verbosely. */ |
| |
| DEBUG_FUNCTION void |
| debug_verbose (expr_def &ref) |
| { |
| switch_dump (stderr); |
| dump_expr_1 (&ref, DUMP_EXPR_ALL); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| DEBUG_FUNCTION void |
| debug_verbose (expr_def *ptr) |
| { |
| if (ptr) |
| debug_verbose (*ptr); |
| else |
| fprintf (stderr, "<nil>\n"); |
| } |
| |
| /* Dump insn I honoring FLAGS. */ |
| void |
| dump_insn_1 (insn_t i, int flags) |
| { |
| int all; |
| |
| all = flags & 1; |
| if (all) |
| flags |= DUMP_INSN_ALL; |
| |
| if (!sched_dump_to_dot_p) |
| sel_print ("("); |
| |
| if (flags & DUMP_INSN_EXPR) |
| { |
| dump_expr_1 (INSN_EXPR (i), dump_expr_flags | all); |
| sel_print (";"); |
| } |
| else if (flags & DUMP_INSN_PATTERN) |
| { |
| dump_insn_rtx_1 (i, DUMP_INSN_RTX_PATTERN | all); |
| sel_print (";"); |
| } |
| else if (flags & DUMP_INSN_UID) |
| sel_print ("uid:%d;", INSN_UID (i)); |
| |
| if (flags & DUMP_INSN_SEQNO) |
| sel_print ("seqno:%d;", INSN_SEQNO (i)); |
| |
| if (flags & DUMP_INSN_SCHED_CYCLE) |
| { |
| int cycle = INSN_SCHED_CYCLE (i); |
| |
| if (cycle != 0) |
| sel_print ("cycle:%d;", cycle); |
| } |
| |
| if (!sched_dump_to_dot_p) |
| sel_print (")"); |
| } |
| |
| /* Dump insn I with default flags. */ |
| void |
| dump_insn (insn_t i) |
| { |
| dump_insn_1 (i, DUMP_INSN_EXPR | DUMP_INSN_SCHED_CYCLE); |
| } |
| |
| /* Dump INSN to stderr. */ |
| DEBUG_FUNCTION void |
| debug_insn (insn_t insn) |
| { |
| switch_dump (stderr); |
| dump_insn_1 (insn, debug_insn_flags); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| /* Dumps av_set AV. */ |
| void |
| dump_av_set (av_set_t av) |
| { |
| av_set_iterator i; |
| expr_t expr; |
| |
| if (!sched_dump_to_dot_p) |
| sel_print ("{"); |
| |
| FOR_EACH_EXPR (expr, i, av) |
| { |
| dump_expr (expr); |
| if (!sched_dump_to_dot_p) |
| sel_print (" "); |
| else |
| sel_print ("\n"); |
| } |
| |
| if (!sched_dump_to_dot_p) |
| sel_print ("}"); |
| } |
| |
| /* Dumps lvset LV. */ |
| void |
| dump_lv_set (regset lv) |
| { |
| sel_print ("{"); |
| |
| /* This code was adapted from cfg.c: dump_regset (). */ |
| if (lv == NULL) |
| sel_print ("nil"); |
| else |
| { |
| unsigned i; |
| reg_set_iterator rsi; |
| int count = 0; |
| |
| EXECUTE_IF_SET_IN_REG_SET (lv, 0, i, rsi) |
| { |
| sel_print (" %d", i); |
| if (i < FIRST_PSEUDO_REGISTER) |
| { |
| sel_print (" [%s]", reg_names[i]); |
| ++count; |
| } |
| |
| ++count; |
| |
| if (sched_dump_to_dot_p && count == 12) |
| { |
| count = 0; |
| sel_print ("\n"); |
| } |
| } |
| } |
| |
| sel_print ("}\n"); |
| } |
| |
| /* Dumps a list of instructions pointed to by P. */ |
| static void |
| dump_ilist (ilist_t p) |
| { |
| while (p) |
| { |
| dump_insn (ILIST_INSN (p)); |
| p = ILIST_NEXT (p); |
| } |
| } |
| |
| /* Dumps a list of boundaries pointed to by BNDS. */ |
| void |
| dump_blist (blist_t bnds) |
| { |
| for (; bnds; bnds = BLIST_NEXT (bnds)) |
| { |
| bnd_t bnd = BLIST_BND (bnds); |
| |
| sel_print ("[to: %d; ptr: ", INSN_UID (BND_TO (bnd))); |
| dump_ilist (BND_PTR (bnd)); |
| sel_print ("] "); |
| } |
| } |
| |
| /* Dumps a list of fences pointed to by L. */ |
| void |
| dump_flist (flist_t l) |
| { |
| while (l) |
| { |
| dump_insn_1 (FENCE_INSN (FLIST_FENCE (l)), dump_flist_insn_flags); |
| sel_print (" "); |
| l = FLIST_NEXT (l); |
| } |
| } |
| |
| /* Dumps an insn vector SUCCS. */ |
| void |
| dump_insn_vector (rtx_vec_t succs) |
| { |
| int i; |
| rtx_insn *succ; |
| |
| FOR_EACH_VEC_ELT (succs, i, succ) |
| if (succ) |
| dump_insn (succ); |
| else |
| sel_print ("NULL "); |
| } |
| |
| /* Dumps a hard reg set SET to FILE using PREFIX. */ |
| static void |
| print_hard_reg_set (FILE *file, const char *prefix, HARD_REG_SET set) |
| { |
| int i; |
| |
| fprintf (file, "%s{ ", prefix); |
| for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) |
| { |
| if (TEST_HARD_REG_BIT (set, i)) |
| fprintf (file, "%d ", i); |
| } |
| fprintf (file, "}\n"); |
| } |
| |
| /* Dumps a hard reg set SET using PREFIX. */ |
| void |
| dump_hard_reg_set (const char *prefix, HARD_REG_SET set) |
| { |
| print_hard_reg_set (sched_dump, prefix, set); |
| } |
| |
| /* Pretty print INSN. This is used as a hook. */ |
| const char * |
| sel_print_insn (const rtx_insn *insn, int aligned ATTRIBUTE_UNUSED) |
| { |
| static char buf[80]; |
| |
| /* '+' before insn means it is a new cycle start and it's not been |
| scheduled yet. '>' - has been scheduled. */ |
| if (s_i_d.exists () && INSN_LUID (insn) > 0) |
| if (GET_MODE (insn) == TImode) |
| sprintf (buf, "%s %4d", |
| INSN_SCHED_TIMES (insn) > 0 ? "> " : "< ", |
| INSN_UID (insn)); |
| else |
| sprintf (buf, "%s %4d", |
| INSN_SCHED_TIMES (insn) > 0 ? "! " : " ", |
| INSN_UID (insn)); |
| else |
| if (GET_MODE (insn) == TImode) |
| sprintf (buf, "+ %4d", INSN_UID (insn)); |
| else |
| sprintf (buf, " %4d", INSN_UID (insn)); |
| |
| return buf; |
| } |
| |
| |
| /* Functions for pretty printing of CFG. */ |
| /* FIXME: Using pretty-print here could simplify this stuff. */ |
| |
| /* Replace all occurencies of STR1 to STR2 in BUF. |
| The BUF must be large enough to hold the result. */ |
| static void |
| replace_str_in_buf (char *buf, const char *str1, const char *str2) |
| { |
| int buf_len = strlen (buf); |
| int str1_len = strlen (str1); |
| int str2_len = strlen (str2); |
| int diff = str2_len - str1_len; |
| |
| char *p = buf; |
| do |
| { |
| p = strstr (p, str1); |
| if (p) |
| { |
| char *p1 = p + str1_len; |
| /* Copy the rest of buf and '\0'. */ |
| int n = buf + buf_len - p1; |
| int i; |
| |
| /* Shift str by DIFF chars. */ |
| if (diff > 0) |
| for (i = n; i >= 0; i--) |
| p1[i + diff] = p1[i]; |
| else |
| for (i = 0; i <= n; i++) |
| p1[i + diff] = p1[i]; |
| |
| /* Copy str2. */ |
| for (i = 0; i < str2_len; i++) |
| p[i] = str2[i]; |
| |
| p += str2_len; |
| buf_len += diff; |
| } |
| |
| } |
| while (p); |
| } |
| |
| /* Replace characters in BUF that have special meaning in .dot file. |
| Similar to pp_write_text_as_dot_label_to_stream. */ |
| static void |
| sel_prepare_string_for_dot_label (char *buf) |
| { |
| static char specials_from[7][2] = { "<", ">", "{", "|", "}", "\"", |
| "\n" }; |
| static char specials_to[7][3] = { "\\<", "\\>", "\\{", "\\|", "\\}", |
| "\\\"", "\\l" }; |
| unsigned i; |
| |
| for (i = 0; i < 7; i++) |
| replace_str_in_buf (buf, specials_from[i], specials_to[i]); |
| } |
| |
| /* This function acts like printf but dumps to the sched_dump file. */ |
| void |
| sel_print (const char *fmt, ...) |
| { |
| va_list ap; |
| va_start (ap, fmt); |
| if (sched_dump_to_dot_p) |
| { |
| char *message; |
| if (vasprintf (&message, fmt, ap) >= 0 && message != NULL) |
| { |
| message = (char *) xrealloc (message, 2 * strlen (message) + 1); |
| sel_prepare_string_for_dot_label (message); |
| fprintf (sched_dump, "%s", message); |
| free (message); |
| } |
| } |
| else |
| vfprintf (sched_dump, fmt, ap); |
| va_end (ap); |
| } |
| |
| /* Dump INSN with FLAGS. */ |
| static void |
| sel_dump_cfg_insn (insn_t insn, int flags) |
| { |
| int insn_flags = DUMP_INSN_UID | DUMP_INSN_PATTERN; |
| |
| if (sched_luids.exists () && INSN_LUID (insn) > 0) |
| { |
| if (flags & SEL_DUMP_CFG_INSN_SEQNO) |
| insn_flags |= DUMP_INSN_SEQNO | DUMP_INSN_SCHED_CYCLE | DUMP_INSN_EXPR; |
| } |
| |
| dump_insn_1 (insn, insn_flags); |
| } |
| |
| /* Dump E to the dot file F. */ |
| static void |
| sel_dump_cfg_edge (FILE *f, edge e) |
| { |
| int w; |
| const char *color; |
| |
| if (e->flags & EDGE_FALLTHRU) |
| { |
| w = 10; |
| color = ", color = red"; |
| } |
| else if (e->src->next_bb == e->dest) |
| { |
| w = 3; |
| color = ", color = blue"; |
| } |
| else |
| { |
| w = 1; |
| color = ""; |
| } |
| |
| fprintf (f, "\tbb%d -> bb%d [weight = %d%s];\n", |
| e->src->index, e->dest->index, w, color); |
| } |
| |
| |
| /* Return true if BB has a predesessor from current region. |
| TODO: Either make this function to trace back through empty block |
| or just remove those empty blocks. */ |
| static bool |
| has_preds_in_current_region_p (basic_block bb) |
| { |
| edge e; |
| edge_iterator ei; |
| |
| gcc_assert (!in_current_region_p (bb)); |
| |
| FOR_EACH_EDGE (e, ei, bb->preds) |
| if (in_current_region_p (e->src)) |
| return true; |
| |
| return false; |
| } |
| |
| /* Dump a cfg region to the dot file F honoring FLAGS. */ |
| static void |
| sel_dump_cfg_2 (FILE *f, int flags) |
| { |
| basic_block bb; |
| |
| sched_dump_to_dot_p = true; |
| switch_dump (f); |
| |
| fprintf (f, "digraph G {\n" |
| "\tratio = 2.25;\n" |
| "\tnode [shape = record, fontsize = 9];\n"); |
| |
| if (flags & SEL_DUMP_CFG_FUNCTION_NAME) |
| fprintf (f, "function [label = \"%s\"];\n", current_function_name ()); |
| |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| insn_t insn = BB_HEAD (bb); |
| insn_t next_tail = NEXT_INSN (BB_END (bb)); |
| edge e; |
| edge_iterator ei; |
| bool in_region_p = ((flags & SEL_DUMP_CFG_CURRENT_REGION) |
| && in_current_region_p (bb)); |
| bool full_p = (!(flags & SEL_DUMP_CFG_CURRENT_REGION) |
| || in_region_p); |
| bool some_p = full_p || has_preds_in_current_region_p (bb); |
| const char *color; |
| const char *style; |
| |
| if (!some_p) |
| continue; |
| |
| if ((flags & SEL_DUMP_CFG_CURRENT_REGION) |
| && in_current_region_p (bb) |
| && BLOCK_TO_BB (bb->index) == 0) |
| color = "color = green, "; |
| else |
| color = ""; |
| |
| if ((flags & SEL_DUMP_CFG_FENCES) |
| && in_region_p) |
| { |
| style = ""; |
| |
| if (!sel_bb_empty_p (bb)) |
| { |
| bool first_p = true; |
| insn_t tail = BB_END (bb); |
| insn_t cur_insn; |
| |
| cur_insn = bb_note (bb); |
| |
| do |
| { |
| fence_t fence; |
| |
| cur_insn = NEXT_INSN (cur_insn); |
| fence = flist_lookup (fences, cur_insn); |
| |
| if (fence != NULL) |
| { |
| if (!FENCE_SCHEDULED_P (fence)) |
| { |
| if (first_p) |
| color = "color = red, "; |
| else |
| color = "color = yellow, "; |
| } |
| else |
| color = "color = blue, "; |
| } |
| |
| first_p = false; |
| } |
| while (cur_insn != tail); |
| } |
| } |
| else if (!full_p) |
| style = "style = dashed, "; |
| else |
| style = ""; |
| |
| fprintf (f, "\tbb%d [%s%slabel = \"{Basic block %d", bb->index, |
| style, color, bb->index); |
| |
| if ((flags & SEL_DUMP_CFG_BB_LOOP) |
| && bb->loop_father != NULL) |
| fprintf (f, ", loop %d", bb->loop_father->num); |
| |
| if (full_p |
| && (flags & SEL_DUMP_CFG_BB_NOTES_LIST)) |
| { |
| insn_t notes = BB_NOTE_LIST (bb); |
| |
| if (notes != NULL_RTX) |
| { |
| fprintf (f, "|"); |
| |
| /* For simplicity, we dump notes from note_list in reversed order |
| to that what they will appear in the code. */ |
| while (notes != NULL_RTX) |
| { |
| sel_dump_cfg_insn (notes, flags); |
| fprintf (f, "\\l"); |
| |
| notes = PREV_INSN (notes); |
| } |
| } |
| } |
| |
| if (full_p |
| && (flags & SEL_DUMP_CFG_AV_SET) |
| && in_current_region_p (bb) |
| && !sel_bb_empty_p (bb)) |
| { |
| fprintf (f, "|"); |
| |
| if (BB_AV_SET_VALID_P (bb)) |
| dump_av_set (BB_AV_SET (bb)); |
| else if (BB_AV_LEVEL (bb) == -1) |
| fprintf (f, "AV_SET needs update"); |
| } |
| |
| if ((flags & SEL_DUMP_CFG_LV_SET) |
| && !sel_bb_empty_p (bb)) |
| { |
| fprintf (f, "|"); |
| |
| if (BB_LV_SET_VALID_P (bb)) |
| dump_lv_set (BB_LV_SET (bb)); |
| else |
| fprintf (f, "LV_SET needs update"); |
| } |
| |
| if (full_p |
| && (flags & SEL_DUMP_CFG_BB_INSNS)) |
| { |
| fprintf (f, "|"); |
| while (insn != next_tail) |
| { |
| sel_dump_cfg_insn (insn, flags); |
| fprintf (f, "\\l"); |
| |
| insn = NEXT_INSN (insn); |
| } |
| } |
| |
| fprintf (f, "}\"];\n"); |
| |
| FOR_EACH_EDGE (e, ei, bb->succs) |
| if (full_p || in_current_region_p (e->dest)) |
| sel_dump_cfg_edge (f, e); |
| } |
| |
| fprintf (f, "}"); |
| |
| restore_dump (); |
| sched_dump_to_dot_p = false; |
| } |
| |
| /* Dump a cfg region to the file specified by TAG honoring flags. |
| The file is created by the function. */ |
| static void |
| sel_dump_cfg_1 (const char *tag, int flags) |
| { |
| char *buf; |
| int i; |
| FILE *f; |
| |
| ++sel_dump_cfg_fileno; |
| |
| if (!sel_dump_cfg_p) |
| return; |
| |
| i = 1 + snprintf (NULL, 0, "%s/%s%05d-%s.dot", sel_debug_cfg_root, |
| sel_debug_cfg_root_postfix, sel_dump_cfg_fileno, tag); |
| buf = XNEWVEC (char, i); |
| snprintf (buf, i, "%s/%s%05d-%s.dot", sel_debug_cfg_root, |
| sel_debug_cfg_root_postfix, sel_dump_cfg_fileno, tag); |
| |
| f = fopen (buf, "w"); |
| |
| if (f == NULL) |
| fprintf (stderr, "Can't create file: %s.\n", buf); |
| else |
| { |
| sel_dump_cfg_2 (f, flags); |
| |
| fclose (f); |
| } |
| |
| free (buf); |
| } |
| |
| /* Setup cfg dumping flags. Used for debugging. */ |
| void |
| setup_dump_cfg_params (void) |
| { |
| sel_dump_cfg_flags = SEL_DUMP_CFG_FLAGS; |
| sel_dump_cfg_p = 0; |
| sel_debug_cfg_root_postfix = sel_debug_cfg_root_postfix_default; |
| } |
| |
| /* Debug a cfg region with FLAGS. */ |
| void |
| sel_debug_cfg_1 (int flags) |
| { |
| bool t1 = sel_dump_cfg_p; |
| int t2 = sel_dump_cfg_fileno; |
| |
| sel_dump_cfg_p = true; |
| sel_dump_cfg_fileno = ++sel_debug_cfg_fileno; |
| |
| sel_dump_cfg_1 ("sel-debug-cfg", flags); |
| |
| sel_dump_cfg_fileno = t2; |
| sel_dump_cfg_p = t1; |
| } |
| |
| /* Dumps av_set AV to stderr. */ |
| DEBUG_FUNCTION void |
| debug_av_set (av_set_t av) |
| { |
| switch_dump (stderr); |
| dump_av_set (av); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| /* Dump LV to stderr. */ |
| DEBUG_FUNCTION void |
| debug_lv_set (regset lv) |
| { |
| switch_dump (stderr); |
| dump_lv_set (lv); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| /* Dump an instruction list P to stderr. */ |
| DEBUG_FUNCTION void |
| debug_ilist (ilist_t p) |
| { |
| switch_dump (stderr); |
| dump_ilist (p); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| /* Dump a boundary list BNDS to stderr. */ |
| DEBUG_FUNCTION void |
| debug_blist (blist_t bnds) |
| { |
| switch_dump (stderr); |
| dump_blist (bnds); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| /* Dump a rtx vector REF. */ |
| DEBUG_FUNCTION void |
| debug (vec<rtx_insn *> &ref) |
| { |
| switch_dump (stderr); |
| dump_insn_vector (ref); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| DEBUG_FUNCTION void |
| debug (vec<rtx_insn *> *ptr) |
| { |
| if (ptr) |
| debug (*ptr); |
| else |
| fprintf (stderr, "<nil>\n"); |
| } |
| |
| /* Dump an insn vector SUCCS. */ |
| DEBUG_FUNCTION void |
| debug_insn_vector (rtx_vec_t succs) |
| { |
| switch_dump (stderr); |
| dump_insn_vector (succs); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| /* Dump a hard reg set SET to stderr. */ |
| DEBUG_FUNCTION void |
| debug_hard_reg_set (HARD_REG_SET set) |
| { |
| switch_dump (stderr); |
| dump_hard_reg_set ("", set); |
| sel_print ("\n"); |
| restore_dump (); |
| } |
| |
| /* Debug a cfg region with default flags. */ |
| void |
| sel_debug_cfg (void) |
| { |
| sel_debug_cfg_1 (sel_debug_cfg_flags); |
| } |
| |
| /* Print a current cselib value for X's address to stderr. */ |
| DEBUG_FUNCTION rtx |
| debug_mem_addr_value (rtx x) |
| { |
| rtx t, addr; |
| machine_mode address_mode; |
| |
| gcc_assert (MEM_P (x)); |
| address_mode = get_address_mode (x); |
| |
| t = shallow_copy_rtx (x); |
| if (cselib_lookup (XEXP (t, 0), address_mode, 0, GET_MODE (t))) |
| XEXP (t, 0) = cselib_subst_to_values (XEXP (t, 0), GET_MODE (t)); |
| |
| t = canon_rtx (t); |
| addr = get_addr (XEXP (t, 0)); |
| debug_rtx (t); |
| debug_rtx (addr); |
| return t; |
| } |
| #endif |
| |