<[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;

Reply via email to