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)
>