<[email protected]> writes:
> From: Kyrylo Tkachov <[email protected]>
>
> init_all_optabs evaluated one HAVE_* macro per optab pattern.  That is 3328
> assignments on aarch64 and 14830 on riscv64.  genopinit already worked around
> the cost of compiling them by splitting them into functions of 1000
> statements each, with the comment "In order to prevent pathological
> situations in dataflow analysis".
>
> genflags.cc derives HAVE_<name> from the pattern's C test, and defines it as
> 1 when that test is statically true, so patterns whose tests agree after the
> same normalisation also agree on the definition of HAVE_<name>.  Targets have
> far fewer distinct tests than patterns.  Number the distinct tests, emit a
> pattern to test map, and evaluate each test once.
>
>   static void
>   init_optabs_00 (struct target_optabs *optabs)
>   {
>     bool *ena = optabs->pat_enable;
>     ena[0] = HAVE_extendqihi2;
>     ...                                 /* 1000 of these, four times */
>   }
>
> becomes
>
>   static const unsigned char pat_cond[NUM_OPTAB_PATTERNS] = { 0, 0, ... };
>
>   void
>   init_all_optabs (struct target_optabs *optabs)
>   {
>     const bool cond[] = {
>       !!HAVE_extendqihi2,
>       ...                               /* one per distinct condition */
>     };
>     bool *ena = optabs->pat_enable;
>     for (unsigned int i = 0; i < NUM_OPTAB_PATTERNS; ++i)
>       ena[i] = cond[pat_cond[i]];
>   }
>
> The conditions stay local to init_all_optabs, so every call evaluates them
> for the current target options, and each pat_enable entry stays independent
> for swap_optab_enable.
>
> Distinct conditions per target: aarch64 136 of 3328 patterns, riscv64 986 of
> 14830, x86_64 346 of 2975, i686 288 of 2614, avr 30 of 926.
>
> On aarch64 insn-opinit.cc shrinks from 279KB to 174KB.  Compiling that file
> on its own with the host g++ 11.4.0 at -g -O2 takes 93% less wall time
> and 65% less peak memory, 609MB down to 216MB.
> init_all_optabs also gets cheaper to run: it executes once per target_optabs
> initialisation, so each #pragma GCC target and each target attribute now costs
> 136 condition evaluations instead of 3328.
>
> Checked on the five targets above that every pattern's HAVE_ definition text
> matches that of its group representative, and that no group holds two
> different texts.
>
> Bootstrapped on aarch64-none-linux-gnu.
> Ok for trunk?
>
> gcc/ChangeLog:
>
>       * genopinit.cc (struct pattern_info): New.
>       (patterns): Use it.
>       (gen_insn): Record the pattern's C test.
>       (pattern_cmp): Use pattern_info.
>       (narrowest_uint_type): New function.
>       (main): Emit a condition-number table and a loop instead of one
>       assignment per pattern, and drop the split into 1000-pattern
>       functions.
>
> Suggested-by: Tamar Christina <[email protected]>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/genopinit.cc | 112 ++++++++++++++++++++++++++++++-----------------
>  1 file changed, 73 insertions(+), 39 deletions(-)
>
> diff --git a/gcc/genopinit.cc b/gcc/genopinit.cc
> index 62eaf5bd378..4a8cfed7c27 100644
> --- a/gcc/genopinit.cc
> +++ b/gcc/genopinit.cc
> @@ -35,22 +35,37 @@ static const char * const rtx_upname[] = {
>  
>  #undef DEF_RTL_EXPR
>  
> +/* An optab pattern and the condition under which it is available.  */
> +
> +struct pattern_info : optab_pattern
> +{
> +  /* The pattern's C test, or "" if the pattern is always available.
> +     genflags.cc defines HAVE_<name> as 1 for a statically true test and
> +     as the test itself otherwise, so two patterns that agree on COND
> +     also agree on the definition of HAVE_<name>.  */
> +  const char *cond;
> +};
> +
>  /* Vector in which to collect insns that match.  */
> -static vec<optab_pattern> patterns;
> +static vec<pattern_info> patterns;
>  
>  static void
>  gen_insn (md_rtx_info *info)
>  {
> -  optab_pattern p;
> +  pattern_info p;
>    if (find_optab (&p, XSTR (info->def, 0)))
> -    patterns.safe_push (p);
> +    {
> +      const char *cond = get_c_test (info->def);
> +      p.cond = maybe_eval_c_test (cond) == 1 ? "" : cond;
> +      patterns.safe_push (p);
> +    }
>  }
>  
>  static int
>  pattern_cmp (const void *va, const void *vb)
>  {
> -  const optab_pattern *a = (const optab_pattern *)va;
> -  const optab_pattern *b = (const optab_pattern *)vb;
> +  const pattern_info *a = (const pattern_info *)va;
> +  const pattern_info *b = (const pattern_info *)vb;
>    return a->sort_num - b->sort_num;
>  }
>  
> @@ -173,12 +188,29 @@ handle_overloaded_gen (FILE *file, overloaded_name 
> *oname)
>      }
>  }
>  
> +/* Return the name of the narrowest unsigned integer type that is guaranteed
> +   to hold every value below LIMIT.  The generated file is compiled for the
> +   host, which need not be the machine that runs this generator, so use the
> +   ranges that ISO C requires rather than the ones this machine happens to
> +   provide.  GCC hosts have at least 32-bit ints, which cover every vec
> +   index.  */
> +
> +static const char *
> +narrowest_uint_type (unsigned int limit)
> +{
> +  if (limit <= 256)
> +    return "unsigned char";
> +  if (limit <= 65536)
> +    return "unsigned short";
> +  return "unsigned int";
> +}
> +
>  int
>  main (int argc, const char **argv)
>  {
>    FILE *h_file, *s_file;
>    unsigned int i, j, n, last_kind[5];
> -  optab_pattern *p;
> +  pattern_info *p;
>  
>    progname = "genopinit";
>  
> @@ -367,45 +399,47 @@ main (int argc, const char **argv)
>      fprintf (s_file, "  { %#08x, CODE_FOR_%s },\n", p->sort_num, p->name);
>    fprintf (s_file, "};\n\n");
>  
> -  /* Some targets like riscv have a large number of patterns.  In order to
> -     prevent pathological situations in dataflow analysis split the init
> -     function into separate ones that initialize 1000 patterns each.  */
> +  /* Targets have far fewer distinct conditions than optab patterns.  Number
> +     the distinct conditions and record the number that each pattern uses, so
> +     that init_all_optabs can evaluate every condition once and then fill
> +     pat_enable from a table.  That keeps init_all_optabs a fixed size no
> +     matter how many patterns a target has, and makes it cheaper to run.  */
>  
> -  const int patterns_per_function = 1000;
> -
> -  if (patterns.length () > patterns_per_function)
> +  auto_vec<unsigned int> pat_cond (patterns.length ());
> +  auto_vec<unsigned int> cond_rep;
> +  hash_map<nofree_string_hash, unsigned int> cond_ids;
> +  for (i = 0; patterns.iterate (i, &p); ++i)
>      {
> -      unsigned num_init_functions
> -     = patterns.length () / patterns_per_function + 1;
> -      for (i = 0; i < num_init_functions; i++)
> +      bool existed;
> +      unsigned int &id = cond_ids.get_or_insert (p->cond, &existed);
> +      if (!existed)
>       {
> -       fprintf (s_file, "static void\ninit_optabs_%02d "
> -                "(struct target_optabs *optabs)\n{\n", i);
> -       fprintf (s_file, "  bool *ena = optabs->pat_enable;\n");
> -       unsigned start = i * patterns_per_function;
> -       unsigned end = MIN (patterns.length (),
> -                           (i + 1) * patterns_per_function);
> -       for (j = start; j < end; ++j)
> -         fprintf (s_file, "  ena[%u] = HAVE_%s;\n", j, patterns[j].name);
> -       fprintf (s_file, "}\n\n");
> +       id = cond_rep.length ();
> +       cond_rep.safe_push (i);
>       }
> -
> -      fprintf (s_file, "void\ninit_all_optabs "
> -            "(struct target_optabs *optabs)\n{\n");
> -      for (i = 0; i < num_init_functions; ++i)
> -     fprintf (s_file, "  init_optabs_%02d (optabs);\n", i);
> -      fprintf (s_file, "}\n\n");
> -    }
> -  else
> -    {
> -      fprintf (s_file, "void\ninit_all_optabs "
> -            "(struct target_optabs *optabs)\n{\n");
> -      fprintf (s_file, "  bool *ena = optabs->pat_enable;\n");
> -      for (i = 0; patterns.iterate (i, &p); ++i)
> -     fprintf (s_file, "  ena[%u] = HAVE_%s;\n", i, p->name);
> -      fprintf (s_file, "}\n\n");
> +      pat_cond.quick_push (id);
>      }
>  
> +  fprintf (s_file, "static const %s pat_cond[NUM_OPTAB_PATTERNS] = {\n ",
> +        narrowest_uint_type (cond_rep.length ()));
> +  for (i = 0; i < pat_cond.length (); ++i)
> +    fprintf (s_file, " %u,%s", pat_cond[i], (i % 20) == 19 ? "\n " : "");
> +  fprintf (s_file, "\n};\n\n");
> +
> +  /* The conditions are local to init_all_optabs, so that each call evaluates
> +     them for the current target options.  Force each one to bool, since a
> +     HAVE_* macro can expand to an int-valued expression and narrowing in a
> +     braced initializer is ill-formed.  */
> +  fprintf (s_file, "void\ninit_all_optabs (struct target_optabs 
> *optabs)\n{\n");
> +  fprintf (s_file, "  const bool cond[] = {\n");
> +  for (i = 0; i < cond_rep.length (); ++i)
> +    fprintf (s_file, "    !!HAVE_%s,\n", patterns[cond_rep[i]].name);

Using:

  bool (HAVE_%s)

seems more direct.  OK with that change, thanks (independently of the rest,
which I hope to get to "soon").

Richard


> +  fprintf (s_file, "  };\n");
> +  fprintf (s_file, "  bool *ena = optabs->pat_enable;\n");
> +  fprintf (s_file, "  for (unsigned int i = 0; i < NUM_OPTAB_PATTERNS; 
> ++i)\n");
> +  fprintf (s_file, "    ena[i] = cond[pat_cond[i]];\n");
> +  fprintf (s_file, "}\n\n");
> +
>    fprintf (s_file,
>          "/* Returns TRUE if the target supports any of the partial vector\n"
>          "   optabs: while_ult_optab, len_load_optab, len_store_optab,\n"

Reply via email to