[2/5] gimple-match: Add a gimple_extract_op function

Message ID mpt8rxww56o.fsf@arm.com
State Committed
Headers
Series [1/5] Add IFN_COND_FMIN/FMAX functions |

Commit Message

Richard Sandiford Nov. 10, 2021, 12:44 p.m. UTC
  code_helper and gimple_match_op seem like generally useful ways
of summing up a gimple_assign or gimple_call (or gimple_cond).
This patch adds a gimple_extract_op function that can be used
for that.

Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?

Richard


gcc/
	* gimple-match.h (gimple_extract_op): Declare.
	* gimple-match.c (gimple_extract): New function, extracted from...
	(gimple_simplify): ...here.
	(gimple_extract_op): New function.
---
 gcc/gimple-match-head.c | 261 +++++++++++++++++++++++-----------------
 gcc/gimple-match.h      |   1 +
 2 files changed, 149 insertions(+), 113 deletions(-)
  

Comments

Richard Biener Nov. 11, 2021, 10:49 a.m. UTC | #1
On Wed, Nov 10, 2021 at 1:46 PM Richard Sandiford via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> code_helper and gimple_match_op seem like generally useful ways
> of summing up a gimple_assign or gimple_call (or gimple_cond).
> This patch adds a gimple_extract_op function that can be used
> for that.
>
> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?
>
> Richard
>
>
> gcc/
>         * gimple-match.h (gimple_extract_op): Declare.
>         * gimple-match.c (gimple_extract): New function, extracted from...
>         (gimple_simplify): ...here.
>         (gimple_extract_op): New function.
> ---
>  gcc/gimple-match-head.c | 261 +++++++++++++++++++++++-----------------
>  gcc/gimple-match.h      |   1 +
>  2 files changed, 149 insertions(+), 113 deletions(-)
>
> diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c
> index 9d88b2f8551..4c6e0883ba4 100644
> --- a/gcc/gimple-match-head.c
> +++ b/gcc/gimple-match-head.c
> @@ -890,12 +890,29 @@ try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
>    return true;
>  }
>
> -/* The main STMT based simplification entry.  It is used by the fold_stmt
> -   and the fold_stmt_to_constant APIs.  */
> +/* Common subroutine of gimple_extract_op and gimple_simplify.  Try to
> +   describe STMT in RES_OP.  Return:
>
> -bool
> -gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> -                tree (*valueize)(tree), tree (*top_valueize)(tree))
> +   - -1 if extraction failed
> +   - otherwise, 0 if no simplification should take place
> +   - otherwise, the number of operands for a GIMPLE_ASSIGN or GIMPLE_COND
> +   - otherwise, -2 for a GIMPLE_CALL
> +
> +   Before recording an operand, call:
> +
> +   - VALUEIZE_CONDITION for a COND_EXPR condition
> +   - VALUEIZE_NAME if the rhs of a GIMPLE_ASSIGN is an SSA_NAME

I think at least VALUEIZE_NAME is unnecessary, see below

> +   - VALUEIZE_OP for every other top-level operand
> +
> +   Each routine takes a tree argument and returns a tree.  */
> +
> +template<typename ValueizeOp, typename ValueizeCondition,
> +        typename ValueizeName>
> +inline int
> +gimple_extract (gimple *stmt, gimple_match_op *res_op,
> +               ValueizeOp valueize_op,
> +               ValueizeCondition valueize_condition,
> +               ValueizeName valueize_name)
>  {
>    switch (gimple_code (stmt))
>      {
> @@ -911,100 +928,53 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>                 || code == VIEW_CONVERT_EXPR)
>               {
>                 tree op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
> -               bool valueized = false;
> -               op0 = do_valueize (op0, top_valueize, valueized);
> -               res_op->set_op (code, type, op0);
> -               return (gimple_resimplify1 (seq, res_op, valueize)
> -                       || valueized);
> +               res_op->set_op (code, type, valueize_op (op0));
> +               return 1;
>               }
>             else if (code == BIT_FIELD_REF)
>               {
>                 tree rhs1 = gimple_assign_rhs1 (stmt);
> -               tree op0 = TREE_OPERAND (rhs1, 0);
> -               bool valueized = false;
> -               op0 = do_valueize (op0, top_valueize, valueized);
> +               tree op0 = valueize_op (TREE_OPERAND (rhs1, 0));
>                 res_op->set_op (code, type, op0,
>                                 TREE_OPERAND (rhs1, 1),
>                                 TREE_OPERAND (rhs1, 2),
>                                 REF_REVERSE_STORAGE_ORDER (rhs1));
> -               if (res_op->reverse)
> -                 return valueized;
> -               return (gimple_resimplify3 (seq, res_op, valueize)
> -                       || valueized);
> +               return res_op->reverse ? 0 : 3;
>               }
> -           else if (code == SSA_NAME
> -                    && top_valueize)
> +           else if (code == SSA_NAME)
>               {
>                 tree op0 = gimple_assign_rhs1 (stmt);
> -               tree valueized = top_valueize (op0);
> +               tree valueized = valueize_name (op0);
>                 if (!valueized || op0 == valueized)
> -                 return false;
> +                 return -1;
>                 res_op->set_op (TREE_CODE (op0), type, valueized);
> -               return true;
> +               return 0;

So the old code in an obfuscated way just knowed nothing simplifies
on the plain not valueized name but returned true when valueization
changed the stmt.  So I'd expect

                     tree valueized = valueize_op (op0);
                     res_op->set_op (TREE_CODE (op0), type, valueized);
                     return 0;

here and the gimple_simplify caller returning 'valueized'.  I think
that the old code treated a NULL top_valueize () as "fail" is just
premature optimization without any effect.

>               }
>             break;
>           case GIMPLE_UNARY_RHS:
>             {
>               tree rhs1 = gimple_assign_rhs1 (stmt);
> -             bool valueized = false;
> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
> -             res_op->set_op (code, type, rhs1);
> -             return (gimple_resimplify1 (seq, res_op, valueize)
> -                     || valueized);
> +             res_op->set_op (code, type, valueize_op (rhs1));
> +             return 1;
>             }
>           case GIMPLE_BINARY_RHS:
>             {
> -             tree rhs1 = gimple_assign_rhs1 (stmt);
> -             tree rhs2 = gimple_assign_rhs2 (stmt);
> -             bool valueized = false;
> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
> -             rhs2 = do_valueize (rhs2, top_valueize, valueized);
> +             tree rhs1 = valueize_op (gimple_assign_rhs1 (stmt));
> +             tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
>               res_op->set_op (code, type, rhs1, rhs2);
> -             return (gimple_resimplify2 (seq, res_op, valueize)
> -                     || valueized);
> +             return 2;
>             }
>           case GIMPLE_TERNARY_RHS:
>             {
> -             bool valueized = false;
>               tree rhs1 = gimple_assign_rhs1 (stmt);
> -             /* If this is a COND_EXPR first try to simplify an
> -                embedded GENERIC condition.  */
> -             if (code == COND_EXPR)
> -               {
> -                 if (COMPARISON_CLASS_P (rhs1))
> -                   {
> -                     tree lhs = TREE_OPERAND (rhs1, 0);
> -                     tree rhs = TREE_OPERAND (rhs1, 1);
> -                     lhs = do_valueize (lhs, top_valueize, valueized);
> -                     rhs = do_valueize (rhs, top_valueize, valueized);
> -                     gimple_match_op res_op2 (res_op->cond, TREE_CODE (rhs1),
> -                                              TREE_TYPE (rhs1), lhs, rhs);
> -                     if ((gimple_resimplify2 (seq, &res_op2, valueize)
> -                          || valueized)
> -                         && res_op2.code.is_tree_code ())
> -                       {
> -                         valueized = true;
> -                         if (TREE_CODE_CLASS ((enum tree_code) res_op2.code)
> -                             == tcc_comparison)
> -                           rhs1 = build2 (res_op2.code, TREE_TYPE (rhs1),
> -                                          res_op2.ops[0], res_op2.ops[1]);
> -                         else if (res_op2.code == SSA_NAME
> -                                  || res_op2.code == INTEGER_CST
> -                                  || res_op2.code == VECTOR_CST)
> -                           rhs1 = res_op2.ops[0];
> -                         else
> -                           valueized = false;
> -                       }
> -                   }
> -               }
> -             tree rhs2 = gimple_assign_rhs2 (stmt);
> -             tree rhs3 = gimple_assign_rhs3 (stmt);
> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
> -             rhs2 = do_valueize (rhs2, top_valueize, valueized);
> -             rhs3 = do_valueize (rhs3, top_valueize, valueized);
> +             if (code == COND_EXPR && COMPARISON_CLASS_P (rhs1))
> +               rhs1 = valueize_condition (rhs1);
> +             else
> +               rhs1 = valueize_op (rhs1);
> +             tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
> +             tree rhs3 = valueize_op (gimple_assign_rhs3 (stmt));
>               res_op->set_op (code, type, rhs1, rhs2, rhs3);
> -             return (gimple_resimplify3 (seq, res_op, valueize)
> -                     || valueized);
> +             return 3;
>             }
>           default:
>             gcc_unreachable ();
> @@ -1018,7 +988,6 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>           && gimple_call_num_args (stmt) >= 1
>           && gimple_call_num_args (stmt) <= 5)
>         {
> -         bool valueized = false;
>           combined_fn cfn;
>           if (gimple_call_internal_p (stmt))
>             cfn = as_combined_fn (gimple_call_internal_fn (stmt));
> @@ -1026,17 +995,17 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>             {
>               tree fn = gimple_call_fn (stmt);
>               if (!fn)
> -               return false;
> +               return -1;
>
> -             fn = do_valueize (fn, top_valueize, valueized);
> +             fn = valueize_op (fn);
>               if (TREE_CODE (fn) != ADDR_EXPR
>                   || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL)
> -               return false;
> +               return -1;
>
>               tree decl = TREE_OPERAND (fn, 0);
>               if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL
>                   || !gimple_builtin_call_types_compatible_p (stmt, decl))
> -               return false;
> +               return -1;
>
>               cfn = as_combined_fn (DECL_FUNCTION_CODE (decl));
>             }
> @@ -1044,56 +1013,122 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>           unsigned int num_args = gimple_call_num_args (stmt);
>           res_op->set_op (cfn, TREE_TYPE (gimple_call_lhs (stmt)), num_args);
>           for (unsigned i = 0; i < num_args; ++i)
> -           {
> -             tree arg = gimple_call_arg (stmt, i);
> -             res_op->ops[i] = do_valueize (arg, top_valueize, valueized);
> -           }
> -         if (internal_fn_p (cfn)
> -             && try_conditional_simplification (as_internal_fn (cfn),
> -                                                res_op, seq, valueize))
> -           return true;
> -         switch (num_args)
> -           {
> -           case 1:
> -             return (gimple_resimplify1 (seq, res_op, valueize)
> -                     || valueized);
> -           case 2:
> -             return (gimple_resimplify2 (seq, res_op, valueize)
> -                     || valueized);
> -           case 3:
> -             return (gimple_resimplify3 (seq, res_op, valueize)
> -                     || valueized);
> -           case 4:
> -             return (gimple_resimplify4 (seq, res_op, valueize)
> -                     || valueized);
> -           case 5:
> -             return (gimple_resimplify5 (seq, res_op, valueize)
> -                     || valueized);
> -           default:
> -            gcc_unreachable ();
> -           }
> +           res_op->ops[i] = valueize_op (gimple_call_arg (stmt, i));
> +         return -2;
>         }
>        break;
>
>      case GIMPLE_COND:
>        {
> -       tree lhs = gimple_cond_lhs (stmt);
> -       tree rhs = gimple_cond_rhs (stmt);
> -       bool valueized = false;
> -       lhs = do_valueize (lhs, top_valueize, valueized);
> -       rhs = do_valueize (rhs, top_valueize, valueized);
> +       tree lhs = valueize_op (gimple_cond_lhs (stmt));
> +       tree rhs = valueize_op (gimple_cond_rhs (stmt));
>         res_op->set_op (gimple_cond_code (stmt), boolean_type_node, lhs, rhs);
> -       return (gimple_resimplify2 (seq, res_op, valueize)
> -               || valueized);
> +       return 2;
>        }
>
>      default:
>        break;
>      }
>
> -  return false;
> +  return -1;
>  }
>
> +/* Try to describe STMT in RES_OP, returning true on success.
> +   For GIMPLE_CONDs, describe the condition that is being tested.
> +   For GIMPLE_ASSIGNs, describe the rhs of the assignment.
> +   For GIMPLE_CALLs, describe the call.  */
> +
> +bool
> +gimple_extract_op (gimple *stmt, gimple_match_op *res_op)
> +{
> +  auto nop = [](tree op) { return op; };
> +  return gimple_extract (stmt, res_op, nop, nop, nop) != -1;
> +}
> +
> +/* The main STMT based simplification entry.  It is used by the fold_stmt
> +   and the fold_stmt_to_constant APIs.  */
> +
> +bool
> +gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> +                tree (*valueize)(tree), tree (*top_valueize)(tree))
> +{
> +  bool valueized = false;
> +  auto valueize_op = [&](tree op)
> +    {
> +      return do_valueize (op, top_valueize, valueized);
> +    };
> +  auto valueize_condition = [&](tree op) -> tree
> +    {
> +      bool cond_valueized = false;
> +      tree lhs = do_valueize (TREE_OPERAND (op, 0), top_valueize,
> +                             cond_valueized);
> +      tree rhs = do_valueize (TREE_OPERAND (op, 1), top_valueize,
> +                             cond_valueized);
> +      gimple_match_op res_op2 (res_op->cond, TREE_CODE (op),
> +                              TREE_TYPE (op), lhs, rhs);
> +      if ((gimple_resimplify2 (seq, &res_op2, valueize)
> +          || cond_valueized)
> +         && res_op2.code.is_tree_code ())
> +       {
> +         if (TREE_CODE_CLASS ((tree_code) res_op2.code) == tcc_comparison)
> +           {
> +             valueized = true;
> +             return build2 (res_op2.code, TREE_TYPE (op),
> +                            res_op2.ops[0], res_op2.ops[1]);
> +           }
> +         else if (res_op2.code == SSA_NAME
> +                  || res_op2.code == INTEGER_CST
> +                  || res_op2.code == VECTOR_CST)
> +           {
> +             valueized = true;
> +             return res_op2.ops[0];
> +           }
> +       }
> +      return valueize_op (op);
> +    };
> +  auto valueize_name = [&](tree op)
> +    {
> +      return top_valueize ? top_valueize (op) : op;
> +    };
> +
> +  int res = gimple_extract (stmt, res_op, valueize_op, valueize_condition,
> +                           valueize_name);
> +  if (res == -1)
> +    return false;
> +
> +  if (res == -2)
> +    {
> +      combined_fn cfn = combined_fn (res_op->code);
> +      if (internal_fn_p (cfn)
> +         && try_conditional_simplification (as_internal_fn (cfn),
> +                                            res_op, seq, valueize))
> +       return true;
> +      res = res_op->num_ops;
> +    }
> +
> +  switch (res)
> +    {
> +    case 0:
> +      return valueized;
> +    case 1:
> +      return (gimple_resimplify1 (seq, res_op, valueize)
> +             || valueized);
> +    case 2:
> +      return (gimple_resimplify2 (seq, res_op, valueize)
> +             || valueized);
> +    case 3:
> +      return (gimple_resimplify3 (seq, res_op, valueize)
> +             || valueized);
> +    case 4:
> +      return (gimple_resimplify4 (seq, res_op, valueize)
> +             || valueized);
> +    case 5:
> +      return (gimple_resimplify5 (seq, res_op, valueize)
> +             || valueized);

can't you use just res.resimplify (seq, valueize) || valueized instead of the
switch here when res is not zero?

Otherwise looks OK.

Thanks,
Richard.

> +    default:
> +      gcc_unreachable ();
> +    }
> +}
>
>  /* Helper for the autogenerated code, valueize OP.  */
>
> diff --git a/gcc/gimple-match.h b/gcc/gimple-match.h
> index 2d4ea476076..15a0f584db7 100644
> --- a/gcc/gimple-match.h
> +++ b/gcc/gimple-match.h
> @@ -333,6 +333,7 @@ gimple_simplified_result_is_gimple_val (const gimple_match_op *op)
>
>  extern tree (*mprts_hook) (gimple_match_op *);
>
> +bool gimple_extract_op (gimple *, gimple_match_op *);
>  bool gimple_simplify (gimple *, gimple_match_op *, gimple_seq *,
>                       tree (*)(tree), tree (*)(tree));
>  tree maybe_push_res_to_seq (gimple_match_op *, gimple_seq *,
> --
> 2.25.1
>
  
Richard Sandiford Nov. 16, 2021, 3:51 p.m. UTC | #2
Richard Biener <richard.guenther@gmail.com> writes:
> On Wed, Nov 10, 2021 at 1:46 PM Richard Sandiford via Gcc-patches
> <gcc-patches@gcc.gnu.org> wrote:
>>
>> code_helper and gimple_match_op seem like generally useful ways
>> of summing up a gimple_assign or gimple_call (or gimple_cond).
>> This patch adds a gimple_extract_op function that can be used
>> for that.
>>
>> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?
>>
>> Richard
>>
>>
>> gcc/
>>         * gimple-match.h (gimple_extract_op): Declare.
>>         * gimple-match.c (gimple_extract): New function, extracted from...
>>         (gimple_simplify): ...here.
>>         (gimple_extract_op): New function.
>> ---
>>  gcc/gimple-match-head.c | 261 +++++++++++++++++++++++-----------------
>>  gcc/gimple-match.h      |   1 +
>>  2 files changed, 149 insertions(+), 113 deletions(-)
>>
>> diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c
>> index 9d88b2f8551..4c6e0883ba4 100644
>> --- a/gcc/gimple-match-head.c
>> +++ b/gcc/gimple-match-head.c
>> @@ -890,12 +890,29 @@ try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
>>    return true;
>>  }
>>
>> -/* The main STMT based simplification entry.  It is used by the fold_stmt
>> -   and the fold_stmt_to_constant APIs.  */
>> +/* Common subroutine of gimple_extract_op and gimple_simplify.  Try to
>> +   describe STMT in RES_OP.  Return:
>>
>> -bool
>> -gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>> -                tree (*valueize)(tree), tree (*top_valueize)(tree))
>> +   - -1 if extraction failed
>> +   - otherwise, 0 if no simplification should take place
>> +   - otherwise, the number of operands for a GIMPLE_ASSIGN or GIMPLE_COND
>> +   - otherwise, -2 for a GIMPLE_CALL
>> +
>> +   Before recording an operand, call:
>> +
>> +   - VALUEIZE_CONDITION for a COND_EXPR condition
>> +   - VALUEIZE_NAME if the rhs of a GIMPLE_ASSIGN is an SSA_NAME
>
> I think at least VALUEIZE_NAME is unnecessary, see below

Yeah, it's unnecessary.  The idea was to (try to) ensure that
gimple_simplify keeps all the microoptimisations that it had
previously.  This includes open-coding do_valueize for SSA_NAMEs
and jumping straight to the right gimplify_resimplifyN routine
when the number of operands is already known.

(The two calls to gimple_extract<> produce different functions
that ought to get inlined into their single callers.  A lot of the
jumps should then be threaded.)

I can drop all that if you don't think it's worth it though.
Just wanted to double-check first.

Thanks,
Richard

>> +   - VALUEIZE_OP for every other top-level operand
>> +
>> +   Each routine takes a tree argument and returns a tree.  */
>> +
>> +template<typename ValueizeOp, typename ValueizeCondition,
>> +        typename ValueizeName>
>> +inline int
>> +gimple_extract (gimple *stmt, gimple_match_op *res_op,
>> +               ValueizeOp valueize_op,
>> +               ValueizeCondition valueize_condition,
>> +               ValueizeName valueize_name)
>>  {
>>    switch (gimple_code (stmt))
>>      {
>> @@ -911,100 +928,53 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>>                 || code == VIEW_CONVERT_EXPR)
>>               {
>>                 tree op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
>> -               bool valueized = false;
>> -               op0 = do_valueize (op0, top_valueize, valueized);
>> -               res_op->set_op (code, type, op0);
>> -               return (gimple_resimplify1 (seq, res_op, valueize)
>> -                       || valueized);
>> +               res_op->set_op (code, type, valueize_op (op0));
>> +               return 1;
>>               }
>>             else if (code == BIT_FIELD_REF)
>>               {
>>                 tree rhs1 = gimple_assign_rhs1 (stmt);
>> -               tree op0 = TREE_OPERAND (rhs1, 0);
>> -               bool valueized = false;
>> -               op0 = do_valueize (op0, top_valueize, valueized);
>> +               tree op0 = valueize_op (TREE_OPERAND (rhs1, 0));
>>                 res_op->set_op (code, type, op0,
>>                                 TREE_OPERAND (rhs1, 1),
>>                                 TREE_OPERAND (rhs1, 2),
>>                                 REF_REVERSE_STORAGE_ORDER (rhs1));
>> -               if (res_op->reverse)
>> -                 return valueized;
>> -               return (gimple_resimplify3 (seq, res_op, valueize)
>> -                       || valueized);
>> +               return res_op->reverse ? 0 : 3;
>>               }
>> -           else if (code == SSA_NAME
>> -                    && top_valueize)
>> +           else if (code == SSA_NAME)
>>               {
>>                 tree op0 = gimple_assign_rhs1 (stmt);
>> -               tree valueized = top_valueize (op0);
>> +               tree valueized = valueize_name (op0);
>>                 if (!valueized || op0 == valueized)
>> -                 return false;
>> +                 return -1;
>>                 res_op->set_op (TREE_CODE (op0), type, valueized);
>> -               return true;
>> +               return 0;
>
> So the old code in an obfuscated way just knowed nothing simplifies
> on the plain not valueized name but returned true when valueization
> changed the stmt.  So I'd expect
>
>                      tree valueized = valueize_op (op0);
>                      res_op->set_op (TREE_CODE (op0), type, valueized);
>                      return 0;
>
> here and the gimple_simplify caller returning 'valueized'.  I think
> that the old code treated a NULL top_valueize () as "fail" is just
> premature optimization without any effect.
>
>>               }
>>             break;
>>           case GIMPLE_UNARY_RHS:
>>             {
>>               tree rhs1 = gimple_assign_rhs1 (stmt);
>> -             bool valueized = false;
>> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
>> -             res_op->set_op (code, type, rhs1);
>> -             return (gimple_resimplify1 (seq, res_op, valueize)
>> -                     || valueized);
>> +             res_op->set_op (code, type, valueize_op (rhs1));
>> +             return 1;
>>             }
>>           case GIMPLE_BINARY_RHS:
>>             {
>> -             tree rhs1 = gimple_assign_rhs1 (stmt);
>> -             tree rhs2 = gimple_assign_rhs2 (stmt);
>> -             bool valueized = false;
>> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
>> -             rhs2 = do_valueize (rhs2, top_valueize, valueized);
>> +             tree rhs1 = valueize_op (gimple_assign_rhs1 (stmt));
>> +             tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
>>               res_op->set_op (code, type, rhs1, rhs2);
>> -             return (gimple_resimplify2 (seq, res_op, valueize)
>> -                     || valueized);
>> +             return 2;
>>             }
>>           case GIMPLE_TERNARY_RHS:
>>             {
>> -             bool valueized = false;
>>               tree rhs1 = gimple_assign_rhs1 (stmt);
>> -             /* If this is a COND_EXPR first try to simplify an
>> -                embedded GENERIC condition.  */
>> -             if (code == COND_EXPR)
>> -               {
>> -                 if (COMPARISON_CLASS_P (rhs1))
>> -                   {
>> -                     tree lhs = TREE_OPERAND (rhs1, 0);
>> -                     tree rhs = TREE_OPERAND (rhs1, 1);
>> -                     lhs = do_valueize (lhs, top_valueize, valueized);
>> -                     rhs = do_valueize (rhs, top_valueize, valueized);
>> -                     gimple_match_op res_op2 (res_op->cond, TREE_CODE (rhs1),
>> -                                              TREE_TYPE (rhs1), lhs, rhs);
>> -                     if ((gimple_resimplify2 (seq, &res_op2, valueize)
>> -                          || valueized)
>> -                         && res_op2.code.is_tree_code ())
>> -                       {
>> -                         valueized = true;
>> -                         if (TREE_CODE_CLASS ((enum tree_code) res_op2.code)
>> -                             == tcc_comparison)
>> -                           rhs1 = build2 (res_op2.code, TREE_TYPE (rhs1),
>> -                                          res_op2.ops[0], res_op2.ops[1]);
>> -                         else if (res_op2.code == SSA_NAME
>> -                                  || res_op2.code == INTEGER_CST
>> -                                  || res_op2.code == VECTOR_CST)
>> -                           rhs1 = res_op2.ops[0];
>> -                         else
>> -                           valueized = false;
>> -                       }
>> -                   }
>> -               }
>> -             tree rhs2 = gimple_assign_rhs2 (stmt);
>> -             tree rhs3 = gimple_assign_rhs3 (stmt);
>> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
>> -             rhs2 = do_valueize (rhs2, top_valueize, valueized);
>> -             rhs3 = do_valueize (rhs3, top_valueize, valueized);
>> +             if (code == COND_EXPR && COMPARISON_CLASS_P (rhs1))
>> +               rhs1 = valueize_condition (rhs1);
>> +             else
>> +               rhs1 = valueize_op (rhs1);
>> +             tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
>> +             tree rhs3 = valueize_op (gimple_assign_rhs3 (stmt));
>>               res_op->set_op (code, type, rhs1, rhs2, rhs3);
>> -             return (gimple_resimplify3 (seq, res_op, valueize)
>> -                     || valueized);
>> +             return 3;
>>             }
>>           default:
>>             gcc_unreachable ();
>> @@ -1018,7 +988,6 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>>           && gimple_call_num_args (stmt) >= 1
>>           && gimple_call_num_args (stmt) <= 5)
>>         {
>> -         bool valueized = false;
>>           combined_fn cfn;
>>           if (gimple_call_internal_p (stmt))
>>             cfn = as_combined_fn (gimple_call_internal_fn (stmt));
>> @@ -1026,17 +995,17 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>>             {
>>               tree fn = gimple_call_fn (stmt);
>>               if (!fn)
>> -               return false;
>> +               return -1;
>>
>> -             fn = do_valueize (fn, top_valueize, valueized);
>> +             fn = valueize_op (fn);
>>               if (TREE_CODE (fn) != ADDR_EXPR
>>                   || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL)
>> -               return false;
>> +               return -1;
>>
>>               tree decl = TREE_OPERAND (fn, 0);
>>               if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL
>>                   || !gimple_builtin_call_types_compatible_p (stmt, decl))
>> -               return false;
>> +               return -1;
>>
>>               cfn = as_combined_fn (DECL_FUNCTION_CODE (decl));
>>             }
>> @@ -1044,56 +1013,122 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>>           unsigned int num_args = gimple_call_num_args (stmt);
>>           res_op->set_op (cfn, TREE_TYPE (gimple_call_lhs (stmt)), num_args);
>>           for (unsigned i = 0; i < num_args; ++i)
>> -           {
>> -             tree arg = gimple_call_arg (stmt, i);
>> -             res_op->ops[i] = do_valueize (arg, top_valueize, valueized);
>> -           }
>> -         if (internal_fn_p (cfn)
>> -             && try_conditional_simplification (as_internal_fn (cfn),
>> -                                                res_op, seq, valueize))
>> -           return true;
>> -         switch (num_args)
>> -           {
>> -           case 1:
>> -             return (gimple_resimplify1 (seq, res_op, valueize)
>> -                     || valueized);
>> -           case 2:
>> -             return (gimple_resimplify2 (seq, res_op, valueize)
>> -                     || valueized);
>> -           case 3:
>> -             return (gimple_resimplify3 (seq, res_op, valueize)
>> -                     || valueized);
>> -           case 4:
>> -             return (gimple_resimplify4 (seq, res_op, valueize)
>> -                     || valueized);
>> -           case 5:
>> -             return (gimple_resimplify5 (seq, res_op, valueize)
>> -                     || valueized);
>> -           default:
>> -            gcc_unreachable ();
>> -           }
>> +           res_op->ops[i] = valueize_op (gimple_call_arg (stmt, i));
>> +         return -2;
>>         }
>>        break;
>>
>>      case GIMPLE_COND:
>>        {
>> -       tree lhs = gimple_cond_lhs (stmt);
>> -       tree rhs = gimple_cond_rhs (stmt);
>> -       bool valueized = false;
>> -       lhs = do_valueize (lhs, top_valueize, valueized);
>> -       rhs = do_valueize (rhs, top_valueize, valueized);
>> +       tree lhs = valueize_op (gimple_cond_lhs (stmt));
>> +       tree rhs = valueize_op (gimple_cond_rhs (stmt));
>>         res_op->set_op (gimple_cond_code (stmt), boolean_type_node, lhs, rhs);
>> -       return (gimple_resimplify2 (seq, res_op, valueize)
>> -               || valueized);
>> +       return 2;
>>        }
>>
>>      default:
>>        break;
>>      }
>>
>> -  return false;
>> +  return -1;
>>  }
>>
>> +/* Try to describe STMT in RES_OP, returning true on success.
>> +   For GIMPLE_CONDs, describe the condition that is being tested.
>> +   For GIMPLE_ASSIGNs, describe the rhs of the assignment.
>> +   For GIMPLE_CALLs, describe the call.  */
>> +
>> +bool
>> +gimple_extract_op (gimple *stmt, gimple_match_op *res_op)
>> +{
>> +  auto nop = [](tree op) { return op; };
>> +  return gimple_extract (stmt, res_op, nop, nop, nop) != -1;
>> +}
>> +
>> +/* The main STMT based simplification entry.  It is used by the fold_stmt
>> +   and the fold_stmt_to_constant APIs.  */
>> +
>> +bool
>> +gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>> +                tree (*valueize)(tree), tree (*top_valueize)(tree))
>> +{
>> +  bool valueized = false;
>> +  auto valueize_op = [&](tree op)
>> +    {
>> +      return do_valueize (op, top_valueize, valueized);
>> +    };
>> +  auto valueize_condition = [&](tree op) -> tree
>> +    {
>> +      bool cond_valueized = false;
>> +      tree lhs = do_valueize (TREE_OPERAND (op, 0), top_valueize,
>> +                             cond_valueized);
>> +      tree rhs = do_valueize (TREE_OPERAND (op, 1), top_valueize,
>> +                             cond_valueized);
>> +      gimple_match_op res_op2 (res_op->cond, TREE_CODE (op),
>> +                              TREE_TYPE (op), lhs, rhs);
>> +      if ((gimple_resimplify2 (seq, &res_op2, valueize)
>> +          || cond_valueized)
>> +         && res_op2.code.is_tree_code ())
>> +       {
>> +         if (TREE_CODE_CLASS ((tree_code) res_op2.code) == tcc_comparison)
>> +           {
>> +             valueized = true;
>> +             return build2 (res_op2.code, TREE_TYPE (op),
>> +                            res_op2.ops[0], res_op2.ops[1]);
>> +           }
>> +         else if (res_op2.code == SSA_NAME
>> +                  || res_op2.code == INTEGER_CST
>> +                  || res_op2.code == VECTOR_CST)
>> +           {
>> +             valueized = true;
>> +             return res_op2.ops[0];
>> +           }
>> +       }
>> +      return valueize_op (op);
>> +    };
>> +  auto valueize_name = [&](tree op)
>> +    {
>> +      return top_valueize ? top_valueize (op) : op;
>> +    };
>> +
>> +  int res = gimple_extract (stmt, res_op, valueize_op, valueize_condition,
>> +                           valueize_name);
>> +  if (res == -1)
>> +    return false;
>> +
>> +  if (res == -2)
>> +    {
>> +      combined_fn cfn = combined_fn (res_op->code);
>> +      if (internal_fn_p (cfn)
>> +         && try_conditional_simplification (as_internal_fn (cfn),
>> +                                            res_op, seq, valueize))
>> +       return true;
>> +      res = res_op->num_ops;
>> +    }
>> +
>> +  switch (res)
>> +    {
>> +    case 0:
>> +      return valueized;
>> +    case 1:
>> +      return (gimple_resimplify1 (seq, res_op, valueize)
>> +             || valueized);
>> +    case 2:
>> +      return (gimple_resimplify2 (seq, res_op, valueize)
>> +             || valueized);
>> +    case 3:
>> +      return (gimple_resimplify3 (seq, res_op, valueize)
>> +             || valueized);
>> +    case 4:
>> +      return (gimple_resimplify4 (seq, res_op, valueize)
>> +             || valueized);
>> +    case 5:
>> +      return (gimple_resimplify5 (seq, res_op, valueize)
>> +             || valueized);
>
> can't you use just res.resimplify (seq, valueize) || valueized instead of the
> switch here when res is not zero?
>
> Otherwise looks OK.
>
> Thanks,
> Richard.
>
>> +    default:
>> +      gcc_unreachable ();
>> +    }
>> +}
>>
>>  /* Helper for the autogenerated code, valueize OP.  */
>>
>> diff --git a/gcc/gimple-match.h b/gcc/gimple-match.h
>> index 2d4ea476076..15a0f584db7 100644
>> --- a/gcc/gimple-match.h
>> +++ b/gcc/gimple-match.h
>> @@ -333,6 +333,7 @@ gimple_simplified_result_is_gimple_val (const gimple_match_op *op)
>>
>>  extern tree (*mprts_hook) (gimple_match_op *);
>>
>> +bool gimple_extract_op (gimple *, gimple_match_op *);
>>  bool gimple_simplify (gimple *, gimple_match_op *, gimple_seq *,
>>                       tree (*)(tree), tree (*)(tree));
>>  tree maybe_push_res_to_seq (gimple_match_op *, gimple_seq *,
>> --
>> 2.25.1
>>
  
Richard Biener Nov. 18, 2021, 2:27 p.m. UTC | #3
On Tue, Nov 16, 2021 at 4:51 PM Richard Sandiford
<richard.sandiford@arm.com> wrote:
>
> Richard Biener <richard.guenther@gmail.com> writes:
> > On Wed, Nov 10, 2021 at 1:46 PM Richard Sandiford via Gcc-patches
> > <gcc-patches@gcc.gnu.org> wrote:
> >>
> >> code_helper and gimple_match_op seem like generally useful ways
> >> of summing up a gimple_assign or gimple_call (or gimple_cond).
> >> This patch adds a gimple_extract_op function that can be used
> >> for that.
> >>
> >> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?
> >>
> >> Richard
> >>
> >>
> >> gcc/
> >>         * gimple-match.h (gimple_extract_op): Declare.
> >>         * gimple-match.c (gimple_extract): New function, extracted from...
> >>         (gimple_simplify): ...here.
> >>         (gimple_extract_op): New function.
> >> ---
> >>  gcc/gimple-match-head.c | 261 +++++++++++++++++++++++-----------------
> >>  gcc/gimple-match.h      |   1 +
> >>  2 files changed, 149 insertions(+), 113 deletions(-)
> >>
> >> diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c
> >> index 9d88b2f8551..4c6e0883ba4 100644
> >> --- a/gcc/gimple-match-head.c
> >> +++ b/gcc/gimple-match-head.c
> >> @@ -890,12 +890,29 @@ try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
> >>    return true;
> >>  }
> >>
> >> -/* The main STMT based simplification entry.  It is used by the fold_stmt
> >> -   and the fold_stmt_to_constant APIs.  */
> >> +/* Common subroutine of gimple_extract_op and gimple_simplify.  Try to
> >> +   describe STMT in RES_OP.  Return:
> >>
> >> -bool
> >> -gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> >> -                tree (*valueize)(tree), tree (*top_valueize)(tree))
> >> +   - -1 if extraction failed
> >> +   - otherwise, 0 if no simplification should take place
> >> +   - otherwise, the number of operands for a GIMPLE_ASSIGN or GIMPLE_COND
> >> +   - otherwise, -2 for a GIMPLE_CALL
> >> +
> >> +   Before recording an operand, call:
> >> +
> >> +   - VALUEIZE_CONDITION for a COND_EXPR condition
> >> +   - VALUEIZE_NAME if the rhs of a GIMPLE_ASSIGN is an SSA_NAME
> >
> > I think at least VALUEIZE_NAME is unnecessary, see below
>
> Yeah, it's unnecessary.  The idea was to (try to) ensure that
> gimple_simplify keeps all the microoptimisations that it had
> previously.  This includes open-coding do_valueize for SSA_NAMEs
> and jumping straight to the right gimplify_resimplifyN routine
> when the number of operands is already known.
>
> (The two calls to gimple_extract<> produce different functions
> that ought to get inlined into their single callers.  A lot of the
> jumps should then be threaded.)
>
> I can drop all that if you don't think it's worth it though.
> Just wanted to double-check first.

Yes, I'd prefer the simpler and more understandable variant.

Richard.

>
> Thanks,
> Richard
>
> >> +   - VALUEIZE_OP for every other top-level operand
> >> +
> >> +   Each routine takes a tree argument and returns a tree.  */
> >> +
> >> +template<typename ValueizeOp, typename ValueizeCondition,
> >> +        typename ValueizeName>
> >> +inline int
> >> +gimple_extract (gimple *stmt, gimple_match_op *res_op,
> >> +               ValueizeOp valueize_op,
> >> +               ValueizeCondition valueize_condition,
> >> +               ValueizeName valueize_name)
> >>  {
> >>    switch (gimple_code (stmt))
> >>      {
> >> @@ -911,100 +928,53 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> >>                 || code == VIEW_CONVERT_EXPR)
> >>               {
> >>                 tree op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
> >> -               bool valueized = false;
> >> -               op0 = do_valueize (op0, top_valueize, valueized);
> >> -               res_op->set_op (code, type, op0);
> >> -               return (gimple_resimplify1 (seq, res_op, valueize)
> >> -                       || valueized);
> >> +               res_op->set_op (code, type, valueize_op (op0));
> >> +               return 1;
> >>               }
> >>             else if (code == BIT_FIELD_REF)
> >>               {
> >>                 tree rhs1 = gimple_assign_rhs1 (stmt);
> >> -               tree op0 = TREE_OPERAND (rhs1, 0);
> >> -               bool valueized = false;
> >> -               op0 = do_valueize (op0, top_valueize, valueized);
> >> +               tree op0 = valueize_op (TREE_OPERAND (rhs1, 0));
> >>                 res_op->set_op (code, type, op0,
> >>                                 TREE_OPERAND (rhs1, 1),
> >>                                 TREE_OPERAND (rhs1, 2),
> >>                                 REF_REVERSE_STORAGE_ORDER (rhs1));
> >> -               if (res_op->reverse)
> >> -                 return valueized;
> >> -               return (gimple_resimplify3 (seq, res_op, valueize)
> >> -                       || valueized);
> >> +               return res_op->reverse ? 0 : 3;
> >>               }
> >> -           else if (code == SSA_NAME
> >> -                    && top_valueize)
> >> +           else if (code == SSA_NAME)
> >>               {
> >>                 tree op0 = gimple_assign_rhs1 (stmt);
> >> -               tree valueized = top_valueize (op0);
> >> +               tree valueized = valueize_name (op0);
> >>                 if (!valueized || op0 == valueized)
> >> -                 return false;
> >> +                 return -1;
> >>                 res_op->set_op (TREE_CODE (op0), type, valueized);
> >> -               return true;
> >> +               return 0;
> >
> > So the old code in an obfuscated way just knowed nothing simplifies
> > on the plain not valueized name but returned true when valueization
> > changed the stmt.  So I'd expect
> >
> >                      tree valueized = valueize_op (op0);
> >                      res_op->set_op (TREE_CODE (op0), type, valueized);
> >                      return 0;
> >
> > here and the gimple_simplify caller returning 'valueized'.  I think
> > that the old code treated a NULL top_valueize () as "fail" is just
> > premature optimization without any effect.
> >
> >>               }
> >>             break;
> >>           case GIMPLE_UNARY_RHS:
> >>             {
> >>               tree rhs1 = gimple_assign_rhs1 (stmt);
> >> -             bool valueized = false;
> >> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
> >> -             res_op->set_op (code, type, rhs1);
> >> -             return (gimple_resimplify1 (seq, res_op, valueize)
> >> -                     || valueized);
> >> +             res_op->set_op (code, type, valueize_op (rhs1));
> >> +             return 1;
> >>             }
> >>           case GIMPLE_BINARY_RHS:
> >>             {
> >> -             tree rhs1 = gimple_assign_rhs1 (stmt);
> >> -             tree rhs2 = gimple_assign_rhs2 (stmt);
> >> -             bool valueized = false;
> >> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
> >> -             rhs2 = do_valueize (rhs2, top_valueize, valueized);
> >> +             tree rhs1 = valueize_op (gimple_assign_rhs1 (stmt));
> >> +             tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
> >>               res_op->set_op (code, type, rhs1, rhs2);
> >> -             return (gimple_resimplify2 (seq, res_op, valueize)
> >> -                     || valueized);
> >> +             return 2;
> >>             }
> >>           case GIMPLE_TERNARY_RHS:
> >>             {
> >> -             bool valueized = false;
> >>               tree rhs1 = gimple_assign_rhs1 (stmt);
> >> -             /* If this is a COND_EXPR first try to simplify an
> >> -                embedded GENERIC condition.  */
> >> -             if (code == COND_EXPR)
> >> -               {
> >> -                 if (COMPARISON_CLASS_P (rhs1))
> >> -                   {
> >> -                     tree lhs = TREE_OPERAND (rhs1, 0);
> >> -                     tree rhs = TREE_OPERAND (rhs1, 1);
> >> -                     lhs = do_valueize (lhs, top_valueize, valueized);
> >> -                     rhs = do_valueize (rhs, top_valueize, valueized);
> >> -                     gimple_match_op res_op2 (res_op->cond, TREE_CODE (rhs1),
> >> -                                              TREE_TYPE (rhs1), lhs, rhs);
> >> -                     if ((gimple_resimplify2 (seq, &res_op2, valueize)
> >> -                          || valueized)
> >> -                         && res_op2.code.is_tree_code ())
> >> -                       {
> >> -                         valueized = true;
> >> -                         if (TREE_CODE_CLASS ((enum tree_code) res_op2.code)
> >> -                             == tcc_comparison)
> >> -                           rhs1 = build2 (res_op2.code, TREE_TYPE (rhs1),
> >> -                                          res_op2.ops[0], res_op2.ops[1]);
> >> -                         else if (res_op2.code == SSA_NAME
> >> -                                  || res_op2.code == INTEGER_CST
> >> -                                  || res_op2.code == VECTOR_CST)
> >> -                           rhs1 = res_op2.ops[0];
> >> -                         else
> >> -                           valueized = false;
> >> -                       }
> >> -                   }
> >> -               }
> >> -             tree rhs2 = gimple_assign_rhs2 (stmt);
> >> -             tree rhs3 = gimple_assign_rhs3 (stmt);
> >> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
> >> -             rhs2 = do_valueize (rhs2, top_valueize, valueized);
> >> -             rhs3 = do_valueize (rhs3, top_valueize, valueized);
> >> +             if (code == COND_EXPR && COMPARISON_CLASS_P (rhs1))
> >> +               rhs1 = valueize_condition (rhs1);
> >> +             else
> >> +               rhs1 = valueize_op (rhs1);
> >> +             tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
> >> +             tree rhs3 = valueize_op (gimple_assign_rhs3 (stmt));
> >>               res_op->set_op (code, type, rhs1, rhs2, rhs3);
> >> -             return (gimple_resimplify3 (seq, res_op, valueize)
> >> -                     || valueized);
> >> +             return 3;
> >>             }
> >>           default:
> >>             gcc_unreachable ();
> >> @@ -1018,7 +988,6 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> >>           && gimple_call_num_args (stmt) >= 1
> >>           && gimple_call_num_args (stmt) <= 5)
> >>         {
> >> -         bool valueized = false;
> >>           combined_fn cfn;
> >>           if (gimple_call_internal_p (stmt))
> >>             cfn = as_combined_fn (gimple_call_internal_fn (stmt));
> >> @@ -1026,17 +995,17 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> >>             {
> >>               tree fn = gimple_call_fn (stmt);
> >>               if (!fn)
> >> -               return false;
> >> +               return -1;
> >>
> >> -             fn = do_valueize (fn, top_valueize, valueized);
> >> +             fn = valueize_op (fn);
> >>               if (TREE_CODE (fn) != ADDR_EXPR
> >>                   || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL)
> >> -               return false;
> >> +               return -1;
> >>
> >>               tree decl = TREE_OPERAND (fn, 0);
> >>               if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL
> >>                   || !gimple_builtin_call_types_compatible_p (stmt, decl))
> >> -               return false;
> >> +               return -1;
> >>
> >>               cfn = as_combined_fn (DECL_FUNCTION_CODE (decl));
> >>             }
> >> @@ -1044,56 +1013,122 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> >>           unsigned int num_args = gimple_call_num_args (stmt);
> >>           res_op->set_op (cfn, TREE_TYPE (gimple_call_lhs (stmt)), num_args);
> >>           for (unsigned i = 0; i < num_args; ++i)
> >> -           {
> >> -             tree arg = gimple_call_arg (stmt, i);
> >> -             res_op->ops[i] = do_valueize (arg, top_valueize, valueized);
> >> -           }
> >> -         if (internal_fn_p (cfn)
> >> -             && try_conditional_simplification (as_internal_fn (cfn),
> >> -                                                res_op, seq, valueize))
> >> -           return true;
> >> -         switch (num_args)
> >> -           {
> >> -           case 1:
> >> -             return (gimple_resimplify1 (seq, res_op, valueize)
> >> -                     || valueized);
> >> -           case 2:
> >> -             return (gimple_resimplify2 (seq, res_op, valueize)
> >> -                     || valueized);
> >> -           case 3:
> >> -             return (gimple_resimplify3 (seq, res_op, valueize)
> >> -                     || valueized);
> >> -           case 4:
> >> -             return (gimple_resimplify4 (seq, res_op, valueize)
> >> -                     || valueized);
> >> -           case 5:
> >> -             return (gimple_resimplify5 (seq, res_op, valueize)
> >> -                     || valueized);
> >> -           default:
> >> -            gcc_unreachable ();
> >> -           }
> >> +           res_op->ops[i] = valueize_op (gimple_call_arg (stmt, i));
> >> +         return -2;
> >>         }
> >>        break;
> >>
> >>      case GIMPLE_COND:
> >>        {
> >> -       tree lhs = gimple_cond_lhs (stmt);
> >> -       tree rhs = gimple_cond_rhs (stmt);
> >> -       bool valueized = false;
> >> -       lhs = do_valueize (lhs, top_valueize, valueized);
> >> -       rhs = do_valueize (rhs, top_valueize, valueized);
> >> +       tree lhs = valueize_op (gimple_cond_lhs (stmt));
> >> +       tree rhs = valueize_op (gimple_cond_rhs (stmt));
> >>         res_op->set_op (gimple_cond_code (stmt), boolean_type_node, lhs, rhs);
> >> -       return (gimple_resimplify2 (seq, res_op, valueize)
> >> -               || valueized);
> >> +       return 2;
> >>        }
> >>
> >>      default:
> >>        break;
> >>      }
> >>
> >> -  return false;
> >> +  return -1;
> >>  }
> >>
> >> +/* Try to describe STMT in RES_OP, returning true on success.
> >> +   For GIMPLE_CONDs, describe the condition that is being tested.
> >> +   For GIMPLE_ASSIGNs, describe the rhs of the assignment.
> >> +   For GIMPLE_CALLs, describe the call.  */
> >> +
> >> +bool
> >> +gimple_extract_op (gimple *stmt, gimple_match_op *res_op)
> >> +{
> >> +  auto nop = [](tree op) { return op; };
> >> +  return gimple_extract (stmt, res_op, nop, nop, nop) != -1;
> >> +}
> >> +
> >> +/* The main STMT based simplification entry.  It is used by the fold_stmt
> >> +   and the fold_stmt_to_constant APIs.  */
> >> +
> >> +bool
> >> +gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> >> +                tree (*valueize)(tree), tree (*top_valueize)(tree))
> >> +{
> >> +  bool valueized = false;
> >> +  auto valueize_op = [&](tree op)
> >> +    {
> >> +      return do_valueize (op, top_valueize, valueized);
> >> +    };
> >> +  auto valueize_condition = [&](tree op) -> tree
> >> +    {
> >> +      bool cond_valueized = false;
> >> +      tree lhs = do_valueize (TREE_OPERAND (op, 0), top_valueize,
> >> +                             cond_valueized);
> >> +      tree rhs = do_valueize (TREE_OPERAND (op, 1), top_valueize,
> >> +                             cond_valueized);
> >> +      gimple_match_op res_op2 (res_op->cond, TREE_CODE (op),
> >> +                              TREE_TYPE (op), lhs, rhs);
> >> +      if ((gimple_resimplify2 (seq, &res_op2, valueize)
> >> +          || cond_valueized)
> >> +         && res_op2.code.is_tree_code ())
> >> +       {
> >> +         if (TREE_CODE_CLASS ((tree_code) res_op2.code) == tcc_comparison)
> >> +           {
> >> +             valueized = true;
> >> +             return build2 (res_op2.code, TREE_TYPE (op),
> >> +                            res_op2.ops[0], res_op2.ops[1]);
> >> +           }
> >> +         else if (res_op2.code == SSA_NAME
> >> +                  || res_op2.code == INTEGER_CST
> >> +                  || res_op2.code == VECTOR_CST)
> >> +           {
> >> +             valueized = true;
> >> +             return res_op2.ops[0];
> >> +           }
> >> +       }
> >> +      return valueize_op (op);
> >> +    };
> >> +  auto valueize_name = [&](tree op)
> >> +    {
> >> +      return top_valueize ? top_valueize (op) : op;
> >> +    };
> >> +
> >> +  int res = gimple_extract (stmt, res_op, valueize_op, valueize_condition,
> >> +                           valueize_name);
> >> +  if (res == -1)
> >> +    return false;
> >> +
> >> +  if (res == -2)
> >> +    {
> >> +      combined_fn cfn = combined_fn (res_op->code);
> >> +      if (internal_fn_p (cfn)
> >> +         && try_conditional_simplification (as_internal_fn (cfn),
> >> +                                            res_op, seq, valueize))
> >> +       return true;
> >> +      res = res_op->num_ops;
> >> +    }
> >> +
> >> +  switch (res)
> >> +    {
> >> +    case 0:
> >> +      return valueized;
> >> +    case 1:
> >> +      return (gimple_resimplify1 (seq, res_op, valueize)
> >> +             || valueized);
> >> +    case 2:
> >> +      return (gimple_resimplify2 (seq, res_op, valueize)
> >> +             || valueized);
> >> +    case 3:
> >> +      return (gimple_resimplify3 (seq, res_op, valueize)
> >> +             || valueized);
> >> +    case 4:
> >> +      return (gimple_resimplify4 (seq, res_op, valueize)
> >> +             || valueized);
> >> +    case 5:
> >> +      return (gimple_resimplify5 (seq, res_op, valueize)
> >> +             || valueized);
> >
> > can't you use just res.resimplify (seq, valueize) || valueized instead of the
> > switch here when res is not zero?
> >
> > Otherwise looks OK.
> >
> > Thanks,
> > Richard.
> >
> >> +    default:
> >> +      gcc_unreachable ();
> >> +    }
> >> +}
> >>
> >>  /* Helper for the autogenerated code, valueize OP.  */
> >>
> >> diff --git a/gcc/gimple-match.h b/gcc/gimple-match.h
> >> index 2d4ea476076..15a0f584db7 100644
> >> --- a/gcc/gimple-match.h
> >> +++ b/gcc/gimple-match.h
> >> @@ -333,6 +333,7 @@ gimple_simplified_result_is_gimple_val (const gimple_match_op *op)
> >>
> >>  extern tree (*mprts_hook) (gimple_match_op *);
> >>
> >> +bool gimple_extract_op (gimple *, gimple_match_op *);
> >>  bool gimple_simplify (gimple *, gimple_match_op *, gimple_seq *,
> >>                       tree (*)(tree), tree (*)(tree));
> >>  tree maybe_push_res_to_seq (gimple_match_op *, gimple_seq *,
> >> --
> >> 2.25.1
> >>
  
Richard Sandiford Nov. 18, 2021, 7:01 p.m. UTC | #4
Richard Biener <richard.guenther@gmail.com> writes:
> On Tue, Nov 16, 2021 at 4:51 PM Richard Sandiford
> <richard.sandiford@arm.com> wrote:
>>
>> Richard Biener <richard.guenther@gmail.com> writes:
>> > On Wed, Nov 10, 2021 at 1:46 PM Richard Sandiford via Gcc-patches
>> > <gcc-patches@gcc.gnu.org> wrote:
>> >>
>> >> code_helper and gimple_match_op seem like generally useful ways
>> >> of summing up a gimple_assign or gimple_call (or gimple_cond).
>> >> This patch adds a gimple_extract_op function that can be used
>> >> for that.
>> >>
>> >> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?
>> >>
>> >> Richard
>> >>
>> >>
>> >> gcc/
>> >>         * gimple-match.h (gimple_extract_op): Declare.
>> >>         * gimple-match.c (gimple_extract): New function, extracted from...
>> >>         (gimple_simplify): ...here.
>> >>         (gimple_extract_op): New function.
>> >> ---
>> >>  gcc/gimple-match-head.c | 261 +++++++++++++++++++++++-----------------
>> >>  gcc/gimple-match.h      |   1 +
>> >>  2 files changed, 149 insertions(+), 113 deletions(-)
>> >>
>> >> diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c
>> >> index 9d88b2f8551..4c6e0883ba4 100644
>> >> --- a/gcc/gimple-match-head.c
>> >> +++ b/gcc/gimple-match-head.c
>> >> @@ -890,12 +890,29 @@ try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
>> >>    return true;
>> >>  }
>> >>
>> >> -/* The main STMT based simplification entry.  It is used by the fold_stmt
>> >> -   and the fold_stmt_to_constant APIs.  */
>> >> +/* Common subroutine of gimple_extract_op and gimple_simplify.  Try to
>> >> +   describe STMT in RES_OP.  Return:
>> >>
>> >> -bool
>> >> -gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>> >> -                tree (*valueize)(tree), tree (*top_valueize)(tree))
>> >> +   - -1 if extraction failed
>> >> +   - otherwise, 0 if no simplification should take place
>> >> +   - otherwise, the number of operands for a GIMPLE_ASSIGN or GIMPLE_COND
>> >> +   - otherwise, -2 for a GIMPLE_CALL
>> >> +
>> >> +   Before recording an operand, call:
>> >> +
>> >> +   - VALUEIZE_CONDITION for a COND_EXPR condition
>> >> +   - VALUEIZE_NAME if the rhs of a GIMPLE_ASSIGN is an SSA_NAME
>> >
>> > I think at least VALUEIZE_NAME is unnecessary, see below
>>
>> Yeah, it's unnecessary.  The idea was to (try to) ensure that
>> gimple_simplify keeps all the microoptimisations that it had
>> previously.  This includes open-coding do_valueize for SSA_NAMEs
>> and jumping straight to the right gimplify_resimplifyN routine
>> when the number of operands is already known.
>>
>> (The two calls to gimple_extract<> produce different functions
>> that ought to get inlined into their single callers.  A lot of the
>> jumps should then be threaded.)
>>
>> I can drop all that if you don't think it's worth it though.
>> Just wanted to double-check first.
>
> Yes, I'd prefer the simpler and more understandable variant.

Fair :-)  How does this look?  I pulled in the extra conversions
you mentioned in the later review since they're useful for the
new gimple_simplify routine.

Tested as before.

Richard


gcc/
	* gimple-match.h (code_helper): Add functions for querying whether
	the code represents an internal_fn or a built_in_function.
	Provide explicit conversion operators for both cases.
	(gimple_extract_op): Declare.
	* gimple-match-head.c (gimple_extract): New function, extracted from...
	(gimple_simplify): ...here.
	(gimple_extract_op): New function.
---
 gcc/gimple-match-head.c | 218 ++++++++++++++++++++--------------------
 gcc/gimple-match.h      |  27 +++++
 2 files changed, 135 insertions(+), 110 deletions(-)

diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c
index 9d88b2f8551..15053d1189e 100644
--- a/gcc/gimple-match-head.c
+++ b/gcc/gimple-match-head.c
@@ -890,12 +890,20 @@ try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
   return true;
 }
 
-/* The main STMT based simplification entry.  It is used by the fold_stmt
-   and the fold_stmt_to_constant APIs.  */
+/* Common subroutine of gimple_extract_op and gimple_simplify.  Try to
+   describe STMT in RES_OP, returning true on success.  Before recording
+   an operand, call:
 
-bool
-gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
-		 tree (*valueize)(tree), tree (*top_valueize)(tree))
+   - VALUEIZE_CONDITION for a COND_EXPR condition
+   - VALUEIZE_OP for every other top-level operand
+
+   Both routines take a tree argument and returns a tree.  */
+
+template<typename ValueizeOp, typename ValueizeCondition>
+inline bool
+gimple_extract (gimple *stmt, gimple_match_op *res_op,
+		ValueizeOp valueize_op,
+		ValueizeCondition valueize_condition)
 {
   switch (gimple_code (stmt))
     {
@@ -911,100 +919,50 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
 		|| code == VIEW_CONVERT_EXPR)
 	      {
 		tree op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
-		bool valueized = false;
-		op0 = do_valueize (op0, top_valueize, valueized);
-		res_op->set_op (code, type, op0);
-		return (gimple_resimplify1 (seq, res_op, valueize)
-			|| valueized);
+		res_op->set_op (code, type, valueize_op (op0));
+		return true;
 	      }
 	    else if (code == BIT_FIELD_REF)
 	      {
 		tree rhs1 = gimple_assign_rhs1 (stmt);
-		tree op0 = TREE_OPERAND (rhs1, 0);
-		bool valueized = false;
-		op0 = do_valueize (op0, top_valueize, valueized);
+		tree op0 = valueize_op (TREE_OPERAND (rhs1, 0));
 		res_op->set_op (code, type, op0,
 				TREE_OPERAND (rhs1, 1),
 				TREE_OPERAND (rhs1, 2),
 				REF_REVERSE_STORAGE_ORDER (rhs1));
-		if (res_op->reverse)
-		  return valueized;
-		return (gimple_resimplify3 (seq, res_op, valueize)
-			|| valueized);
+		return true;
 	      }
-	    else if (code == SSA_NAME
-		     && top_valueize)
+	    else if (code == SSA_NAME)
 	      {
 		tree op0 = gimple_assign_rhs1 (stmt);
-		tree valueized = top_valueize (op0);
-		if (!valueized || op0 == valueized)
-		  return false;
-		res_op->set_op (TREE_CODE (op0), type, valueized);
+		res_op->set_op (TREE_CODE (op0), type, valueize_op (op0));
 		return true;
 	      }
 	    break;
 	  case GIMPLE_UNARY_RHS:
 	    {
 	      tree rhs1 = gimple_assign_rhs1 (stmt);
-	      bool valueized = false;
-	      rhs1 = do_valueize (rhs1, top_valueize, valueized);
-	      res_op->set_op (code, type, rhs1);
-	      return (gimple_resimplify1 (seq, res_op, valueize)
-		      || valueized);
+	      res_op->set_op (code, type, valueize_op (rhs1));
+	      return true;
 	    }
 	  case GIMPLE_BINARY_RHS:
 	    {
-	      tree rhs1 = gimple_assign_rhs1 (stmt);
-	      tree rhs2 = gimple_assign_rhs2 (stmt);
-	      bool valueized = false;
-	      rhs1 = do_valueize (rhs1, top_valueize, valueized);
-	      rhs2 = do_valueize (rhs2, top_valueize, valueized);
+	      tree rhs1 = valueize_op (gimple_assign_rhs1 (stmt));
+	      tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
 	      res_op->set_op (code, type, rhs1, rhs2);
-	      return (gimple_resimplify2 (seq, res_op, valueize)
-		      || valueized);
+	      return true;
 	    }
 	  case GIMPLE_TERNARY_RHS:
 	    {
-	      bool valueized = false;
 	      tree rhs1 = gimple_assign_rhs1 (stmt);
-	      /* If this is a COND_EXPR first try to simplify an
-		 embedded GENERIC condition.  */
-	      if (code == COND_EXPR)
-		{
-		  if (COMPARISON_CLASS_P (rhs1))
-		    {
-		      tree lhs = TREE_OPERAND (rhs1, 0);
-		      tree rhs = TREE_OPERAND (rhs1, 1);
-		      lhs = do_valueize (lhs, top_valueize, valueized);
-		      rhs = do_valueize (rhs, top_valueize, valueized);
-		      gimple_match_op res_op2 (res_op->cond, TREE_CODE (rhs1),
-					       TREE_TYPE (rhs1), lhs, rhs);
-		      if ((gimple_resimplify2 (seq, &res_op2, valueize)
-			   || valueized)
-			  && res_op2.code.is_tree_code ())
-			{
-			  valueized = true;
-			  if (TREE_CODE_CLASS ((enum tree_code) res_op2.code)
-			      == tcc_comparison)
-			    rhs1 = build2 (res_op2.code, TREE_TYPE (rhs1),
-					   res_op2.ops[0], res_op2.ops[1]);
-			  else if (res_op2.code == SSA_NAME
-				   || res_op2.code == INTEGER_CST
-				   || res_op2.code == VECTOR_CST)
-			    rhs1 = res_op2.ops[0];
-			  else
-			    valueized = false;
-			}
-		    }
-		}
-	      tree rhs2 = gimple_assign_rhs2 (stmt);
-	      tree rhs3 = gimple_assign_rhs3 (stmt);
-	      rhs1 = do_valueize (rhs1, top_valueize, valueized);
-	      rhs2 = do_valueize (rhs2, top_valueize, valueized);
-	      rhs3 = do_valueize (rhs3, top_valueize, valueized);
+	      if (code == COND_EXPR && COMPARISON_CLASS_P (rhs1))
+		rhs1 = valueize_condition (rhs1);
+	      else
+		rhs1 = valueize_op (rhs1);
+	      tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
+	      tree rhs3 = valueize_op (gimple_assign_rhs3 (stmt));
 	      res_op->set_op (code, type, rhs1, rhs2, rhs3);
-	      return (gimple_resimplify3 (seq, res_op, valueize)
-		      || valueized);
+	      return true;
 	    }
 	  default:
 	    gcc_unreachable ();
@@ -1018,7 +976,6 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
 	  && gimple_call_num_args (stmt) >= 1
 	  && gimple_call_num_args (stmt) <= 5)
 	{
-	  bool valueized = false;
 	  combined_fn cfn;
 	  if (gimple_call_internal_p (stmt))
 	    cfn = as_combined_fn (gimple_call_internal_fn (stmt));
@@ -1028,7 +985,7 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
 	      if (!fn)
 		return false;
 
-	      fn = do_valueize (fn, top_valueize, valueized);
+	      fn = valueize_op (fn);
 	      if (TREE_CODE (fn) != ADDR_EXPR
 		  || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL)
 		return false;
@@ -1044,47 +1001,17 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
 	  unsigned int num_args = gimple_call_num_args (stmt);
 	  res_op->set_op (cfn, TREE_TYPE (gimple_call_lhs (stmt)), num_args);
 	  for (unsigned i = 0; i < num_args; ++i)
-	    {
-	      tree arg = gimple_call_arg (stmt, i);
-	      res_op->ops[i] = do_valueize (arg, top_valueize, valueized);
-	    }
-	  if (internal_fn_p (cfn)
-	      && try_conditional_simplification (as_internal_fn (cfn),
-						 res_op, seq, valueize))
-	    return true;
-	  switch (num_args)
-	    {
-	    case 1:
-	      return (gimple_resimplify1 (seq, res_op, valueize)
-		      || valueized);
-	    case 2:
-	      return (gimple_resimplify2 (seq, res_op, valueize)
-		      || valueized);
-	    case 3:
-	      return (gimple_resimplify3 (seq, res_op, valueize)
-		      || valueized);
-	    case 4:
-	      return (gimple_resimplify4 (seq, res_op, valueize)
-		      || valueized);
-	    case 5:
-	      return (gimple_resimplify5 (seq, res_op, valueize)
-		      || valueized);
-	    default:
-	     gcc_unreachable ();
-	    }
+	    res_op->ops[i] = valueize_op (gimple_call_arg (stmt, i));
+	  return true;
 	}
       break;
 
     case GIMPLE_COND:
       {
-	tree lhs = gimple_cond_lhs (stmt);
-	tree rhs = gimple_cond_rhs (stmt);
-	bool valueized = false;
-	lhs = do_valueize (lhs, top_valueize, valueized);
-	rhs = do_valueize (rhs, top_valueize, valueized);
+	tree lhs = valueize_op (gimple_cond_lhs (stmt));
+	tree rhs = valueize_op (gimple_cond_rhs (stmt));
 	res_op->set_op (gimple_cond_code (stmt), boolean_type_node, lhs, rhs);
-	return (gimple_resimplify2 (seq, res_op, valueize)
-		|| valueized);
+	return true;
       }
 
     default:
@@ -1094,6 +1021,77 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
   return false;
 }
 
+/* Try to describe STMT in RES_OP, returning true on success.
+   For GIMPLE_CONDs, describe the condition that is being tested.
+   For GIMPLE_ASSIGNs, describe the rhs of the assignment.
+   For GIMPLE_CALLs, describe the call.  */
+
+bool
+gimple_extract_op (gimple *stmt, gimple_match_op *res_op)
+{
+  auto nop = [](tree op) { return op; };
+  return gimple_extract (stmt, res_op, nop, nop);
+}
+
+/* The main STMT based simplification entry.  It is used by the fold_stmt
+   and the fold_stmt_to_constant APIs.  */
+
+bool
+gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
+		 tree (*valueize)(tree), tree (*top_valueize)(tree))
+{
+  bool valueized = false;
+  auto valueize_op = [&](tree op)
+    {
+      return do_valueize (op, top_valueize, valueized);
+    };
+  auto valueize_condition = [&](tree op) -> tree
+    {
+      bool cond_valueized = false;
+      tree lhs = do_valueize (TREE_OPERAND (op, 0), top_valueize,
+			      cond_valueized);
+      tree rhs = do_valueize (TREE_OPERAND (op, 1), top_valueize,
+			      cond_valueized);
+      gimple_match_op res_op2 (res_op->cond, TREE_CODE (op),
+			       TREE_TYPE (op), lhs, rhs);
+      if ((gimple_resimplify2 (seq, &res_op2, valueize)
+	   || cond_valueized)
+	  && res_op2.code.is_tree_code ())
+	{
+	  if (TREE_CODE_CLASS ((tree_code) res_op2.code) == tcc_comparison)
+	    {
+	      valueized = true;
+	      return build2 (res_op2.code, TREE_TYPE (op),
+			     res_op2.ops[0], res_op2.ops[1]);
+	    }
+	  else if (res_op2.code == SSA_NAME
+		   || res_op2.code == INTEGER_CST
+		   || res_op2.code == VECTOR_CST)
+	    {
+	      valueized = true;
+	      return res_op2.ops[0];
+	    }
+	}
+      return valueize_op (op);
+    };
+
+  if (!gimple_extract (stmt, res_op, valueize_op, valueize_condition))
+    return false;
+
+  if (res_op->code.is_internal_fn ())
+    {
+      internal_fn ifn = internal_fn (res_op->code);
+      if (try_conditional_simplification (ifn, res_op, seq, valueize))
+	return true;
+    }
+
+  if (!res_op->reverse
+      && res_op->num_ops
+      && res_op->resimplify (seq, valueize))
+    return true;
+
+  return valueized;
+}
 
 /* Helper for the autogenerated code, valueize OP.  */
 
diff --git a/gcc/gimple-match.h b/gcc/gimple-match.h
index 2d4ea476076..8abeafebb00 100644
--- a/gcc/gimple-match.h
+++ b/gcc/gimple-match.h
@@ -33,13 +33,39 @@ public:
   code_helper (combined_fn fn) : rep (-(int) fn) {}
   operator tree_code () const { return (tree_code) rep; }
   operator combined_fn () const { return (combined_fn) -rep; }
+  explicit operator internal_fn () const;
+  explicit operator built_in_function () const;
   bool is_tree_code () const { return rep > 0; }
   bool is_fn_code () const { return rep < 0; }
+  bool is_internal_fn () const;
+  bool is_builtin_fn () const;
   int get_rep () const { return rep; }
 private:
   int rep;
 };
 
+inline code_helper::operator internal_fn () const
+{
+  return as_internal_fn (combined_fn (*this));
+}
+
+inline code_helper::operator built_in_function () const
+{
+  return as_builtin_fn (combined_fn (*this));
+}
+
+inline bool
+code_helper::is_internal_fn () const
+{
+  return is_fn_code () && internal_fn_p (combined_fn (*this));
+}
+
+inline bool
+code_helper::is_builtin_fn () const
+{
+  return is_fn_code () && builtin_fn_p (combined_fn (*this));
+}
+
 /* Represents the condition under which an operation should happen,
    and the value to use otherwise.  The condition applies elementwise
    (as for VEC_COND_EXPR) if the values are vectors.  */
@@ -333,6 +359,7 @@ gimple_simplified_result_is_gimple_val (const gimple_match_op *op)
 
 extern tree (*mprts_hook) (gimple_match_op *);
 
+bool gimple_extract_op (gimple *, gimple_match_op *);
 bool gimple_simplify (gimple *, gimple_match_op *, gimple_seq *,
 		      tree (*)(tree), tree (*)(tree));
 tree maybe_push_res_to_seq (gimple_match_op *, gimple_seq *,
  
Richard Biener Nov. 19, 2021, 9:13 a.m. UTC | #5
On Thu, Nov 18, 2021 at 8:01 PM Richard Sandiford
<richard.sandiford@arm.com> wrote:
>
> Richard Biener <richard.guenther@gmail.com> writes:
> > On Tue, Nov 16, 2021 at 4:51 PM Richard Sandiford
> > <richard.sandiford@arm.com> wrote:
> >>
> >> Richard Biener <richard.guenther@gmail.com> writes:
> >> > On Wed, Nov 10, 2021 at 1:46 PM Richard Sandiford via Gcc-patches
> >> > <gcc-patches@gcc.gnu.org> wrote:
> >> >>
> >> >> code_helper and gimple_match_op seem like generally useful ways
> >> >> of summing up a gimple_assign or gimple_call (or gimple_cond).
> >> >> This patch adds a gimple_extract_op function that can be used
> >> >> for that.
> >> >>
> >> >> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?
> >> >>
> >> >> Richard
> >> >>
> >> >>
> >> >> gcc/
> >> >>         * gimple-match.h (gimple_extract_op): Declare.
> >> >>         * gimple-match.c (gimple_extract): New function, extracted from...
> >> >>         (gimple_simplify): ...here.
> >> >>         (gimple_extract_op): New function.
> >> >> ---
> >> >>  gcc/gimple-match-head.c | 261 +++++++++++++++++++++++-----------------
> >> >>  gcc/gimple-match.h      |   1 +
> >> >>  2 files changed, 149 insertions(+), 113 deletions(-)
> >> >>
> >> >> diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c
> >> >> index 9d88b2f8551..4c6e0883ba4 100644
> >> >> --- a/gcc/gimple-match-head.c
> >> >> +++ b/gcc/gimple-match-head.c
> >> >> @@ -890,12 +890,29 @@ try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
> >> >>    return true;
> >> >>  }
> >> >>
> >> >> -/* The main STMT based simplification entry.  It is used by the fold_stmt
> >> >> -   and the fold_stmt_to_constant APIs.  */
> >> >> +/* Common subroutine of gimple_extract_op and gimple_simplify.  Try to
> >> >> +   describe STMT in RES_OP.  Return:
> >> >>
> >> >> -bool
> >> >> -gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> >> >> -                tree (*valueize)(tree), tree (*top_valueize)(tree))
> >> >> +   - -1 if extraction failed
> >> >> +   - otherwise, 0 if no simplification should take place
> >> >> +   - otherwise, the number of operands for a GIMPLE_ASSIGN or GIMPLE_COND
> >> >> +   - otherwise, -2 for a GIMPLE_CALL
> >> >> +
> >> >> +   Before recording an operand, call:
> >> >> +
> >> >> +   - VALUEIZE_CONDITION for a COND_EXPR condition
> >> >> +   - VALUEIZE_NAME if the rhs of a GIMPLE_ASSIGN is an SSA_NAME
> >> >
> >> > I think at least VALUEIZE_NAME is unnecessary, see below
> >>
> >> Yeah, it's unnecessary.  The idea was to (try to) ensure that
> >> gimple_simplify keeps all the microoptimisations that it had
> >> previously.  This includes open-coding do_valueize for SSA_NAMEs
> >> and jumping straight to the right gimplify_resimplifyN routine
> >> when the number of operands is already known.
> >>
> >> (The two calls to gimple_extract<> produce different functions
> >> that ought to get inlined into their single callers.  A lot of the
> >> jumps should then be threaded.)
> >>
> >> I can drop all that if you don't think it's worth it though.
> >> Just wanted to double-check first.
> >
> > Yes, I'd prefer the simpler and more understandable variant.
>
> Fair :-)  How does this look?  I pulled in the extra conversions
> you mentioned in the later review since they're useful for the
> new gimple_simplify routine.

OK.

Thanks,
Richard.

> Tested as before.
>
> Richard
>
>
> gcc/
>         * gimple-match.h (code_helper): Add functions for querying whether
>         the code represents an internal_fn or a built_in_function.
>         Provide explicit conversion operators for both cases.
>         (gimple_extract_op): Declare.
>         * gimple-match-head.c (gimple_extract): New function, extracted from...
>         (gimple_simplify): ...here.
>         (gimple_extract_op): New function.
> ---
>  gcc/gimple-match-head.c | 218 ++++++++++++++++++++--------------------
>  gcc/gimple-match.h      |  27 +++++
>  2 files changed, 135 insertions(+), 110 deletions(-)
>
> diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c
> index 9d88b2f8551..15053d1189e 100644
> --- a/gcc/gimple-match-head.c
> +++ b/gcc/gimple-match-head.c
> @@ -890,12 +890,20 @@ try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
>    return true;
>  }
>
> -/* The main STMT based simplification entry.  It is used by the fold_stmt
> -   and the fold_stmt_to_constant APIs.  */
> +/* Common subroutine of gimple_extract_op and gimple_simplify.  Try to
> +   describe STMT in RES_OP, returning true on success.  Before recording
> +   an operand, call:
>
> -bool
> -gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> -                tree (*valueize)(tree), tree (*top_valueize)(tree))
> +   - VALUEIZE_CONDITION for a COND_EXPR condition
> +   - VALUEIZE_OP for every other top-level operand
> +
> +   Both routines take a tree argument and returns a tree.  */
> +
> +template<typename ValueizeOp, typename ValueizeCondition>
> +inline bool
> +gimple_extract (gimple *stmt, gimple_match_op *res_op,
> +               ValueizeOp valueize_op,
> +               ValueizeCondition valueize_condition)
>  {
>    switch (gimple_code (stmt))
>      {
> @@ -911,100 +919,50 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>                 || code == VIEW_CONVERT_EXPR)
>               {
>                 tree op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
> -               bool valueized = false;
> -               op0 = do_valueize (op0, top_valueize, valueized);
> -               res_op->set_op (code, type, op0);
> -               return (gimple_resimplify1 (seq, res_op, valueize)
> -                       || valueized);
> +               res_op->set_op (code, type, valueize_op (op0));
> +               return true;
>               }
>             else if (code == BIT_FIELD_REF)
>               {
>                 tree rhs1 = gimple_assign_rhs1 (stmt);
> -               tree op0 = TREE_OPERAND (rhs1, 0);
> -               bool valueized = false;
> -               op0 = do_valueize (op0, top_valueize, valueized);
> +               tree op0 = valueize_op (TREE_OPERAND (rhs1, 0));
>                 res_op->set_op (code, type, op0,
>                                 TREE_OPERAND (rhs1, 1),
>                                 TREE_OPERAND (rhs1, 2),
>                                 REF_REVERSE_STORAGE_ORDER (rhs1));
> -               if (res_op->reverse)
> -                 return valueized;
> -               return (gimple_resimplify3 (seq, res_op, valueize)
> -                       || valueized);
> +               return true;
>               }
> -           else if (code == SSA_NAME
> -                    && top_valueize)
> +           else if (code == SSA_NAME)
>               {
>                 tree op0 = gimple_assign_rhs1 (stmt);
> -               tree valueized = top_valueize (op0);
> -               if (!valueized || op0 == valueized)
> -                 return false;
> -               res_op->set_op (TREE_CODE (op0), type, valueized);
> +               res_op->set_op (TREE_CODE (op0), type, valueize_op (op0));
>                 return true;
>               }
>             break;
>           case GIMPLE_UNARY_RHS:
>             {
>               tree rhs1 = gimple_assign_rhs1 (stmt);
> -             bool valueized = false;
> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
> -             res_op->set_op (code, type, rhs1);
> -             return (gimple_resimplify1 (seq, res_op, valueize)
> -                     || valueized);
> +             res_op->set_op (code, type, valueize_op (rhs1));
> +             return true;
>             }
>           case GIMPLE_BINARY_RHS:
>             {
> -             tree rhs1 = gimple_assign_rhs1 (stmt);
> -             tree rhs2 = gimple_assign_rhs2 (stmt);
> -             bool valueized = false;
> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
> -             rhs2 = do_valueize (rhs2, top_valueize, valueized);
> +             tree rhs1 = valueize_op (gimple_assign_rhs1 (stmt));
> +             tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
>               res_op->set_op (code, type, rhs1, rhs2);
> -             return (gimple_resimplify2 (seq, res_op, valueize)
> -                     || valueized);
> +             return true;
>             }
>           case GIMPLE_TERNARY_RHS:
>             {
> -             bool valueized = false;
>               tree rhs1 = gimple_assign_rhs1 (stmt);
> -             /* If this is a COND_EXPR first try to simplify an
> -                embedded GENERIC condition.  */
> -             if (code == COND_EXPR)
> -               {
> -                 if (COMPARISON_CLASS_P (rhs1))
> -                   {
> -                     tree lhs = TREE_OPERAND (rhs1, 0);
> -                     tree rhs = TREE_OPERAND (rhs1, 1);
> -                     lhs = do_valueize (lhs, top_valueize, valueized);
> -                     rhs = do_valueize (rhs, top_valueize, valueized);
> -                     gimple_match_op res_op2 (res_op->cond, TREE_CODE (rhs1),
> -                                              TREE_TYPE (rhs1), lhs, rhs);
> -                     if ((gimple_resimplify2 (seq, &res_op2, valueize)
> -                          || valueized)
> -                         && res_op2.code.is_tree_code ())
> -                       {
> -                         valueized = true;
> -                         if (TREE_CODE_CLASS ((enum tree_code) res_op2.code)
> -                             == tcc_comparison)
> -                           rhs1 = build2 (res_op2.code, TREE_TYPE (rhs1),
> -                                          res_op2.ops[0], res_op2.ops[1]);
> -                         else if (res_op2.code == SSA_NAME
> -                                  || res_op2.code == INTEGER_CST
> -                                  || res_op2.code == VECTOR_CST)
> -                           rhs1 = res_op2.ops[0];
> -                         else
> -                           valueized = false;
> -                       }
> -                   }
> -               }
> -             tree rhs2 = gimple_assign_rhs2 (stmt);
> -             tree rhs3 = gimple_assign_rhs3 (stmt);
> -             rhs1 = do_valueize (rhs1, top_valueize, valueized);
> -             rhs2 = do_valueize (rhs2, top_valueize, valueized);
> -             rhs3 = do_valueize (rhs3, top_valueize, valueized);
> +             if (code == COND_EXPR && COMPARISON_CLASS_P (rhs1))
> +               rhs1 = valueize_condition (rhs1);
> +             else
> +               rhs1 = valueize_op (rhs1);
> +             tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
> +             tree rhs3 = valueize_op (gimple_assign_rhs3 (stmt));
>               res_op->set_op (code, type, rhs1, rhs2, rhs3);
> -             return (gimple_resimplify3 (seq, res_op, valueize)
> -                     || valueized);
> +             return true;
>             }
>           default:
>             gcc_unreachable ();
> @@ -1018,7 +976,6 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>           && gimple_call_num_args (stmt) >= 1
>           && gimple_call_num_args (stmt) <= 5)
>         {
> -         bool valueized = false;
>           combined_fn cfn;
>           if (gimple_call_internal_p (stmt))
>             cfn = as_combined_fn (gimple_call_internal_fn (stmt));
> @@ -1028,7 +985,7 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>               if (!fn)
>                 return false;
>
> -             fn = do_valueize (fn, top_valueize, valueized);
> +             fn = valueize_op (fn);
>               if (TREE_CODE (fn) != ADDR_EXPR
>                   || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL)
>                 return false;
> @@ -1044,47 +1001,17 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>           unsigned int num_args = gimple_call_num_args (stmt);
>           res_op->set_op (cfn, TREE_TYPE (gimple_call_lhs (stmt)), num_args);
>           for (unsigned i = 0; i < num_args; ++i)
> -           {
> -             tree arg = gimple_call_arg (stmt, i);
> -             res_op->ops[i] = do_valueize (arg, top_valueize, valueized);
> -           }
> -         if (internal_fn_p (cfn)
> -             && try_conditional_simplification (as_internal_fn (cfn),
> -                                                res_op, seq, valueize))
> -           return true;
> -         switch (num_args)
> -           {
> -           case 1:
> -             return (gimple_resimplify1 (seq, res_op, valueize)
> -                     || valueized);
> -           case 2:
> -             return (gimple_resimplify2 (seq, res_op, valueize)
> -                     || valueized);
> -           case 3:
> -             return (gimple_resimplify3 (seq, res_op, valueize)
> -                     || valueized);
> -           case 4:
> -             return (gimple_resimplify4 (seq, res_op, valueize)
> -                     || valueized);
> -           case 5:
> -             return (gimple_resimplify5 (seq, res_op, valueize)
> -                     || valueized);
> -           default:
> -            gcc_unreachable ();
> -           }
> +           res_op->ops[i] = valueize_op (gimple_call_arg (stmt, i));
> +         return true;
>         }
>        break;
>
>      case GIMPLE_COND:
>        {
> -       tree lhs = gimple_cond_lhs (stmt);
> -       tree rhs = gimple_cond_rhs (stmt);
> -       bool valueized = false;
> -       lhs = do_valueize (lhs, top_valueize, valueized);
> -       rhs = do_valueize (rhs, top_valueize, valueized);
> +       tree lhs = valueize_op (gimple_cond_lhs (stmt));
> +       tree rhs = valueize_op (gimple_cond_rhs (stmt));
>         res_op->set_op (gimple_cond_code (stmt), boolean_type_node, lhs, rhs);
> -       return (gimple_resimplify2 (seq, res_op, valueize)
> -               || valueized);
> +       return true;
>        }
>
>      default:
> @@ -1094,6 +1021,77 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
>    return false;
>  }
>
> +/* Try to describe STMT in RES_OP, returning true on success.
> +   For GIMPLE_CONDs, describe the condition that is being tested.
> +   For GIMPLE_ASSIGNs, describe the rhs of the assignment.
> +   For GIMPLE_CALLs, describe the call.  */
> +
> +bool
> +gimple_extract_op (gimple *stmt, gimple_match_op *res_op)
> +{
> +  auto nop = [](tree op) { return op; };
> +  return gimple_extract (stmt, res_op, nop, nop);
> +}
> +
> +/* The main STMT based simplification entry.  It is used by the fold_stmt
> +   and the fold_stmt_to_constant APIs.  */
> +
> +bool
> +gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
> +                tree (*valueize)(tree), tree (*top_valueize)(tree))
> +{
> +  bool valueized = false;
> +  auto valueize_op = [&](tree op)
> +    {
> +      return do_valueize (op, top_valueize, valueized);
> +    };
> +  auto valueize_condition = [&](tree op) -> tree
> +    {
> +      bool cond_valueized = false;
> +      tree lhs = do_valueize (TREE_OPERAND (op, 0), top_valueize,
> +                             cond_valueized);
> +      tree rhs = do_valueize (TREE_OPERAND (op, 1), top_valueize,
> +                             cond_valueized);
> +      gimple_match_op res_op2 (res_op->cond, TREE_CODE (op),
> +                              TREE_TYPE (op), lhs, rhs);
> +      if ((gimple_resimplify2 (seq, &res_op2, valueize)
> +          || cond_valueized)
> +         && res_op2.code.is_tree_code ())
> +       {
> +         if (TREE_CODE_CLASS ((tree_code) res_op2.code) == tcc_comparison)
> +           {
> +             valueized = true;
> +             return build2 (res_op2.code, TREE_TYPE (op),
> +                            res_op2.ops[0], res_op2.ops[1]);
> +           }
> +         else if (res_op2.code == SSA_NAME
> +                  || res_op2.code == INTEGER_CST
> +                  || res_op2.code == VECTOR_CST)
> +           {
> +             valueized = true;
> +             return res_op2.ops[0];
> +           }
> +       }
> +      return valueize_op (op);
> +    };
> +
> +  if (!gimple_extract (stmt, res_op, valueize_op, valueize_condition))
> +    return false;
> +
> +  if (res_op->code.is_internal_fn ())
> +    {
> +      internal_fn ifn = internal_fn (res_op->code);
> +      if (try_conditional_simplification (ifn, res_op, seq, valueize))
> +       return true;
> +    }
> +
> +  if (!res_op->reverse
> +      && res_op->num_ops
> +      && res_op->resimplify (seq, valueize))
> +    return true;
> +
> +  return valueized;
> +}
>
>  /* Helper for the autogenerated code, valueize OP.  */
>
> diff --git a/gcc/gimple-match.h b/gcc/gimple-match.h
> index 2d4ea476076..8abeafebb00 100644
> --- a/gcc/gimple-match.h
> +++ b/gcc/gimple-match.h
> @@ -33,13 +33,39 @@ public:
>    code_helper (combined_fn fn) : rep (-(int) fn) {}
>    operator tree_code () const { return (tree_code) rep; }
>    operator combined_fn () const { return (combined_fn) -rep; }
> +  explicit operator internal_fn () const;
> +  explicit operator built_in_function () const;
>    bool is_tree_code () const { return rep > 0; }
>    bool is_fn_code () const { return rep < 0; }
> +  bool is_internal_fn () const;
> +  bool is_builtin_fn () const;
>    int get_rep () const { return rep; }
>  private:
>    int rep;
>  };
>
> +inline code_helper::operator internal_fn () const
> +{
> +  return as_internal_fn (combined_fn (*this));
> +}
> +
> +inline code_helper::operator built_in_function () const
> +{
> +  return as_builtin_fn (combined_fn (*this));
> +}
> +
> +inline bool
> +code_helper::is_internal_fn () const
> +{
> +  return is_fn_code () && internal_fn_p (combined_fn (*this));
> +}
> +
> +inline bool
> +code_helper::is_builtin_fn () const
> +{
> +  return is_fn_code () && builtin_fn_p (combined_fn (*this));
> +}
> +
>  /* Represents the condition under which an operation should happen,
>     and the value to use otherwise.  The condition applies elementwise
>     (as for VEC_COND_EXPR) if the values are vectors.  */
> @@ -333,6 +359,7 @@ gimple_simplified_result_is_gimple_val (const gimple_match_op *op)
>
>  extern tree (*mprts_hook) (gimple_match_op *);
>
> +bool gimple_extract_op (gimple *, gimple_match_op *);
>  bool gimple_simplify (gimple *, gimple_match_op *, gimple_seq *,
>                       tree (*)(tree), tree (*)(tree));
>  tree maybe_push_res_to_seq (gimple_match_op *, gimple_seq *,
> --
> 2.25.1
>
  

Patch

diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c
index 9d88b2f8551..4c6e0883ba4 100644
--- a/gcc/gimple-match-head.c
+++ b/gcc/gimple-match-head.c
@@ -890,12 +890,29 @@  try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
   return true;
 }
 
-/* The main STMT based simplification entry.  It is used by the fold_stmt
-   and the fold_stmt_to_constant APIs.  */
+/* Common subroutine of gimple_extract_op and gimple_simplify.  Try to
+   describe STMT in RES_OP.  Return:
 
-bool
-gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
-		 tree (*valueize)(tree), tree (*top_valueize)(tree))
+   - -1 if extraction failed
+   - otherwise, 0 if no simplification should take place
+   - otherwise, the number of operands for a GIMPLE_ASSIGN or GIMPLE_COND
+   - otherwise, -2 for a GIMPLE_CALL
+
+   Before recording an operand, call:
+
+   - VALUEIZE_CONDITION for a COND_EXPR condition
+   - VALUEIZE_NAME if the rhs of a GIMPLE_ASSIGN is an SSA_NAME
+   - VALUEIZE_OP for every other top-level operand
+
+   Each routine takes a tree argument and returns a tree.  */
+
+template<typename ValueizeOp, typename ValueizeCondition,
+	 typename ValueizeName>
+inline int
+gimple_extract (gimple *stmt, gimple_match_op *res_op,
+		ValueizeOp valueize_op,
+		ValueizeCondition valueize_condition,
+		ValueizeName valueize_name)
 {
   switch (gimple_code (stmt))
     {
@@ -911,100 +928,53 @@  gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
 		|| code == VIEW_CONVERT_EXPR)
 	      {
 		tree op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
-		bool valueized = false;
-		op0 = do_valueize (op0, top_valueize, valueized);
-		res_op->set_op (code, type, op0);
-		return (gimple_resimplify1 (seq, res_op, valueize)
-			|| valueized);
+		res_op->set_op (code, type, valueize_op (op0));
+		return 1;
 	      }
 	    else if (code == BIT_FIELD_REF)
 	      {
 		tree rhs1 = gimple_assign_rhs1 (stmt);
-		tree op0 = TREE_OPERAND (rhs1, 0);
-		bool valueized = false;
-		op0 = do_valueize (op0, top_valueize, valueized);
+		tree op0 = valueize_op (TREE_OPERAND (rhs1, 0));
 		res_op->set_op (code, type, op0,
 				TREE_OPERAND (rhs1, 1),
 				TREE_OPERAND (rhs1, 2),
 				REF_REVERSE_STORAGE_ORDER (rhs1));
-		if (res_op->reverse)
-		  return valueized;
-		return (gimple_resimplify3 (seq, res_op, valueize)
-			|| valueized);
+		return res_op->reverse ? 0 : 3;
 	      }
-	    else if (code == SSA_NAME
-		     && top_valueize)
+	    else if (code == SSA_NAME)
 	      {
 		tree op0 = gimple_assign_rhs1 (stmt);
-		tree valueized = top_valueize (op0);
+		tree valueized = valueize_name (op0);
 		if (!valueized || op0 == valueized)
-		  return false;
+		  return -1;
 		res_op->set_op (TREE_CODE (op0), type, valueized);
-		return true;
+		return 0;
 	      }
 	    break;
 	  case GIMPLE_UNARY_RHS:
 	    {
 	      tree rhs1 = gimple_assign_rhs1 (stmt);
-	      bool valueized = false;
-	      rhs1 = do_valueize (rhs1, top_valueize, valueized);
-	      res_op->set_op (code, type, rhs1);
-	      return (gimple_resimplify1 (seq, res_op, valueize)
-		      || valueized);
+	      res_op->set_op (code, type, valueize_op (rhs1));
+	      return 1;
 	    }
 	  case GIMPLE_BINARY_RHS:
 	    {
-	      tree rhs1 = gimple_assign_rhs1 (stmt);
-	      tree rhs2 = gimple_assign_rhs2 (stmt);
-	      bool valueized = false;
-	      rhs1 = do_valueize (rhs1, top_valueize, valueized);
-	      rhs2 = do_valueize (rhs2, top_valueize, valueized);
+	      tree rhs1 = valueize_op (gimple_assign_rhs1 (stmt));
+	      tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
 	      res_op->set_op (code, type, rhs1, rhs2);
-	      return (gimple_resimplify2 (seq, res_op, valueize)
-		      || valueized);
+	      return 2;
 	    }
 	  case GIMPLE_TERNARY_RHS:
 	    {
-	      bool valueized = false;
 	      tree rhs1 = gimple_assign_rhs1 (stmt);
-	      /* If this is a COND_EXPR first try to simplify an
-		 embedded GENERIC condition.  */
-	      if (code == COND_EXPR)
-		{
-		  if (COMPARISON_CLASS_P (rhs1))
-		    {
-		      tree lhs = TREE_OPERAND (rhs1, 0);
-		      tree rhs = TREE_OPERAND (rhs1, 1);
-		      lhs = do_valueize (lhs, top_valueize, valueized);
-		      rhs = do_valueize (rhs, top_valueize, valueized);
-		      gimple_match_op res_op2 (res_op->cond, TREE_CODE (rhs1),
-					       TREE_TYPE (rhs1), lhs, rhs);
-		      if ((gimple_resimplify2 (seq, &res_op2, valueize)
-			   || valueized)
-			  && res_op2.code.is_tree_code ())
-			{
-			  valueized = true;
-			  if (TREE_CODE_CLASS ((enum tree_code) res_op2.code)
-			      == tcc_comparison)
-			    rhs1 = build2 (res_op2.code, TREE_TYPE (rhs1),
-					   res_op2.ops[0], res_op2.ops[1]);
-			  else if (res_op2.code == SSA_NAME
-				   || res_op2.code == INTEGER_CST
-				   || res_op2.code == VECTOR_CST)
-			    rhs1 = res_op2.ops[0];
-			  else
-			    valueized = false;
-			}
-		    }
-		}
-	      tree rhs2 = gimple_assign_rhs2 (stmt);
-	      tree rhs3 = gimple_assign_rhs3 (stmt);
-	      rhs1 = do_valueize (rhs1, top_valueize, valueized);
-	      rhs2 = do_valueize (rhs2, top_valueize, valueized);
-	      rhs3 = do_valueize (rhs3, top_valueize, valueized);
+	      if (code == COND_EXPR && COMPARISON_CLASS_P (rhs1))
+		rhs1 = valueize_condition (rhs1);
+	      else
+		rhs1 = valueize_op (rhs1);
+	      tree rhs2 = valueize_op (gimple_assign_rhs2 (stmt));
+	      tree rhs3 = valueize_op (gimple_assign_rhs3 (stmt));
 	      res_op->set_op (code, type, rhs1, rhs2, rhs3);
-	      return (gimple_resimplify3 (seq, res_op, valueize)
-		      || valueized);
+	      return 3;
 	    }
 	  default:
 	    gcc_unreachable ();
@@ -1018,7 +988,6 @@  gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
 	  && gimple_call_num_args (stmt) >= 1
 	  && gimple_call_num_args (stmt) <= 5)
 	{
-	  bool valueized = false;
 	  combined_fn cfn;
 	  if (gimple_call_internal_p (stmt))
 	    cfn = as_combined_fn (gimple_call_internal_fn (stmt));
@@ -1026,17 +995,17 @@  gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
 	    {
 	      tree fn = gimple_call_fn (stmt);
 	      if (!fn)
-		return false;
+		return -1;
 
-	      fn = do_valueize (fn, top_valueize, valueized);
+	      fn = valueize_op (fn);
 	      if (TREE_CODE (fn) != ADDR_EXPR
 		  || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL)
-		return false;
+		return -1;
 
 	      tree decl = TREE_OPERAND (fn, 0);
 	      if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL
 		  || !gimple_builtin_call_types_compatible_p (stmt, decl))
-		return false;
+		return -1;
 
 	      cfn = as_combined_fn (DECL_FUNCTION_CODE (decl));
 	    }
@@ -1044,56 +1013,122 @@  gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
 	  unsigned int num_args = gimple_call_num_args (stmt);
 	  res_op->set_op (cfn, TREE_TYPE (gimple_call_lhs (stmt)), num_args);
 	  for (unsigned i = 0; i < num_args; ++i)
-	    {
-	      tree arg = gimple_call_arg (stmt, i);
-	      res_op->ops[i] = do_valueize (arg, top_valueize, valueized);
-	    }
-	  if (internal_fn_p (cfn)
-	      && try_conditional_simplification (as_internal_fn (cfn),
-						 res_op, seq, valueize))
-	    return true;
-	  switch (num_args)
-	    {
-	    case 1:
-	      return (gimple_resimplify1 (seq, res_op, valueize)
-		      || valueized);
-	    case 2:
-	      return (gimple_resimplify2 (seq, res_op, valueize)
-		      || valueized);
-	    case 3:
-	      return (gimple_resimplify3 (seq, res_op, valueize)
-		      || valueized);
-	    case 4:
-	      return (gimple_resimplify4 (seq, res_op, valueize)
-		      || valueized);
-	    case 5:
-	      return (gimple_resimplify5 (seq, res_op, valueize)
-		      || valueized);
-	    default:
-	     gcc_unreachable ();
-	    }
+	    res_op->ops[i] = valueize_op (gimple_call_arg (stmt, i));
+	  return -2;
 	}
       break;
 
     case GIMPLE_COND:
       {
-	tree lhs = gimple_cond_lhs (stmt);
-	tree rhs = gimple_cond_rhs (stmt);
-	bool valueized = false;
-	lhs = do_valueize (lhs, top_valueize, valueized);
-	rhs = do_valueize (rhs, top_valueize, valueized);
+	tree lhs = valueize_op (gimple_cond_lhs (stmt));
+	tree rhs = valueize_op (gimple_cond_rhs (stmt));
 	res_op->set_op (gimple_cond_code (stmt), boolean_type_node, lhs, rhs);
-	return (gimple_resimplify2 (seq, res_op, valueize)
-		|| valueized);
+	return 2;
       }
 
     default:
       break;
     }
 
-  return false;
+  return -1;
 }
 
+/* Try to describe STMT in RES_OP, returning true on success.
+   For GIMPLE_CONDs, describe the condition that is being tested.
+   For GIMPLE_ASSIGNs, describe the rhs of the assignment.
+   For GIMPLE_CALLs, describe the call.  */
+
+bool
+gimple_extract_op (gimple *stmt, gimple_match_op *res_op)
+{
+  auto nop = [](tree op) { return op; };
+  return gimple_extract (stmt, res_op, nop, nop, nop) != -1;
+}
+
+/* The main STMT based simplification entry.  It is used by the fold_stmt
+   and the fold_stmt_to_constant APIs.  */
+
+bool
+gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
+		 tree (*valueize)(tree), tree (*top_valueize)(tree))
+{
+  bool valueized = false;
+  auto valueize_op = [&](tree op)
+    {
+      return do_valueize (op, top_valueize, valueized);
+    };
+  auto valueize_condition = [&](tree op) -> tree
+    {
+      bool cond_valueized = false;
+      tree lhs = do_valueize (TREE_OPERAND (op, 0), top_valueize,
+			      cond_valueized);
+      tree rhs = do_valueize (TREE_OPERAND (op, 1), top_valueize,
+			      cond_valueized);
+      gimple_match_op res_op2 (res_op->cond, TREE_CODE (op),
+			       TREE_TYPE (op), lhs, rhs);
+      if ((gimple_resimplify2 (seq, &res_op2, valueize)
+	   || cond_valueized)
+	  && res_op2.code.is_tree_code ())
+	{
+	  if (TREE_CODE_CLASS ((tree_code) res_op2.code) == tcc_comparison)
+	    {
+	      valueized = true;
+	      return build2 (res_op2.code, TREE_TYPE (op),
+			     res_op2.ops[0], res_op2.ops[1]);
+	    }
+	  else if (res_op2.code == SSA_NAME
+		   || res_op2.code == INTEGER_CST
+		   || res_op2.code == VECTOR_CST)
+	    {
+	      valueized = true;
+	      return res_op2.ops[0];
+	    }
+	}
+      return valueize_op (op);
+    };
+  auto valueize_name = [&](tree op)
+    {
+      return top_valueize ? top_valueize (op) : op;
+    };
+
+  int res = gimple_extract (stmt, res_op, valueize_op, valueize_condition,
+			    valueize_name);
+  if (res == -1)
+    return false;
+
+  if (res == -2)
+    {
+      combined_fn cfn = combined_fn (res_op->code);
+      if (internal_fn_p (cfn)
+	  && try_conditional_simplification (as_internal_fn (cfn),
+					     res_op, seq, valueize))
+	return true;
+      res = res_op->num_ops;
+    }
+
+  switch (res)
+    {
+    case 0:
+      return valueized;
+    case 1:
+      return (gimple_resimplify1 (seq, res_op, valueize)
+	      || valueized);
+    case 2:
+      return (gimple_resimplify2 (seq, res_op, valueize)
+	      || valueized);
+    case 3:
+      return (gimple_resimplify3 (seq, res_op, valueize)
+	      || valueized);
+    case 4:
+      return (gimple_resimplify4 (seq, res_op, valueize)
+	      || valueized);
+    case 5:
+      return (gimple_resimplify5 (seq, res_op, valueize)
+	      || valueized);
+    default:
+      gcc_unreachable ();
+    }
+}
 
 /* Helper for the autogenerated code, valueize OP.  */
 
diff --git a/gcc/gimple-match.h b/gcc/gimple-match.h
index 2d4ea476076..15a0f584db7 100644
--- a/gcc/gimple-match.h
+++ b/gcc/gimple-match.h
@@ -333,6 +333,7 @@  gimple_simplified_result_is_gimple_val (const gimple_match_op *op)
 
 extern tree (*mprts_hook) (gimple_match_op *);
 
+bool gimple_extract_op (gimple *, gimple_match_op *);
 bool gimple_simplify (gimple *, gimple_match_op *, gimple_seq *,
 		      tree (*)(tree), tree (*)(tree));
 tree maybe_push_res_to_seq (gimple_match_op *, gimple_seq *,