The attached is the revised patch. Bootstrap and regression tested in trunk on x86-64/linux.
Ok for checkin? Thanks, David On Wed, May 18, 2011 at 1:34 PM, Xinliang David Li <davi...@google.com> wrote: > Will fix the Changelog, and add documentation. > > Thanks, > > David > > > > On Wed, May 18, 2011 at 1:26 PM, Richard Guenther > <richard.guent...@gmail.com> wrote: >> On Wed, May 18, 2011 at 8:37 PM, David Li <davi...@google.com> wrote: >>> >>> In gcc, not all passes have user level control to turn it on/off, and >>> there is no way to flip on/off the pass for a subset of functions. I >>> implemented a generic option handling scheme in gcc to allow >>> disabling/enabling any gcc pass for any specified function(s). The >>> new options will be very useful for things like performance >>> experiments and bug triaging (gcc has dbgcnt mechanism, but not all >>> passes have the counter). >>> >>> The option syntax is very similar to -fdump- options. The following >>> are some examples: >>> >>> -fdisable-tree-ccp1 <--- disable ccp1 for all functions >>> -fenable-tree-cunroll=1 <--- enable complete unroll for the function >>> whose cgraphnode uid is 1 >>> -fdisable-rtl-gcse2=1:100,300,400:1000 <-- disable gcse2 for >>> functions at the following >>> ranges [1,1], [300,400], and >>> [400,1000] >>> -fdisable-tree-einline --> disable early inlining for all callers >>> -fdisable-ipa-inline --> disable ipa inlininig >>> >>> In the gcc dumps, the uid numbers are displayed in the function header. >>> >>> The options are intended to be used internally by gcc developers. >>> >>> Ok for trunk ? (There is a little LIPO specific change that can be removed). >>> >>> David >>> >>> 2011-05-18 David Li <davi...@google.com> >>> >>> * final.c (rest_of_clean_state): Call function header dumper. >>> * opts-global.c (handle_common_deferred_options): Handle new options. >>> * tree-cfg.c (gimple_dump_cfg): Call function header dumper. >>> * passes.c (register_one_dump_file): Call register_pass_name. >>> (pass_init_dump_file): Call function header dumper. >>> (execute_one_pass): Check explicit enable/disable flag. >>> (passr_hash): New function. >>> (passr_eq): >>> (register_pass_name): >>> (get_pass_by_name): >>> (pass_hash): >>> (pass_eq): >>> (enable_disable_pass): >>> (is_pass_explicitly_enabled_or_disabled): >>> (is_pass_explicitly_enabled): >>> (is_pass_explicitly_disabled): >> >> Bogus changelog entry. >> >> New options need documenting in doc/invoke.texi. >> >> Richard. >> >>> >>> Index: tree-pass.h >>> =================================================================== >>> --- tree-pass.h (revision 173635) >>> +++ tree-pass.h (working copy) >>> @@ -644,4 +644,12 @@ extern bool first_pass_instance; >>> /* Declare for plugins. */ >>> extern void do_per_function_toporder (void (*) (void *), void *); >>> >>> +extern void enable_disable_pass (const char *, bool); >>> +extern bool is_pass_explicitly_disabled (struct opt_pass *, tree); >>> +extern bool is_pass_explicitly_enabled (struct opt_pass *, tree); >>> +extern void register_pass_name (struct opt_pass *, const char *); >>> +extern struct opt_pass *get_pass_by_name (const char *); >>> +struct function; >>> +extern void pass_dump_function_header (FILE *, tree, struct function *); >>> + >>> #endif /* GCC_TREE_PASS_H */ >>> Index: final.c >>> =================================================================== >>> --- final.c (revision 173635) >>> +++ final.c (working copy) >>> @@ -4456,19 +4456,7 @@ rest_of_clean_state (void) >>> } >>> else >>> { >>> - const char *aname; >>> - struct cgraph_node *node = cgraph_node (current_function_decl); >>> - >>> - aname = (IDENTIFIER_POINTER >>> - (DECL_ASSEMBLER_NAME (current_function_decl))); >>> - fprintf (final_output, "\n;; Function (%s) %s\n\n", aname, >>> - node->frequency == NODE_FREQUENCY_HOT >>> - ? " (hot)" >>> - : node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED >>> - ? " (unlikely executed)" >>> - : node->frequency == NODE_FREQUENCY_EXECUTED_ONCE >>> - ? " (executed once)" >>> - : ""); >>> + pass_dump_function_header (final_output, current_function_decl, >>> cfun); >>> >>> flag_dump_noaddr = flag_dump_unnumbered = 1; >>> if (flag_compare_debug_opt || flag_compare_debug) >>> Index: common.opt >>> =================================================================== >>> --- common.opt (revision 173635) >>> +++ common.opt (working copy) >>> @@ -1018,6 +1018,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]-<pass>=range1+range2 disables an optimization pass >>> + >>> +fenable- >>> +Common Joined RejectNegative Var(common_deferred_options) Defer >>> +-fenable-[tree|rtl|ipa]-<pass>=range1+range2 enables an optimization pass >>> + >>> fdump- >>> Common Joined RejectNegative Var(common_deferred_options) Defer >>> -fdump-<type> Dump various compiler internals to a file >>> Index: opts-global.c >>> =================================================================== >>> --- opts-global.c (revision 173635) >>> +++ opts-global.c (working copy) >>> @@ -411,6 +411,12 @@ handle_common_deferred_options (void) >>> error ("unrecognized command line option %<-fdump-%s%>", >>> opt->arg); >>> break; >>> >>> + case OPT_fenable_: >>> + case OPT_fdisable_: >>> + enable_disable_pass (opt->arg, (opt->opt_index == OPT_fenable_? >>> + true : false)); >>> + break; >>> + >>> case OPT_ffixed_: >>> /* Deferred. */ >>> fix_register (opt->arg, 1, 1); >>> Index: tree-cfg.c >>> =================================================================== >>> --- tree-cfg.c (revision 173636) >>> +++ tree-cfg.c (working copy) >>> @@ -2090,11 +2090,7 @@ gimple_dump_cfg (FILE *file, int flags) >>> { >>> if (flags & TDF_DETAILS) >>> { >>> - const char *funcname >>> - = lang_hooks.decl_printable_name (current_function_decl, 2); >>> - >>> - fputc ('\n', file); >>> - fprintf (file, ";; Function %s\n\n", funcname); >>> + pass_dump_function_header (file, current_function_decl, cfun); >>> fprintf (file, ";; \n%d basic blocks, %d edges, last basic block >>> %d.\n\n", >>> n_basic_blocks, n_edges, last_basic_block); >>> >>> Index: passes.c >>> =================================================================== >>> --- passes.c (revision 173635) >>> +++ passes.c (working copy) >>> @@ -382,7 +382,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; >>> >>> @@ -411,6 +411,8 @@ register_one_dump_file (struct opt_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. */ >>> @@ -454,6 +456,298 @@ register_dump_files (struct opt_pass *pa >>> 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. */ >>> + >>> +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 (10, 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 >>> + gcc_assert ((*slot)->pass == pass); >>> +} >>> + >>> +/* Returns the pass with NAME. */ >>> + >>> +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 >>> +{ >>> + struct opt_pass *pass; >>> + unsigned int start; >>> + unsigned int last; >>> + struct uid_range *next; >>> +}; >>> + >>> +/* Hash function for pass structure. */ >>> + >>> +static hashval_t >>> +pass_hash (const void *s) >>> +{ >>> + const struct uid_range *const p = (const struct uid_range *const) s; >>> + return p->pass->static_pass_number; >>> +} >>> + >>> +/* Pass equal function */ >>> + >>> +static int >>> +pass_eq (const void *s1, const void *s2) >>> +{ >>> + const struct uid_range *const p1 = (const struct uid_range *const) s1; >>> + const struct uid_range *const p2 = (const struct uid_range *const) s2; >>> + return p1->pass->static_pass_number == p2->pass->static_pass_number; >>> +} >>> + >>> +htab_t enabled_pass_uid_range_tab = NULL; >>> +htab_t disabled_pass_uid_range_tab = NULL; >>> + >>> +/* Parse option string for -fdisable- and -fenabler- >>> + The syntax of the options: >>> + >>> + -fenable-<pass_name> >>> + -fdisable-<pass_name> >>> + >>> + -fenable-<pass_name>=s1:e1,s2:e2,... >>> + -fdisable-<pass_name>=s1:e1,s2:e2,... >>> +*/ >>> + >>> +void >>> +enable_disable_pass (const char *arg, bool is_enable) >>> +{ >>> + struct opt_pass *pass; >>> + htab_t the_tab; >>> + char *range_str, *phase_name; >>> + char *argstr = xstrdup (arg); >>> + >>> + range_str = strchr (argstr,'='); >>> + if (range_str) >>> + { >>> + *range_str = '\0'; >>> + range_str++; >>> + } >>> + >>> + phase_name = argstr; >>> + if (!*phase_name) >>> + { >>> + error ("Unrecognized option %s", is_enable ? "-fenable" : >>> "-fdisable"); >>> + free (argstr); >>> + return; >>> + } >>> + pass = get_pass_by_name (phase_name); >>> + if (!pass) >>> + { >>> + error ("Unknown pass %s specified in %s", >>> + phase_name, >>> + is_enable ? "-fenable" : "-fdisable"); >>> + free (argstr); >>> + return; >>> + } >>> + if (is_enable) >>> + { >>> + if (!enabled_pass_uid_range_tab) >>> + enabled_pass_uid_range_tab = htab_create (10, pass_hash, pass_eq, >>> NULL); >>> + the_tab = enabled_pass_uid_range_tab; >>> + } >>> + else >>> + { >>> + if (!disabled_pass_uid_range_tab) >>> + disabled_pass_uid_range_tab = htab_create (10, pass_hash, pass_eq, >>> NULL); >>> + the_tab = disabled_pass_uid_range_tab; >>> + } >>> + >>> + if (!range_str) >>> + { >>> + struct uid_range **slot; >>> + struct uid_range *new_range = XCNEW (struct uid_range); >>> + >>> + new_range->pass = pass; >>> + new_range->start = 0; >>> + new_range->last = (unsigned)-1; >>> + >>> + slot = (struct uid_range **) htab_find_slot (the_tab, new_range, >>> INSERT); >>> + new_range->next = *slot; >>> + *slot = new_range; >>> + inform (UNKNOWN_LOCATION, "%s pass %s for functions in the range of >>> [%u, %u]\n", >>> + is_enable? "Enable":"Disable", phase_name, new_range->start, >>> new_range->last); >>> + } >>> + else >>> + { >>> + char *next_range = NULL; >>> + char *one_range = range_str; >>> + char *end_val = NULL; >>> + >>> + do >>> + { >>> + struct uid_range **slot; >>> + struct uid_range *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->pass = pass; >>> + 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->pass = pass; >>> + new_range->start = (unsigned) start; >>> + new_range->last = (unsigned) last; >>> + } >>> + slot = (struct uid_range **) htab_find_slot (the_tab, new_range, >>> INSERT); >>> + new_range->next = *slot; >>> + *slot = new_range; >>> + inform (UNKNOWN_LOCATION, "%s pass %s for functions in the range >>> of [%u, %u]\n", >>> + is_enable? "Enable":"Disable", phase_name, >>> new_range->start, new_range->last); >>> + >>> + one_range = next_range; >>> + } while (next_range); >>> + } >>> + >>> + free (argstr); >>> +} >>> + >>> +/* Returns true if PASS is explicitly enabled/disabled for FUNC. */ >>> + >>> +static bool >>> +is_pass_explicitly_enabled_or_disabled (struct opt_pass *pass, >>> + tree func, htab_t tab) >>> +{ >>> + struct uid_range **slot, *range, key; >>> + int cgraph_uid; >>> + >>> + if (!tab) >>> + return false; >>> + >>> + key.pass = pass; >>> + slot = (struct uid_range **) htab_find_slot (tab, &key, NO_INSERT); >>> + if (!slot || !*slot) >>> + return false; >>> + >>> + cgraph_uid = func ? cgraph_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; >>> +} >>> + >>> +/* Returns true if PASS is explicitly enabled for FUNC. */ >>> + >>> +bool >>> +is_pass_explicitly_enabled (struct opt_pass *pass, tree func) >>> +{ >>> + return is_pass_explicitly_enabled_or_disabled (pass, func, >>> enabled_pass_uid_range_tab); >>> +} >>> + >>> +/* Returns true if PASS is explicitly disabled for FUNC. */ >>> + >>> +bool >>> +is_pass_explicitly_disabled (struct opt_pass *pass, tree func) >>> +{ >>> + return is_pass_explicitly_enabled_or_disabled (pass, func, >>> disabled_pass_uid_range_tab); >>> +} >>> + >>> + >>> /* Look at the static_pass_number and duplicate the pass >>> if it is already added to a list. */ >>> >>> @@ -1349,6 +1643,29 @@ verify_curr_properties (void *data) >>> } >>> #endif >>> >>> +void >>> +pass_dump_function_header (FILE *dump_file, tree fdecl, struct function >>> *fun) >>> +{ >>> + const char *dname, *aname; >>> + struct cgraph_node *node = cgraph_node (fdecl); >>> + dname = lang_hooks.decl_printable_name (fdecl, 2); >>> + aname = (IDENTIFIER_POINTER >>> + (DECL_ASSEMBLER_NAME (fdecl))); >>> + if (L_IPO_COMP_MODE) >>> + fprintf (dump_file, "\n;; Function %s (%s)[%d:%d][uid=%d]", dname, >>> aname, >>> + FUNC_DECL_MODULE_ID (fun), FUNC_DECL_FUNC_ID (fun), node->uid); >>> + else >>> + fprintf (dump_file, "\n;; Function %s (%s)[uid=%d]", dname, aname, >>> 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)" >>> + : ""); >>> +} >>> + >>> /* Initialize pass dump file. */ >>> /* This is non-static so that the plugins can use it. */ >>> >>> @@ -1362,26 +1679,7 @@ pass_init_dump_file (struct opt_pass *pa >>> dump_file_name = get_dump_file_name (pass->static_pass_number); >>> dump_file = dump_begin (pass->static_pass_number, &dump_flags); >>> if (dump_file && current_function_decl) >>> - { >>> - const char *dname, *aname; >>> - struct cgraph_node *node = cgraph_node (current_function_decl); >>> - dname = lang_hooks.decl_printable_name (current_function_decl, 2); >>> - aname = (IDENTIFIER_POINTER >>> - (DECL_ASSEMBLER_NAME (current_function_decl))); >>> - if (L_IPO_COMP_MODE) >>> - fprintf (dump_file, "\n;; Function %s (%s)[%d:%d]", dname, >>> aname, >>> - FUNC_DECL_MODULE_ID (cfun), FUNC_DECL_FUNC_ID (cfun)); >>> - else >>> - fprintf (dump_file, "\n;; Function %s (%s)", dname, aname); >>> - 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)" >>> - : ""); >>> - } >>> + pass_dump_function_header (dump_file, current_function_decl, cfun); >>> return initializing_dump; >>> } >>> else >>> @@ -1525,6 +1823,8 @@ execute_one_pass (struct opt_pass *pass) >>> { >>> bool initializing_dump; >>> unsigned int todo_after = 0; >>> + bool explicitly_enabled = false; >>> + bool explicitly_disabled = false; >>> >>> bool gate_status; >>> >>> @@ -1535,11 +1835,15 @@ execute_one_pass (struct opt_pass *pass) >>> else >>> gcc_assert (cfun && current_function_decl); >>> >>> + explicitly_enabled = is_pass_explicitly_enabled (pass, >>> current_function_decl); >>> + explicitly_disabled = is_pass_explicitly_disabled (pass, >>> current_function_decl); >>> + >>> current_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 = !explicitly_disabled && (gate_status || >>> explicitly_enabled); >>> >>> /* Override gate with plugin. */ >>> invoke_plugin_callbacks (PLUGIN_OVERRIDE_GATE, &gate_status); >>> >>> -- >>> This patch is available for review at http://codereview.appspot.com/4550056 >>> >> >
Index: doc/invoke.texi =================================================================== --- doc/invoke.texi (revision 173837) +++ doc/invoke.texi (working copy) @@ -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,10 @@ 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-ipa-@var{pass} @gol +-fenable-rtl-@var{pass} @gol +-fenable-rtl-@var{pass}=@var{range-list} @gol +-fenable-tree-@var{pass} @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 @@ -4993,7 +5002,36 @@ All debug counters have the initial uppe 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. + +@item -fdisable-ipa-@var{pass} +@opindex fdisable-ipa +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} +@itemx -fdisable-rtl-@var{pass}=@var{range-list} +@opindex fdisable-rtl +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} +@itemx -fdisable-tree-@var{pass}=@var{range-list} +@opindex fdisable-tree +Disable 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 + +@end smallexample @item -d@var{letters} @itemx -fdump-rtl-@var{pass} @@ -5583,6 +5621,20 @@ is made by appending @file{.vrp} to the Enable all the available tree dumps with the flags provided in this option. @end table +@item -fenable-ipa-@var{pass} +@opindex fenable-ipa +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} +@itemx -fenable-rtl-@var{pass}=@var{range-list} +@opindex fenable-rtl +Enable rtl pass @var{pass}. See @option{-fdisable-rtl} for option argument description and examples. + +@item -fenable-tree-@var{pass} +@itemx -fenable-tree-@var{pass}=@var{range-list} +@opindex fenable-tree +Enable tree pass @var{pass}. See @option{-fdisable-rtl} for the description of option arguments and examples. + @item -ftree-vectorizer-verbose=@var{n} @opindex ftree-vectorizer-verbose This option controls the amount of debugging output the vectorizer prints. Index: tree-pass.h =================================================================== --- tree-pass.h (revision 173837) +++ tree-pass.h (working copy) @@ -637,4 +637,12 @@ extern bool first_pass_instance; /* Declare for plugins. */ extern void do_per_function_toporder (void (*) (void *), void *); +extern void enable_disable_pass (const char *, bool); +extern bool is_pass_explicitly_disabled (struct opt_pass *, tree); +extern bool is_pass_explicitly_enabled (struct opt_pass *, tree); +extern void register_pass_name (struct opt_pass *, const char *); +extern struct opt_pass *get_pass_by_name (const char *); +struct function; +extern void pass_dump_function_header (FILE *, tree, struct function *); + #endif /* GCC_TREE_PASS_H */ Index: final.c =================================================================== --- final.c (revision 173837) +++ final.c (working copy) @@ -4360,20 +4360,7 @@ rest_of_clean_state (void) } else { - const char *aname; - struct cgraph_node *node = cgraph_get_node (current_function_decl); - - aname = (IDENTIFIER_POINTER - (DECL_ASSEMBLER_NAME (current_function_decl))); - fprintf (final_output, "\n;; Function (%s) %s\n\n", aname, - node->frequency == NODE_FREQUENCY_HOT - ? " (hot)" - : node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED - ? " (unlikely executed)" - : node->frequency == NODE_FREQUENCY_EXECUTED_ONCE - ? " (executed once)" - : ""); - + pass_dump_function_header (final_output, current_function_decl, cfun); flag_dump_noaddr = flag_dump_unnumbered = 1; if (flag_compare_debug_opt || flag_compare_debug) dump_flags |= TDF_NOUID; Index: tree-ssa-loop-ivopts.c =================================================================== --- tree-ssa-loop-ivopts.c (revision 173837) +++ tree-ssa-loop-ivopts.c (working copy) @@ -3968,7 +3968,7 @@ get_computation_cost_at (struct ivopts_d int *inv_expr_id) { tree ubase = use->iv->base, ustep = use->iv->step; - tree cbase, cstep; + tree cbase, cstep, cbase_strip; tree utype = TREE_TYPE (ubase), ctype; unsigned HOST_WIDE_INT cstepi, offset = 0; HOST_WIDE_INT ratio, aratio; @@ -4026,6 +4026,13 @@ get_computation_cost_at (struct ivopts_d if (!constant_multiple_of (ustep, cstep, &rat)) return infinite_cost; + cbase_strip = STRIP_NOPS (cbase); + /* Avoid confusing aliaser. */ + if (TREE_CODE (cbase_strip) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (cbase_strip, 0)) == VAR_DECL + && (HOST_WIDE_INT) cstepi < 0) + return infinite_cost; + if (double_int_fits_in_shwi_p (rat)) ratio = double_int_to_shwi (rat); else Index: common.opt =================================================================== --- common.opt (revision 173837) +++ common.opt (working copy) @@ -974,6 +974,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]-<pass>=range1+range2 disables an optimization pass + +fenable- +Common Joined RejectNegative Var(common_deferred_options) Defer +-fenable-[tree|rtl|ipa]-<pass>=range1+range2 enables an optimization pass + fdump- Common Joined RejectNegative Var(common_deferred_options) Defer -fdump-<type> Dump various compiler internals to a file Index: opts-global.c =================================================================== --- opts-global.c (revision 173837) +++ opts-global.c (working copy) @@ -370,6 +370,12 @@ handle_common_deferred_options (void) error ("unrecognized command line option %<-fdump-%s%>", opt->arg); break; + case OPT_fenable_: + case OPT_fdisable_: + enable_disable_pass (opt->arg, (opt->opt_index == OPT_fenable_? + true : false)); + break; + case OPT_ffixed_: /* Deferred. */ fix_register (opt->arg, 1, 1); Index: tree-cfg.c =================================================================== --- tree-cfg.c (revision 173837) +++ tree-cfg.c (working copy) @@ -2052,11 +2052,7 @@ gimple_dump_cfg (FILE *file, int flags) { if (flags & TDF_DETAILS) { - const char *funcname - = lang_hooks.decl_printable_name (current_function_decl, 2); - - fputc ('\n', file); - fprintf (file, ";; Function %s\n\n", funcname); + pass_dump_function_header (file, current_function_decl, cfun); fprintf (file, ";; \n%d basic blocks, %d edges, last basic block %d.\n\n", n_basic_blocks, n_edges, last_basic_block); Index: passes.c =================================================================== --- passes.c (revision 173837) +++ passes.c (working copy) @@ -375,7 +375,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 +404,8 @@ register_one_dump_file (struct opt_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 +449,317 @@ register_dump_files (struct opt_pass *pa 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. */ + +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 (10, 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. */ + +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 +{ + struct opt_pass *pass; + unsigned int start; + unsigned int last; + struct uid_range *next; +}; + +/* Hash function for pass structure. */ + +static hashval_t +pass_hash (const void *s) +{ + const struct uid_range *const p = (const struct uid_range *const) s; + return p->pass->static_pass_number; +} + +/* Pass equal function */ + +static int +pass_eq (const void *s1, const void *s2) +{ + const struct uid_range *const p1 = (const struct uid_range *const) s1; + const struct uid_range *const p2 = (const struct uid_range *const) s2; + return p1->pass->static_pass_number == p2->pass->static_pass_number; +} + +htab_t enabled_pass_uid_range_tab = NULL; +htab_t disabled_pass_uid_range_tab = NULL; + +/* Parse option string for -fdisable- and -fenable- + The syntax of the options: + + -fenable-<pass_name> + -fdisable-<pass_name> + + -fenable-<pass_name>=s1:e1,s2:e2,... + -fdisable-<pass_name>=s1:e1,s2:e2,... +*/ + +void +enable_disable_pass (const char *arg, bool is_enable) +{ + struct opt_pass *pass; + htab_t the_tab; + char *range_str, *phase_name; + char *argstr = xstrdup (arg); + + 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) + { + 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) + { + if (!enabled_pass_uid_range_tab) + enabled_pass_uid_range_tab = htab_create (10, pass_hash, pass_eq, NULL); + the_tab = enabled_pass_uid_range_tab; + } + else + { + if (!disabled_pass_uid_range_tab) + disabled_pass_uid_range_tab = htab_create (10, pass_hash, + pass_eq, NULL); + the_tab = disabled_pass_uid_range_tab; + } + + if (!range_str) + { + struct uid_range **slot; + struct uid_range *new_range = XCNEW (struct uid_range); + + new_range->pass = pass; + new_range->start = 0; + new_range->last = (unsigned)-1; + + slot = (struct uid_range **) htab_find_slot (the_tab, new_range, INSERT); + new_range->next = *slot; + *slot = 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 + { + struct uid_range **slot; + struct uid_range *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->pass = pass; + 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->pass = pass; + new_range->start = (unsigned) start; + new_range->last = (unsigned) last; + } + slot = (struct uid_range **) htab_find_slot (the_tab, new_range, + INSERT); + new_range->next = *slot; + *slot = 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); +} + +/* Returns true if PASS is explicitly enabled/disabled for FUNC. */ + +static bool +is_pass_explicitly_enabled_or_disabled (struct opt_pass *pass, + tree func, htab_t tab) +{ + struct uid_range **slot, *range, key; + int cgraph_uid; + + if (!tab) + return false; + + key.pass = pass; + slot = (struct uid_range **) htab_find_slot (tab, &key, NO_INSERT); + if (!slot || !*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; +} + +/* Returns true if PASS is explicitly enabled for FUNC. */ + +bool +is_pass_explicitly_enabled (struct opt_pass *pass, tree func) +{ + return is_pass_explicitly_enabled_or_disabled (pass, func, + enabled_pass_uid_range_tab); +} + +/* Returns true if PASS is explicitly disabled for FUNC. */ + +bool +is_pass_explicitly_disabled (struct opt_pass *pass, tree func) +{ + return is_pass_explicitly_enabled_or_disabled (pass, func, + disabled_pass_uid_range_tab); +} + + /* Look at the static_pass_number and duplicate the pass if it is already added to a list. */ @@ -1329,6 +1642,26 @@ verify_curr_properties (void *data) } #endif +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)" + : ""); +} + /* Initialize pass dump file. */ /* This is non-static so that the plugins can use it. */ @@ -1342,21 +1675,7 @@ pass_init_dump_file (struct opt_pass *pa dump_file_name = get_dump_file_name (pass->static_pass_number); dump_file = dump_begin (pass->static_pass_number, &dump_flags); if (dump_file && current_function_decl) - { - const char *dname, *aname; - struct cgraph_node *node = cgraph_get_node (current_function_decl); - dname = lang_hooks.decl_printable_name (current_function_decl, 2); - aname = (IDENTIFIER_POINTER - (DECL_ASSEMBLER_NAME (current_function_decl))); - fprintf (dump_file, "\n;; Function %s (%s)%s\n\n", dname, aname, - node->frequency == NODE_FREQUENCY_HOT - ? " (hot)" - : node->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED - ? " (unlikely executed)" - : node->frequency == NODE_FREQUENCY_EXECUTED_ONCE - ? " (executed once)" - : ""); - } + pass_dump_function_header (dump_file, current_function_decl, cfun); return initializing_dump; } else @@ -1500,6 +1819,8 @@ execute_one_pass (struct opt_pass *pass) { bool initializing_dump; unsigned int todo_after = 0; + bool explicitly_enabled = false; + bool explicitly_disabled = false; bool gate_status; @@ -1510,11 +1831,15 @@ execute_one_pass (struct opt_pass *pass) else gcc_assert (cfun && current_function_decl); + explicitly_enabled = is_pass_explicitly_enabled (pass, current_function_decl); + explicitly_disabled = is_pass_explicitly_disabled (pass, current_function_decl); + current_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 = !explicitly_disabled && (gate_status || explicitly_enabled); /* Override gate with plugin. */ invoke_plugin_callbacks (PLUGIN_OVERRIDE_GATE, &gate_status);