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)