https://gcc.gnu.org/g:832a088e9259f4eeefb2856913ff2e146b5d1073
commit r17-4087-g832a088e9259f4eeefb2856913ff2e146b5d1073 Author: Kyrylo Tkachov <[email protected]> Date: Sun Aug 9 12:09:39 2026 +0200 genopinit: group optab patterns by their availability condition init_all_optabs evaluated one HAVE_* macro per optab pattern. That is 3328 assignments on aarch64 and 14830 on riscv64. genopinit already worked around the cost of compiling them by splitting them into functions of 1000 statements each, with the comment "In order to prevent pathological situations in dataflow analysis". genflags.cc derives HAVE_<name> from the pattern's C test, and defines it as 1 when that test is statically true, so patterns whose tests agree after the same normalisation also agree on the definition of HAVE_<name>. Targets have far fewer distinct tests than patterns. Number the distinct tests, emit a pattern to test map, and evaluate each test once. static void init_optabs_00 (struct target_optabs *optabs) { bool *ena = optabs->pat_enable; ena[0] = HAVE_extendqihi2; ... /* 1000 of these, four times */ } becomes static const unsigned char pat_cond[NUM_OPTAB_PATTERNS] = { 0, 0, ... }; void init_all_optabs (struct target_optabs *optabs) { const bool cond[] = { bool (HAVE_extendqihi2), ... /* one per distinct condition */ }; bool *ena = optabs->pat_enable; for (unsigned int i = 0; i < NUM_OPTAB_PATTERNS; ++i) ena[i] = cond[pat_cond[i]]; } The conditions stay local to init_all_optabs, so every call evaluates them for the current target options, and each pat_enable entry stays independent for swap_optab_enable. Each one is converted to bool because a HAVE_* macro can expand to an int-valued expression, and narrowing in a braced initializer is ill-formed. Distinct conditions per target: aarch64 136 of 3328 patterns, riscv64 986 of 14830, x86_64 346 of 2975, i686 288 of 2614, avr 30 of 926. On aarch64 insn-opinit.cc shrinks from 279KB to 174KB. Compiling that file on its own with the host g++ 11.4.0 at -g -O2 takes 93% less wall time and 65% less peak memory, 609MB down to 216MB. This is a standalone compile, not a bootstrap measurement. init_all_optabs also gets cheaper to run: it executes once per target_optabs initialisation, so each #pragma GCC target and each target attribute now costs 136 condition evaluations instead of 3328. Bootstrapped on aarch64-none-linux-gnu. gcc/ChangeLog: * genopinit.cc (struct pattern_info): New. (patterns): Use it. (gen_insn): Record the pattern's C test. (pattern_cmp): Use pattern_info. (narrowest_uint_type): New function. (main): Emit a condition-number table and a loop instead of one assignment per pattern, and drop the split into 1000-pattern functions. Suggested-by: Tamar Christina <[email protected]> Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/genopinit.cc | 112 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 39 deletions(-) diff --git a/gcc/genopinit.cc b/gcc/genopinit.cc index 62eaf5bd3785..a6dfdd56ff29 100644 --- a/gcc/genopinit.cc +++ b/gcc/genopinit.cc @@ -35,22 +35,37 @@ static const char * const rtx_upname[] = { #undef DEF_RTL_EXPR +/* An optab pattern and the condition under which it is available. */ + +struct pattern_info : optab_pattern +{ + /* The pattern's C test, or "" if the pattern is always available. + genflags.cc defines HAVE_<name> as 1 for a statically true test and + as the test itself otherwise, so two patterns that agree on COND + also agree on the definition of HAVE_<name>. */ + const char *cond; +}; + /* Vector in which to collect insns that match. */ -static vec<optab_pattern> patterns; +static vec<pattern_info> patterns; static void gen_insn (md_rtx_info *info) { - optab_pattern p; + pattern_info p; if (find_optab (&p, XSTR (info->def, 0))) - patterns.safe_push (p); + { + const char *cond = get_c_test (info->def); + p.cond = maybe_eval_c_test (cond) == 1 ? "" : cond; + patterns.safe_push (p); + } } static int pattern_cmp (const void *va, const void *vb) { - const optab_pattern *a = (const optab_pattern *)va; - const optab_pattern *b = (const optab_pattern *)vb; + const pattern_info *a = (const pattern_info *)va; + const pattern_info *b = (const pattern_info *)vb; return a->sort_num - b->sort_num; } @@ -173,12 +188,29 @@ handle_overloaded_gen (FILE *file, overloaded_name *oname) } } +/* Return the name of the narrowest unsigned integer type that is guaranteed + to hold every value below LIMIT. The generated file is compiled for the + host, which need not be the machine that runs this generator, so use the + ranges that ISO C requires rather than the ones this machine happens to + provide. GCC hosts have at least 32-bit ints, which cover every vec + index. */ + +static const char * +narrowest_uint_type (unsigned int limit) +{ + if (limit <= 256) + return "unsigned char"; + if (limit <= 65536) + return "unsigned short"; + return "unsigned int"; +} + int main (int argc, const char **argv) { FILE *h_file, *s_file; unsigned int i, j, n, last_kind[5]; - optab_pattern *p; + pattern_info *p; progname = "genopinit"; @@ -367,45 +399,47 @@ 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. */ + /* Targets have far fewer distinct conditions than optab patterns. Number + the distinct conditions and record the number that each pattern uses, so + that init_all_optabs can evaluate every condition once and then fill + pat_enable from a table. That keeps init_all_optabs a fixed size no + matter how many patterns a target has, and makes it cheaper to run. */ - const int patterns_per_function = 1000; - - if (patterns.length () > patterns_per_function) + auto_vec<unsigned int> pat_cond (patterns.length ()); + auto_vec<unsigned int> cond_rep; + hash_map<nofree_string_hash, unsigned int> cond_ids; + for (i = 0; patterns.iterate (i, &p); ++i) { - unsigned num_init_functions - = patterns.length () / patterns_per_function + 1; - for (i = 0; i < num_init_functions; i++) + bool existed; + unsigned int &id = cond_ids.get_or_insert (p->cond, &existed); + if (!existed) { - 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"); + id = cond_rep.length (); + cond_rep.safe_push (i); } - - 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"); + pat_cond.quick_push (id); } + fprintf (s_file, "static const %s pat_cond[NUM_OPTAB_PATTERNS] = {\n ", + narrowest_uint_type (cond_rep.length ())); + for (i = 0; i < pat_cond.length (); ++i) + fprintf (s_file, " %u,%s", pat_cond[i], (i % 20) == 19 ? "\n " : ""); + fprintf (s_file, "\n};\n\n"); + + /* The conditions are local to init_all_optabs, so that each call evaluates + them for the current target options. Convert each one to bool, since a + HAVE_* macro can expand to an int-valued expression and narrowing in a + braced initializer is ill-formed. */ + fprintf (s_file, "void\ninit_all_optabs (struct target_optabs *optabs)\n{\n"); + fprintf (s_file, " const bool cond[] = {\n"); + for (i = 0; i < cond_rep.length (); ++i) + fprintf (s_file, " bool (HAVE_%s),\n", patterns[cond_rep[i]].name); + fprintf (s_file, " };\n"); + fprintf (s_file, " bool *ena = optabs->pat_enable;\n"); + fprintf (s_file, " for (unsigned int i = 0; i < NUM_OPTAB_PATTERNS; ++i)\n"); + fprintf (s_file, " ena[i] = cond[pat_cond[i]];\n"); + fprintf (s_file, "}\n\n"); + fprintf (s_file, "/* Returns TRUE if the target supports any of the partial vector\n" " optabs: while_ult_optab, len_load_optab, len_store_optab,\n"
