Ping.

https://gcc.gnu.org/pipermail/gcc-patches/2026-September/731627.html
Thanks,
Kyrill


> On 17 Sep 2026, at 08:48, Kyrylo Tkachov <[email protected]> wrote:
> 
> From: Kyrylo Tkachov <[email protected]>
> 
> This a respin after Richard's feedback, with an attempt to improve the
> locality of the splitted code.
> 
> genmatch writes one function for each outer operation.  Output partitioning
> places those functions whole, so one operation matcher can dominate an output
> part: on trunk gimple_simplify_COND_EXPR is 58429 lines, and the largest
> GIMPLE part is 2.417 times the smallest.  Compiling it with profile
> generation for Armv8-A Thumb also used to fail to assemble due to branch out 
> of
> range assembly errors. That is a separate arm bug that can be fixed elsewhere.
> 
> Split a large per-operation matcher between its top-level alternatives.  The
> entry function keeps the dispatch and calls the function that holds the group
> it selected, so a match enters one group rather than walking all of them.
> 
> Before, one function holds the dispatch and every alternative it reaches:
> 
>  gimple_simplify_COND_EXPR (gimple_match_op *res_op, ...)
>  {
>    switch (TREE_CODE (_p0))
>      {
>      case SSA_NAME:
> if (gimple *_d1 = get_def (valueize, _p0))
>  if (gassign *_a1 = dyn_cast <gassign *> (_d1))
>    switch (gimple_assign_rhs_code (_a1))
>      {
>      case LT_EXPR:
> {
>  tree _q20 = gimple_assign_rhs1 (_a1);
>  ...                             <- 58429 lines in all
> }
>      case NE_EXPR:
> { ... }
> 
> After, the entry holds the dispatch and nothing else:
> 
>  gimple_simplify_COND_EXPR (gimple_match_op *res_op, ...)
>  {
>    switch (TREE_CODE (_p0))
>      {
>      case SSA_NAME:
> if (gimple *_d1 = get_def (valueize, _p0))
>  if (gassign *_a1 = dyn_cast <gassign *> (_d1))
>    switch (gimple_assign_rhs_code (_a1))
>      {
>      case LT_EXPR:
>      case GE_EXPR:
>      case GT_EXPR:
>      case LE_EXPR:
>      case EQ_EXPR:
> {
>  if (genmatch_gimple_simplify_COND_EXPR_chunk_1 (res_op, seq,
> valueize, code, type, _p0, _p1, _p2))
>    return true;
>  break;
> }
>      case NE_EXPR:
> {
>  if (genmatch_gimple_simplify_COND_EXPR_chunk_2 (...))
>    return true;
>  break;
> }
> 
> and each group becomes a function of its own, which output partitioning is
> then free to place in any file:
> 
>  genmatch_gimple_simplify_COND_EXPR_chunk_1 (gimple_match_op *res_op, ...)
>  {
>    switch (TREE_CODE (_p0))
>      {
>      case SSA_NAME:
> if (gimple *_d1 = get_def (valueize, _p0))
>  if (gassign *_a1 = dyn_cast <gassign *> (_d1))
>    switch (gimple_assign_rhs_code (_a1))
>      {
>      case LT_EXPR:
> {
>  tree _q20 = gimple_assign_rhs1 (_a1);
>  ...
> 
> The case labels of consecutive groups that land in the same function fall
> through to one call rather than each getting its own, which is what the run of
> labels above is.
> 
> The groups are the ones collect_operand_groups already forms, which merges
> any two alternatives whose cases overlap.  Distinct groups therefore have
> disjoint cases, and selecting one is what trying them in turn does, so the
> order in which patterns are tried does not change.  Alternatives that carry
> no case, the predicates and the remaining decision-tree nodes, stay in the
> entry function.  Use one tenth of the average number of decision-tree nodes
> per output part as the target group size.  Keep single-file output unchanged
> because it has no shared declaration header.
> 
> On aarch64 with the default ten parts the largest-to-smallest GIMPLE part
> ratio goes from 2.417 to 1.116, compiling the twenty generated sources at a
> parallelism of ten takes about a fifth less wall time, and a bootstrap at
> -j110 comes out 10.7% shorter. The generated text
> grows by 2.4%, which is the dispatch in the entry plus the scaffolding each
> group repeats to reach its own alternatives.
> 
> The compile time performance is not worse.  Over seven large preprocessed
> translation units, against trunk: instructions +0.035%, cycles
> -0.078%, instruction-TLB misses -7.62%, L1I refills -1.18%.
> A match enters the dispatcher and one group, where trunk walks one function
> holding all of them, so the working set of a match is smaller.
> 
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Ok for trunk?
> Thanks,
> Kyrill
> 
> gcc/ChangeLog:
> 
> * genmatch.cc (MATCHER_FN_FILE_FRACTION): New constant.
> (class fn_splitter): New class.
> (fn_splitter::full, fn_splitter::same_chunk_p)
> (fn_splitter::print_params, fn_splitter::print_args)
> (fn_splitter::choose_chunk_file, fn_splitter::open_chunk)
> (fn_splitter::close_chunk, fn_splitter::emit_dispatch)
> (collect_alt_units, fn_splitter::emit_alternatives): New functions.
> (dt_node::gen_kids): Add an optional fn_splitter argument and use it
> at valid function-level boundaries.
> (dt_node::gen_kids_1): Add an optional fn_splitter argument and call
> the function holding a group in place of its body.
> (decision_tree::gen): Set the matcher function budget and use
> fn_splitter for large per-operation matchers.
> 
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
> gcc/genmatch.cc | 435 +++++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 411 insertions(+), 24 deletions(-)
> 
> diff --git a/gcc/genmatch.cc b/gcc/genmatch.cc
> index 42a04436c57..7b786057dab 100644
> --- a/gcc/genmatch.cc
> +++ b/gcc/genmatch.cc
> @@ -2516,6 +2516,7 @@ lower (vec<simplify *>& simplifiers, bool gimple)
>    simplifies and has those as its leafs.  */
> 
> class dt_simplify;
> +class fn_splitter;
> 
> /* A hash-map collecting semantically equivalent leafs in the decision
>    tree for splitting out to separate functions.  */
> @@ -2571,11 +2572,12 @@ public:
> 
>   virtual void gen (FILE *, int, bool, int) {}
> 
> -  void gen_kids (FILE *, int, bool, int, bool);
> +  void gen_kids (FILE *, int, bool, int, bool, fn_splitter * = NULL);
>   void gen_kids_1 (FILE *, int, bool, int,
>   const vec<dt_operand *> &, const vec<dt_operand *> &,
>   const vec<dt_operand *> &, const vec<dt_operand *> &,
> -   const vec<dt_operand *> &, const vec<dt_node *> &, bool);
> +   const vec<dt_operand *> &, const vec<dt_node *> &, bool,
> +   fn_splitter * = NULL);
> 
>   void analyze (sinfo_map_t &);
> };
> @@ -4259,11 +4261,326 @@ emit_fn_group (FILE *f, int indent, bool gimple, int 
> depth,
>   fprintf_indent (f, indent, "  break;\n");
> }
> 
> +/* Target matcher functions at this fraction of the decision-tree nodes that
> +   one output file receives on average.  */
> +
> +static const unsigned int MATCHER_FN_FILE_FRACTION = 10;
> +
> +/* Split a generated matcher across functions.  The entry function keeps the
> +   dispatch that selects between the top-level alternatives and hands each
> +   group of them to the function that holds it, so that matching walks one
> +   group rather than all of them.  */
> +
> +class fn_splitter
> +{
> +public:
> +  /* These are the alternative vectors accepted by gen_kids_1, in their
> +     emission order.  ALT_OTHERS identifies the separate dt_node vector.  */
> +  enum alt_cat
> +  {
> +    ALT_GIMPLE_EXPRS,
> +    ALT_FNS,
> +    ALT_GENERIC_EXPRS,
> +    ALT_GENERIC_FNS,
> +    ALT_PREDS,
> +    ALT_OTHERS
> +  };
> +
> +  /* One complete group from category CAT at INDEX.  NODES estimates the
> +     amount of generated code for the group.  */
> +  struct alt_unit
> +  {
> +    alt_cat cat;
> +    unsigned index;
> +    unsigned nodes;
> +  };
> +
> +  fn_splitter (vec<FILE *> &files_, FILE *f_, const char *fname_,
> +       bool gimple_, unsigned nops_, unsigned budget_)
> +    : f (f_), files (files_), fname (fname_), gimple (gimple_),
> +      nops (nops_), budget (budget_), nchunks (0), dispatching (false)
> +  {}
> +
> +  void emit_alternatives (dt_node *, int, int,
> +  const vec<dt_operand *> &,
> +  const vec<dt_operand *> &,
> +  const vec<dt_operand *> &,
> +  const vec<dt_operand *> &,
> +  const vec<dt_operand *> &,
> +  const vec<dt_node *> &);
> +  bool emit_dispatch (FILE *, int, alt_cat, unsigned);
> +
> +  /* Return true if groups A and B of category CAT go to the same function,
> +     so that the labels of A can fall through to the call emitted for B.  */
> +  bool same_chunk_p (alt_cat cat, unsigned a, unsigned b) const
> +  {
> +    return (dispatching
> +    && b < owner[cat].length ()
> +    && owner[cat][a]
> +    && owner[cat][a] == owner[cat][b]);
> +  }
> +
> +  /* The output file for the entry function.  */
> +  FILE *f;
> +
> +private:
> +  /* Return true if adding MORE nodes to HAVE nodes requires a new function.
> +     Always let an empty function accept one complete group.  A zero budget
> +     disables splitting.  */
> +  bool full (unsigned have, unsigned more) const
> +  {
> +    return budget && have && have + more > budget;
> +  }
> +
> +  void print_params (FILE *);
> +  void print_args (FILE *);
> +  FILE *open_chunk (unsigned);
> +  void close_chunk (FILE *);
> +  FILE *choose_chunk_file ();
> +
> +  /* All output files used for the generated matcher.  */
> +  vec<FILE *> &files;
> +  /* The base name for new functions.  */
> +  const char *fname;
> +  /* True for GIMPLE matching and false for GENERIC matching.  */
> +  bool gimple;
> +  /* The number of operands passed to each function.  */
> +  unsigned nops;
> +  /* The target maximum number of nodes accumulated in one function.  */
> +  unsigned budget;
> +  /* The number of new functions.  */
> +  unsigned nchunks;
> +  /* True while the entry function is being emitted, when the body of a
> +     group is replaced by a call to the function that holds it.  */
> +  bool dispatching;
> +  /* For each category, the function holding each group.  */
> +  auto_vec<unsigned> owner[ALT_PREDS];
> +};
> +
> +/* Print the parameter list of a new function to TO.  Also print its
> +   declaration to the generated header.  */
> +
> +void
> +fn_splitter::print_params (FILE *to)
> +{
> +  if (gimple)
> +    fp_decl (to, " (gimple_match_op *ARG_UNUSED (res_op),"
> + " gimple_seq *ARG_UNUSED (seq),\n"
> + "\t\t tree (*valueize)(tree) ATTRIBUTE_UNUSED,\n"
> + "\t\t code_helper ARG_UNUSED (code),"
> + " tree ARG_UNUSED (type)");
> +  else
> +    fp_decl (to, " (location_t ARG_UNUSED (loc),"
> + " enum tree_code ARG_UNUSED (code),\n"
> + "\t\t     const tree ARG_UNUSED (type)");
> +  for (unsigned i = 0; i < nops; ++i)
> +    fp_decl (to, ", tree ARG_UNUSED (_p%d)", i);
> +  fp_decl (to, ")");
> +}
> +
> +/* Print to TO the arguments that forward the current matcher state to a new
> +   function.  */
> +
> +void
> +fn_splitter::print_args (FILE *to)
> +{
> +  fputs (gimple
> + ? " (res_op, seq, valueize, code, type"
> + : " (loc, code, type", to);
> +  for (unsigned i = 0; i < nops; ++i)
> +    fprintf (to, ", _p%d", i);
> +  fputs (")", to);
> +}
> +
> +/* Return the shortest output file that is not the one the entry function is
> +   being written to, which must not receive another function while it is
> +   open.  */
> +
> +FILE *
> +fn_splitter::choose_chunk_file ()
> +{
> +  FILE *best = NULL;
> +  long best_len = 0;
> +  for (FILE *cand : files)
> +    {
> +      if (cand == f)
> + continue;
> +      long len = ftell (cand);
> +      if (!best || len < best_len)
> + {
> +  best = cand;
> +  best_len = len;
> + }
> +    }
> +  /* Splitting is only enabled with more than one output file.  */
> +  gcc_assert (best);
> +  return best;
> +}
> +
> +/* Open the function holding chunk N and return its output file.  */
> +
> +FILE *
> +fn_splitter::open_chunk (unsigned n)
> +{
> +  /* Multiple output files require a header, which also carries the
> +     declarations for functions emitted in a different file.  */
> +  gcc_assert (header_file);
> +
> +  FILE *cf = choose_chunk_file ();
> +  fp_decl (cf, "\n%s\n%s_chunk_%u", gimple ? "bool" : "tree", fname, n);
> +  print_params (cf);
> +  fp_decl_done (cf, "");
> +  fputs ("{\n", cf);
> +  fprintf_indent (cf, 2, "const bool debug_dump = "
> + "dump_file && (dump_flags & TDF_FOLDING);\n");
> +  return cf;
> +}
> +
> +/* Close the function being written to CF.  */
> +
> +void
> +fn_splitter::close_chunk (FILE *cf)
> +{
> +  fprintf (cf, gimple ? "  return false;\n}\n" : "  return NULL_TREE;\n}\n");
> +}
> +
> +/* Emitted from gen_kids_1 in place of the body of group INDEX of category
> +   CAT.  Hand control to the function holding that group and return true, or
> +   return false to have the body emitted here after all.  */
> +
> +bool
> +fn_splitter::emit_dispatch (FILE *to, int indent, alt_cat cat, unsigned 
> index)
> +{
> +  if (!dispatching || index >= owner[cat].length () || !owner[cat][index])
> +    return false;
> +
> +  fprintf_indent (to, indent, "  {\n");
> +  if (gimple)
> +    {
> +      fprintf_indent (to, indent, "    if (%s_chunk_%u", fname,
> +      owner[cat][index]);
> +      print_args (to);
> +      fprintf (to, ")\n");
> +      fprintf_indent (to, indent, "      return true;\n");
> +    }
> +  else
> +    {
> +      fprintf_indent (to, indent, "    if (tree _chunk_res = %s_chunk_%u",
> +      fname, owner[cat][index]);
> +      print_args (to);
> +      fprintf (to, ")\n");
> +      fprintf_indent (to, indent, "      return _chunk_res;\n");
> +    }
> +  fprintf_indent (to, indent, "    break;\n");
> +  fprintf_indent (to, indent, "  }\n");
> +  return true;
> +}
> +
> +/* Append the complete operand GROUPS to UNITS as category CAT.  */
> +
> +static void
> +collect_alt_units (vec<fn_splitter::alt_unit> &units,
> +   fn_splitter::alt_cat cat,
> +   const vec<dt_operand_group *> &groups)
> +{
> +  for (unsigned i = 0; i < groups.length (); ++i)
> +    {
> +      /* Only alternatives with a level-one parent inspect a parameter of the
> + matcher entry function.  */
> +      unsigned nodes = 0;
> +      for (auto op : groups[i]->operands)
> + {
> +  gcc_assert (op->parent && op->parent->level == 1);
> +  nodes += op->total_size;
> + }
> +      units.safe_push (fn_splitter::alt_unit { cat, i, nodes });
> +    }
> +}
> +
> +/* Emit the alternatives for NODE.  The entry function keeps the dispatch and
> +   each group of alternatives that does not fit the budget alongside the ones
> +   before it goes to a function of its own.  Groups reached from one dispatch
> +   have disjoint cases, so selecting one is what trying them in turn does.  
> */
> +
> +void
> +fn_splitter::emit_alternatives (dt_node *node, int indent, int depth,
> + const vec<dt_operand *> &gimple_exprs,
> + const vec<dt_operand *> &generic_exprs,
> + const vec<dt_operand *> &fns,
> + const vec<dt_operand *> &generic_fns,
> + const vec<dt_operand *> &preds,
> + const vec<dt_node *> &others)
> +{
> +  auto_delete_vec<dt_operand_group> group_storage[ALT_PREDS];
> +  collect_operand_groups (gimple_exprs, group_storage[ALT_GIMPLE_EXPRS]);
> +  collect_operand_groups (fns, group_storage[ALT_FNS]);
> +  collect_operand_groups (generic_exprs, group_storage[ALT_GENERIC_EXPRS]);
> +  collect_operand_groups (generic_fns, group_storage[ALT_GENERIC_FNS]);
> +
> +  auto_vec<alt_unit> units;
> +  for (unsigned cat = ALT_GIMPLE_EXPRS; cat < ALT_PREDS; ++cat)
> +    collect_alt_units (units, (alt_cat) cat, group_storage[cat]);
> +
> +  /* Decide which function holds each group.  Zero keeps it in the entry,
> +     which is where a group stays when nothing has to be split out.  */
> +  for (unsigned cat = ALT_GIMPLE_EXPRS; cat < ALT_PREDS; ++cat)
> +    {
> +      owner[cat].truncate (0);
> +      owner[cat].safe_grow_cleared (group_storage[cat].length ());
> +    }
> +
> +  const unsigned first_chunk = nchunks + 1;
> +  unsigned have = 0;
> +  bool split_p = false;
> +  for (auto &u : units)
> +    {
> +      if (!split_p || full (have, u.nodes))
> + {
> +  ++nchunks;
> +  have = 0;
> +  split_p = true;
> + }
> +      owner[u.cat][u.index] = nchunks;
> +      have += u.nodes;
> +    }
> +
> +  /* The entry function: the dispatch, and whatever cannot be reached
> +     through it.  */
> +  dispatching = true;
> +  node->gen_kids_1 (f, indent, gimple, depth, gimple_exprs, generic_exprs,
> +    fns, generic_fns, preds, others, false, this);
> +  dispatching = false;
> +
> +  /* One function per group of alternatives that was split out.  */
> +  for (unsigned n = first_chunk; n <= nchunks; ++n)
> +    {
> +      auto_vec<dt_operand *> sub[ALT_PREDS];
> +      for (unsigned cat = ALT_GIMPLE_EXPRS; cat < ALT_PREDS; ++cat)
> + for (unsigned i = 0; i < owner[cat].length (); ++i)
> +  if (owner[cat][i] == n)
> +    for (auto op : group_storage[cat][i]->operands)
> +      sub[cat].safe_push (op);
> +
> +      FILE *cf = open_chunk (n);
> +      auto_vec<dt_operand *> no_preds;
> +      auto_vec<dt_node *> no_others;
> +      node->gen_kids_1 (cf, 2, gimple, depth, sub[ALT_GIMPLE_EXPRS],
> + sub[ALT_GENERIC_EXPRS], sub[ALT_FNS],
> + sub[ALT_GENERIC_FNS], no_preds, no_others, false);
> +      close_chunk (cf);
> +    }
> +}
> +
> /* Generate matching code for the children of the decision tree node.  */
> 
> void
> -dt_node::gen_kids (FILE *f, int indent, bool gimple, int depth, bool 
> in_gcond)
> +dt_node::gen_kids (FILE *f, int indent, bool gimple, int depth, bool 
> in_gcond,
> +   fn_splitter *sp)
> {
> +  /* A gcond matcher starts inside an open scope and is not a valid function
> +     boundary.  */
> +  gcc_assert (!sp || !in_gcond);
> +
>   auto_vec<dt_operand *> gimple_exprs;
>   auto_vec<dt_operand *> generic_exprs;
>   auto_vec<dt_operand *> fns;
> @@ -4332,10 +4649,25 @@ dt_node::gen_kids (FILE *f, int indent, bool gimple, 
> int depth, bool in_gcond)
>     Like DT_TRUE, DT_MATCH serves as a barrier as it can cause
>     dependent matches to get out-of-order.  Generate code now
>     for what we have collected sofar.  */
> -  gen_kids_1 (f, indent, gimple, depth, gimple_exprs, generic_exprs,
> -      fns, generic_fns, preds, others, in_gcond);
> +  if (sp)
> +    {
> +      sp->emit_alternatives (this, indent, depth, gimple_exprs,
> + generic_exprs, fns, generic_fns, preds,
> + others);
> +    }
> +  else
> +    gen_kids_1 (f, indent, gimple, depth, gimple_exprs,
> + generic_exprs, fns, generic_fns, preds, others,
> + in_gcond);
>  /* And output the true operand itself.  */
> -  kids[i]->gen (f, indent, gimple, depth);
> +  if (sp && kids[i]->type == dt_node::DT_TRUE)
> +    {
> +      /* A DT_TRUE node emits no code and opens no scope.  Its children
> + remain valid function-level split points.  */
> +      kids[i]->gen_kids (f, indent, gimple, depth, false, sp);
> +    }
> +  else
> +    kids[i]->gen (f, indent, gimple, depth);
>  gimple_exprs.truncate (0);
>  generic_exprs.truncate (0);
>  fns.truncate (0);
> @@ -4348,8 +4680,12 @@ dt_node::gen_kids (FILE *f, int indent, bool gimple, 
> int depth, bool in_gcond)
>     }
> 
>   /* Generate code for the remains.  */
> -  gen_kids_1 (f, indent, gimple, depth, gimple_exprs, generic_exprs,
> -      fns, generic_fns, preds, others, in_gcond);
> +  if (sp)
> +    sp->emit_alternatives (this, indent, depth, gimple_exprs, generic_exprs,
> +   fns, generic_fns, preds, others);
> +  else
> +    gen_kids_1 (f, indent, gimple, depth, gimple_exprs, generic_exprs,
> + fns, generic_fns, preds, others, in_gcond);
> }
> 
> /* Generate matching code for the children of the decision tree node.  */
> @@ -4362,7 +4698,7 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, 
> int depth,
>     const vec<dt_operand *> &generic_fns,
>     const vec<dt_operand *> &preds,
>     const vec<dt_node *> &others,
> -     bool in_gcond)
> +     bool in_gcond, fn_splitter *sp)
> {
>   char buf[128];
>   char *kid_opname = buf;
> @@ -4430,10 +4766,17 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool 
> gimple, int depth,
>  char code[64];
>  snprintf (code, sizeof (code),
>    "gimple_assign_rhs_code (_a%d)", depth);
> -  for (auto group : gimple_expr_groups)
> +  for (unsigned gi = 0; gi < gimple_expr_groups.length (); ++gi)
>    {
> -      emit_group_labels (f, indent, group);
> -      emit_expr_group (f, indent, true, depth, code, group);
> +      emit_group_labels (f, indent, gimple_expr_groups[gi]);
> +      if (sp
> +  && sp->same_chunk_p (fn_splitter::ALT_GIMPLE_EXPRS, gi,
> +       gi + 1))
> + continue;
> +      if (!sp || !sp->emit_dispatch (f, indent,
> +     fn_splitter::ALT_GIMPLE_EXPRS, gi))
> + emit_expr_group (f, indent, true, depth, code,
> + gimple_expr_groups[gi]);
>    }
>  fprintf_indent (f, indent, "default:;\n");
>  fprintf_indent (f, indent, "}\n");
> @@ -4475,10 +4818,15 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool 
> gimple, int depth,
>  char code[64];
>  snprintf (code, sizeof (code),
>    "gimple_call_combined_fn (_c%d)", depth);
> -  for (auto group : fn_groups)
> +  for (unsigned fi = 0; fi < fn_groups.length (); ++fi)
>    {
> -      emit_group_labels (f, indent, group);
> -      emit_fn_group (f, indent, true, depth, NULL, code, group);
> +      emit_group_labels (f, indent, fn_groups[fi]);
> +      if (sp && sp->same_chunk_p (fn_splitter::ALT_FNS, fi, fi + 1))
> + continue;
> +      if (!sp || !sp->emit_dispatch (f, indent, fn_splitter::ALT_FNS,
> +     fi))
> + emit_fn_group (f, indent, true, depth, NULL, code,
> +       fn_groups[fi]);
>    }
>  fprintf_indent (f, indent, "default:;\n");
>  fprintf_indent (f, indent, "}\n");
> @@ -4511,14 +4859,29 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool 
> gimple, int depth,
>       char generic_code[160];
>       snprintf (generic_code, sizeof (generic_code), "TREE_CODE (%s)",
> kid_opname);
> -      for (auto group : generic_expr_groups)
> +      /* The SSA_NAME group was emitted above, so it is not here to fall
> + through to, and a run of labels must not be merged across it.  */
> +      auto emitted_above_p = [&] (unsigned i)
> + {
> +  return (gimple_match_p
> +  && generic_expr_groups[i]->cases.length () == 1
> +  && *generic_expr_groups[i]->cases[0] == SSA_NAME);
> + };
> +      for (unsigned gei = 0; gei < generic_expr_groups.length (); ++gei)
> {
> -  if (gimple_match_p
> -      && group->cases.length () == 1
> -      && *group->cases[0] == SSA_NAME)
> +  dt_operand_group *group = generic_expr_groups[gei];
> +  if (emitted_above_p (gei))
>    continue;
>  emit_group_labels (f, indent, group);
> -  emit_expr_group (f, indent, gimple, depth, generic_code, group);
> +  if (sp
> +      && gei + 1 < generic_expr_groups.length ()
> +      && !emitted_above_p (gei + 1)
> +      && sp->same_chunk_p (fn_splitter::ALT_GENERIC_EXPRS, gei,
> +   gei + 1))
> +    continue;
> +  if (!sp || !sp->emit_dispatch (f, indent,
> + fn_splitter::ALT_GENERIC_EXPRS, gei))
> +    emit_expr_group (f, indent, gimple, depth, generic_code, group);
> }
>     }
> 
> @@ -4536,10 +4899,16 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool 
> gimple, int depth,
>       char code[160];
>       snprintf (code, sizeof (code), "get_call_combined_fn (%s)",
> kid_opname);
> -      for (auto group : generic_fn_groups)
> +      for (unsigned gfi = 0; gfi < generic_fn_groups.length (); ++gfi)
> {
> -  emit_group_labels (f, indent, group);
> -  emit_fn_group (f, indent, false, depth, kid_opname, code, group);
> +  emit_group_labels (f, indent, generic_fn_groups[gfi]);
> +  if (sp && sp->same_chunk_p (fn_splitter::ALT_GENERIC_FNS, gfi,
> +      gfi + 1))
> +    continue;
> +  if (!sp || !sp->emit_dispatch (f, indent,
> + fn_splitter::ALT_GENERIC_FNS, gfi))
> +    emit_fn_group (f, indent, false, depth, kid_opname, code,
> +   generic_fn_groups[gfi]);
> }
>       fprintf_indent (f, indent, "default:;\n");
> 
> @@ -5324,6 +5693,14 @@ decision_tree::gen (vec <FILE *> &files, bool gimple)
>     }
>   fprintf (stderr, "removed %u duplicate tails\n", rcnt);
> 
> +  /* A file receives about ROOT->TOTAL_SIZE / NFILES decision-tree nodes.
> +     Keep matcher functions small enough to spread them across the files.
> +     Keep single-file output unchanged.  */
> +  unsigned nfiles = files.length ();
> +  unsigned budget = (nfiles > 1
> +     ? root->total_size / nfiles / MATCHER_FN_FILE_FRACTION
> +     : 0);
> +
>   for (unsigned n = 1; n <= 7; ++n)
>     {
>       bool has_kids_p = false;
> @@ -5366,7 +5743,17 @@ decision_tree::gen (vec <FILE *> &files, bool gimple)
>  fprintf (f, "{\n");
>  fprintf_indent (f, 2, "const bool debug_dump = "
> "dump_file && (dump_flags & TDF_FOLDING);\n");
> -  dop->gen_kids (f, 2, gimple, 0, false);
> +  if (budget && dop->total_size > budget)
> +    {
> +      char *fname = xasprintf ("genmatch_%s_simplify_%s",
> + gimple ? "gimple" : "generic",
> + e->operation->id);
> +      fn_splitter sp (files, f, fname, gimple, n, budget);
> +      dop->gen_kids (f, 2, gimple, 0, false, &sp);
> +      free (fname);
> +    }
> +  else
> +    dop->gen_kids (f, 2, gimple, 0, false);
>  if (gimple)
>    fprintf (f, "  return false;\n");
>  else
> -- 
> 2.50.1 (Apple Git-155)
> 

Reply via email to