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 >