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.

>       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.  */
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Abhinav Puri; (HRB 36809, AG Nuernberg)

Reply via email to