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
>

Reply via email to