https://gcc.gnu.org/g:6b475612c288c4bfc40bc4747f7efa594c621a9f
commit r17-4262-g6b475612c288c4bfc40bc4747f7efa594c621a9f Author: Kyrylo Tkachov <[email protected]> Date: Tue Aug 4 21:25:50 2026 +0200 genattrtab: Distribute generated code across multiple files 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. Also distribute the fixed delay and length functions. Keep DFA and latency functions in their existing single files. choose_output places each function in the shortest partition so far, which only balances well if the large functions are placed early. Attribute functions differ in size by two orders of magnitude, so an arbitrary order lets a late large function land on an already full partition. Sort the functions by the amount of text write_attr_get will produce, largest first. For a derived attribute that amount is the length of its lookup table, and otherwise the number of case labels that survive find_most_used. 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 the delay and latency outputs are unchanged from the previous generator. The attribute output holds the same lines at the same total size, reordered, because the sort applies at any partition count. On AArch64 with ten partitions the sorted order reduces the spread between the largest and smallest partition from 5.9x to 2.3x. The largest partition is then within 0.1% of the size that no assignment can beat, since one attribute function alone accounts for that much of the total. The largest attribute part is about 83% smaller than the original single source, and the slowest attribute object compiles about 76% faster. Native AArch64 and RISC-V cross all-gcc builds pass. Bootstrapped on aarch64-none-linux-gnu. 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, open_outfile): 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. (attr_output_size, cmp_attr_output_size): New functions. (main): Register fixed outputs and use the common output helpers. Distribute attribute, delay, and length functions, largest first. * 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]> Diff: --- 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 | 152 +++++++++++++++++++++++++-------------- 10 files changed, 145 insertions(+), 75 deletions(-) diff --git a/contrib/filter-clang-warnings.py b/contrib/filter-clang-warnings.py index 9de005990b52..ba2fa532f825 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': ['-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 c718585e6071..f88279839fd8 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) \ @@ -1987,8 +1995,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 \ @@ -2764,7 +2774,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 @@ -2851,13 +2861,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 fc36d52ead06..380f2c397771 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 b06158ba7bc0..292c6b3d9390 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 4c37dd89fbab..2bf590d89dff 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 9b2ed6a33db8..f3973e7fcab4 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 43ca45144b88..9f83764b09c5 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 075b6b68ffab..31aae8459702 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 a99b188d0432..b6be9be99b7f 100644 --- a/gcc/final.cc +++ b/gcc/final.cc @@ -136,7 +136,7 @@ static const char *last_filename; /* 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 503c50637aa0..966f5f9aaab3 100644 --- a/gcc/genattrtab.cc +++ b/gcc/genattrtab.cc @@ -314,16 +314,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. */ @@ -5242,6 +5243,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; @@ -5467,29 +5471,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: @@ -5497,6 +5494,36 @@ handle_arg (const char *arg) } } +/* Return a measure of how much text write_attr_get will produce for ATTR. + For a derived attribute that is the length of its lookup table, and + otherwise the number of case labels, since find_most_used turns the + remaining value into the default arm. */ + +static int +attr_output_size (class attr_desc *attr) +{ + if (attr->derived_from) + return attr->derived_from->num_values; + + struct attr_value *common = find_most_used (attr); + int size = 0; + for (struct attr_value *av = attr->first_value; av; av = av->next) + if (av != common) + size += av->num_insns; + return size; +} + +/* Sort attributes so that the ones producing the most text come first. */ + +static int +cmp_attr_output_size (const void *a, const void *b) +{ + class attr_desc *aa = *(class attr_desc *const *) a; + class attr_desc *bb = *(class attr_desc *const *) b; + int diff = attr_output_size (bb) - attr_output_size (aa); + return diff ? diff : strcmp (aa->name, bb->name); +} + int main (int argc, const char **argv) { @@ -5509,9 +5536,24 @@ 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"); + + /* Add the DFA and latency outputs after the attribute outputs, so that + the attribute outputs occupy the first entries of OUTPUT_FILES. */ + 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); @@ -5628,50 +5670,56 @@ 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. */ + auto_vec<class attr_desc *> to_write; for (i = 0; i < MAX_ATTRS_INDEX; i++) for (attr = attrs[i]; attr; attr = attr->next) - { - FILE *outf; + if (!attr->is_special && !attr->is_const) + to_write.safe_push (attr); - 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; + /* Give choose_output the largest functions first. It assigns each one to + the shortest output so far, and feeding it an arbitrary order lets a + late large function land on an already full partition. Attribute + functions differ in size by two orders of magnitude, so that happens + easily. */ + to_write.qsort (cmp_attr_output_size); - if (! attr->is_special && ! attr->is_const) - write_attr_get (outf, attr); - } + for (class attr_desc *a : to_write) + { + FILE *outf; + + if (startswith (a->name, "*internal_dfa_insn_code")) + outf = dfa_file; + else if (startswith (a->name, "*insn_default_latency")) + outf = latency_file; + else + outf = choose_output (output_files); + + write_attr_get (outf, a); + } /* 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); } -
