<[email protected]> writes: > From: Kyrylo Tkachov <[email protected]> > > 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. > Ok for trunk? > > 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.
OK, thanks. Richard > Signed-off-by: Kyrylo Tkachov <[email protected]> > --- > gcc/genrecog.cc | 57 ++++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 56 insertions(+), 1 deletion(-) > > diff --git a/gcc/genrecog.cc b/gcc/genrecog.cc > index 663620224ab..04fc2330018 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;
