From: Kyrylo Tkachov <[email protected]>

Named matches can keep operator iterators unexpanded until code generation.
The decision tree compares these iterators by identity.  Switch emission
then expands each iterator into its concrete cases.  For example:

  (for cmp (ge gt)
   (match overlap
    (cmp:c @0 @1)))

  (for cmp (lt le)
   (match overlap
    (cmp:c @0 @1)))

produces these invalid labels:

  case GE_EXPR:
  case GT_EXPR:
    ...
  case LE_EXPR:
  case LT_EXPR:
    ...
  case LT_EXPR:
  case LE_EXPR:
    ...
  case GT_EXPR:
  case GE_EXPR:
    ...

The last two label pairs duplicate the first two.  Partially overlapping
sets fail in the same way, so a sort or an equal-set merge is not sufficient.

Partition sibling expression and function nodes into connected groups that
share concrete switch cases.  Emit the cases of each group once.  Guard a
matcher when its operator set covers only part of the group.  Keep the
existing decision-tree barriers.  Do not merge a node across an intervening
overlapping node.  This keeps matcher bodies in source order.  Treat
NOP_EXPR and CONVERT_EXPR as the single CASE_CONVERT dispatch case.

Reduced equal, partial, concrete, conversion, function, and arity overlaps
generate and compile in GIMPLE and GENERIC modes.  Runtime checks also keep
source order across overlapping and repeated-capture paths.

Bootstrapped and regression tested on aarch64-unknown-linux-gnu.

Ok for trunk?

Changes since v3:
- Replace the COND_EXPR-specific eager substitution with general handling of
  overlapping delayed operator sets.
- Preserve source order for overlapping decision-tree paths.

gcc/ChangeLog:

        * genmatch.cc (case_convert_id_p): New function.
        (same_case_p): Likewise.
        (operation_has_case_p): Likewise.
        (operations_overlap_p): Likewise.
        (function_operation_p): Likewise.
        (operand_cases_overlap_p): Likewise.
        (cmp_operand): Handle conversion cases and function iterators.
        (nodes_overlap_p): New function.
        (decision_tree::find_node): Do not merge across an overlapping node.
        (fns_cmp): Remove.
        (dt_operand_group): New structure.
        (add_operation_cases): New function.
        (find_group): Likewise.
        (collect_operand_groups): Likewise.
        (release_operand_groups): Likewise.
        (operation_covers_group_p): Likewise.
        (emit_operation_test): Likewise.
        (emit_group_labels): Likewise.
        (emit_expr_group): Likewise.
        (emit_fn_group): Likewise.
        (dt_node::gen_kids): Do not sort function nodes.
        (dt_node::gen_kids_1): Emit groups with unique case labels.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/genmatch.cc | 500 ++++++++++++++++++++++++++++++++++++------------
 1 file changed, 382 insertions(+), 118 deletions(-)

diff --git a/gcc/genmatch.cc b/gcc/genmatch.cc
index 71a47f95996..32ab083d9ff 100644
--- a/gcc/genmatch.cc
+++ b/gcc/genmatch.cc
@@ -2666,6 +2666,91 @@ public:
   static void print_node (dt_node *, FILE *f = stderr, unsigned = 0);
 };
 
+/* Return true if ID is a tree-code conversion dispatch key.  */
+
+static bool
+case_convert_id_p (id_base *id)
+{
+  if (id->kind != id_base::CODE)
+    return false;
+  enum tree_code code = as_a <operator_id *> (id)->code;
+  return code == NOP_EXPR || code == CONVERT_EXPR;
+}
+
+/* Return true if concrete operations ID1 and ID2 use the same case label.  */
+
+static bool
+same_case_p (id_base *id1, id_base *id2)
+{
+  if (id1->kind != id2->kind)
+    return false;
+
+  if (id1->kind == id_base::CODE)
+    return ((case_convert_id_p (id1) && case_convert_id_p (id2))
+           || (as_a <operator_id *> (id1)->code
+               == as_a <operator_id *> (id2)->code));
+
+  if (id1->kind == id_base::FN)
+    return (as_a <fn_id *> (id1)->fn == as_a <fn_id *> (id2)->fn);
+
+  return false;
+}
+
+/* Return true if OPERATION dispatches on CASE_ID.  */
+
+static bool
+operation_has_case_p (id_base *operation, id_base *case_id)
+{
+  if (user_id *u = dyn_cast <user_id *> (operation))
+    {
+      for (auto id : u->substitutes)
+       if (same_case_p (id, case_id))
+         return true;
+      return false;
+    }
+
+  return same_case_p (operation, case_id);
+}
+
+/* Return true if OP1 and OP2 have a common dispatch case.  */
+
+static bool
+operations_overlap_p (id_base *op1, id_base *op2)
+{
+  if (user_id *u = dyn_cast <user_id *> (op1))
+    {
+      for (auto id : u->substitutes)
+       if (operation_has_case_p (op2, id))
+         return true;
+      return false;
+    }
+
+  return operation_has_case_p (op2, op1);
+}
+
+/* Return true if OPERATION denotes a function.  */
+
+static bool
+function_operation_p (id_base *operation)
+{
+  if (user_id *u = dyn_cast <user_id *> (operation))
+    operation = u->substitutes[0];
+  return operation->kind == id_base::FN;
+}
+
+/* Return true if O1 and O2 can use the same expression dispatch case.  */
+
+static bool
+operand_cases_overlap_p (operand *o1, operand *o2)
+{
+  expr *e1 = dyn_cast <expr *> (o1);
+  expr *e2 = dyn_cast <expr *> (o2);
+  if (!e1 || !e2)
+    return false;
+
+  return operations_overlap_p (e1->operation, e2->operation);
+}
+
 /* Compare two AST operands O1 and O2 and return true if they are equal.  */
 
 bool
@@ -2684,11 +2769,13 @@ cmp_operand (operand *o1, operand *o2)
     {
       expr *e1 = static_cast<expr *>(o1);
       expr *e2 = static_cast<expr *>(o2);
-      if (e1->operation != e2->operation
+      if ((e1->operation != e2->operation
+          && !(case_convert_id_p (e1->operation)
+               && case_convert_id_p (e2->operation)))
          || e1->is_generic != e2->is_generic
          || e1->match_phi != e2->match_phi)
        return false;
-      if (e1->operation->kind == id_base::FN
+      if (function_operation_p (e1->operation)
          /* For function calls also compare number of arguments.  */
          && e1->ops.length () != e2->ops.length ())
        return false;
@@ -2724,6 +2811,28 @@ decision_tree::cmp_node (dt_node *n1, dt_node *n2)
   return false;
 }
 
+/* Return true if N1 and N2 can match at the same decision-tree point.  */
+
+static bool
+nodes_overlap_p (dt_node *n1, dt_node *n2)
+{
+  if (!is_a <dt_operand *> (n1) || !is_a <dt_operand *> (n2))
+    return false;
+
+  if (n1->type == dt_node::DT_MATCH || n2->type == dt_node::DT_MATCH)
+    return (n1->type == dt_node::DT_MATCH
+           || n1->type == dt_node::DT_OPERAND)
+          && (n2->type == dt_node::DT_MATCH
+              || n2->type == dt_node::DT_OPERAND);
+
+  if (n1->type != dt_node::DT_OPERAND
+      || n2->type != dt_node::DT_OPERAND)
+    return false;
+
+  return operand_cases_overlap_p (as_a <dt_operand *> (n1)->op,
+                                 as_a <dt_operand *> (n2)->op);
+}
+
 /* Search OPS for a decision tree node like P and return it if found.  */
 
 dt_node *
@@ -2735,6 +2844,7 @@ decision_tree::find_node (vec<dt_node *>& ops, dt_node *p)
       && ops.last ()->type == dt_node::DT_TRUE)
     return ops.last ();
   dt_operand *true_node = NULL;
+  dt_operand *overlap_node = NULL;
   for (int i = ops.length () - 1; i >= 0; --i)
     {
       /* But we can't merge across DT_TRUE nodes as they serve as
@@ -2750,25 +2860,28 @@ decision_tree::find_node (vec<dt_node *>& ops, dt_node 
*p)
        {
          /* Unless we are processing the same pattern or the blocking
             pattern is before the one we are going to merge with.  */
-         if (true_node
-             && true_node->for_id != current_id
-             && true_node->for_id > as_a <dt_operand *> (ops[i])->for_id)
+         if ((true_node
+              && true_node->for_id != current_id
+              && true_node->for_id > as_a <dt_operand *> (ops[i])->for_id)
+             || overlap_node)
            {
              if (verbose >= 1)
                {
+                 dt_operand *barrier = overlap_node ? overlap_node : true_node;
                  location_t p_loc = 0;
-                 if (p->type == dt_node::DT_OPERAND)
+                 if (is_a <dt_operand *> (p)
+                     && as_a <dt_operand *> (p)->op)
                    p_loc = as_a <dt_operand *> (p)->op->location;
                  location_t op_loc = 0;
-                 if (ops[i]->type == dt_node::DT_OPERAND)
+                 if (is_a <dt_operand *> (ops[i])
+                     && as_a <dt_operand *> (ops[i])->op)
                    op_loc = as_a <dt_operand *> (ops[i])->op->location;
-                 location_t true_loc = 0;
-                 true_loc = true_node->op->location;
+                 location_t barrier_loc = barrier->op->location;
                  warning_at (p_loc,
                              "failed to merge decision tree node");
                  warning_at (op_loc,
                              "with the following");
-                 warning_at (true_loc,
+                 warning_at (barrier_loc,
                              "because of the following which serves as 
ordering "
                              "barrier");
                }
@@ -2776,6 +2889,10 @@ decision_tree::find_node (vec<dt_node *>& ops, dt_node 
*p)
            }
          return ops[i];
        }
+      if (is_a <dt_operand *> (ops[i])
+         && as_a <dt_operand *> (ops[i])->for_id != current_id
+         && nodes_overlap_p (ops[i], p))
+       overlap_node = as_a <dt_operand *> (ops[i]);
     }
   return NULL;
 }
@@ -3954,24 +4071,210 @@ dt_operand::gen_generic_expr (FILE *f, int indent, 
const char *opname)
   return 0;
 }
 
-/* Compare 2 fns or generic_fns vector entries for vector sorting.
-   Same operation entries with different number of arguments should
-   be adjacent.  */
+/* A connected set of decision-tree operands with overlapping dispatch
+   cases.  */
 
-static int
-fns_cmp (const void *p1, const void *p2)
-{
-  dt_operand *op1 = *(dt_operand *const *) p1;
-  dt_operand *op2 = *(dt_operand *const *) p2;
-  expr *e1 = as_a <expr *> (op1->op);
-  expr *e2 = as_a <expr *> (op2->op);
-  id_base *b1 = e1->operation;
-  id_base *b2 = e2->operation;
-  if (b1->hashval < b2->hashval)
-    return -1;
-  if (b1->hashval > b2->hashval)
-    return 1;
-  return strcmp (b1->id, b2->id);
+struct dt_operand_group
+{
+  vec<id_base *> cases;
+  vec<dt_operand *> operands;
+
+  dt_operand_group () : cases (vNULL), operands (vNULL)
+  {}
+};
+
+/* Add the concrete cases for OPERATION to CASES.  */
+
+static void
+add_operation_cases (vec<id_base *> &cases, id_base *operation)
+{
+  if (user_id *u = dyn_cast <user_id *> (operation))
+    {
+      for (auto id : u->substitutes)
+       add_operation_cases (cases, id);
+      return;
+    }
+
+  for (auto id : cases)
+    if (same_case_p (id, operation))
+      return;
+  cases.safe_push (operation);
+}
+
+/* Find the representative of INDEX in PARENTS.  */
+
+static unsigned
+find_group (const vec<unsigned> &parents, unsigned index)
+{
+  while (parents[index] != index)
+    index = parents[index];
+  return index;
+}
+
+/* Partition OPS into overlap-connected GROUPS.  */
+
+static void
+collect_operand_groups (const vec<dt_operand *> &ops,
+                       vec<dt_operand_group *> &groups)
+{
+  auto_vec<unsigned> parents;
+  for (unsigned i = 0; i < ops.length (); ++i)
+    parents.safe_push (i);
+
+  for (unsigned i = 0; i < ops.length (); ++i)
+    for (unsigned j = 0; j < i; ++j)
+      if (operand_cases_overlap_p (ops[i]->op, ops[j]->op))
+       {
+         unsigned root1 = find_group (parents, i);
+         unsigned root2 = find_group (parents, j);
+         if (root1 != root2)
+           parents[MAX (root1, root2)] = MIN (root1, root2);
+       }
+
+  auto_vec<unsigned> roots;
+  for (unsigned i = 0; i < ops.length (); ++i)
+    {
+      unsigned root = find_group (parents, i);
+      unsigned group_index;
+      for (group_index = 0; group_index < roots.length (); ++group_index)
+       if (roots[group_index] == root)
+         break;
+      if (group_index == roots.length ())
+       {
+         roots.safe_push (root);
+         groups.safe_push (new dt_operand_group ());
+       }
+
+      dt_operand_group *group = groups[group_index];
+      group->operands.safe_push (ops[i]);
+      expr *e = as_a <expr *> (ops[i]->op);
+      add_operation_cases (group->cases, e->operation);
+    }
+}
+
+/* Release GROUPS and their vectors.  */
+
+static void
+release_operand_groups (vec<dt_operand_group *> &groups)
+{
+  for (auto group : groups)
+    {
+      group->cases.release ();
+      group->operands.release ();
+      delete group;
+    }
+}
+
+/* Return true if OPERATION covers all cases in GROUP.  */
+
+static bool
+operation_covers_group_p (id_base *operation, dt_operand_group *group)
+{
+  for (auto id : group->cases)
+    if (!operation_has_case_p (operation, id))
+      return false;
+  return true;
+}
+
+/* Emit an if condition that tests CODE against OPERATION.  */
+
+static void
+emit_operation_test (FILE *f, int indent, const char *code,
+                    id_base *operation)
+{
+  auto_vec<id_base *> cases;
+  add_operation_cases (cases, operation);
+
+  fprintf_indent (f, indent, "if (");
+  for (unsigned i = 0; i < cases.length (); ++i)
+    {
+      if (i)
+       fprintf (f, "\n%*s|| ", indent + 4, "");
+      if (case_convert_id_p (cases[i]))
+       fprintf (f, "CONVERT_EXPR_CODE_P (%s)", code);
+      else
+       fprintf (f, "%s == %s", code, cases[i]->id);
+    }
+  fprintf (f, ")\n");
+}
+
+/* Emit the labels for GROUP.  */
+
+static void
+emit_group_labels (FILE *f, int indent, dt_operand_group *group)
+{
+  for (auto id : group->cases)
+    if (case_convert_id_p (id))
+      fprintf_indent (f, indent, "CASE_CONVERT:\n");
+    else
+      fprintf_indent (f, indent, "case %s:\n", id->id);
+}
+
+/* Emit an expression GROUP.  CODE is the dispatched tree code.  */
+
+static void
+emit_expr_group (FILE *f, int indent, bool gimple, int depth,
+                const char *code, dt_operand_group *group)
+{
+  fprintf_indent (f, indent, "  {\n");
+  bool multiple_p = group->operands.length () > 1;
+  for (auto op : group->operands)
+    {
+      expr *e = as_a <expr *> (op->op);
+      bool test_p = !operation_covers_group_p (e->operation, group);
+      if (test_p)
+       {
+         emit_operation_test (f, indent + 4, code, e->operation);
+         fprintf_indent (f, indent + 4, "  {\n");
+       }
+      else if (multiple_p)
+       fprintf_indent (f, indent + 4, "{\n");
+
+      op->gen (f, indent + ((test_p || multiple_p) ? 6 : 4),
+              gimple, depth);
+
+      if (test_p)
+       fprintf_indent (f, indent + 4, "  }\n");
+      else if (multiple_p)
+       fprintf_indent (f, indent + 4, "}\n");
+    }
+  fprintf_indent (f, indent, "    break;\n");
+  fprintf_indent (f, indent, "  }\n");
+}
+
+/* Emit a function GROUP.  CODE is the dispatched combined function.  */
+
+static void
+emit_fn_group (FILE *f, int indent, bool gimple, int depth,
+              const char *opname, const char *code, dt_operand_group *group)
+{
+  for (auto op : group->operands)
+    {
+      expr *e = as_a <expr *> (op->op);
+      bool test_p = !operation_covers_group_p (e->operation, group);
+      if (test_p)
+       {
+         emit_operation_test (f, indent + 2, code, e->operation);
+         fprintf_indent (f, indent + 2, "  {\n");
+       }
+
+      int inner_indent = indent + (test_p ? 4 : 0);
+      if (gimple)
+       fprintf_indent (f, inner_indent,
+                       "  if (gimple_call_num_args (_c%d) == %d)\n",
+                       depth, e->ops.length ());
+      else
+       fprintf_indent (f, inner_indent,
+                       "  if (call_expr_nargs (%s) == %d)\n",
+                       opname, e->ops.length ());
+      fprintf_indent (f, inner_indent, "    {\n");
+      op->gen (f, inner_indent + 6, gimple, depth);
+      fprintf_indent (f, inner_indent, "    }\n");
+
+      if (test_p)
+       fprintf_indent (f, indent + 2, "  }\n");
+    }
+  fprintf_indent (f, indent, "  break;\n");
 }
 
 /* Generate matching code for the children of the decision tree node.  */
@@ -4047,8 +4350,6 @@ 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.  */
-         fns.qsort (fns_cmp);
-         generic_fns.qsort (fns_cmp);
          gen_kids_1 (f, indent, gimple, depth, gimple_exprs, generic_exprs,
                      fns, generic_fns, preds, others, in_gcond);
          /* And output the true operand itself.  */
@@ -4065,8 +4366,6 @@ dt_node::gen_kids (FILE *f, int indent, bool gimple, int 
depth, bool in_gcond)
     }
 
   /* Generate code for the remains.  */
-  fns.qsort (fns_cmp);
-  generic_fns.qsort (fns_cmp);
   gen_kids_1 (f, indent, gimple, depth, gimple_exprs, generic_exprs,
              fns, generic_fns, preds, others, in_gcond);
 }
@@ -4091,6 +4390,15 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, 
int depth,
   unsigned fns_len = fns.length ();
   unsigned gfns_len = generic_fns.length ();
 
+  auto_vec<dt_operand_group *> gimple_expr_groups;
+  auto_vec<dt_operand_group *> generic_expr_groups;
+  auto_vec<dt_operand_group *> fn_groups;
+  auto_vec<dt_operand_group *> generic_fn_groups;
+  collect_operand_groups (gimple_exprs, gimple_expr_groups);
+  collect_operand_groups (generic_exprs, generic_expr_groups);
+  collect_operand_groups (fns, fn_groups);
+  collect_operand_groups (generic_fns, generic_fn_groups);
+
   if (exprs_len || fns_len || gexprs_len || gfns_len)
     {
       if (exprs_len)
@@ -4134,24 +4442,15 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, 
int depth,
          for (unsigned i = 0; i < exprs_len; ++i)
            {
              expr *e = as_a <expr *> (gimple_exprs[i]->op);
-             if (user_id *u = dyn_cast <user_id *> (e->operation))
-               {
-                 for (auto id : u->substitutes)
-                   fprintf_indent (f, indent, "case %s:\n", id->id);
-               }
-             else
-               {
-                 id_base *op = e->operation;
-                 cond_expr_p |= (*op == COND_EXPR && e->match_phi);
-                 if (*op == CONVERT_EXPR || *op == NOP_EXPR)
-                   fprintf_indent (f, indent, "CASE_CONVERT:\n");
-                 else
-                   fprintf_indent (f, indent, "case %s:\n", op->id);
-               }
-             fprintf_indent (f, indent, "  {\n");
-             gimple_exprs[i]->gen (f, indent + 4, true, depth);
-             fprintf_indent (f, indent, "    break;\n");
-             fprintf_indent (f, indent, "  }\n");
+             cond_expr_p |= (*e->operation == COND_EXPR && e->match_phi);
+           }
+         char code[64];
+         snprintf (code, sizeof (code),
+                   "gimple_assign_rhs_code (_a%d)", depth);
+         for (auto group : gimple_expr_groups)
+           {
+             emit_group_labels (f, indent, group);
+             emit_expr_group (f, indent, true, depth, code, group);
            }
          fprintf_indent (f, indent, "default:;\n");
          fprintf_indent (f, indent, "}\n");
@@ -4190,32 +4489,14 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, 
int depth,
 
          indent += 4;
          fprintf_indent (f, indent, "{\n");
-         id_base *last_op = NULL;
-         for (unsigned i = 0; i < fns_len; ++i)
+         char code[64];
+         snprintf (code, sizeof (code),
+                   "gimple_call_combined_fn (_c%d)", depth);
+         for (auto group : fn_groups)
            {
-             expr *e = as_a <expr *>(fns[i]->op);
-             if (e->operation != last_op)
-               {
-                 if (i)
-                   fprintf_indent (f, indent, "  break;\n");
-                 if (user_id *u = dyn_cast <user_id *> (e->operation))
-                   for (auto id : u->substitutes)
-                     fprintf_indent (f, indent, "case %s:\n", id->id);
-                 else
-                   fprintf_indent (f, indent, "case %s:\n", e->operation->id);
-               }
-             last_op = e->operation;
-             /* We need to be defensive against bogus prototypes allowing
-                calls with not enough arguments.  */
-             fprintf_indent (f, indent,
-                             "  if (gimple_call_num_args (_c%d) == %d)\n",
-                             depth, e->ops.length ());
-             fprintf_indent (f, indent, "    {\n");
-             fns[i]->gen (f, indent + 6, true, depth);
-             fprintf_indent (f, indent, "    }\n");
+             emit_group_labels (f, indent, group);
+             emit_fn_group (f, indent, true, depth, NULL, code, group);
            }
-
-         fprintf_indent (f, indent, "  break;\n");
          fprintf_indent (f, indent, "default:;\n");
          fprintf_indent (f, indent, "}\n");
          indent -= 4;
@@ -4226,42 +4507,31 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, 
int depth,
       fprintf_indent (f, indent, "    }\n");
       /* See if there is SSA_NAME among generic_exprs and if yes, emit it
         here rather than in the next loop.  */
-      for (unsigned i = 0; i < generic_exprs.length (); ++i)
-       {
-         expr *e = as_a <expr *>(generic_exprs[i]->op);
-         id_base *op = e->operation;
-         if (*op == SSA_NAME && (exprs_len || fns_len))
+      for (auto group : generic_expr_groups)
+       for (auto id : group->cases)
+         if (*id == SSA_NAME && (exprs_len || fns_len))
            {
-             fprintf_indent (f, indent + 4, "{\n");
-             generic_exprs[i]->gen (f, indent + 6, gimple, depth);
-             fprintf_indent (f, indent + 4, "}\n");
+             gcc_checking_assert (group->cases.length () == 1);
+             for (auto op : group->operands)
+               {
+                 fprintf_indent (f, indent + 4, "{\n");
+                 op->gen (f, indent + 6, gimple, depth);
+                 fprintf_indent (f, indent + 4, "}\n");
+               }
            }
-       }
 
       fprintf_indent (f, indent, "  break;\n");
     }
 
-  for (unsigned i = 0; i < generic_exprs.length (); ++i)
+  char generic_code[160];
+  snprintf (generic_code, sizeof (generic_code), "TREE_CODE (%s)",
+           kid_opname);
+  for (auto group : generic_expr_groups)
     {
-      expr *e = as_a <expr *>(generic_exprs[i]->op);
-      id_base *op = e->operation;
-      if (*op == CONVERT_EXPR || *op == NOP_EXPR)
-       fprintf_indent (f, indent, "CASE_CONVERT:\n");
-      else if (*op == SSA_NAME && (exprs_len || fns_len))
-       /* Already handled above.  */
+      if (*group->cases[0] == SSA_NAME && (exprs_len || fns_len))
        continue;
-      else
-       {
-         if (user_id *u = dyn_cast <user_id *> (op))
-           for (auto id : u->substitutes)
-             fprintf_indent (f, indent, "case %s:\n", id->id);
-         else
-           fprintf_indent (f, indent, "case %s:\n", op->id);
-       }
-      fprintf_indent (f, indent, "  {\n");
-      generic_exprs[i]->gen (f, indent + 4, gimple, depth);
-      fprintf_indent (f, indent, "    break;\n");
-      fprintf_indent (f, indent, "  }\n");
+      emit_group_labels (f, indent, group);
+      emit_expr_group (f, indent, gimple, depth, generic_code, group);
     }
 
   if (gfns_len)
@@ -4275,25 +4545,14 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, 
int depth,
                      "    {\n");
       indent += 4;
 
-      id_base *last_op = NULL;
-      for (unsigned j = 0; j < generic_fns.length (); ++j)
+      char code[160];
+      snprintf (code, sizeof (code), "get_call_combined_fn (%s)",
+               kid_opname);
+      for (auto group : generic_fn_groups)
        {
-         expr *e = as_a <expr *>(generic_fns[j]->op);
-         gcc_assert (e->operation->kind == id_base::FN);
-
-         if (e->operation != last_op)
-           {
-             if (j)
-               fprintf_indent (f, indent, "  break;\n");
-             fprintf_indent (f, indent, "case %s:\n", e->operation->id);
-           }
-         last_op = e->operation;
-         fprintf_indent (f, indent, "  if (call_expr_nargs (%s) == %d)\n"
-                                    "    {\n", kid_opname, e->ops.length ());
-         generic_fns[j]->gen (f, indent + 6, false, depth);
-         fprintf_indent (f, indent, "    }\n");
+         emit_group_labels (f, indent, group);
+         emit_fn_group (f, indent, false, depth, kid_opname, code, group);
        }
-      fprintf_indent (f, indent, "  break;\n");
       fprintf_indent (f, indent, "default:;\n");
 
       indent -= 4;
@@ -4337,6 +4596,11 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, 
int depth,
 
   for (unsigned i = 0; i < others.length (); ++i)
     others[i]->gen (f, indent, gimple, depth);
+
+  release_operand_groups (gimple_expr_groups);
+  release_operand_groups (generic_expr_groups);
+  release_operand_groups (fn_groups);
+  release_operand_groups (generic_fn_groups);
 }
 
 /* Generate matching code for the decision tree operand.  */
-- 
2.50.1 (Apple Git-155)

Reply via email to