Message ID | 20231122111415.815147-9-maxim.kuvyrkov@linaro.org |
---|---|
State | New |
Headers | show |
Series | Avoid exponential behavior in scheduler and better logging | expand |
Dear scheduler maintainers, Gentle ping. This patch improves debugging output, it does not touch scheduling logic. On Wed, 22 Nov 2023 at 15:15, Maxim Kuvyrkov <maxim.kuvyrkov@linaro.org> wrote: > Scheduler dependency analysis uses two main data structures: > 1. reg_pending_* data contains effects of INSN on the register file, > which is then incorporated into ... > 2. deps_desc object, which contains commulative information about all > instructions processed from deps_desc object's initialization. > > This patch adds debug dumping of (2). > > Dependency analysis contexts (aka deps_desc objects) are huge, but > each instruction affects only a small amount of data in these objects. > Therefore, it is most useful to dump differential information > compared to the dependency state after previous instruction. > > gcc/ChangeLog: > > * sched-deps.cc (reset_deps, dump_rtx_insn_list) > (rtx_insn_list_same_p): New helper functions. > (dump_deps_desc_diff): New function to dump dependency information. > (sched_analysis_prev_deps): New static variable. > (sched_analyze_insn): Dump dependency information. > (init_deps_global, finish_deps_global): Handle > sched_analysis_prev_deps. > * sched-int.h (struct deps_reg): Update comments. > * sched-rgn.cc (concat_insn_list, concat_expr_list): Update > comments. > --- > gcc/sched-deps.cc | 197 ++++++++++++++++++++++++++++++++++++++++++++++ > gcc/sched-int.h | 9 ++- > gcc/sched-rgn.cc | 5 ++ > 3 files changed, 210 insertions(+), 1 deletion(-) > > diff --git a/gcc/sched-deps.cc b/gcc/sched-deps.cc > index f9290c82fd2..edca9927e23 100644 > --- a/gcc/sched-deps.cc > +++ b/gcc/sched-deps.cc > @@ -1677,6 +1677,15 @@ delete_all_dependences (rtx_insn *insn) > sd_delete_dep (sd_it); > } > > +/* Re-initialize existing dependency context DEPS to be a copy of FROM. > */ > +static void > +reset_deps (class deps_desc *deps, class deps_desc *from) > +{ > + free_deps (deps); > + init_deps (deps, false); > + deps_join (deps, from); > +} > + > /* All insns in a scheduling group except the first should only have > dependencies on the previous insn in the group. So we find the > first instruction in the scheduling group by walking the dependence > @@ -2960,6 +2969,177 @@ dump_reg_pending_data (FILE *file, rtx_insn *insn) > } > } > > +/* Dump rtx_insn_list LIST. > + Consider moving to lists.cc if there are users outside of > sched-deps.cc. */ > +static void > +dump_rtx_insn_list (FILE *file, rtx_insn_list *list) > +{ > + for (; list; list = list->next ()) > + fprintf (file, " %d", INSN_UID (list->insn ())); > +} > + > +/* Return TRUE if lists A and B have same elements in the same order. */ > +static bool > +rtx_insn_list_same_p (rtx_insn_list *a, rtx_insn_list *b) > +{ > + for (; a && b; a = a->next (), b = b->next ()) > + if (a->insn () != b->insn ()) > + return false; > + > + if (a || b) > + return false; > + > + return true; > +} > + > +/* Dump parts of DEPS that are different from PREV. > + Dumping all information from dependency context produces huge > + hard-to-analize logs; differential dumping is way more managable. */ > +static void > +dump_deps_desc_diff (FILE *file, class deps_desc *deps, class deps_desc > *prev) > +{ > + /* Each "paragraph" is a single line of output. */ > + > + /* Note on param_max_pending_list_length: > + During normal dependency analysis various lists should not exceed > this > + limit. Searching for "!!!" in scheduler logs can point to potential > bugs > + or poorly-handled corner-cases. */ > + > + if (!rtx_insn_list_same_p (deps->pending_read_insns, > + prev->pending_read_insns)) > + { > + fprintf (file, ";; deps pending mem reads length(%d):", > + deps->pending_read_list_length); > + if ((deps->pending_read_list_length + > deps->pending_write_list_length) > + >= param_max_pending_list_length) > + fprintf (file, "%d insns!!!", deps->pending_read_list_length); > + else > + dump_rtx_insn_list (file, deps->pending_read_insns); > + fprintf (file, "\n"); > + } > + > + if (!rtx_insn_list_same_p (deps->pending_write_insns, > + prev->pending_write_insns)) > + { > + fprintf (file, ";; deps pending mem writes length(%d):", > + deps->pending_write_list_length); > + if ((deps->pending_read_list_length + > deps->pending_write_list_length) > + >= param_max_pending_list_length) > + fprintf (file, "%d insns!!!", deps->pending_write_list_length); > + else > + dump_rtx_insn_list (file, deps->pending_write_insns); > + fprintf (file, "\n"); > + } > + > + if (!rtx_insn_list_same_p (deps->pending_jump_insns, > + prev->pending_jump_insns)) > + { > + fprintf (file, ";; deps pending jump length(%d):", > + deps->pending_flush_length); > + if (deps->pending_flush_length >= param_max_pending_list_length) > + fprintf (file, "%d insns!!!", deps->pending_flush_length); > + else > + dump_rtx_insn_list (file, deps->pending_jump_insns); > + fprintf (file, "\n"); > + } > + > + fprintf (file, ";; last"); > + if (!rtx_insn_list_same_p (deps->last_pending_memory_flush, > + prev->last_pending_memory_flush)) > + { > + fprintf (file, " memory_flush("); > + dump_rtx_insn_list (file, deps->last_pending_memory_flush); > + fprintf (file, ")"); > + } > + if (!rtx_insn_list_same_p (deps->last_function_call, > + prev->last_function_call)) > + { > + fprintf (file, " call("); > + dump_rtx_insn_list (file, deps->last_function_call); > + fprintf (file, ")"); > + } > + if (!rtx_insn_list_same_p (deps->last_function_call_may_noreturn, > + prev->last_function_call_may_noreturn)) > + { > + fprintf (file, " noreturn("); > + dump_rtx_insn_list (file, deps->last_function_call_may_noreturn); > + fprintf (file, ")"); > + } > + fprintf (file, "\n"); > + > + fprintf (file, ";; sched_before_next"); > + if (!rtx_insn_list_same_p (deps->sched_before_next_call, > + prev->sched_before_next_call)) > + { > + fprintf (file, " call("); > + dump_rtx_insn_list (file, deps->sched_before_next_call); > + fprintf (file, ")"); > + } > + if (!rtx_insn_list_same_p (deps->sched_before_next_jump, > + prev->sched_before_next_jump)) > + { > + fprintf (file, " jump("); > + dump_rtx_insn_list (file, deps->sched_before_next_jump); > + fprintf (file, ")"); > + } > + fprintf (file, " in_post_call_group_p(%d)\n", > deps->in_post_call_group_p); > + > + fprintf (file, ";; last_debug_insn(%d) last_args_size(%d) > last_prologue(", > + deps->last_debug_insn ? INSN_UID (deps->last_debug_insn) : -1, > + deps->last_args_size ? INSN_UID (deps->last_args_size) : -1); > + dump_rtx_insn_list (file, deps->last_prologue); > + fprintf (file, ") last_epilogue("); > + dump_rtx_insn_list (file, deps->last_epilogue); > + fprintf (file, ") last_logue_was_epilogue(%d)\n", > + deps->last_logue_was_epilogue); > + > + int i; > + > + gcc_assert (deps->max_reg == prev->max_reg); > + for (i = 0; i < deps->max_reg; ++i) > + { > + struct deps_reg *reg_last = &deps->reg_last[i]; > + struct deps_reg *reg_prev = &prev->reg_last[i]; > + > + if (rtx_insn_list_same_p (reg_last->uses, reg_prev->uses) > + && rtx_insn_list_same_p (reg_last->sets, reg_prev->sets) > + && rtx_insn_list_same_p (reg_last->implicit_sets, > + reg_prev->implicit_sets) > + && rtx_insn_list_same_p (reg_last->control_uses, > + reg_prev->control_uses) > + && rtx_insn_list_same_p (reg_last->clobbers, > + reg_prev->clobbers)) > + continue; > + > + fprintf (file, ";; reg %u: uses(", i); > + if (reg_last->uses_length >= param_max_pending_list_length) > + fprintf (file, "%d insns!!!", reg_last->uses_length); > + else > + dump_rtx_insn_list (file, reg_last->uses); > + if (reg_last->clobbers_length >= param_max_pending_list_length) > + { > + fprintf (file, ") clobbers("); > + fprintf (file, "%d insns!!!", reg_last->clobbers_length); > + } > + else > + { > + fprintf (file, ") sets("); > + dump_rtx_insn_list (file, reg_last->sets); > + fprintf (file, ") implicit_sets("); > + dump_rtx_insn_list (file, reg_last->implicit_sets); > + fprintf (file, ") control_uses("); > + dump_rtx_insn_list (file, reg_last->control_uses); > + fprintf (file, ") clobbers("); > + dump_rtx_insn_list (file, reg_last->clobbers); > + } > + fprintf (file, ")\n"); > + } > +} > + > +/* Dependency analysis state before current insn. > + Used by dump_deps_desc_diff(). */ > +static class deps_desc *sched_analysis_prev_deps; > + > /* Analyze an INSN with pattern X to find all dependencies. > This analysis uses two main data structures: > 1. reg_pending_* data contains effects of INSN on the register file, > @@ -3627,6 +3807,16 @@ sched_analyze_insn (class deps_desc *deps, rtx x, > rtx_insn *insn) > deps->last_logue_was_epilogue = true; > } > } > + > + if (sched_verbose >= 8) > + { > + dump_deps_desc_diff (sched_dump, deps, sched_analysis_prev_deps); > + reset_deps (sched_analysis_prev_deps, deps); > + fprintf (sched_dump, ";; "); > + dump_lists (sched_dump, insn, SD_LIST_BACK, > + DUMP_LISTS_ALL | DUMP_DEP_PRO); > + fprintf (sched_dump, "\n"); > + } > } > > /* Return TRUE if INSN might not always return normally (e.g. call exit, > @@ -4305,6 +4495,9 @@ init_deps_global (void) > sched_deps_info->note_mem_dep = haifa_note_mem_dep; > sched_deps_info->note_dep = haifa_note_dep; > } > + > + sched_analysis_prev_deps = XNEW (class deps_desc); > + init_deps (sched_analysis_prev_deps, false); > } > > /* Free everything used by the dependency analysis code. */ > @@ -4316,6 +4509,10 @@ finish_deps_global (void) > FREE_REG_SET (reg_pending_clobbers); > FREE_REG_SET (reg_pending_uses); > FREE_REG_SET (reg_pending_control_uses); > + > + free_deps (sched_analysis_prev_deps); > + free (sched_analysis_prev_deps); > + sched_analysis_prev_deps = NULL; > } > > /* Estimate the weakness of dependence between MEM1 and MEM2. */ > diff --git a/gcc/sched-int.h b/gcc/sched-int.h > index 64a2f0bcff9..8a3109ce86e 100644 > --- a/gcc/sched-int.h > +++ b/gcc/sched-int.h > @@ -455,7 +455,9 @@ struct deps_reg > int clobbers_length; > }; > > -/* Describe state of dependencies used during sched_analyze phase. */ > +/* Describe state of dependencies used during sched_analyze phase. > + Please consider updating sched-deps.cc:dump_deps_desc_diff() when > adding > + new fields. */ > class deps_desc > { > public: > @@ -499,6 +501,11 @@ public: > large lists. */ > int pending_flush_length; > > + /* Below are lists (and not just a single instructions) to allow > inter-block > + dependency analysis. Each block > + may add a single insn to below lists, but when merging dependency > + analysis context from multiple predecessor blocks, we may get a > list. */ > + > /* The last insn upon which all memory references must depend. > This is an insn which flushed the pending lists, creating a > dependency > between it and all previously pending memory references. This > creates > diff --git a/gcc/sched-rgn.cc b/gcc/sched-rgn.cc > index da3ec0458ff..72f0a2de66e 100644 > --- a/gcc/sched-rgn.cc > +++ b/gcc/sched-rgn.cc > @@ -2590,6 +2590,10 @@ static rtx_insn_list * > concat_insn_list (rtx_insn_list *copy, rtx_insn_list *old) > { > if (!old) > + /* concat_*_LIST() will return a reversed copy of COPY. Working with > + reversed copies would complicate dependency dumping in > + dump_deps_desc_diff(), which internally uses reset_deps() and > + deps_join(). Therefore, use copy_INSN_LIST() when OLD is NULL. */ > return copy_INSN_LIST (copy); > return concat_INSN_LIST (copy, old); > } > @@ -2599,6 +2603,7 @@ static rtx_expr_list * > concat_expr_list (rtx_expr_list *copy, rtx_expr_list *old) > { > if (!old) > + /* See comment in concat_insn_list() above. */ > return copy_EXPR_LIST (copy); > return concat_EXPR_LIST (copy, old); > } > -- > 2.34.1 > >
diff --git a/gcc/sched-deps.cc b/gcc/sched-deps.cc index f9290c82fd2..edca9927e23 100644 --- a/gcc/sched-deps.cc +++ b/gcc/sched-deps.cc @@ -1677,6 +1677,15 @@ delete_all_dependences (rtx_insn *insn) sd_delete_dep (sd_it); } +/* Re-initialize existing dependency context DEPS to be a copy of FROM. */ +static void +reset_deps (class deps_desc *deps, class deps_desc *from) +{ + free_deps (deps); + init_deps (deps, false); + deps_join (deps, from); +} + /* All insns in a scheduling group except the first should only have dependencies on the previous insn in the group. So we find the first instruction in the scheduling group by walking the dependence @@ -2960,6 +2969,177 @@ dump_reg_pending_data (FILE *file, rtx_insn *insn) } } +/* Dump rtx_insn_list LIST. + Consider moving to lists.cc if there are users outside of sched-deps.cc. */ +static void +dump_rtx_insn_list (FILE *file, rtx_insn_list *list) +{ + for (; list; list = list->next ()) + fprintf (file, " %d", INSN_UID (list->insn ())); +} + +/* Return TRUE if lists A and B have same elements in the same order. */ +static bool +rtx_insn_list_same_p (rtx_insn_list *a, rtx_insn_list *b) +{ + for (; a && b; a = a->next (), b = b->next ()) + if (a->insn () != b->insn ()) + return false; + + if (a || b) + return false; + + return true; +} + +/* Dump parts of DEPS that are different from PREV. + Dumping all information from dependency context produces huge + hard-to-analize logs; differential dumping is way more managable. */ +static void +dump_deps_desc_diff (FILE *file, class deps_desc *deps, class deps_desc *prev) +{ + /* Each "paragraph" is a single line of output. */ + + /* Note on param_max_pending_list_length: + During normal dependency analysis various lists should not exceed this + limit. Searching for "!!!" in scheduler logs can point to potential bugs + or poorly-handled corner-cases. */ + + if (!rtx_insn_list_same_p (deps->pending_read_insns, + prev->pending_read_insns)) + { + fprintf (file, ";; deps pending mem reads length(%d):", + deps->pending_read_list_length); + if ((deps->pending_read_list_length + deps->pending_write_list_length) + >= param_max_pending_list_length) + fprintf (file, "%d insns!!!", deps->pending_read_list_length); + else + dump_rtx_insn_list (file, deps->pending_read_insns); + fprintf (file, "\n"); + } + + if (!rtx_insn_list_same_p (deps->pending_write_insns, + prev->pending_write_insns)) + { + fprintf (file, ";; deps pending mem writes length(%d):", + deps->pending_write_list_length); + if ((deps->pending_read_list_length + deps->pending_write_list_length) + >= param_max_pending_list_length) + fprintf (file, "%d insns!!!", deps->pending_write_list_length); + else + dump_rtx_insn_list (file, deps->pending_write_insns); + fprintf (file, "\n"); + } + + if (!rtx_insn_list_same_p (deps->pending_jump_insns, + prev->pending_jump_insns)) + { + fprintf (file, ";; deps pending jump length(%d):", + deps->pending_flush_length); + if (deps->pending_flush_length >= param_max_pending_list_length) + fprintf (file, "%d insns!!!", deps->pending_flush_length); + else + dump_rtx_insn_list (file, deps->pending_jump_insns); + fprintf (file, "\n"); + } + + fprintf (file, ";; last"); + if (!rtx_insn_list_same_p (deps->last_pending_memory_flush, + prev->last_pending_memory_flush)) + { + fprintf (file, " memory_flush("); + dump_rtx_insn_list (file, deps->last_pending_memory_flush); + fprintf (file, ")"); + } + if (!rtx_insn_list_same_p (deps->last_function_call, + prev->last_function_call)) + { + fprintf (file, " call("); + dump_rtx_insn_list (file, deps->last_function_call); + fprintf (file, ")"); + } + if (!rtx_insn_list_same_p (deps->last_function_call_may_noreturn, + prev->last_function_call_may_noreturn)) + { + fprintf (file, " noreturn("); + dump_rtx_insn_list (file, deps->last_function_call_may_noreturn); + fprintf (file, ")"); + } + fprintf (file, "\n"); + + fprintf (file, ";; sched_before_next"); + if (!rtx_insn_list_same_p (deps->sched_before_next_call, + prev->sched_before_next_call)) + { + fprintf (file, " call("); + dump_rtx_insn_list (file, deps->sched_before_next_call); + fprintf (file, ")"); + } + if (!rtx_insn_list_same_p (deps->sched_before_next_jump, + prev->sched_before_next_jump)) + { + fprintf (file, " jump("); + dump_rtx_insn_list (file, deps->sched_before_next_jump); + fprintf (file, ")"); + } + fprintf (file, " in_post_call_group_p(%d)\n", deps->in_post_call_group_p); + + fprintf (file, ";; last_debug_insn(%d) last_args_size(%d) last_prologue(", + deps->last_debug_insn ? INSN_UID (deps->last_debug_insn) : -1, + deps->last_args_size ? INSN_UID (deps->last_args_size) : -1); + dump_rtx_insn_list (file, deps->last_prologue); + fprintf (file, ") last_epilogue("); + dump_rtx_insn_list (file, deps->last_epilogue); + fprintf (file, ") last_logue_was_epilogue(%d)\n", + deps->last_logue_was_epilogue); + + int i; + + gcc_assert (deps->max_reg == prev->max_reg); + for (i = 0; i < deps->max_reg; ++i) + { + struct deps_reg *reg_last = &deps->reg_last[i]; + struct deps_reg *reg_prev = &prev->reg_last[i]; + + if (rtx_insn_list_same_p (reg_last->uses, reg_prev->uses) + && rtx_insn_list_same_p (reg_last->sets, reg_prev->sets) + && rtx_insn_list_same_p (reg_last->implicit_sets, + reg_prev->implicit_sets) + && rtx_insn_list_same_p (reg_last->control_uses, + reg_prev->control_uses) + && rtx_insn_list_same_p (reg_last->clobbers, + reg_prev->clobbers)) + continue; + + fprintf (file, ";; reg %u: uses(", i); + if (reg_last->uses_length >= param_max_pending_list_length) + fprintf (file, "%d insns!!!", reg_last->uses_length); + else + dump_rtx_insn_list (file, reg_last->uses); + if (reg_last->clobbers_length >= param_max_pending_list_length) + { + fprintf (file, ") clobbers("); + fprintf (file, "%d insns!!!", reg_last->clobbers_length); + } + else + { + fprintf (file, ") sets("); + dump_rtx_insn_list (file, reg_last->sets); + fprintf (file, ") implicit_sets("); + dump_rtx_insn_list (file, reg_last->implicit_sets); + fprintf (file, ") control_uses("); + dump_rtx_insn_list (file, reg_last->control_uses); + fprintf (file, ") clobbers("); + dump_rtx_insn_list (file, reg_last->clobbers); + } + fprintf (file, ")\n"); + } +} + +/* Dependency analysis state before current insn. + Used by dump_deps_desc_diff(). */ +static class deps_desc *sched_analysis_prev_deps; + /* Analyze an INSN with pattern X to find all dependencies. This analysis uses two main data structures: 1. reg_pending_* data contains effects of INSN on the register file, @@ -3627,6 +3807,16 @@ sched_analyze_insn (class deps_desc *deps, rtx x, rtx_insn *insn) deps->last_logue_was_epilogue = true; } } + + if (sched_verbose >= 8) + { + dump_deps_desc_diff (sched_dump, deps, sched_analysis_prev_deps); + reset_deps (sched_analysis_prev_deps, deps); + fprintf (sched_dump, ";; "); + dump_lists (sched_dump, insn, SD_LIST_BACK, + DUMP_LISTS_ALL | DUMP_DEP_PRO); + fprintf (sched_dump, "\n"); + } } /* Return TRUE if INSN might not always return normally (e.g. call exit, @@ -4305,6 +4495,9 @@ init_deps_global (void) sched_deps_info->note_mem_dep = haifa_note_mem_dep; sched_deps_info->note_dep = haifa_note_dep; } + + sched_analysis_prev_deps = XNEW (class deps_desc); + init_deps (sched_analysis_prev_deps, false); } /* Free everything used by the dependency analysis code. */ @@ -4316,6 +4509,10 @@ finish_deps_global (void) FREE_REG_SET (reg_pending_clobbers); FREE_REG_SET (reg_pending_uses); FREE_REG_SET (reg_pending_control_uses); + + free_deps (sched_analysis_prev_deps); + free (sched_analysis_prev_deps); + sched_analysis_prev_deps = NULL; } /* Estimate the weakness of dependence between MEM1 and MEM2. */ diff --git a/gcc/sched-int.h b/gcc/sched-int.h index 64a2f0bcff9..8a3109ce86e 100644 --- a/gcc/sched-int.h +++ b/gcc/sched-int.h @@ -455,7 +455,9 @@ struct deps_reg int clobbers_length; }; -/* Describe state of dependencies used during sched_analyze phase. */ +/* Describe state of dependencies used during sched_analyze phase. + Please consider updating sched-deps.cc:dump_deps_desc_diff() when adding + new fields. */ class deps_desc { public: @@ -499,6 +501,11 @@ public: large lists. */ int pending_flush_length; + /* Below are lists (and not just a single instructions) to allow inter-block + dependency analysis. Each block + may add a single insn to below lists, but when merging dependency + analysis context from multiple predecessor blocks, we may get a list. */ + /* The last insn upon which all memory references must depend. This is an insn which flushed the pending lists, creating a dependency between it and all previously pending memory references. This creates diff --git a/gcc/sched-rgn.cc b/gcc/sched-rgn.cc index da3ec0458ff..72f0a2de66e 100644 --- a/gcc/sched-rgn.cc +++ b/gcc/sched-rgn.cc @@ -2590,6 +2590,10 @@ static rtx_insn_list * concat_insn_list (rtx_insn_list *copy, rtx_insn_list *old) { if (!old) + /* concat_*_LIST() will return a reversed copy of COPY. Working with + reversed copies would complicate dependency dumping in + dump_deps_desc_diff(), which internally uses reset_deps() and + deps_join(). Therefore, use copy_INSN_LIST() when OLD is NULL. */ return copy_INSN_LIST (copy); return concat_INSN_LIST (copy, old); } @@ -2599,6 +2603,7 @@ static rtx_expr_list * concat_expr_list (rtx_expr_list *copy, rtx_expr_list *old) { if (!old) + /* See comment in concat_insn_list() above. */ return copy_EXPR_LIST (copy); return concat_EXPR_LIST (copy, old); }