[PATCH 3/4] genattrtab: Distribute generated code across multiple files

<[email protected]> Wed, 5 Aug 2026 16:35:25 +0200
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
From: Kyrylo Tkachov <[email protected]>

insn-attrtab.cc contains many independent get_attr_* functions.  Several have
large switches over every instruction code, making the single source one of
the slowest objects in a measured AArch64 bootstrap.

Let genattrtab write multiple attribute outputs, following genemit and
genrecog.  Reuse --with-insnemit-partitions and make -A repeatable.  Register
the -A, -D, and -L outputs through the common gensupport machinery.  This gives
all output roles one validation, open, selection, and close lifecycle.  Place
each attribute function in the shortest partition.  Also distribute the fixed
delay and length functions.  Keep DFA and latency functions in their existing
single files.

The functions refer to one another through insn-attr.h and
insn-attr-common.h, so generated headers do not change.  With one -A option,
all three outputs are byte-identical to the previous generator.

With ten AArch64 partitions, the largest attribute part is about 85% smaller
and its compile time is about 67% lower than for the original source.  Native
AArch64 and RISC-V cross all-gcc builds pass.

Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux.
Ok for trunk?
Thanks,
Kyrill

gcc/ChangeLog:

	* Makefile.in (INSNATTRTAB_SPLITS_SEQ, INSNATTRTAB_SEQ_SRC)
	(INSNATTRTAB_SEQ_TMP, INSNATTRTAB_SEQ_O): New variables.
	(OBJS): Replace insn-attrtab.o with $(INSNATTRTAB_SEQ_O).
	(MOSTLYCLEANFILES): Add $(INSNATTRTAB_SEQ_SRC), retaining the legacy
	unnumbered source for cleanup.
	(.PRECIOUS): Use $(INSNATTRTAB_SEQ_SRC).
	(s-attrtab): Generate and move numbered attribute files.  Stop when a
	move fails.
	* configure.ac (--with-insnemit-partitions): Document all generators
	which use the option.
	* configure: Regenerate.
	* genattrtab.cc (attr_file_name, attr_file): Remove.
	(output_files): New variable.
	(dfa_file_name, latency_file_name): Retain output names.
	(make_automaton_attrs): Write to the first attribute partition.
	(handle_arg): Register repeatable attribute outputs.
	(main): Register fixed outputs and use the common output helpers.
	Distribute attribute, delay, and length functions.
	* final.cc (length_unit_log): Update comment for partitioned output.
	* config/arc/arc-protos.h (regno_clobbered_p): Likewise.
	* config/ia64/ia64.cc (bundling): Likewise.
	* config/ia64/t-ia64 (insn-attrtab.o-warn): Replace with per-partition
	warning variables.
	* config/sh/sh.h (code_for_indirect_jump_scratch): Update comment for
	partitioned output.

contrib/ChangeLog:

	* filter-clang-warnings.py (skip_warning): Match numbered attribute
	files.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 contrib/filter-clang-warnings.py |  2 +-
 gcc/Makefile.in                  | 32 ++++++++---
 gcc/config/arc/arc-protos.h      |  3 +-
 gcc/config/ia64/ia64.cc          |  4 +-
 gcc/config/ia64/t-ia64           |  3 +-
 gcc/config/sh/sh.h               |  5 +-
 gcc/configure                    | 11 ++--
 gcc/configure.ac                 |  6 +-
 gcc/final.cc                     |  2 +-
 gcc/genattrtab.cc                | 99 +++++++++++++++++---------------
 10 files changed, 98 insertions(+), 69 deletions(-)

diff --git a/contrib/filter-clang-warnings.py b/contrib/filter-clang-warnings.py
index 8eec366b4bc..6b01b559b6e 100755
--- a/contrib/filter-clang-warnings.py
+++ b/contrib/filter-clang-warnings.py
@@ -56,7 +56,7 @@ def skip_warning(filename, message):
             'ipa-strub.cc': ['-Wunused-but-set-variable'],
             'insn-modes.cc': ['-Wshift-count-overflow'],
             'insn-emit.cc': ['-Wtautological-compare'],
-            'insn-attrtab.cc': ['-Wparentheses-equality'],
+            'insn-attrtab': ['-Wparentheses-equality'],
             'omp-builtins.def': ['-Wc++11-narrowing'],
             'wide-int.h': ['-Wnontrivial-memcall'],
             'i386.md': ['-Wparentheses-equality', '-Wtautological-compare',
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index ee2f9022eab..19957477e5b 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -241,6 +241,14 @@ 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-attrtab as well.
+INSNATTRTAB_SPLITS_SEQ = $(wordlist 1,$(NUM_INSNEMIT_SPLITS),$(one_to_9999))
+INSNATTRTAB_SEQ_SRC = $(patsubst %, insn-attrtab-%.cc, \
+	$(INSNATTRTAB_SPLITS_SEQ))
+INSNATTRTAB_SEQ_TMP = $(patsubst %, tmp-attrtab-%.cc, \
+	$(INSNATTRTAB_SPLITS_SEQ))
+INSNATTRTAB_SEQ_O = $(patsubst %, insn-attrtab-%.o, $(INSNATTRTAB_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
@@ -1405,7 +1413,7 @@ OBJS = \
 	$(GIMPLE_MATCH_PD_SEQ_O) \
 	gimple-match-exports.o \
 	$(GENERIC_MATCH_PD_SEQ_O) \
-	insn-attrtab.o \
+	$(INSNATTRTAB_SEQ_O) \
 	insn-automata.o \
 	insn-dfatab.o \
 	$(INSNEMIT_SEQ_O) \
@@ -1986,8 +1994,10 @@ FULL_DRIVER_NAME=$(target_noncanonical)-gcc-$(version)$(exeext)
 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-attr.h insn-attr-common.h $(INSNATTRTAB_SEQ_SRC) insn-attrtab.cc \
+ insn-dfatab.cc insn-latencytab.cc 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 \
@@ -2763,7 +2773,7 @@ $(common_out_object_file): $(common_out_file)
 .PRECIOUS: insn-config.h insn-flags.h insn-codes.h insn-constants.h \
   $(INSNEMIT_SEQ_SRC) insn-recog.h $(INSNRECOG_SEQ_SRC) \
   insn-extract.cc insn-output.cc \
-  insn-peep.cc insn-attr.h insn-attr-common.h insn-attrtab.cc \
+  insn-peep.cc insn-attr.h insn-attr-common.h $(INSNATTRTAB_SEQ_SRC) \
   insn-dfatab.cc insn-latencytab.cc insn-preds.cc \
   $(GIMPLE_MATCH_PD_SEQ_SRC) $(GENERIC_MATCH_PD_SEQ_SRC) \
   gimple-match-auto.h generic-match-auto.h insn-target-def.h
@@ -2850,13 +2860,19 @@ s-check : build/gencheck$(build_exeext)
 	$(SHELL) $(srcdir)/../move-if-change tmp-check.h tree-check.h
 	$(STAMP) s-check
 
-# genattrtab produces three files: tmp-{attrtab.cc,dfatab.cc,latencytab.cc}
-insn-attrtab.cc insn-dfatab.cc insn-latencytab.cc: s-attrtab ; @true
+# genattrtab splits the attribute functions like genemit and additionally
+# produces tmp-dfatab.cc and tmp-latencytab.cc.
+$(INSNATTRTAB_SEQ_SRC): s-attrtab ; @true
+insn-dfatab.cc insn-latencytab.cc: s-attrtab ; @true
 s-attrtab : $(MD_DEPS) build/genattrtab$(build_exeext) \
   insn-conditions.md
 	$(RUN_GEN) build/genattrtab$(build_exeext) $(md_file) insn-conditions.md \
-		-Atmp-attrtab.cc -Dtmp-dfatab.cc -Ltmp-latencytab.cc
-	$(SHELL) $(srcdir)/../move-if-change tmp-attrtab.cc    insn-attrtab.cc
+		$(addprefix -A,${INSNATTRTAB_SEQ_TMP}) \
+		-Dtmp-dfatab.cc -Ltmp-latencytab.cc
+	for id in $(INSNATTRTAB_SPLITS_SEQ); do \
+	  $(SHELL) $(srcdir)/../move-if-change tmp-attrtab-$$id.cc \
+	    insn-attrtab-$$id.cc || exit 1; \
+	done
 	$(SHELL) $(srcdir)/../move-if-change tmp-dfatab.cc     insn-dfatab.cc
 	$(SHELL) $(srcdir)/../move-if-change tmp-latencytab.cc insn-latencytab.cc
 	$(STAMP) s-attrtab
diff --git a/gcc/config/arc/arc-protos.h b/gcc/config/arc/arc-protos.h
index fc36d52ead0..380f2c39777 100644
--- a/gcc/config/arc/arc-protos.h
+++ b/gcc/config/arc/arc-protos.h
@@ -93,7 +93,8 @@ extern bool arc_text_label (rtx_insn *insn);
 extern bool arc_short_comparison_p (rtx, int);
 extern bool arc_epilogue_uses (int regno);
 extern bool arc_eh_uses (int regno);
-/* insn-attrtab.cc doesn't include reload.h, which declares regno_clobbered_p. */
+/* Generated attribute code does not include reload.h, which declares
+   regno_clobbered_p.  */
 extern int regno_clobbered_p (unsigned int, rtx_insn *, machine_mode, int);
 extern bool arc_legitimize_reload_address (rtx *, machine_mode, int, int);
 extern void arc_secondary_reload_conv (rtx, rtx, rtx, bool);
diff --git a/gcc/config/ia64/ia64.cc b/gcc/config/ia64/ia64.cc
index b06158ba7bc..292c6b3d939 100644
--- a/gcc/config/ia64/ia64.cc
+++ b/gcc/config/ia64/ia64.cc
@@ -9286,8 +9286,8 @@ bundling (FILE *dump, int verbose, rtx_insn *prev_head_insn, rtx_insn *tail)
 	   curr_state = curr_state->next)
 	if (verbose >= 2 && dump)
 	  {
-	    /* This structure is taken from generated code of the
-	       pipeline hazard recognizer (see file insn-attrtab.cc).
+	    /* This structure is taken from the generated pipeline hazard
+	       recognizer code.
 	       Please don't forget to change the structure if a new
 	       automaton is added to .md file.  */
 	    struct DFA_chip
diff --git a/gcc/config/ia64/t-ia64 b/gcc/config/ia64/t-ia64
index 4c37dd89fba..2bf590d89df 100644
--- a/gcc/config/ia64/t-ia64
+++ b/gcc/config/ia64/t-ia64
@@ -22,7 +22,8 @@ ia64-c.o: $(srcdir)/config/ia64/ia64-c.cc $(CONFIG_H) $(SYSTEM_H) \
 		$(srcdir)/config/ia64/ia64-c.cc
 
 # genattrtab generates very long string literals.
-insn-attrtab.o-warn = -Wno-error
+$(foreach id, $(INSNATTRTAB_SPLITS_SEQ), \
+  $(eval insn-attrtab-$(id).o-warn = -Wno-error))
 
 ia64.o: $(srcdir)/config/ia64/ia64.cc debug.h $(PARAMS_H) sel-sched.h reload.h \
 	$(OPTS_H) dumpfile.h $(HASH_TABLE_H)
diff --git a/gcc/config/sh/sh.h b/gcc/config/sh/sh.h
index 9b2ed6a33db..f3973e7fcab 100644
--- a/gcc/config/sh/sh.h
+++ b/gcc/config/sh/sh.h
@@ -24,8 +24,9 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "config/vxworks-dummy.h"
 
-/* Unfortunately, insn-attrtab.cc doesn't include insn-codes.h.  We can't
-   include it here, because bconfig.h is also included by gencodes.cc .  */
+/* Unfortunately, generated attribute code does not include insn-codes.h.
+   We can't include it here, because bconfig.h is also included by
+   gencodes.cc .  */
 /* ??? No longer true.  */
 extern int code_for_indirect_jump_scratch;
 
diff --git a/gcc/configure b/gcc/configure
index eea63c9721c..c9f1feee86b 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 partitions used by genemit,
+                          genrecog and genattrtab. [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,8 @@ fi
 
 
 
-# Specify the number of splits of insn-emit.cc and insn-recog.cc to generate.
+# Specify the number of output partitions used by the machine-description
+# generators.
 
 # Check whether --with-insnemit-partitions was given.
 if test "${with_insnemit_partitions+set}" = set; then :
@@ -21921,7 +21922,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 21924 "configure"
+#line 21925 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -22027,7 +22028,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 22030 "configure"
+#line 22031 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
diff --git a/gcc/configure.ac b/gcc/configure.ac
index 58576227136..a8b0a08f20d 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -902,10 +902,12 @@ 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 partitions used by the machine-description
+# 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 partitions used by genemit, genrecog and genattrtab.
+[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/final.cc b/gcc/final.cc
index 0152be59fc8..e2761d056ea 100644
--- a/gcc/final.cc
+++ b/gcc/final.cc
@@ -142,7 +142,7 @@ static int override_discriminator;
 /* Whether to force emission of a line note before the next insn.  */
 static bool force_source_line = false;
 
-extern const int length_unit_log; /* This is defined in insn-attrtab.cc.  */
+extern const int length_unit_log; /* Defined in generated attribute code.  */
 
 /* Nonzero while outputting an `asm' with operands.
    This means that inconsistencies are the user's fault, so don't die.
diff --git a/gcc/genattrtab.cc b/gcc/genattrtab.cc
index a2cf08d5305..eab05cefeb8 100644
--- a/gcc/genattrtab.cc
+++ b/gcc/genattrtab.cc
@@ -305,16 +305,17 @@ static rtx min_fn		   (rtx);
    functions and tables.  This made insn-attrtab.cc _the_ bottle-neck in
    a parallel build, and even made it impossible to build GCC on machines
    with relatively small RAM space (PR other/29442).  Therefore, the
-   attribute functions/tables are now written out to three separate
-   files: all "*insn_default_latency" functions go to LATENCY_FILE_NAME,
-   all "*internal_dfa_insn_code" functions go to DFA_FILE_NAME, and the
-   rest goes to ATTR_FILE_NAME.  */
+   attribute functions/tables are now written out to separate files: all
+   "*insn_default_latency" functions go to the latency output, all
+   "*internal_dfa_insn_code" functions go to the DFA output, and the rest is
+   distributed across the attribute outputs the way genemit and genrecog
+   distribute their output.  */
 
-static const char *attr_file_name = NULL;
-static const char *dfa_file_name = NULL;
-static const char *latency_file_name = NULL;
+/* The files produced by the generator.  */
+static auto_vec<generator_output, 10> output_files;
 
-static FILE *attr_file, *dfa_file, *latency_file;
+static FILE *dfa_file, *latency_file;
+static const char *dfa_file_name, *latency_file_name;
 
 /* Hash table for sharing RTL and strings.  */
 
@@ -4981,6 +4982,9 @@ make_automaton_attrs (void)
   tune_attr = find_tune_attr (all_insn_reservs->condexp);
   if (tune_attr != NULL)
     {
+      /* The function pointers and init_sched_attrs go to the first
+	 attribute file.  */
+      FILE *attr_file = output_files[0].file;
       rtx *condexps = XNEWVEC (rtx, n_insn_reservs * 3);
       struct attr_value *val;
       bool first = true;
@@ -5206,29 +5210,22 @@ write_header (FILE *outf)
   fprintf (outf, "#define operands recog_data.operand\n\n");
 }
 
-static FILE *
-open_outfile (const char *file_name)
-{
-  FILE *outf;
-  outf = fopen (file_name, "w");
-  if (! outf)
-    fatal ("cannot open file %s: %s", file_name, xstrerror (errno));
-  write_header (outf);
-  return outf;
-}
-
 static bool
 handle_arg (const char *arg)
 {
   switch (arg[1])
     {
     case 'A':
-      attr_file_name = &arg[2];
+      add_generator_output (output_files, &arg[2], true);
       return true;
     case 'D':
+      if (dfa_file_name)
+	fatal ("option -D specified more than once");
       dfa_file_name = &arg[2];
       return true;
     case 'L':
+      if (latency_file_name)
+	fatal ("option -L specified more than once");
       latency_file_name = &arg[2];
       return true;
     default:
@@ -5248,9 +5245,22 @@ main (int argc, const char **argv)
   if (!init_rtx_reader_args_cb (argc, argv, handle_arg))
     return FATAL_EXIT_CODE;
 
-  attr_file = open_outfile (attr_file_name);
-  dfa_file = open_outfile (dfa_file_name);
-  latency_file = open_outfile (latency_file_name);
+  if (output_files.is_empty ())
+    fatal ("no -A output file specified");
+  if (!dfa_file_name)
+    fatal ("no -D output file specified");
+  if (!latency_file_name)
+    fatal ("no -L output file specified");
+
+  unsigned int dfa_index
+    = add_generator_output (output_files, dfa_file_name, false);
+  unsigned int latency_index
+    = add_generator_output (output_files, latency_file_name, false);
+  open_generator_outputs (output_files);
+  dfa_file = output_files[dfa_index].file;
+  latency_file = output_files[latency_index].file;
+  for (generator_output &output : output_files)
+    write_header (output.file);
 
   obstack_init (hash_obstack);
   obstack_init (temp_obstack);
@@ -5363,50 +5373,47 @@ main (int argc, const char **argv)
   /* Perform any possible optimizations to speed up compilation.  */
   optimize_attrs (num_insn_codes);
 
-  /* Now write out all the `gen_attr_...' routines.  Do these before the
-     special routines so that they get defined before they are used.  */
+  /* Now write out all the `get_attr_...' routines.  The DFA and latency
+     routines go to their own files; the rest are distributed across the
+     attribute files.  They only refer to each other through the extern
+     declarations in insn-attr.h and insn-attr-common.h.  */
 
   for (i = 0; i < MAX_ATTRS_INDEX; i++)
     for (attr = attrs[i]; attr; attr = attr->next)
       {
-        FILE *outf;
+	FILE *outf;
 
-	if (startswith(attr->name, "*internal_dfa_insn_code"))
+	if (attr->is_special || attr->is_const)
+	  continue;
+
+	if (startswith (attr->name, "*internal_dfa_insn_code"))
 	  outf = dfa_file;
 	else if (startswith (attr->name, "*insn_default_latency"))
 	  outf = latency_file;
 	else
-	  outf = attr_file;
+	  outf = choose_output (output_files);
 
-	if (! attr->is_special && ! attr->is_const)
-	  write_attr_get (outf, attr);
+	write_attr_get (outf, attr);
       }
 
   /* Write out delay eligibility information, if DEFINE_DELAY present.
      (The function to compute the number of delay slots will be written
      below.)  */
-  write_eligible_delay (attr_file, "delay");
+  write_eligible_delay (choose_output (output_files), "delay");
   if (have_annul_true)
-    write_eligible_delay (attr_file, "annul_true");
+    write_eligible_delay (choose_output (output_files), "annul_true");
   else
-    write_dummy_eligible_delay (attr_file, "annul_true");
+    write_dummy_eligible_delay (choose_output (output_files), "annul_true");
   if (have_annul_false)
-    write_eligible_delay (attr_file, "annul_false");
+    write_eligible_delay (choose_output (output_files), "annul_false");
   else
-    write_dummy_eligible_delay (attr_file, "annul_false");
+    write_dummy_eligible_delay (choose_output (output_files), "annul_false");
 
   /* Write out constant delay slot info.  */
-  write_const_num_delay_slots (attr_file);
-
-  write_length_unit_log (attr_file);
+  write_const_num_delay_slots (choose_output (output_files));
 
-  if (fclose (attr_file) != 0)
-    fatal ("cannot close file %s: %s", attr_file_name, xstrerror (errno));
-  if (fclose (dfa_file) != 0)
-    fatal ("cannot close file %s: %s", dfa_file_name, xstrerror (errno));
-  if (fclose (latency_file) != 0)
-    fatal ("cannot close file %s: %s", latency_file_name, xstrerror (errno));
+  write_length_unit_log (choose_output (output_files));
 
-  return SUCCESS_EXIT_CODE;
+  return (close_generator_outputs (output_files)
+	  ? SUCCESS_EXIT_CODE : FATAL_EXIT_CODE);
 }
-
-- 
2.50.1 (Apple Git-155)