On Mon, 22 Jun 2026, Richard Sandiford wrote:

> Sorry for the slow reply.
> 
> Richard Biener <[email protected]> writes:
> > The following makes it possible to have can_vec_perm_const_p guards
> > that can tell us whether vector lowering would have lowered the
> > permute if false.  For this purpose we have to enhance the API
> > to allow anticipation of the vec_shl/vec_shr expansion path.
> > So this adds two boolean flags indicating whether the permuted
> > vectors are known zero and move the vector lowering detection
> > code to can_vec_perm_const_p which makes both consistent.
> >
> > For now I have adjusted only vector lowering and match.pd.  ISTR
> > the vectorizer explicitly has some vec_shl/shr matching that can
> > be cleaned up.  Up for discussion might be whether to pass
> > down the actual permuted vectors rather than flags, the latter
> > looked enough and also suitable for use from RTL.
> >
> > Bootstrapped and tested on x86_64-unknown-linux-gnu.
> >
> > OK?
> 
> It looks ok to me.  But I wonder whether we should instead store this
> information in vec_perm_indices (like we already do for single input vs
> dual input).  We could then have one member function that takes the tree
> inputs and (potentially, in future) another that takes rtx inputs.
> It would then be easy to add more information without updating all users.

Heh, I have a pending patch that does that which I added to make
the errors with regard to passing it the correct 'nelts'.  So
I'll build upon that and see if this works out for the issue at
hand.

Richard.

> For example, I can imagine that "is the input uniform?" would also be
> useful in future.  And passing four booleans to can_vec_perm_const_p
> might be awkward.
> 
> If vec_perm_indices knew that an input was uniform, it could use that
> information in queries like series_p.  For example, the shift right
> detection would reduce to the existing:
> 
>    sel.series_p (0, 1, sel[0], 1)
> 
> with series_p handling the "Indices into the second vector are all
> equivalent" rule.
> 
> That's just a suggestion though.
> 
> Thanks,
> Richard
> 
> >
> > For branches I'll probably add a after-vector-lowering guard
> > to the patterns instead.
> >
> > Thanks,
> > Richard.
> >
> >     PR middle-end/125875
> >     * optabs-query.h (can_vec_perm_const_p): Add defaulted
> >     params to indicate the permuted vectors are bitwise zero.
> >     * optabs-query.cc (can_vec_perm_const_p): Handle permute
> >     patterns mapping to vec_shl/vec_shr.
> >     * tree-vect-generic.cc (lower_vec_perm): Move vec_shl/vec_shr
> >     detection to can_vec_perm_const_p and simplify.
> >     * match.pd: When checking for !can_vec_perm_const_p also
> >     allow variable permutes and pass in whether the permuted
> >     vectors are known constant zero.
> >
> >     * gcc.dg/torture/pr125875.c: New testcase.
> > ---
> >  gcc/match.pd                            | 36 +++++++++++----
> >  gcc/optabs-query.cc                     | 55 ++++++++++++++++++++---
> >  gcc/optabs-query.h                      |  3 +-
> >  gcc/testsuite/gcc.dg/torture/pr125875.c | 29 ++++++++++++
> >  gcc/tree-vect-generic.cc                | 59 ++-----------------------
> >  5 files changed, 111 insertions(+), 71 deletions(-)
> >  create mode 100644 gcc/testsuite/gcc.dg/torture/pr125875.c
> >
> > diff --git a/gcc/match.pd b/gcc/match.pd
> > index 8c410c2f3b3..7fecb4466bb 100644
> > --- a/gcc/match.pd
> > +++ b/gcc/match.pd
> > @@ -11720,6 +11720,9 @@ and,
> >  
> >         vec_perm_indices sel2 (builder2, 2, nelts);
> >  
> > +       bool op2_zerop = initializer_zerop (@2);
> > +       bool op3_zerop = initializer_zerop (@3);
> > +       bool op0_zerop = initializer_zerop (@0);
> >         tree op0 = NULL_TREE;
> >         /* If the new VEC_PERM_EXPR can't be handled but both
> >       original VEC_PERM_EXPRs can, punt.
> > @@ -11728,9 +11731,12 @@ and,
> >       number of VEC_PERM_EXPRs that can't be handled.  */
> >         if (can_vec_perm_const_p (result_mode, op_mode, sel2, false)
> >        || (single_use (@0)
> > -          ? (!can_vec_perm_const_p (result_mode, op_mode, sel0, false)
> > -             || !can_vec_perm_const_p (result_mode, op_mode, sel1, false))
> > -          : !can_vec_perm_const_p (result_mode, op_mode, sel1, false)))
> > +          ? (!can_vec_perm_const_p (result_mode, op_mode, sel0, true,
> > +                                    op2_zerop, op3_zerop)
> > +             || !can_vec_perm_const_p (result_mode, op_mode, sel1, true,
> > +                                       false, op0_zerop))
> > +          : !can_vec_perm_const_p (result_mode, op_mode, sel1, true,
> > +                                   false, op0_zerop)))
> >      op0 = vec_perm_indices_to_tree (TREE_TYPE (@5), sel2);
> >       }
> >       (if (op0)
> > @@ -11788,6 +11794,9 @@ and,
> >         {
> >      vec_perm_indices sel2 (builder2, 2, nelts);
> >      tree op0 = NULL_TREE;
> > +    bool op1_zerop = initializer_zerop (@1);
> > +    bool op2_zerop = initializer_zerop (@2);
> > +    bool op5_zerop = initializer_zerop (@5);
> >      /* If the new VEC_PERM_EXPR can't be handled but both
> >         original VEC_PERM_EXPRs can, punt.
> >         If one or both of the original VEC_PERM_EXPRs can't be
> > @@ -11795,9 +11804,12 @@ and,
> >         number of VEC_PERM_EXPRs that can't be handled.  */
> >      if (can_vec_perm_const_p (result_mode, op_mode, sel2, false)
> >          || (single_use (@0)
> > -            ? (!can_vec_perm_const_p (result_mode, op_mode, sel0, false)
> > -               || !can_vec_perm_const_p (result_mode, op_mode, sel1, 
> > false))
> > -            : !can_vec_perm_const_p (result_mode, op_mode, sel1, false)))
> > +            ? (!can_vec_perm_const_p (result_mode, op_mode, sel0, true,
> > +                                      op1_zerop, op2_zerop)
> > +               || !can_vec_perm_const_p (result_mode, op_mode, sel1, true,
> > +                                         op5_zerop, false))
> > +            : !can_vec_perm_const_p (result_mode, op_mode, sel1, true,
> > +                                     op5_zerop, false)))
> >        op0 = vec_perm_indices_to_tree (TREE_TYPE (@4), sel2);
> >         }
> >         (if (op0)
> > @@ -11852,6 +11864,9 @@ and,
> >         {
> >      vec_perm_indices sel2 (builder2, 2, nelts);
> >      tree op0 = NULL_TREE;
> > +    bool op1_zerop = initializer_zerop (@1);
> > +    bool op2_zerop = initializer_zerop (@2);
> > +    bool op5_zerop = initializer_zerop (@5);
> >      /* If the new VEC_PERM_EXPR can't be handled but both
> >         original VEC_PERM_EXPRs can, punt.
> >         If one or both of the original VEC_PERM_EXPRs can't be
> > @@ -11859,9 +11874,12 @@ and,
> >         number of VEC_PERM_EXPRs that can't be handled.  */
> >      if (can_vec_perm_const_p (result_mode, op_mode, sel2, false)
> >          || (single_use (@0)
> > -            ? (!can_vec_perm_const_p (result_mode, op_mode, sel0, false)
> > -               || !can_vec_perm_const_p (result_mode, op_mode, sel1, 
> > false))
> > -            : !can_vec_perm_const_p (result_mode, op_mode, sel1, false)))
> > +            ? (!can_vec_perm_const_p (result_mode, op_mode, sel0, true,
> > +                                      op1_zerop, op2_zerop)
> > +               || !can_vec_perm_const_p (result_mode, op_mode, sel1, true,
> > +                                         false, op5_zerop))
> > +            : !can_vec_perm_const_p (result_mode, op_mode, sel1, true,
> > +                                     false, op5_zerop)))
> >        op0 = vec_perm_indices_to_tree (TREE_TYPE (@4), sel2);
> >         }
> >         (if (op0)
> > diff --git a/gcc/optabs-query.cc b/gcc/optabs-query.cc
> > index 58842e40ed6..2e74acb0664 100644
> > --- a/gcc/optabs-query.cc
> > +++ b/gcc/optabs-query.cc
> > @@ -417,14 +417,13 @@ can_vec_perm_var_p (machine_mode mode)
> >     ALLOW_VARIABLE_P is true if it is acceptable to force the selector into 
> > a
> >     register and use a variable permute (if the target supports that).
> >  
> > -   Note that additional permutations representing whole-vector shifts may
> > -   also be handled via the vec_shr or vec_shl optab, but only where the
> > -   second input vector is entirely constant zeroes; this case is not dealt
> > -   with here.  */
> > +   If OP0_ZEROP or OP1_ZEROP additional permutations representing 
> > whole-vector
> > +   shifts may also be handled via the vec_shr or vec_shl optab.  */
> >  
> >  bool
> >  can_vec_perm_const_p (machine_mode mode, machine_mode op_mode,
> > -                 const vec_perm_indices &sel, bool allow_variable_p)
> > +                 const vec_perm_indices &sel, bool allow_variable_p,
> > +                 bool op0_zerop, bool op1_zerop)
> >  {
> >    /* If the target doesn't implement a vector mode for the vector type,
> >       then no operations are supported.  */
> > @@ -465,6 +464,52 @@ can_vec_perm_const_p (machine_mode mode, machine_mode 
> > op_mode,
> >      into integer operations.  */
> >      }
> >  
> > +  unsigned elements;
> > +  if (mode == op_mode
> > +      && GET_MODE_NUNITS (mode).is_constant (&elements))
> > +    {
> > +      if (op0_zerop
> > +     && can_implement_p (vec_shl_optab, mode))
> > +   {
> > +     unsigned int first = 0, i;
> > +     for (i = 0; i < elements; ++i)
> > +       if (known_eq (poly_uint64 (sel[i]), elements))
> > +         {
> > +           if (i == 0 || first)
> > +             break;
> > +           first = i;
> > +         }
> > +       else if (first
> > +                ? maybe_ne (poly_uint64 (sel[i]),
> > +                            elements + i - first)
> > +                : maybe_ge (poly_uint64 (sel[i]), elements))
> > +         break;
> > +     if (first && i == elements)
> > +       return true;
> > +   }
> > +      if (op1_zerop
> > +     && maybe_ne (sel[0], 0)
> > +     && known_lt (sel[0], elements)
> > +     && can_implement_p (vec_shr_optab, mode))
> > +   {
> > +     if (sel.series_p (0, 1, sel[0], 1))
> > +       return true;
> > +     unsigned i;
> > +     for (i = 1; i < elements; ++i)
> > +       {
> > +         poly_uint64 actual = sel[i];
> > +         poly_uint64 expected = i + sel[0];
> > +         /* Indices into the second vector are all equivalent.  */
> > +         if (maybe_lt (actual, elements)
> > +             ? maybe_ne (actual, expected)
> > +             : maybe_lt (expected, elements))
> > +           break;
> > +       }
> > +     if (i == elements)
> > +       return true;
> > +   }
> > +    }
> > +
> >    return false;
> >  }
> >  
> > diff --git a/gcc/optabs-query.h b/gcc/optabs-query.h
> > index faa688a0609..4a7eb1036f2 100644
> > --- a/gcc/optabs-query.h
> > +++ b/gcc/optabs-query.h
> > @@ -156,7 +156,8 @@ opt_machine_mode qimode_for_vec_perm (machine_mode);
> >  bool selector_fits_mode_p (machine_mode, const vec_perm_indices &);
> >  bool can_vec_perm_var_p (machine_mode);
> >  bool can_vec_perm_const_p (machine_mode, machine_mode,
> > -                      const vec_perm_indices &, bool = true);
> > +                      const vec_perm_indices &, bool = true,
> > +                      bool op0_zerop = false, bool op1_zerop = false);
> >  /* Find a widening optab even if it doesn't widen as much as we want.  */
> >  #define find_widening_optab_handler(A, B, C) \
> >    find_widening_optab_handler_and_mode (A, B, C, NULL)
> > diff --git a/gcc/testsuite/gcc.dg/torture/pr125875.c 
> > b/gcc/testsuite/gcc.dg/torture/pr125875.c
> > new file mode 100644
> > index 00000000000..0cccb190c73
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/torture/pr125875.c
> > @@ -0,0 +1,29 @@
> > +/* { dg-do compile } */
> > +
> > +#include <stdint.h>
> > +typedef int16_t v4i16 __attribute__((vector_size(8)));
> > +v4i16 g7, g19;
> > +int64_t g16;
> > +_Bool f16_a0_c3, f16_a0_ob6;
> > +void f16_a0()
> > +{
> > +    int32_t ov10;
> > +    uint64_t __ov_tmp_g16 = __builtin_sub_overflow(0, 0, &__ov_tmp_g16);
> > +    g16 = __ov_tmp_g16;
> > +lbl_b1:
> > +    __builtin_mul_overflow(g16, g16, &ov10);
> > +    switch (ov10)
> > +    case 33:
> > +        goto lbl_sw_def57;
> > +lbl_sw_def15:
> > +    if (f16_a0_c3) goto lbl_b1;
> > +    g7 = __builtin_shufflevector(g7, g7, 4, 3, 4, 5);
> > +    f16_a0_ob6 = f16_a0;
> > +    g19 = g19 <= g19;
> > +lbl_bf43:
> > +    g19 = ~g19;
> > +    g7 = __builtin_shufflevector(g7, g19, 6, 7, 0, 1);
> > +lbl_sw_def57:
> > +    if (f16_a0_ob6) goto lbl_sw_def15;
> > +    goto lbl_bf43;
> > +}
> > diff --git a/gcc/tree-vect-generic.cc b/gcc/tree-vect-generic.cc
> > index fddb44bfe86..e43b749e8cc 100644
> > --- a/gcc/tree-vect-generic.cc
> > +++ b/gcc/tree-vect-generic.cc
> > @@ -1649,67 +1649,14 @@ lower_vec_perm (gimple_stmt_iterator *gsi)
> >        machine_mode vmode = TYPE_MODE (vect_type);
> >        tree lhs_type = TREE_TYPE (gimple_assign_lhs (stmt));
> >        machine_mode lhs_mode = TYPE_MODE (lhs_type);
> > -      if (can_vec_perm_const_p (lhs_mode, vmode, indices))
> > +      if (can_vec_perm_const_p (lhs_mode, vmode, indices, true,
> > +                           initializer_zerop (vec0),
> > +                           initializer_zerop (vec1)))
> >     {
> >       gimple_assign_set_rhs3 (stmt, mask);
> >       update_stmt (stmt);
> >       return;
> >     }
> > -      /* Also detect vec_shr pattern - VEC_PERM_EXPR with zero
> > -    vector as VEC1 and a right element shift MASK.  */
> > -      if (can_implement_p (vec_shr_optab, TYPE_MODE (vect_type))
> > -     && TREE_CODE (vec1) == VECTOR_CST
> > -     && initializer_zerop (vec1)
> > -     && maybe_ne (indices[0], 0)
> > -     && known_lt (poly_uint64 (indices[0]), elements))
> > -   {
> > -     bool ok_p = indices.series_p (0, 1, indices[0], 1);
> > -     if (!ok_p)
> > -       {
> > -         for (i = 1; i < elements; ++i)
> > -           {
> > -             poly_uint64 actual = indices[i];
> > -             poly_uint64 expected = i + indices[0];
> > -             /* Indices into the second vector are all equivalent.  */
> > -             if (maybe_lt (actual, elements)
> > -                 ? maybe_ne (actual, expected)
> > -                 : maybe_lt (expected, elements))
> > -               break;
> > -           }
> > -         ok_p = i == elements;
> > -       }
> > -     if (ok_p)
> > -       {
> > -         gimple_assign_set_rhs3 (stmt, mask);
> > -         update_stmt (stmt);
> > -         return;
> > -       }
> > -   }
> > -      /* And similarly vec_shl pattern.  */
> > -      if (can_implement_p (vec_shl_optab, TYPE_MODE (vect_type))
> > -     && TREE_CODE (vec0) == VECTOR_CST
> > -     && initializer_zerop (vec0))
> > -   {
> > -     unsigned int first = 0;
> > -     for (i = 0; i < elements; ++i)
> > -       if (known_eq (poly_uint64 (indices[i]), elements))
> > -         {
> > -           if (i == 0 || first)
> > -             break;
> > -           first = i;
> > -         }
> > -       else if (first
> > -                ? maybe_ne (poly_uint64 (indices[i]),
> > -                                         elements + i - first)
> > -                : maybe_ge (poly_uint64 (indices[i]), elements))
> > -         break;
> > -     if (first && i == elements)
> > -       {
> > -         gimple_assign_set_rhs3 (stmt, mask);
> > -         update_stmt (stmt);
> > -         return;
> > -       }
> > -   }
> >      }
> >    else if (can_vec_perm_var_p (TYPE_MODE (vect_type)))
> >      return;
> 

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

Reply via email to