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