From 226c52aafbfd114a846d7e8a01b76ed92ebb2441 Mon Sep 17 00:00:00 2001 From: Xinliang David Li Date: Mon, 30 May 2011 04:38:42 +0000 Subject: [PATCH] New option to disable/enable optimization passes. From-SVN: r174423 --- gcc/ChangeLog | 15 ++ gcc/common.opt | 8 + gcc/doc/invoke.texi | 76 +++++++++- gcc/opts-global.c | 8 + gcc/passes.c | 322 +++++++++++++++++++++++++++++++++++++++- gcc/tree-pass.h | 3 + gcc/tree-pretty-print.c | 22 +++ 7 files changed, 452 insertions(+), 2 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 02c1bf734ce..ad6be6da202 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,18 @@ +2011-05-29 Xinliang David Li + + * opts-global.c (handle_common_deferred_options): Handle new options. + * passes.c (register_one_dump_file): Call register_pass_name. + (execute_one_pass): Check explicit enable/disable flag. + (passr_hash): New function. + (passr_eq): Ditto. + (register_pass_name): Ditto. + (get_pass_by_name): Ditto. + (pass_hash): Ditto. + (pass_eq): Ditto. + (enable_pass): Ditto. + (disable_pass): Ditto. + (is_pass_explicitly_enabled_or_disabled): Ditto. + 2011-05-29 Uros Bizjak * config/i386/i386.md (*movoi_internal_avx): Use diff --git a/gcc/common.opt b/gcc/common.opt index 3ee9ded4624..5a5360785bd 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -985,6 +985,14 @@ fdiagnostics-show-option Common Var(flag_diagnostics_show_option) Init(1) Amend appropriate diagnostic messages with the command line option that controls them +fdisable- +Common Joined RejectNegative Var(common_deferred_options) Defer +-fdisable-[tree|rtl|ipa]-=range1+range2 disables an optimization pass + +fenable- +Common Joined RejectNegative Var(common_deferred_options) Defer +-fenable-[tree|rtl|ipa]-=range1+range2 enables an optimization pass + fdump- Common Joined RejectNegative Var(common_deferred_options) Defer -fdump- Dump various compiler internals to a file diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index f53d6107114..4b54c4abdae 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -282,6 +282,11 @@ Objective-C and Objective-C++ Dialects}. @xref{Debugging Options,,Options for Debugging Your Program or GCC}. @gccoptlist{-d@var{letters} -dumpspecs -dumpmachine -dumpversion @gol -fdbg-cnt-list -fdbg-cnt=@var{counter-value-list} @gol +-fdisable-ipa-@var{pass_name} @gol +-fdisable-rtl-@var{pass_name} @gol +-fdisable-rtl-@var{pass-name}=@var{range-list} @gol +-fdisable-tree-@var{pass_name} @gol +-fdisable-tree-@var{pass-name}=@var{range-list} @gol -fdump-noaddr -fdump-unnumbered -fdump-unnumbered-links @gol -fdump-translation-unit@r{[}-@var{n}@r{]} @gol -fdump-class-hierarchy@r{[}-@var{n}@r{]} @gol @@ -313,6 +318,8 @@ Objective-C and Objective-C++ Dialects}. -fcompare-debug@r{[}=@var{opts}@r{]} -fcompare-debug-second @gol -feliminate-dwarf2-dups -feliminate-unused-debug-types @gol -feliminate-unused-debug-symbols -femit-class-debug-always @gol +-fenable-@var{kind}-@var{pass} @gol +-fenable-@var{kind}-@var{pass}=@var{range-list} @gol -fdebug-types-section @gol -fmem-report -fpre-ipa-mem-report -fpost-ipa-mem-report -fprofile-arcs @gol -frandom-seed=@var{string} -fsched-verbose=@var{n} @gol @@ -5017,6 +5024,7 @@ more closely, if you do not optimize. @opindex fdbg-cnt-list Print the name and the counter upper bound for all debug counters. + @item -fdbg-cnt=@var{counter-value-list} @opindex fdbg-cnt Set the internal debug counter upper bound. @var{counter-value-list} @@ -5026,7 +5034,73 @@ All debug counters have the initial upper bound of @var{UINT_MAX}, thus dbg_cnt() returns true always unless the upper bound is set by this option. e.g. With -fdbg-cnt=dce:10,tail_call:0 dbg_cnt(dce) will return true only for first 10 invocations -and dbg_cnt(tail_call) will return false always. + +@itemx -fenable-@var{kind}-@var{pass} +@itemx -fdisable-@var{kind}-@var{pass}=@var{range-list} +@opindex fdisable- +@opindex fenable- + +This is a set of debugging options that are used to explicitly disable/enable +optimization passes. For compiler users, regular options for enabling/disabling +passes should be used instead. + +@itemize + +@item -fdisable-ipa-@var{pass} +Disable ipa pass @var{pass}. @var{pass} is the pass name. If the same pass is +statically invoked in the compiler multiple times, the pass name should be +appended with a sequential number starting from 1. + +@item -fdisable-rtl-@var{pass} +@item -fdisable-rtl-@var{pass}=@var{range-list} +Disable rtl pass @var{pass}. @var{pass} is the pass name. If the same pass is +statically invoked in the compiler multiple times, the pass name should be +appended with a sequential number starting from 1. @var{range-list} is a comma +seperated list of function ranges. Each range is a number pair seperated by a colon. +The range is inclusive in both ends. If the range is trivial, the number pair can be +simplified a a single number. If the function's cgraph node's @var{uid} is falling +within one of the specified ranges, the @var{pass} is disabled for that function. +The @var{uid} is shown in the function header of a dump file. + +@item -fdisable-tree-@var{pass} +@item -fdisable-tree-@var{pass}=@var{range-list} +Disable tree pass @var{pass}. See @option{-fdisable-rtl} for the description of +option arguments. + +@item -fenable-ipa-@var{pass} +Enable ipa pass @var{pass}. @var{pass} is the pass name. If the same pass is +statically invoked in the compiler multiple times, the pass name should be +appended with a sequential number starting from 1. + +@item -fenable-rtl-@var{pass} +@item -fenable-rtl-@var{pass}=@var{range-list} +Enable rtl pass @var{pass}. See @option{-fdisable-rtl} for option argument +description and examples. + +@item -fenable-tree-@var{pass} +@item -fenable-tree-@var{pass}=@var{range-list} +Enable tree pass @var{pass}. See @option{-fdisable-rtl} for the description +of option arguments. + +@smallexample + +# disable ccp1 for all functions + -fdisable-tree-ccp1 +# disable complete unroll for function whose cgraph node uid is 1 + -fenable-tree-cunroll=1 +# disable gcse2 for functions at the following ranges [1,1], +# [300,400], and [400,1000] + -fdisable-rtl-gcse2=1:100,300,400:1000 +# disable early inlining + -fdisable-tree-einline +# disable ipa inlining + -fdisable-ipa-inline +# enable tree full unroll + -fenable-tree-unroll + +@end smallexample + +@end itemize @item -d@var{letters} @itemx -fdump-rtl-@var{pass} diff --git a/gcc/opts-global.c b/gcc/opts-global.c index 1134e3a2745..6fdc9519ca9 100644 --- a/gcc/opts-global.c +++ b/gcc/opts-global.c @@ -370,6 +370,14 @@ handle_common_deferred_options (void) error ("unrecognized command line option %<-fdump-%s%>", opt->arg); break; + case OPT_fenable_: + case OPT_fdisable_: + if (opt->opt_index == OPT_fenable_) + enable_pass (opt->arg); + else + disable_pass (opt->arg); + break; + case OPT_ffixed_: /* Deferred. */ fix_register (opt->arg, 1, 1); diff --git a/gcc/passes.c b/gcc/passes.c index e176d1c05ba..4cfc4d37ee6 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -97,6 +97,8 @@ along with GCC; see the file COPYING3. If not see The variable current_pass is also used for statistics and plugins. */ struct opt_pass *current_pass; +static void register_pass_name (struct opt_pass *, const char *); + /* Call from anywhere to find out what pass this is. Useful for printing out debugging information deep inside an service routine. */ @@ -375,7 +377,7 @@ void register_one_dump_file (struct opt_pass *pass) { char *dot_name, *flag_name, *glob_name; - const char *name, *prefix; + const char *name, *full_name, *prefix; char num[10]; int flags, id; @@ -404,6 +406,8 @@ register_one_dump_file (struct opt_pass *pass) glob_name = concat (prefix, name, NULL); id = dump_register (dot_name, flag_name, glob_name, flags); set_pass_for_id (id, pass); + full_name = concat (prefix, pass->name, num, NULL); + register_pass_name (pass, full_name); } /* Recursive worker function for register_dump_files. */ @@ -447,6 +451,297 @@ register_dump_files (struct opt_pass *pass,int properties) register_dump_files_1 (pass, properties); } +struct pass_registry +{ + const char* unique_name; + struct opt_pass *pass; +}; + +/* Pass registry hash function. */ + +static hashval_t +passr_hash (const void *p) +{ + const struct pass_registry *const s = (const struct pass_registry *const) p; + return htab_hash_string (s->unique_name); +} + +/* Hash equal function */ + +static int +passr_eq (const void *p1, const void *p2) +{ + const struct pass_registry *const s1 = (const struct pass_registry *const) p1; + const struct pass_registry *const s2 = (const struct pass_registry *const) p2; + + return !strcmp (s1->unique_name, s2->unique_name); +} + +static htab_t pass_name_tab = NULL; + +/* Register PASS with NAME. */ + +static void +register_pass_name (struct opt_pass *pass, const char *name) +{ + struct pass_registry **slot; + struct pass_registry pr; + + if (!pass_name_tab) + pass_name_tab = htab_create (256, passr_hash, passr_eq, NULL); + + pr.unique_name = name; + slot = (struct pass_registry **) htab_find_slot (pass_name_tab, &pr, INSERT); + if (!*slot) + { + struct pass_registry *new_pr; + + new_pr = XCNEW (struct pass_registry); + new_pr->unique_name = xstrdup (name); + new_pr->pass = pass; + *slot = new_pr; + } + else + return; /* Ignore plugin passes. */ +} + +/* Returns the pass with NAME. */ + +static struct opt_pass * +get_pass_by_name (const char *name) +{ + struct pass_registry **slot, pr; + + gcc_assert (pass_name_tab); + pr.unique_name = name; + slot = (struct pass_registry **) htab_find_slot (pass_name_tab, + &pr, NO_INSERT); + + if (!slot || !*slot) + return NULL; + + return (*slot)->pass; +} + + +/* Range [start, last]. */ + +struct uid_range +{ + unsigned int start; + unsigned int last; + struct uid_range *next; +}; + +typedef struct uid_range *uid_range_p; + +DEF_VEC_P(uid_range_p); +DEF_VEC_ALLOC_P(uid_range_p, heap); + +static VEC(uid_range_p, heap) *enabled_pass_uid_range_tab = NULL; +static VEC(uid_range_p, heap) *disabled_pass_uid_range_tab = NULL; + +/* Parse option string for -fdisable- and -fenable- + The syntax of the options: + + -fenable- + -fdisable- + + -fenable-=s1:e1,s2:e2,... + -fdisable-=s1:e1,s2:e2,... +*/ + +static void +enable_disable_pass (const char *arg, bool is_enable) +{ + struct opt_pass *pass; + char *range_str, *phase_name; + char *argstr = xstrdup (arg); + VEC(uid_range_p, heap) **tab = 0; + + range_str = strchr (argstr,'='); + if (range_str) + { + *range_str = '\0'; + range_str++; + } + + phase_name = argstr; + if (!*phase_name) + { + if (is_enable) + error ("unrecognized option -fenable"); + else + error ("unrecognized option -fdisable"); + free (argstr); + return; + } + pass = get_pass_by_name (phase_name); + if (!pass || pass->static_pass_number == -1) + { + if (is_enable) + error ("unknown pass %s specified in -fenable", phase_name); + else + error ("unknown pass %s specified in -fdisble", phase_name); + free (argstr); + return; + } + + if (is_enable) + tab = &enabled_pass_uid_range_tab; + else + tab = &disabled_pass_uid_range_tab; + + if ((unsigned) pass->static_pass_number >= VEC_length (uid_range_p, *tab)) + VEC_safe_grow_cleared (uid_range_p, heap, + *tab, pass->static_pass_number + 1); + + if (!range_str) + { + uid_range_p slot; + uid_range_p new_range = XCNEW (struct uid_range); + + new_range->start = 0; + new_range->last = (unsigned)-1; + + slot = VEC_index (uid_range_p, *tab, pass->static_pass_number); + new_range->next = slot; + VEC_replace (uid_range_p, *tab, pass->static_pass_number, + new_range); + if (is_enable) + inform (UNKNOWN_LOCATION, "enable pass %s for functions in the range " + "of [%u, %u]", phase_name, new_range->start, new_range->last); + else + inform (UNKNOWN_LOCATION, "disable pass %s for functions in the range " + "of [%u, %u]", phase_name, new_range->start, new_range->last); + } + else + { + char *next_range = NULL; + char *one_range = range_str; + char *end_val = NULL; + + do + { + uid_range_p slot; + uid_range_p new_range; + char *invalid = NULL; + long start; + + next_range = strchr (one_range, ','); + if (next_range) + { + *next_range = '\0'; + next_range++; + } + + end_val = strchr (one_range, ':'); + if (end_val) + { + *end_val = '\0'; + end_val++; + } + start = strtol (one_range, &invalid, 10); + if (*invalid || start < 0) + { + error ("Invalid range %s in option %s", + one_range, + is_enable ? "-fenable" : "-fdisable"); + free (argstr); + return; + } + if (!end_val) + { + new_range = XCNEW (struct uid_range); + new_range->start = (unsigned) start; + new_range->last = (unsigned) start; + } + else + { + long last = strtol (end_val, &invalid, 10); + if (*invalid || last < start) + { + error ("Invalid range %s in option %s", + end_val, + is_enable ? "-fenable" : "-fdisable"); + free (argstr); + return; + } + new_range = XCNEW (struct uid_range); + new_range->start = (unsigned) start; + new_range->last = (unsigned) last; + } + + slot = VEC_index (uid_range_p, *tab, pass->static_pass_number); + new_range->next = slot; + VEC_replace (uid_range_p, *tab, pass->static_pass_number, + new_range); + + if (is_enable) + inform (UNKNOWN_LOCATION, + "enable pass %s for functions in the range of [%u, %u]", + phase_name, new_range->start, new_range->last); + else + inform (UNKNOWN_LOCATION, + "disable pass %s for functions in the range of [%u, %u]", + phase_name, new_range->start, new_range->last); + + one_range = next_range; + } while (next_range); + } + + free (argstr); +} + +/* Enable pass specified by ARG. */ + +void +enable_pass (const char *arg) +{ + enable_disable_pass (arg, true); +} + +/* Disable pass specified by ARG. */ + +void +disable_pass (const char *arg) +{ + enable_disable_pass (arg, false); +} + +/* Returns true if PASS is explicitly enabled/disabled for FUNC. */ + +static bool +is_pass_explicitly_enabled_or_disabled (struct opt_pass *pass, + tree func, + VEC(uid_range_p, heap) *tab) +{ + uid_range_p slot, range; + int cgraph_uid; + + if (!tab + || (unsigned) pass->static_pass_number >= VEC_length (uid_range_p, tab) + || pass->static_pass_number == -1) + return false; + + slot = VEC_index (uid_range_p, tab, pass->static_pass_number); + if (!slot) + return false; + + cgraph_uid = func ? cgraph_get_node (func)->uid : 0; + + range = slot; + while (range) + { + if ((unsigned) cgraph_uid >= range->start + && (unsigned) cgraph_uid <= range->last) + return true; + range = range->next; + } + + return false; +} + /* Look at the static_pass_number and duplicate the pass if it is already added to a list. */ @@ -1493,6 +1788,30 @@ execute_all_ipa_transforms (void) } } +/* Check if PASS is explicitly disabled or enabled and return + the gate status. FUNC is the function to be processed, and + GATE_STATUS is the gate status determined by pass manager by + default. */ + +static bool +override_gate_status (struct opt_pass *pass, tree func, bool gate_status) +{ + bool explicitly_enabled = false; + bool explicitly_disabled = false; + + explicitly_enabled + = is_pass_explicitly_enabled_or_disabled (pass, func, + enabled_pass_uid_range_tab); + explicitly_disabled + = is_pass_explicitly_enabled_or_disabled (pass, func, + disabled_pass_uid_range_tab); + + gate_status = !explicitly_disabled && (gate_status || explicitly_enabled); + + return gate_status; +} + + /* Execute PASS. */ bool @@ -1515,6 +1834,7 @@ execute_one_pass (struct opt_pass *pass) /* Check whether gate check should be avoided. User controls the value of the gate through the parameter "gate_status". */ gate_status = (pass->gate == NULL) ? true : pass->gate(); + gate_status = override_gate_status (pass, current_function_decl, gate_status); /* Override gate with plugin. */ invoke_plugin_callbacks (PLUGIN_OVERRIDE_GATE, &gate_status); diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 9520c178c96..daf7202f4f6 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -637,4 +637,7 @@ extern bool first_pass_instance; /* Declare for plugins. */ extern void do_per_function_toporder (void (*) (void *), void *); +extern void disable_pass (const char *); +extern void enable_pass (const char *); + #endif /* GCC_TREE_PASS_H */ diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c index f2f5a220018..b62417a7797 100644 --- a/gcc/tree-pretty-print.c +++ b/gcc/tree-pretty-print.c @@ -3013,3 +3013,25 @@ pp_base_tree_identifier (pretty_printer *pp, tree id) pp_append_text (pp, IDENTIFIER_POINTER (id), IDENTIFIER_POINTER (id) + IDENTIFIER_LENGTH (id)); } + +#if 0 +void +pass_dump_function_header (FILE *dump_file, tree fdecl, struct function *fun) +{ + const char *dname, *aname; + struct cgraph_node *node = cgraph_get_node (fdecl); + dname = lang_hooks.decl_printable_name (fdecl, 2); + aname = (IDENTIFIER_POINTER + (DECL_ASSEMBLER_NAME (fdecl))); + fprintf (dump_file, "\n;; Function %s (%s)[fundef_no:%d][uid=%d]", + dname, aname, fun->funcdef_no, node->uid); + fprintf (dump_file, "%s\n\n", + node->frequency == NODE_FREQUENCY_HOT + ? " (hot)" + : node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED + ? " (unlikely executed)" + : node->frequency == NODE_FREQUENCY_EXECUTED_ONCE + ? " (executed once)" + : ""); +} +#endif -- 2.30.2