On Wed, May 18, 2011 at 8:37 PM, David Li <[email protected]> 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 <[email protected]>
>
> * 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
>