https://gcc.gnu.org/g:4756a4c0cfaeb18e7fff9108cd9c5d1e1e116690
commit r17-4221-g4756a4c0cfaeb18e7fff9108cd9c5d1e1e116690 Author: Kyrylo Tkachov <[email protected]> Date: Fri Aug 7 13:31:42 2026 +0200 genrecog: write each .md condition out once The decision tree that genrecog builds tests a pattern's .md condition on every path that can accept that pattern, and the same condition guards many patterns. So a condition is emitted once per decision that needs it, in full, inline. On aarch64 that is 27344 emissions of 1059 distinct conditions, and `(TARGET_SVE)' alone accounts for 5269 of them. Together with the #line directive that read-md.cc puts in front of each one, they are about a third of insn-recog-*.cc. Give each condition a helper function in the header that every partition already includes, and call it: static inline bool insn_condition_408 (rtx *operands ATTRIBUTE_UNUSED, rtx_insn *insn ATTRIBUTE_UNUSED) { return #line 1172 ".../config/aarch64/aarch64-sve2.md" (TARGET_SVE2); } so that if (rtx_equal_p (x5, operands[2]) && #line 1172 ".../config/aarch64/aarch64-sve2.md" (TARGET_SVE2)) return 13383; /* *cond_urshlvnx4si_2 */ becomes if (rtx_equal_p (x5, operands[2]) && insn_condition_408 (operands, insn)) return 13383; /* *cond_urshlvnx4si_2 */ The helpers take the operands and the insn because that is everything a condition may read, so no condition has to be classified and none is treated specially. Nothing moves: each condition is still evaluated at exactly the decision that evaluated it before, so the change cannot alter what recog matches or in what order. The helpers are inline, so the host compiler folds them back into their callers and the emitted code is the same as before. On aarch64, insn-recog-*.cc goes from 8994833 bytes in 292736 lines to 6362888 bytes in 228402 lines, and compiles in 50% less time, with peak memory 387MB against 479MB. cc1 itself drops from 533MB to 512MB. Compiling a 39-file C corpus with -mcpu=generic, cortex-a57, cortex-a53, exynos-m1, tsv110, neoverse-v2 and neoverse-n1 gives identical assembly over all 273 compilations, and the built compiler takes the same time as before. Bootstrapped on aarch64-none-linux-gnu. gcc/ChangeLog: * genrecog.cc (md_conditions, md_condition_ids): New variables. (md_condition_id, print_md_conditions): New functions. (print_test): Call a helper for a C test rather than emitting the condition. (main): Write the helpers to the header. Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/genrecog.cc | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/gcc/genrecog.cc b/gcc/genrecog.cc index 663620224abd..04fc23300185 100644 --- a/gcc/genrecog.cc +++ b/gcc/genrecog.cc @@ -113,6 +113,7 @@ #include "errors.h" #include "read-md.h" #include "gensupport.h" +#include "hash-map.h" #undef GENERATOR_FILE enum true_rtx_doe { @@ -1788,6 +1789,54 @@ public: int peep2_count; }; +/* The distinct .md conditions, in the order they were first printed, and a + map from the condition text to that position. A condition is written out + once as a helper function and called wherever it is needed, rather than + inlined at each of the decisions that test it. */ + +static auto_vec<const char *> md_conditions; +static hash_map<nofree_string_hash, unsigned int> md_condition_ids; + +/* Return the index of COND's helper function, registering it if this is the + first time it has been seen. */ + +static unsigned int +md_condition_id (const char *cond) +{ + bool existed; + unsigned int &id = md_condition_ids.get_or_insert (cond, &existed); + if (!existed) + { + id = md_conditions.length (); + md_conditions.safe_push (cond); + } + return id; +} + +/* Write a helper function for each condition to F. They go in the header, + which every generated file includes after the target headers, so that a + condition is compiled once however many decisions test it. + + The helpers take the operands and the insn, which is everything a + condition may read, and are declared inline so that the host compiler can + fold them back into their callers. */ + +static void +print_md_conditions (FILE *f) +{ + unsigned int i; + const char *cond; + + FOR_EACH_VEC_ELT (md_conditions, i, cond) + { + fprintf (f, "\nstatic inline bool\ninsn_condition_%d " + "(rtx *operands ATTRIBUTE_UNUSED,\n" + "\t\t rtx_insn *insn ATTRIBUTE_UNUSED)\n{\n return ", i); + rtx_reader_ptr->print_c_condition (f, cond); + fprintf (f, ";\n}\n"); + } +} + /* Return true if TEST can safely be performed at D, where the conditions in KC hold. TEST is known to occur along the first path from D (i.e. always following the first transition @@ -4762,10 +4811,13 @@ print_test (FILE *f, output_state *os, const rtx_test &test, bool is_param, break; case rtx_test::C_TEST: + /* Pattern routines take no insn, and pattern_c_test_p keeps C tests + out of them for that reason. */ gcc_assert (!is_param && value == 1); if (invert_p) fprintf (f, "!"); - rtx_reader_ptr->print_c_condition (f, test.u.string); + fprintf (f, "insn_condition_%d (operands, insn)", + md_condition_id (test.u.string)); break; case rtx_test::ACCEPT: @@ -5533,6 +5585,9 @@ main (int argc, const char **argv) print_subroutine_group (output_files, header, &os, SPLIT, &split_root); print_subroutine_group (output_files, header, &os, PEEPHOLE2, &peephole2_root); + /* Every test has been printed, so the set of conditions is complete. */ + print_md_conditions (header); + fclose (header); int ret = SUCCESS_EXIT_CODE;
