On Wed, 9 Sep 2026, Kyrylo Tkachov wrote:

> 
> 
> > On 9 Sep 2026, at 15:55, Richard Biener <[email protected]> wrote:
> > 
> > On Wed, 9 Sep 2026, [email protected] wrote:
> > 
> >> 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);
> >> +
> > 
> > Hmm, isn't this rather
> > 
> >     /* A DT_MATCH is emitted as an ordering barrier by gen_kids, so it
> >        can be reached by anything that reaches its siblings.  */
> >     if (n1->type == dt_node::DT_TRUE || n2->type == dt_node::DT_TRUE)
> >       return false;
> >     if (n1->type == dt_node::DT_MATCH || n2->type == dt_node::DT_MATCH)
> >       return true;
> > 
> >> +  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;
> > 
> > please use auto_vec<> here, this should simplify lifetime
> > management in gen_kids_1.
> > 
> >> +
> >> +  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);
> > 
> > it looks like kid_opname is only filled in conditional on
> > if (exprs_len || fns_len || gexprs_len || gfns_len).
> > valgrind over match.pd produces
> > 
> >  Conditional jump or move depends on uninitialised value(s)
> >     at 0x484B2A9: strlen
> >     by 0x49CF131: snprintf
> >     by 0x40E1E0: dt_node::gen_kids_1(...) (genmatch.cc:4527)
> >     by 0x40D637: dt_node::gen_kids(...) (genmatch.cc:4353)
> >     ...
> >   Uninitialised value was created by a stack allocation
> >     at 0x40D786: dt_node::gen_kids_1(...) (genmatch.cc:4384)
> > 
> > the snprintf can be sunk to where it's used and thus initialization
> > is guarded.
> > 
> > 
> > 
> >> +  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))
> > 
> > this test is inconsistent with the assert which also asserts
> > group->cases.length () == 1.  Maybe you want to test that here
> > as well?
> > 
> > Maybe the loop invariant (eprs_len || fns_len) could be hoisted
> > to make the intent clearer?
> > 
> > 
> > It seems this patch also fixes processing order for sth like
> > 
> >     (simplify (plus @0 @0) @0)              ; #0
> >     (simplify (plus @0 (minus @1 @2)) @1)   ; #1
> >     (simplify (plus @0 @0) (negate @0))     ; #2
> > 
> > where previously we matched (oops) #0, #2, #1 and now correctly
> > in order.  But we lose the duplicate match warning on #0/#2
> > because the nodes are no longer siblings, not sure if it's
> > worth to attempt to preserve that?
> > 
> > 
> > Further, decision_tree::insert_operand (genmatch.cc:3040-3060) does
> > 
> >    dt_operand temp (dt_node::DT_TRUE, 0, 0, 0, 0);
> >    elm = decision_tree::find_node (p->kids, &temp);
> >    if (elm == 0)
> >      {
> >        dt_operand match (dt_node::DT_MATCH, 0, match_op, 0, 0);
> >        match.value_match = c->value_match;
> >        elm = decision_tree::find_node (p->kids, &match);
> >      }
> >  ...
> >  at_assert_elm:
> >    gcc_assert (elm->type == dt_node::DT_TRUE
> >                || elm->type == dt_node::DT_OPERAND
> >                || elm->type == dt_node::DT_MATCH);
> > 
> > The DT_MATCH probe has for_id == current_id, so before the patch it
> > could only be blocked by the true_node rule; with the new overlap rule
> > (see 3a) *any* preceding sibling dt_operand from a different pattern
> > sets overlap_node, so this find_node returns NULL far more readily, and
> > then "elm->type" is a NULL dereference.
> > 
> > Maybe clarify by adding an gcc_assert (elm) or handle NULL elm?
> > I'm not sure whether we can hit this case.
> > 
> > Otherwise looks good.
> > 
> > Thanks for reworking the original fix,
> > Richard.
> 
> Thanks. Something like the attached?

Yes, this looks good from a quick look.  One nit at least:

@@ -4337,6 +4584,7 @@ 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);
+
 }
 
 /* Generate matching code for the decision tree operand.  */
-- 

spurious white-space change.

Richard.

Reply via email to