Extend tree code folds to IFN_COND_*

Richard Biener richard.guenther@gmail.com
Wed Jun 6 11:38:00 GMT 2018


On Thu, May 24, 2018 at 11:36 AM Richard Sandiford
<richard.sandiford@linaro.org> wrote:
>
> This patch adds match.pd support for applying normal folds to their
> IFN_COND_* forms.  E.g. the rule:
>
>   (plus @0 (negate @1)) -> (minus @0 @1)
>
> also allows the fold:
>
>   (IFN_COND_ADD @0 @1 (negate @2) @3) -> (IFN_COND_SUB @0 @1 @2 @3)
>
> Actually doing this by direct matches in gimple-match.c would
> probably lead to combinatorial explosion, so instead, the patch
> makes gimple_match_op carry a condition under which the operation
> happens ("cond"), and the value to use when the condition is false
> ("else_value").  Thus in the example above we'd do the following
>
> (a) convert:
>
>       cond:NULL_TREE (IFN_COND_ADD @0 @1 @4 @3) else_value:NULL_TREE
>
>     to:
>
>       cond:@0 (plus @1 @4) else_value:@3
>
> (b) apply gimple_resimplify to (plus @1 @4)
>
> (c) reintroduce cond and else_value when constructing the result.
>
> Nested operations inherit the condition of the outer operation
> (so that we don't introduce extra faults) but have a null else_value.
> If we try to build such an operation, the target gets to choose what
> else_value it can handle efficiently: obvious choices include one of
> the operands or a zero constant.  (The alternative would be to have some
> representation for an undefined value, but that seems a bit invasive,
> and isn't likely to be useful here.)
>
> I've made the condition a mandatory part of the gimple_match_op
> constructor so that it doesn't accidentally get dropped.
>
> Tested on aarch64-linux-gnu (with and without SVE), aarch64_be-elf
> and x86_64-linux-gnu.  OK to install?

It looks somewhat clever but after looking for a while it doesn't handle
simplifying

 (IFN_COND_ADD @0 @1 (IFN_COND_SUB @0 @2 @1 @3) @3)

to

 (cond @0 @2 @3)

right?  Because while the conditional gimple_match_op is built
by try_conditional_simplification it isn't built when doing
SSA use->def following in the generated matching code?

So it looks like a bit much noise for this very special case?

I suppose you ran into the need of these foldings from looking
at real code - which foldings specifically were appearing here?
Usually code is well optimized before if-conversion/vectorization
so we shouldn't need full-blown handling?

That said, I'm not sure how much work it is to massage

      if (gimple *def_stmt = get_def (valueize, op2))
        {
          if (gassign *def = dyn_cast <gassign *> (def_stmt))
            switch (gimple_assign_rhs_code (def))
              {
              case PLUS_EXPR:

to look like

      if (gimple *def_stmt = get_def (valueize, op2))
        {
           code = ERROR_MARK;
           if (!is_cond_ifn_with_cond (curr_gimple_match_op, &code))
             if (gassign *def dyn_cast <gassign *> (def_stmt))
               code = gimple_assign_rhs_code (def);
           switch (code)
             {
             case PLUS_EXPR:

thus transparently treat the IFN_COND_* as their "code" if the condition
matches that of the context (I'm not sure if we can do anything for
mismatching contexts).

Richard.

> Richard
>
>
> 2018-05-24  Richard Sandiford  <richard.sandiford@linaro.org>
>
> gcc/
>         * target.def (preferred_else_value): New target hook.
>         * doc/tm.texi.in (TARGET_PREFERRED_ELSE_VALUE): New hook.
>         * doc/tm.texi: Regenerate.
>         * targhooks.h (default_preferred_else_value): Declare.
>         * targhooks.c (default_preferred_else_value): New function.
>         * internal-fn.h (conditional_internal_fn_code): Declare.
>         * internal-fn.c (FOR_EACH_CODE_MAPPING): New macro.
>         (get_conditional_internal_fn): Use it.
>         (conditional_internal_fn_code): New function.
>         * gimple-match.h (gimple_match_cond): New struct.
>         (gimple_match_op): Add a cond member function.
>         (gimple_match_op::gimple_match_op): Update all forms to take a
>         gimple_match_cond.
>         * genmatch.c (expr::gen_transform): Use the same condition as res_op
>         for the suboperation, but don't specify a particular else_value.
>         * tree-ssa-sccvn.c (vn_nary_simplify, vn_reference_lookup_3)
>         (visit_nary_op, visit_reference_op_load): Pass
>         gimple_match_cond::UNCOND to the gimple_match_op constructor.
>         * gimple-match-head.c: Include tree-eh.h
>         (convert_conditional_op): New function.
>         (maybe_resimplify_conditional_op): Likewise.
>         (gimple_resimplify1): Call maybe_resimplify_conditional_op.
>         (gimple_resimplify2): Likewise.
>         (gimple_resimplify3): Likewise.
>         (gimple_resimplify4): Likewise.
>         (maybe_push_res_to_seq): Return null for conditional operations.
>         (try_conditional_simplification): New function.
>         (gimple_simplify): Call it.  Pass conditions to the gimple_match_op
>         constructor.
>         * match.pd: Fold VEC_COND_EXPRs of an IFN_COND_* call to a new
>         IFN_COND_* call.
>         * config/aarch64/aarch64.c (aarch64_preferred_else_value): New
>         function.
>         (TARGET_PREFERRED_ELSE_VALUE): Redefine.
>
> gcc/testsuite/
>         * gcc.dg/vect/vect-cond-arith-2.c: New test.
>         * gcc.target/aarch64/sve/loop_add_6.c: Likewise.
>
> Index: gcc/target.def
> ===================================================================
> --- gcc/target.def      2018-05-01 19:30:30.159632586 +0100
> +++ gcc/target.def      2018-05-24 10:33:30.871095132 +0100
> @@ -2040,6 +2040,25 @@ HOOK_VECTOR_END (vectorize)
>  #define HOOK_PREFIX "TARGET_"
>
>  DEFHOOK
> +(preferred_else_value,
> + "This hook returns the target's preferred final argument for a call\n\
> +to conditional internal function @var{ifn} (really of type\n\
> +@code{internal_fn}).  @var{type} specifies the return type of the\n\
> +function and @var{ops} are the operands to the conditional operation,\n\
> +of which there are @var{nops}.\n\
> +\n\
> +For example, if @var{ifn} is @code{IFN_COND_ADD}, the hook returns\n\
> +a value of type @var{type} that should be used when @samp{@var{ops}[0]}\n\
> +and @samp{@var{ops}[1]} are conditionally added together.\n\
> +\n\
> +This hook is only relevant if the target supports conditional patterns\n\
> +like @code{cond_add@var{m}}.  The default implementation returns a zero\n\
> +constant of type @var{type}.",
> + tree,
> + (unsigned ifn, tree type, unsigned nops, tree *ops),
> + default_preferred_else_value)
> +
> +DEFHOOK
>  (record_offload_symbol,
>   "Used when offloaded functions are seen in the compilation unit and no named\n\
>  sections are available.  It is called once for each symbol that must be\n\
> Index: gcc/doc/tm.texi.in
> ===================================================================
> --- gcc/doc/tm.texi.in  2018-05-01 19:30:28.730694873 +0100
> +++ gcc/doc/tm.texi.in  2018-05-24 10:33:30.869095197 +0100
> @@ -4149,6 +4149,8 @@ address;  but often a machine-dependent
>
>  @hook TARGET_GOACC_REDUCTION
>
> +@hook TARGET_PREFERRED_ELSE_VALUE
> +
>  @node Anchored Addresses
>  @section Anchored Addresses
>  @cindex anchored addresses
> Index: gcc/doc/tm.texi
> ===================================================================
> --- gcc/doc/tm.texi     2018-05-01 19:30:28.722695224 +0100
> +++ gcc/doc/tm.texi     2018-05-24 10:33:30.868095229 +0100
> @@ -6046,6 +6046,22 @@ expanded sequence has been inserted.  Th
>  for allocating any storage for reductions when necessary.
>  @end deftypefn
>
> +@deftypefn {Target Hook} tree TARGET_PREFERRED_ELSE_VALUE (unsigned @var{ifn}, tree @var{type}, unsigned @var{nops}, tree *@var{ops})
> +This hook returns the target's preferred final argument for a call
> +to conditional internal function @var{ifn} (really of type
> +@code{internal_fn}).  @var{type} specifies the return type of the
> +function and @var{ops} are the operands to the conditional operation,
> +of which there are @var{nops}.
> +
> +For example, if @var{ifn} is @code{IFN_COND_ADD}, the hook returns
> +a value of type @var{type} that should be used when @samp{@var{ops}[0]}
> +and @samp{@var{ops}[1]} are conditionally added together.
> +
> +This hook is only relevant if the target supports conditional patterns
> +like @code{cond_add@var{m}}.  The default implementation returns a zero
> +constant of type @var{type}.
> +@end deftypefn
> +
>  @node Anchored Addresses
>  @section Anchored Addresses
>  @cindex anchored addresses
> Index: gcc/targhooks.h
> ===================================================================
> --- gcc/targhooks.h     2018-05-01 19:30:29.390666052 +0100
> +++ gcc/targhooks.h     2018-05-24 10:33:30.872095099 +0100
> @@ -289,5 +289,6 @@ extern unsigned int default_min_arithmet
>  default_excess_precision (enum excess_precision_type ATTRIBUTE_UNUSED);
>  extern bool default_stack_clash_protection_final_dynamic_probe (rtx);
>  extern void default_select_early_remat_modes (sbitmap);
> +extern tree default_preferred_else_value (unsigned, tree, unsigned, tree *);
>
>  #endif /* GCC_TARGHOOKS_H */
> Index: gcc/targhooks.c
> ===================================================================
> --- gcc/targhooks.c     2018-05-01 19:30:29.390666052 +0100
> +++ gcc/targhooks.c     2018-05-24 10:33:30.871095132 +0100
> @@ -2345,4 +2345,12 @@ default_select_early_remat_modes (sbitma
>  {
>  }
>
> +/* The default implementation of TARGET_PREFERRED_ELSE_VALUE.  */
> +
> +tree
> +default_preferred_else_value (unsigned, tree type, unsigned, tree *)
> +{
> +  return build_zero_cst (type);
> +}
> +
>  #include "gt-targhooks.h"
> Index: gcc/internal-fn.h
> ===================================================================
> --- gcc/internal-fn.h   2018-05-17 11:52:13.507173989 +0100
> +++ gcc/internal-fn.h   2018-05-24 10:33:30.870095164 +0100
> @@ -193,6 +193,7 @@ direct_internal_fn_supported_p (internal
>  extern bool set_edom_supported_p (void);
>
>  extern internal_fn get_conditional_internal_fn (tree_code);
> +extern tree_code conditional_internal_fn_code (internal_fn);
>
>  extern bool internal_load_fn_p (internal_fn);
>  extern bool internal_store_fn_p (internal_fn);
> Index: gcc/internal-fn.c
> ===================================================================
> --- gcc/internal-fn.c   2018-05-24 10:12:10.146352152 +0100
> +++ gcc/internal-fn.c   2018-05-24 10:33:30.870095164 +0100
> @@ -3219,6 +3219,21 @@ #define DEF_INTERNAL_FN(CODE, FLAGS, FNS
>    0
>  };
>
> +/* Invoke T(CODE, IFN) for each conditional function IFN that maps to a
> +   tree code CODE.  */
> +#define FOR_EACH_CODE_MAPPING(T) \
> +  T (PLUS_EXPR, IFN_COND_ADD) \
> +  T (MINUS_EXPR, IFN_COND_SUB) \
> +  T (MULT_EXPR, IFN_COND_MUL) \
> +  T (TRUNC_DIV_EXPR, IFN_COND_DIV) \
> +  T (TRUNC_MOD_EXPR, IFN_COND_MOD) \
> +  T (RDIV_EXPR, IFN_COND_RDIV) \
> +  T (MIN_EXPR, IFN_COND_MIN) \
> +  T (MAX_EXPR, IFN_COND_MAX) \
> +  T (BIT_AND_EXPR, IFN_COND_AND) \
> +  T (BIT_IOR_EXPR, IFN_COND_IOR) \
> +  T (BIT_XOR_EXPR, IFN_COND_XOR)
> +
>  /* Return a function that only performs CODE when a certain condition is met
>     and that uses a given fallback value otherwise.  For example, if CODE is
>     a binary operation associated with conditional function FN:
> @@ -3238,31 +3253,30 @@ get_conditional_internal_fn (tree_code c
>  {
>    switch (code)
>      {
> -    case PLUS_EXPR:
> -      return IFN_COND_ADD;
> -    case MINUS_EXPR:
> -      return IFN_COND_SUB;
> -    case MIN_EXPR:
> -      return IFN_COND_MIN;
> -    case MAX_EXPR:
> -      return IFN_COND_MAX;
> -    case TRUNC_DIV_EXPR:
> -      return IFN_COND_DIV;
> -    case TRUNC_MOD_EXPR:
> -      return IFN_COND_MOD;
> -    case RDIV_EXPR:
> -      return IFN_COND_RDIV;
> -    case BIT_AND_EXPR:
> -      return IFN_COND_AND;
> -    case BIT_IOR_EXPR:
> -      return IFN_COND_IOR;
> -    case BIT_XOR_EXPR:
> -      return IFN_COND_XOR;
> +#define CASE(CODE, IFN) case CODE: return IFN;
> +      FOR_EACH_CODE_MAPPING(CASE)
> +#undef CASE
>      default:
>        return IFN_LAST;
>      }
>  }
>
> +/* If IFN implements the conditional form of a tree code, return that
> +   tree code, otherwise return ERROR_MARK.  */
> +
> +tree_code
> +conditional_internal_fn_code (internal_fn ifn)
> +{
> +  switch (ifn)
> +    {
> +#define CASE(CODE, IFN) case IFN: return CODE;
> +      FOR_EACH_CODE_MAPPING(CASE)
> +#undef CASE
> +    default:
> +      return ERROR_MARK;
> +    }
> +}
> +
>  /* Return true if IFN is some form of load from memory.  */
>
>  bool
> Index: gcc/gimple-match.h
> ===================================================================
> --- gcc/gimple-match.h  2018-05-24 09:54:37.509451356 +0100
> +++ gcc/gimple-match.h  2018-05-24 10:33:30.870095164 +0100
> @@ -40,16 +40,57 @@ #define GCC_GIMPLE_MATCH_H
>    int rep;
>  };
>
> +/* 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.  */
> +struct gimple_match_cond
> +{
> +  enum uncond { UNCOND };
> +
> +  /* Build an unconditional op.  */
> +  gimple_match_cond (uncond) : cond (NULL_TREE), else_value (NULL_TREE) {}
> +  gimple_match_cond (tree, tree);
> +
> +  gimple_match_cond any_else () const;
> +
> +  /* The condition under which the operation occurs, or NULL_TREE
> +     if the operation is unconditional.  */
> +  tree cond;
> +
> +  /* The value to use when the condition is false.  This is NULL_TREE if
> +     the operation is unconditional or if the value doesn't matter.  */
> +  tree else_value;
> +};
> +
> +inline
> +gimple_match_cond::gimple_match_cond (tree cond_in, tree else_value_in)
> +  : cond (cond_in), else_value (else_value_in)
> +{
> +}
> +
> +/* Return a gimple_match_cond with the same condition but with an
> +   arbitrary ELSE_VALUE.  */
> +
> +inline gimple_match_cond
> +gimple_match_cond::any_else () const
> +{
> +  return gimple_match_cond (cond, NULL_TREE);
> +}
> +
>  /* Represents an operation to be simplified, or the result of the
>     simplification.  */
>  struct gimple_match_op
>  {
> -  gimple_match_op () : type (NULL_TREE), num_ops (0) {}
> -  gimple_match_op (code_helper, tree, unsigned int);
> -  gimple_match_op (code_helper, tree, tree);
> -  gimple_match_op (code_helper, tree, tree, tree);
> -  gimple_match_op (code_helper, tree, tree, tree, tree);
> -  gimple_match_op (code_helper, tree, tree, tree, tree, tree);
> +  gimple_match_op ();
> +  gimple_match_op (const gimple_match_cond &, code_helper, tree, unsigned int);
> +  gimple_match_op (const gimple_match_cond &,
> +                  code_helper, tree, tree);
> +  gimple_match_op (const gimple_match_cond &,
> +                  code_helper, tree, tree, tree);
> +  gimple_match_op (const gimple_match_cond &,
> +                  code_helper, tree, tree, tree, tree);
> +  gimple_match_op (const gimple_match_cond &,
> +                  code_helper, tree, tree, tree, tree, tree);
>
>    void set_op (code_helper, tree, unsigned int);
>    void set_op (code_helper, tree, tree);
> @@ -63,6 +104,10 @@ struct gimple_match_op
>    /* The maximum value of NUM_OPS.  */
>    static const unsigned int MAX_NUM_OPS = 4;
>
> +  /* The conditions under which the operation is performed, and the value to
> +     use as a fallback.  */
> +  gimple_match_cond cond;
> +
>    /* The operation being performed.  */
>    code_helper code;
>
> @@ -76,39 +121,49 @@ struct gimple_match_op
>    tree ops[MAX_NUM_OPS];
>  };
>
> -/* Constructor that takes the code, type and number of operands, but leaves
> -   the caller to fill in the operands.  */
> +inline
> +gimple_match_op::gimple_match_op ()
> +  : cond (gimple_match_cond::UNCOND), type (NULL_TREE), num_ops (0)
> +{
> +}
> +
> +/* Constructor that takes the condition, code, type and number of
> +   operands, but leaves the caller to fill in the operands.  */
>
>  inline
> -gimple_match_op::gimple_match_op (code_helper code_in, tree type_in,
> +gimple_match_op::gimple_match_op (const gimple_match_cond &cond_in,
> +                                 code_helper code_in, tree type_in,
>                                   unsigned int num_ops_in)
> -  : code (code_in), type (type_in), num_ops (num_ops_in)
> +  : cond (cond_in), code (code_in), type (type_in), num_ops (num_ops_in)
>  {
>  }
>
>  /* Constructors for various numbers of operands.  */
>
>  inline
> -gimple_match_op::gimple_match_op (code_helper code_in, tree type_in,
> +gimple_match_op::gimple_match_op (const gimple_match_cond &cond_in,
> +                                 code_helper code_in, tree type_in,
>                                   tree op0)
> -  : code (code_in), type (type_in), num_ops (1)
> +  : cond (cond_in), code (code_in), type (type_in), num_ops (1)
>  {
>    ops[0] = op0;
>  }
>
>  inline
> -gimple_match_op::gimple_match_op (code_helper code_in, tree type_in,
> +gimple_match_op::gimple_match_op (const gimple_match_cond &cond_in,
> +                                 code_helper code_in, tree type_in,
>                                   tree op0, tree op1)
> -  : code (code_in), type (type_in), num_ops (2)
> +  : cond (cond_in), code (code_in), type (type_in), num_ops (2)
>  {
>    ops[0] = op0;
>    ops[1] = op1;
>  }
>
>  inline
> -gimple_match_op::gimple_match_op (code_helper code_in, tree type_in,
> +gimple_match_op::gimple_match_op (const gimple_match_cond &cond_in,
> +                                 code_helper code_in, tree type_in,
>                                   tree op0, tree op1, tree op2)
> -  : code (code_in), type (type_in), num_ops (3)
> +  : cond (cond_in), code (code_in), type (type_in), num_ops (3)
>  {
>    ops[0] = op0;
>    ops[1] = op1;
> @@ -116,9 +171,10 @@ gimple_match_op::gimple_match_op (code_h
>  }
>
>  inline
> -gimple_match_op::gimple_match_op (code_helper code_in, tree type_in,
> +gimple_match_op::gimple_match_op (const gimple_match_cond &cond_in,
> +                                 code_helper code_in, tree type_in,
>                                   tree op0, tree op1, tree op2, tree op3)
> -  : code (code_in), type (type_in), num_ops (4)
> +  : cond (cond_in), code (code_in), type (type_in), num_ops (4)
>  {
>    ops[0] = op0;
>    ops[1] = op1;
> Index: gcc/genmatch.c
> ===================================================================
> --- gcc/genmatch.c      2018-05-24 10:12:10.145352193 +0100
> +++ gcc/genmatch.c      2018-05-24 10:33:30.869095197 +0100
> @@ -2507,8 +2507,8 @@ expr::gen_transform (FILE *f, int indent
>        /* ???  Building a stmt can fail for various reasons here, seq being
>           NULL or the stmt referencing SSA names occuring in abnormal PHIs.
>          So if we fail here we should continue matching other patterns.  */
> -      fprintf_indent (f, indent, "gimple_match_op tem_op (%s, %s",
> -                     opr_name, type);
> +      fprintf_indent (f, indent, "gimple_match_op tem_op "
> +                     "(res_op->cond.any_else (), %s, %s", opr_name, type);
>        for (unsigned i = 0; i < ops.length (); ++i)
>         fprintf (f, ", ops%d[%u]", depth, i);
>        fprintf (f, ");\n");
> Index: gcc/tree-ssa-sccvn.c
> ===================================================================
> --- gcc/tree-ssa-sccvn.c        2018-05-24 09:02:28.765328358 +0100
> +++ gcc/tree-ssa-sccvn.c        2018-05-24 10:33:30.872095099 +0100
> @@ -1804,7 +1804,8 @@ vn_nary_simplify (vn_nary_op_t nary)
>  {
>    if (nary->length > gimple_match_op::MAX_NUM_OPS)
>      return NULL_TREE;
> -  gimple_match_op op (nary->opcode, nary->type, nary->length);
> +  gimple_match_op op (gimple_match_cond::UNCOND, nary->opcode,
> +                     nary->type, nary->length);
>    memcpy (op.ops, nary->op, sizeof (tree) * nary->length);
>    return vn_nary_build_or_lookup_1 (&op, false);
>  }
> @@ -2031,8 +2032,8 @@ vn_reference_lookup_3 (ao_ref *ref, tree
>           else if (INTEGRAL_TYPE_P (vr->type)
>                    && known_eq (ref->size, 8))
>             {
> -             gimple_match_op res_op (NOP_EXPR, vr->type,
> -                                     gimple_call_arg (def_stmt, 1));
> +             gimple_match_op res_op (gimple_match_cond::UNCOND, NOP_EXPR,
> +                                     vr->type, gimple_call_arg (def_stmt, 1));
>               val = vn_nary_build_or_lookup (&res_op);
>               if (!val
>                   || (TREE_CODE (val) == SSA_NAME
> @@ -2172,7 +2173,8 @@ vn_reference_lookup_3 (ao_ref *ref, tree
>               || known_eq (ref->size, TYPE_PRECISION (vr->type)))
>           && multiple_p (ref->size, BITS_PER_UNIT))
>         {
> -         gimple_match_op op (BIT_FIELD_REF, vr->type,
> +         gimple_match_op op (gimple_match_cond::UNCOND,
> +                             BIT_FIELD_REF, vr->type,
>                               SSA_VAL (gimple_assign_rhs1 (def_stmt)),
>                               bitsize_int (ref->size),
>                               bitsize_int (offset - offset2));
> @@ -3701,7 +3703,8 @@ visit_nary_op (tree lhs, gassign *stmt)
>                       unsigned rhs_prec = TYPE_PRECISION (TREE_TYPE (rhs1));
>                       if (lhs_prec == rhs_prec)
>                         {
> -                         gimple_match_op match_op (NOP_EXPR, type, ops[0]);
> +                         gimple_match_op match_op (gimple_match_cond::UNCOND,
> +                                                   NOP_EXPR, type, ops[0]);
>                           result = vn_nary_build_or_lookup (&match_op);
>                           if (result)
>                             {
> @@ -3714,7 +3717,8 @@ visit_nary_op (tree lhs, gassign *stmt)
>                         {
>                           tree mask = wide_int_to_tree
>                             (type, wi::mask (rhs_prec, false, lhs_prec));
> -                         gimple_match_op match_op (BIT_AND_EXPR,
> +                         gimple_match_op match_op (gimple_match_cond::UNCOND,
> +                                                   BIT_AND_EXPR,
>                                                     TREE_TYPE (lhs),
>                                                     ops[0], mask);
>                           result = vn_nary_build_or_lookup (&match_op);
> @@ -3838,7 +3842,8 @@ visit_reference_op_load (tree lhs, tree
>          of VIEW_CONVERT_EXPR <TREE_TYPE (result)> (result).
>          So first simplify and lookup this expression to see if it
>          is already available.  */
> -      gimple_match_op res_op (VIEW_CONVERT_EXPR, TREE_TYPE (op), result);
> +      gimple_match_op res_op (gimple_match_cond::UNCOND,
> +                             VIEW_CONVERT_EXPR, TREE_TYPE (op), result);
>        result = vn_nary_build_or_lookup (&res_op);
>      }
>
> Index: gcc/gimple-match-head.c
> ===================================================================
> --- gcc/gimple-match-head.c     2018-05-24 09:54:37.509451356 +0100
> +++ gcc/gimple-match-head.c     2018-05-24 10:33:30.870095164 +0100
> @@ -40,6 +40,7 @@ Software Foundation; either version 3, o
>  #include "case-cfn-macros.h"
>  #include "gimplify.h"
>  #include "optabs-tree.h"
> +#include "tree-eh.h"
>
>
>  /* Forward declarations of the private auto-generated matchers.
> @@ -68,6 +69,95 @@ constant_for_folding (tree t)
>               && TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST));
>  }
>
> +/* Try to convert conditional operation ORIG_OP into an IFN_COND_*
> +   operation.  Return true on success, storing the new operation in NEW_OP.  */
> +
> +static bool
> +convert_conditional_op (gimple_match_op *orig_op,
> +                       gimple_match_op *new_op)
> +{
> +  internal_fn ifn;
> +  if (orig_op->code.is_tree_code ())
> +    ifn = get_conditional_internal_fn ((tree_code) orig_op->code);
> +  else
> +    return false;
> +  if (ifn == IFN_LAST)
> +    return false;
> +  unsigned int num_ops = orig_op->num_ops;
> +  new_op->set_op (as_combined_fn (ifn), orig_op->type, num_ops + 2);
> +  new_op->ops[0] = orig_op->cond.cond;
> +  for (unsigned int i = 0; i < num_ops; ++i)
> +    new_op->ops[i + 1] = orig_op->ops[i];
> +  tree else_value = orig_op->cond.else_value;
> +  if (!else_value)
> +    else_value = targetm.preferred_else_value (ifn, orig_op->type,
> +                                              num_ops, orig_op->ops);
> +  new_op->ops[num_ops + 1] = else_value;
> +  return true;
> +}
> +
> +/* RES_OP is the result of a simplification.  If it is conditional,
> +   try to replace it with the equivalent UNCOND form, such as an
> +   IFN_COND_* call or a VEC_COND_EXPR.  Also try to resimplify the
> +   result of the replacement if appropriate, adding any new statements to
> +   SEQ and using VALUEIZE as the valueization function.  Return true if
> +   this resimplification occurred and resulted in at least one change.  */
> +
> +static bool
> +maybe_resimplify_conditional_op (gimple_seq *seq, gimple_match_op *res_op,
> +                                tree (*valueize) (tree))
> +{
> +  if (!res_op->cond.cond)
> +    return false;
> +
> +  if (!res_op->cond.else_value
> +      && res_op->code.is_tree_code ())
> +    {
> +      /* The "else" value doesn't matter.  If the "then" value is a
> +        gimple value, just use it unconditionally.  This isn't a
> +        simplification in itself, since there was no operation to
> +        build in the first place.  */
> +      if (gimple_simplified_result_is_gimple_val (res_op))
> +       {
> +         res_op->cond.cond = NULL_TREE;
> +         return false;
> +       }
> +
> +      /* Likewise if the operation would not trap.  */
> +      bool honor_trapv = (INTEGRAL_TYPE_P (res_op->type)
> +                         && TYPE_OVERFLOW_TRAPS (res_op->type));
> +      if (!operation_could_trap_p ((tree_code) res_op->code,
> +                                  FLOAT_TYPE_P (res_op->type),
> +                                  honor_trapv, res_op->op_or_null (1)))
> +       {
> +         res_op->cond.cond = NULL_TREE;
> +         return false;
> +       }
> +    }
> +
> +  /* If the "then" value is a gimple value and the "else" value matters,
> +     create a VEC_COND_EXPR between them, then see if it can be further
> +     simplified.  */
> +  gimple_match_op new_op;
> +  if (res_op->cond.else_value
> +      && VECTOR_TYPE_P (res_op->type)
> +      && gimple_simplified_result_is_gimple_val (res_op))
> +    {
> +      new_op.set_op (VEC_COND_EXPR, res_op->type,
> +                    res_op->cond.cond, res_op->ops[0],
> +                    res_op->cond.else_value);
> +      *res_op = new_op;
> +      return gimple_resimplify3 (seq, res_op, valueize);
> +    }
> +
> +  /* Otherwise try rewriting the operation as an IFN_COND_* call.
> +     Again, this isn't a simplification in itself, since it's what
> +     RES_OP already described.  */
> +  if (convert_conditional_op (res_op, &new_op))
> +    *res_op = new_op;
> +
> +  return false;
> +}
>
>  /* Helper that matches and simplifies the toplevel result from
>     a gimple_simplify run (where we don't want to build
> @@ -93,6 +183,7 @@ gimple_resimplify1 (gimple_seq *seq, gim
>           if (TREE_OVERFLOW_P (tem))
>             tem = drop_tree_overflow (tem);
>           res_op->set_value (tem);
> +         maybe_resimplify_conditional_op (seq, res_op, valueize);
>           return true;
>         }
>      }
> @@ -105,6 +196,9 @@ gimple_resimplify1 (gimple_seq *seq, gim
>        return true;
>      }
>
> +  if (maybe_resimplify_conditional_op (seq, res_op, valueize))
> +    return true;
> +
>    return false;
>  }
>
> @@ -134,6 +228,7 @@ gimple_resimplify2 (gimple_seq *seq, gim
>           if (TREE_OVERFLOW_P (tem))
>             tem = drop_tree_overflow (tem);
>           res_op->set_value (tem);
> +         maybe_resimplify_conditional_op (seq, res_op, valueize);
>           return true;
>         }
>      }
> @@ -160,6 +255,9 @@ gimple_resimplify2 (gimple_seq *seq, gim
>        return true;
>      }
>
> +  if (maybe_resimplify_conditional_op (seq, res_op, valueize))
> +    return true;
> +
>    return canonicalized;
>  }
>
> @@ -191,6 +289,7 @@ gimple_resimplify3 (gimple_seq *seq, gim
>           if (TREE_OVERFLOW_P (tem))
>             tem = drop_tree_overflow (tem);
>           res_op->set_value (tem);
> +         maybe_resimplify_conditional_op (seq, res_op, valueize);
>           return true;
>         }
>      }
> @@ -214,6 +313,9 @@ gimple_resimplify3 (gimple_seq *seq, gim
>        return true;
>      }
>
> +  if (maybe_resimplify_conditional_op (seq, res_op, valueize))
> +    return true;
> +
>    return canonicalized;
>  }
>
> @@ -239,6 +341,9 @@ gimple_resimplify4 (gimple_seq *seq, gim
>        return true;
>      }
>
> +  if (maybe_resimplify_conditional_op (seq, res_op, valueize))
> +    return true;
> +
>    return false;
>  }
>
> @@ -297,6 +402,12 @@ maybe_push_res_to_seq (gimple_match_op *
>    tree *ops = res_op->ops;
>    unsigned num_ops = res_op->num_ops;
>
> +  /* The caller should have converted conditional operations into an UNCOND
> +     form and resimplified as appropriate.  The conditional form only
> +     survives this far if that conversion failed.  */
> +  if (res_op->cond.cond)
> +    return NULL_TREE;
> +
>    if (res_op->code.is_tree_code ())
>      {
>        if (!res
> @@ -558,6 +669,50 @@ do_valueize (tree op, tree (*valueize)(t
>    return op;
>  }
>
> +/* If RES_OP is a call to a conditional internal function, try simplifying
> +   the associated unconditional operation and using the result to build
> +   a new conditional operation.  For example, if RES_OP is:
> +
> +     IFN_COND_ADD (COND, A, B, ELSE)
> +
> +   try simplifying (plus A B) and using the result to build a replacement
> +   for the whole IFN_COND_ADD.
> +
> +   Return true if this approach led to a simplification, otherwise leave
> +   RES_OP unchanged (and so suitable for other simplifications).  When
> +   returning true, add any new statements to SEQ and use VALUEIZE as the
> +   valueization function.
> +
> +   RES_OP is known to be a call to IFN.  */
> +
> +static bool
> +try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
> +                               gimple_seq *seq, tree (*valueize) (tree))
> +{
> +  tree_code code = conditional_internal_fn_code (ifn);
> +  if (code == ERROR_MARK)
> +    return false;
> +
> +  unsigned int num_ops = res_op->num_ops;
> +  gimple_match_op cond_op (gimple_match_cond (res_op->ops[0],
> +                                             res_op->ops[num_ops - 1]),
> +                          code, res_op->type, num_ops - 2);
> +  for (unsigned int i = 1; i < num_ops - 1; ++i)
> +    cond_op.ops[i - 1] = res_op->ops[i];
> +  switch (num_ops - 2)
> +    {
> +    case 2:
> +      if (!gimple_resimplify2 (seq, &cond_op, valueize))
> +       return false;
> +      break;
> +    default:
> +      gcc_unreachable ();
> +    }
> +  *res_op = cond_op;
> +  maybe_resimplify_conditional_op (seq, res_op, valueize);
> +  return true;
> +}
> +
>  /* The main STMT based simplification entry.  It is used by the fold_stmt
>     and the fold_stmt_to_constant APIs.  */
>
> @@ -643,7 +798,7 @@ gimple_simplify (gimple *stmt, gimple_ma
>                       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 (TREE_CODE (rhs1),
> +                     gimple_match_op res_op2 (res_op->cond, TREE_CODE (rhs1),
>                                                TREE_TYPE (rhs1), lhs, rhs);
>                       if ((gimple_resimplify2 (seq, &res_op2, valueize)
>                            || valueized)
> @@ -714,6 +869,10 @@ gimple_simplify (gimple *stmt, gimple_ma
>               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:
> Index: gcc/match.pd
> ===================================================================
> --- gcc/match.pd        2018-05-24 10:12:10.146352152 +0100
> +++ gcc/match.pd        2018-05-24 10:33:30.870095164 +0100
> @@ -4797,3 +4797,12 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>    (with { tree op_type = TREE_TYPE (@4); }
>     (if (element_precision (type) == element_precision (op_type))
>      (view_convert (cond_op (bit_not @0) @2 @3 (view_convert:op_type @1)))))))
> +
> +/* Detect cases in which a VEC_COND_EXPR effectively replaces the
> +   "else" value of an IFN_COND_*.  */
> +(for cond_op (COND_BINARY)
> + (simplify
> +  (vec_cond @0 (view_convert? (cond_op @0 @1 @2 @3)) @4)
> +  (with { tree op_type = TREE_TYPE (@3); }
> +   (if (element_precision (type) == element_precision (op_type))
> +    (view_convert (cond_op @0 @1 @2 (view_convert:op_type @4)))))))
> Index: gcc/config/aarch64/aarch64.c
> ===================================================================
> --- gcc/config/aarch64/aarch64.c        2018-05-24 09:54:37.507451418 +0100
> +++ gcc/config/aarch64/aarch64.c        2018-05-24 10:33:30.867095262 +0100
> @@ -1292,6 +1292,16 @@ aarch64_get_mask_mode (poly_uint64 nunit
>    return default_get_mask_mode (nunits, nbytes);
>  }
>
> +/* Implement TARGET_PREFERRED_ELSE_VALUE.  Prefer to use the first
> +   arithmetic operand as the else value if the else value doesn't matter,
> +   since that exactly matches the SVE destructive merging form.  */
> +
> +static tree
> +aarch64_preferred_else_value (unsigned, tree, unsigned int, tree *ops)
> +{
> +  return ops[0];
> +}
> +
>  /* Implement TARGET_HARD_REGNO_NREGS.  */
>
>  static unsigned int
> @@ -17980,6 +17990,9 @@ #define TARGET_VECTORIZE_GET_MASK_MODE a
>  #undef TARGET_VECTORIZE_EMPTY_MASK_IS_EXPENSIVE
>  #define TARGET_VECTORIZE_EMPTY_MASK_IS_EXPENSIVE \
>    aarch64_empty_mask_is_expensive
> +#undef TARGET_PREFERRED_ELSE_VALUE
> +#define TARGET_PREFERRED_ELSE_VALUE \
> +  aarch64_preferred_else_value
>
>  #undef TARGET_INIT_LIBFUNCS
>  #define TARGET_INIT_LIBFUNCS aarch64_init_libfuncs
> Index: gcc/testsuite/gcc.dg/vect/vect-cond-arith-2.c
> ===================================================================
> --- /dev/null   2018-04-20 16:19:46.369131350 +0100
> +++ gcc/testsuite/gcc.dg/vect/vect-cond-arith-2.c       2018-05-24 10:33:30.872095099 +0100
> @@ -0,0 +1,45 @@
> +/* { dg-do compile } */
> +/* { dg-additional-options "-fgimple -fdump-tree-optimized -ffast-math" } */
> +
> +double __GIMPLE (startwith("loop"))
> +neg_xi (double *x)
> +{
> +  int i;
> +  long unsigned int index;
> +  long unsigned int offset;
> +  double * xi_ptr;
> +  double xi;
> +  double neg_xi;
> +  double res;
> +  unsigned int ivtmp;
> +
> + bb_1:
> +  goto bb_2;
> +
> + bb_2:
> +  res_1 = __PHI (bb_1: 0.0, bb_3: res_2);
> +  i_4 = __PHI (bb_1: 0, bb_3: i_5);
> +  ivtmp_6 = __PHI (bb_1: 100U, bb_3: ivtmp_7);
> +  index = (long unsigned int) i_4;
> +  offset = index * 8UL;
> +  xi_ptr = x_8(D) + offset;
> +  xi = *xi_ptr;
> +  neg_xi = -xi;
> +  res_2 = neg_xi + res_1;
> +  i_5 = i_4 + 1;
> +  ivtmp_7 = ivtmp_6 - 1U;
> +  if (ivtmp_7 != 0U)
> +    goto bb_3;
> +  else
> +    goto bb_4;
> +
> + bb_3:
> +  goto bb_2;
> +
> + bb_4:
> +  res_3 = __PHI (bb_2: res_2);
> +  return res_3;
> +}
> +
> +/* { dg-final { scan-tree-dump { = \.COND_ADD} "vect" { target { vect_double_cond_arith && vect_fully_masked } } } } */
> +/* { dg-final { scan-tree-dump { = \.COND_SUB} "optimized" { target { vect_double_cond_arith && vect_fully_masked } } } } */
> Index: gcc/testsuite/gcc.target/aarch64/sve/loop_add_6.c
> ===================================================================
> --- /dev/null   2018-04-20 16:19:46.369131350 +0100
> +++ gcc/testsuite/gcc.target/aarch64/sve/loop_add_6.c   2018-05-24 10:33:30.872095099 +0100
> @@ -0,0 +1,46 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -ftree-vectorize -fgimple -ffast-math" } */
> +
> +double __GIMPLE (startwith("loop"))
> +neg_xi (double *x)
> +{
> +  int i;
> +  long unsigned int index;
> +  long unsigned int offset;
> +  double * xi_ptr;
> +  double xi;
> +  double neg_xi;
> +  double res;
> +  unsigned int ivtmp;
> +
> + bb_1:
> +  goto bb_2;
> +
> + bb_2:
> +  res_1 = __PHI (bb_1: 0.0, bb_3: res_2);
> +  i_4 = __PHI (bb_1: 0, bb_3: i_5);
> +  ivtmp_6 = __PHI (bb_1: 100U, bb_3: ivtmp_7);
> +  index = (long unsigned int) i_4;
> +  offset = index * 8UL;
> +  xi_ptr = x_8(D) + offset;
> +  xi = *xi_ptr;
> +  neg_xi = -xi;
> +  res_2 = neg_xi + res_1;
> +  i_5 = i_4 + 1;
> +  ivtmp_7 = ivtmp_6 - 1U;
> +  if (ivtmp_7 != 0U)
> +    goto bb_3;
> +  else
> +    goto bb_4;
> +
> + bb_3:
> +  goto bb_2;
> +
> + bb_4:
> +  res_3 = __PHI (bb_2: res_2);
> +  return res_3;
> +}
> +
> +/* { dg-final { scan-assembler {\tfsub\tz[0-9]+\.d, p[0-7]/m} } } */
> +/* { dg-final { scan-assembler-not {\tsel\t} } } */
> +/* { dg-final { scan-assembler-not {\tmovprfx\t} } } */



More information about the Gcc-patches mailing list