Ping.
Thanks,
Kyrill

> On 23 Jul 2026, at 15:20, Kyrylo Tkachov <[email protected]> wrote:
> 
> From: Kyrylo Tkachov <[email protected]>
> 
> insn-opinit.cc contains large generated initialization functions with
> thousands of target-feature expressions.  The single file is among the
> slowest objects in every AArch64 bootstrap stage.
> 
> Teach genopinit to accept repeatable -O output options, following genemit and
> genrecog.  Reuse the existing --with-insnemit-partitions count, while
> preserving the default init-opinit.c output and -cFILE interface when -O is 
> not
> used.  Collect and validate all output names before opening them, rejecting
> duplicates, resolvable aliases, header collisions, and incompatible -c and -O
> options.
> 
> Keep shared lookup code and all helper declarations in the first output, then
> place helper definitions in the shortest output.  One-file output retains the
> historical static helper names.  Split output uses generator-private external
> names for cross-file references.
> 
> With the default ten AArch64 partitions, the largest part compiles about 90%
> faster than the original single object.
> 
> Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux.
> Ok for trunk?
> Thanks,
> Kyrill
> 
> gcc/ChangeLog:
> 
> * Makefile.in (INSNOPINIT_SPLITS_SEQ, INSNOPINIT_SEQ_SRC)
> (INSNOPINIT_SEQ_TMP, INSNOPINIT_SEQ_O): New variables.
> (OBJS): Replace insn-opinit.o with $(INSNOPINIT_SEQ_O).
> (MOSTLYCLEANFILES): Add $(INSNOPINIT_SEQ_SRC), retaining the legacy
> unnumbered source for cleanup.
> (s-opinit): Generate and move numbered insn-opinit-N.cc files.  Stop if
> moving an output fails.
> * configure.ac (--with-insnemit-partitions): Mention genopinit outputs.
> * configure: Regenerate.
> * genopinit.cc (source_file_option, output_file_names, output_files):
> New variables.
> (handle_arg): Record and validate repeatable -O output names.
> (main): Validate all output modes before opening files.  Preserve static
> helper linkage for one output and distribute private split helpers.
> 
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
> gcc/Makefile.in  |  26 +++++--
> gcc/configure    |   6 +-
> gcc/configure.ac |   5 +-
> gcc/genopinit.cc | 176 +++++++++++++++++++++++++++++------------------
> 4 files changed, 135 insertions(+), 78 deletions(-)
> 
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index fa46ada4980..42c8e8f9062 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -241,6 +241,12 @@ INSNRECOG_SEQ_SRC = $(patsubst %, insn-recog-%.cc, 
> $(INSNRECOG_SPLITS_SEQ))
> INSNRECOG_SEQ_TMP = $(patsubst %, tmp-recog-%.cc, $(INSNRECOG_SPLITS_SEQ))
> INSNRECOG_SEQ_O = $(patsubst %, insn-recog-%.o, $(INSNRECOG_SPLITS_SEQ))
> 
> +# Re-use the split number for insn-opinit as well.
> +INSNOPINIT_SPLITS_SEQ = $(INSNEMIT_SPLITS_SEQ)
> +INSNOPINIT_SEQ_SRC = $(patsubst %, insn-opinit-%.cc, 
> $(INSNOPINIT_SPLITS_SEQ))
> +INSNOPINIT_SEQ_TMP = $(patsubst %, tmp-opinit-%.cc, $(INSNOPINIT_SPLITS_SEQ))
> +INSNOPINIT_SEQ_O = $(patsubst %, insn-opinit-%.o, $(INSNOPINIT_SPLITS_SEQ))
> +
> # These files are to have specific diagnostics suppressed, or are not to
> # be subject to -Werror:
> # flex output may yield harmless "no previous prototype" warnings
> @@ -1410,7 +1416,7 @@ OBJS = \
> insn-extract.o \
> insn-latencytab.o \
> insn-modes.o \
> - insn-opinit.o \
> + $(INSNOPINIT_SEQ_O) \
> insn-output.o \
> insn-peep.o \
> insn-preds.o \
> @@ -1985,7 +1991,9 @@ MOSTLYCLEANFILES = insn-flags.h insn-config.h 
> insn-codes.h \
>  insn-output.cc $(INSNRECOG_SEQ_SRC) insn-recog.h \
>  $(INSNEMIT_SEQ_SRC) insn-extract.cc insn-peep.cc \
>  insn-attr.h insn-attr-common.h insn-attrtab.cc insn-dfatab.cc \
> - insn-latencytab.cc insn-opinit.cc insn-opinit.h insn-preds.cc 
> insn-constants.h \
> + insn-latencytab.cc $(INSNOPINIT_SEQ_SRC) insn-opinit.cc insn-opinit.h \
> + insn-preds.cc \
> + insn-constants.h \
>  tm-preds.h tm-constrs.h checksum-options $(GIMPLE_MATCH_PD_SEQ_SRC) \
>  $(GENERIC_MATCH_PD_SEQ_SRC) gimple-match-auto.h generic-match-auto.h \
>  tree-check.h min-insn-modes.cc insn-modes.cc insn-modes.h 
> insn-modes-inline.h \
> @@ -2858,13 +2866,19 @@ s-attrtab : $(MD_DEPS) 
> build/genattrtab$(build_exeext) \
> $(SHELL) $(srcdir)/../move-if-change tmp-latencytab.cc insn-latencytab.cc
> $(STAMP) s-attrtab
> 
> -# genopinit produces two files.
> -insn-opinit.cc insn-opinit.h: s-opinit ; @true
> +# genopinit splits its output like genemit and additionally produces
> +# insn-opinit.h.
> +$(INSNOPINIT_SEQ_SRC): s-opinit ; @true
> +insn-opinit.h: s-opinit ; @true
> s-opinit: $(MD_DEPS) build/genopinit$(build_exeext) insn-conditions.md
> $(RUN_GEN) build/genopinit$(build_exeext) $(md_file) \
> -  insn-conditions.md -htmp-opinit.h -ctmp-opinit.cc
> +  insn-conditions.md -htmp-opinit.h \
> +  $(addprefix -O,${INSNOPINIT_SEQ_TMP})
> $(SHELL) $(srcdir)/../move-if-change tmp-opinit.h insn-opinit.h
> - $(SHELL) $(srcdir)/../move-if-change tmp-opinit.cc insn-opinit.cc
> + for id in $(INSNOPINIT_SPLITS_SEQ); do \
> +  $(SHELL) $(srcdir)/../move-if-change tmp-opinit-$$id.cc \
> +    insn-opinit-$$id.cc || exit 1; \
> + done
> $(STAMP) s-opinit
> 
> # gencondmd doesn't use the standard naming convention.
> diff --git a/gcc/configure b/gcc/configure
> index cba5a9ecbd6..b1f31b61725 100755
> --- a/gcc/configure
> +++ b/gcc/configure
> @@ -1882,8 +1882,8 @@ Optional Packages:
>                           Set the number of partitions to make for gimple and
>                           generic when splitting match.pd. [default=10]
>   --with-insnemit-partitions=num
> -                          Set the number of partitions of insn-emit.cc for
> -                          genemit and genrecog to create. [default=10]
> +                          Set the number of insn-emit, insn-recog and
> +                          insn-opinit partitions to generate. [default=10]
>   --with-dwarf2           force the default debug format to be DWARF 2 (or
>                           later)
>   --with-specs=SPECS      add SPECS to driver command-line processing
> @@ -7907,7 +7907,7 @@ fi
> 
> 
> 
> -# Specify the number of splits of insn-emit.cc and insn-recog.cc to generate.
> +# Specify the number of output files used by the insn generators.
> 
> # Check whether --with-insnemit-partitions was given.
> if test "${with_insnemit_partitions+set}" = set; then :
> diff --git a/gcc/configure.ac b/gcc/configure.ac
> index 6c560ceb90f..9e767a7fc5f 100644
> --- a/gcc/configure.ac
> +++ b/gcc/configure.ac
> @@ -902,10 +902,11 @@ fi
> 
> AC_SUBST(DEFAULT_MATCHPD_PARTITIONS)
> 
> -# Specify the number of splits of insn-emit.cc and insn-recog.cc to generate.
> +# Specify the number of output files used by the insn generators.
> AC_ARG_WITH(insnemit-partitions,
> [AS_HELP_STRING([--with-insnemit-partitions=num],
> -[Set the number of partitions of insn-emit.cc for genemit and genrecog to 
> create. [default=10]])],
> +[Set the number of insn-emit, insn-recog and insn-opinit partitions to
> +generate. [default=10]])],
> [DEFAULT_INSNEMIT_PARTITIONS="$with_insnemit_partitions"], 
> [DEFAULT_INSNEMIT_PARTITIONS=10])
> if (test $DEFAULT_INSNEMIT_PARTITIONS -lt 1); then
>   AC_MSG_ERROR(m4_normalize([
> diff --git a/gcc/genopinit.cc b/gcc/genopinit.cc
> index 62eaf5bd378..ce9afadd9e3 100644
> --- a/gcc/genopinit.cc
> +++ b/gcc/genopinit.cc
> @@ -75,6 +75,11 @@ optab_rcode_cmp (const void *va, const void *vb)
> 
> static const char *header_file_name = "init-opinit.h";
> static const char *source_file_name = "init-opinit.c";
> +static bool source_file_option;
> +
> +/* The source file names and files to distribute the init functions across.  
> */
> +static auto_vec<const char *, 10> output_file_names;
> +static auto_vec<FILE *, 10> output_files;
> 
> static bool
> handle_arg (const char *arg)
> @@ -86,6 +91,13 @@ handle_arg (const char *arg)
>       return true;
>     case 'c':
>       source_file_name = &arg[2];
> +      source_file_option = true;
> +      return true;
> +    case 'O':
> +      for (const char *name : output_file_names)
> + if (canonical_filename_eq (name, &arg[2]))
> +  fatal ("output file %s specified more than once", name);
> +      output_file_names.safe_push (&arg[2]);
>       return true;
>     default:
>       return false;
> @@ -176,7 +188,7 @@ handle_overloaded_gen (FILE *file, overloaded_name *oname)
> int
> main (int argc, const char **argv)
> {
> -  FILE *h_file, *s_file;
> +  FILE *h_file;
>   unsigned int i, j, n, last_kind[5];
>   optab_pattern *p;
> 
> @@ -188,8 +200,18 @@ main (int argc, const char **argv)
>   if (!init_rtx_reader_args_cb (argc, argv, handle_arg))
>     return (FATAL_EXIT_CODE);
> 
> +  if (output_file_names.is_empty ())
> +    output_file_names.safe_push (source_file_name);
> +  else if (source_file_option)
> +    fatal ("options -c and -O cannot be used together");
> +
> +  for (const char *name : output_file_names)
> +    if (canonical_filename_eq (name, header_file_name))
> +      fatal ("output file %s specified more than once", name);
> +
>   h_file = open_outfile (header_file_name);
> -  s_file = open_outfile (source_file_name);
> +  for (const char *name : output_file_names)
> +    output_files.safe_push (open_outfile (name));
> 
>   /* Read the machine description.  */
>   md_rtx_info info;
> @@ -330,32 +352,63 @@ main (int argc, const char **argv)
>   "#define this_target_optabs (&default_target_optabs)\n"
>   "#endif\n");
> 
> +  /* The init functions are distributed across all output files; every
> +     file needs the same set of includes.  */
> +  for (FILE *f : output_files)
> +    fprintf (f,
> +     "#define IN_TARGET_CODE 1\n"
> +     "#include \"config.h\"\n"
> +     "#include \"system.h\"\n"
> +     "#include \"coretypes.h\"\n"
> +     "#include \"backend.h\"\n"
> +     "#include \"predict.h\"\n"
> +     "#include \"tree.h\"\n"
> +     "#include \"rtl.h\"\n"
> +     "#include \"alias.h\"\n"
> +     "#include \"varasm.h\"\n"
> +     "#include \"stor-layout.h\"\n"
> +     "#include \"calls.h\"\n"
> +     "#include \"memmodel.h\"\n"
> +     "#include \"tm_p.h\"\n"
> +     "#include \"flags.h\"\n"
> +     "#include \"insn-config.h\"\n"
> +     "#include \"expmed.h\"\n"
> +     "#include \"dojump.h\"\n"
> +     "#include \"explow.h\"\n"
> +     "#include \"emit-rtl.h\"\n"
> +     "#include \"stmt.h\"\n"
> +     "#include \"expr.h\"\n"
> +     "#include \"insn-codes.h\"\n"
> +     "#include \"optabs.h\"\n\n");
> +
> +  /* The first output file also holds the lookup tables and functions.  */
> +  FILE *s_file = output_files[0];
> +  bool split_output = output_files.length () > 1;
> +  const char *function_storage = split_output ? "" : "static ";
> +  const char *function_prefix = split_output ? "genopinit_" : "";
> +
> +  /* 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 at most 1000 patterns
> +     each, and distribute them evenly across the output files.  */
> +
> +  const unsigned patterns_per_function = 1000;
> +  unsigned num_init_functions
> +    = MAX (output_files.length (),
> +   CEIL (patterns.length (), patterns_per_function));
> +  /* Avoid emitting empty functions on targets with fewer patterns than
> +     output files.  */
> +  if (num_init_functions > patterns.length ())
> +    num_init_functions = MAX (patterns.length (), 1);
> +
> +  /* The first file calls every init function, so declare them there.  */
> +  for (i = 0; i < num_init_functions; i++)
> +    fprintf (s_file, "%svoid %sinit_optabs_%02u "
> +     "(struct target_optabs *);\n",
> +     split_output ? "extern " : "static ", function_prefix, i);
> +  fprintf (s_file, "\n");
> +
>   fprintf (s_file,
> -   "#define IN_TARGET_CODE 1\n"
> -   "#include \"config.h\"\n"
> -   "#include \"system.h\"\n"
> -   "#include \"coretypes.h\"\n"
> -   "#include \"backend.h\"\n"
> -   "#include \"predict.h\"\n"
> -   "#include \"tree.h\"\n"
> -   "#include \"rtl.h\"\n"
> -   "#include \"alias.h\"\n"
> -   "#include \"varasm.h\"\n"
> -   "#include \"stor-layout.h\"\n"
> -   "#include \"calls.h\"\n"
> -   "#include \"memmodel.h\"\n"
> -   "#include \"tm_p.h\"\n"
> -   "#include \"flags.h\"\n"
> -   "#include \"insn-config.h\"\n"
> -   "#include \"expmed.h\"\n"
> -   "#include \"dojump.h\"\n"
> -   "#include \"explow.h\"\n"
> -   "#include \"emit-rtl.h\"\n"
> -   "#include \"stmt.h\"\n"
> -   "#include \"expr.h\"\n"
> -   "#include \"insn-codes.h\"\n"
> -   "#include \"optabs.h\"\n"
> -   "\n"
>   "struct optab_pat {\n"
>   "  unsigned scode;\n"
>   "  enum insn_code icode;\n"
> @@ -367,44 +420,11 @@ 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.  */
> -
> -  const int patterns_per_function = 1000;
> -
> -  if (patterns.length () > patterns_per_function)
> -    {
> -      unsigned num_init_functions
> - = patterns.length () / patterns_per_function + 1;
> -      for (i = 0; i < num_init_functions; i++)
> - {
> -  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");
> - }
> -
> -      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");
> -    }
> +  fprintf (s_file, "void\ninit_all_optabs "
> +   "(struct target_optabs *optabs)\n{\n");
> +  for (i = 0; i < num_init_functions; ++i)
> +    fprintf (s_file, "  %sinit_optabs_%02u (optabs);\n", function_prefix, i);
> +  fprintf (s_file, "}\n\n");
> 
>   fprintf (s_file,
>   "/* Returns TRUE if the target supports any of the partial vector\n"
> @@ -524,7 +544,29 @@ main (int argc, const char **argv)
>     }
>   fprintf (s_file, "};\n\n");
> 
> +  /* Emit the common definitions before distributing the init functions so
> +     that their size is included when choosing the shortest output file.  */
> +  unsigned lo = 0;
> +  for (i = 0; i < num_init_functions; i++)
> +    {
> +      unsigned count = (patterns.length () / num_init_functions
> + + (i < patterns.length () % num_init_functions));
> +      unsigned file_idx;
> +      FILE *f = choose_output (output_files, file_idx);
> +      fprintf (f, "%svoid\n%sinit_optabs_%02u "
> +       "(struct target_optabs *optabs)\n{\n", function_storage,
> +       function_prefix, i);
> +      fprintf (f, "  bool *ena = optabs->pat_enable;\n");
> +      for (j = lo; j < lo + count; ++j)
> + fprintf (f, "  ena[%u] = HAVE_%s;\n", j, patterns[j].name);
> +      fprintf (f, "}\n\n");
> +      lo += count;
> +    }
> +  gcc_assert (lo == patterns.length ());
> +
>   fprintf (h_file, "#endif\n");
> -  return (fclose (h_file) == 0 && fclose (s_file) == 0
> -  ? SUCCESS_EXIT_CODE : FATAL_EXIT_CODE);
> +  bool ok = fclose (h_file) == 0;
> +  for (FILE *f : output_files)
> +    ok &= fclose (f) == 0;
> +  return ok ? SUCCESS_EXIT_CODE : FATAL_EXIT_CODE;
> }
> -- 
> 2.50.1 (Apple Git-155)
> 

Reply via email to