On Fri, Nov 13, 2026 at 3:40 PM Richard Earnshaw
<[email protected]> wrote:
>
> On 13/11/2026 13:32, Richard Biener wrote:
> > On Fri, Nov 13, 2026 at 12:45 PM Richard Earnshaw via Sourceware Forge
> > <[email protected]> wrote:
> >>
> >> From: Richard Earnshaw <[email protected]>
> >>
> >> If a vec_perm selects the same element from the source for every
> >> element in the destination, eg with
> >>   __builtin_shuffle (v1, v2, { N, N,... N });
> >> then this can be simplified to
> >>   vec_duplicate (v[N'])
> >> where v and N' are picked from the first vector or the second depending
> >> on the value of N.
> >
> > ISTR we sofar refrained from using VEC_DUPLICATE for non-VLA vectors
> > for reasons that escape me.  Is this to have vec_same_elem_p handle
> > the permute case?  Can we instead handle a canonical vec_permute
> > form there (and in uniform_vector_p)?
> >
>
> The original motivation for this was a test I was running on Arm (aarch32).  
> For
>
> #include <arm_neon.h>
> #include <stdint.h>
>
> int16x8_t be_dupq_lane (int16x8_t v)
> {
>   return (int16x8_t) __builtin_shuffle (v, v,
>                                         (uint16x8_t) { 0, 0, 0, 0,
>                                                        0, 0, 0, 0 });
> }
>
> The compiler was producing better code when compiling for big-endian
>
> be_dupq_lane:
>         vdup.16 q0, d1[3]
>         bx      lr
>
> compared to the code for little-endian:
>
> be_dupq_lane:
>         vmov.i16        q8, #256  @ v16qi
>         vmov    q9, q0  @ v16qi
>         vtbl.8  d0, {d0, d1}, d16
>         vtbl.8  d1, {d18, d19}, d17
>         bx      lr
>
> Investigation showed that this was because we have a vec_perm pattern for 
> little-endian, but not for big-endian.  As a result, the gimple generated 
> becomes
>
> int16x8_t be_dupq_lane (int16x8_t v)
> {
>   int16x8_t _2;
>
>   <bb 2> [local count: 1073741824]:
>   _2 = VEC_PERM_EXPR <v_1(D), v_1(D), { 0, 0, 0, 0, 0, 0, 0, 0 }>;
>   return _2;
> }
>
>
> while on big-endian it's just
>
> int16x8_t be_dupq_lane (int16x8_t v)
> {
>   int16x8_t _2;
>   short int _4;
>
>   <bb 2> [local count: 1073741824]:
>   _4 = BIT_FIELD_REF <v_1(D), 16, 0>;
>   _2 = {_4, _4, _4, _4, _4, _4, _4, _4};
>   return _2;
> }
>
> and at this point I was then faced with the question: do I try to recognize 
> this idiom directly in the expansion code in the back end, or do I try to 
> find a generic way of expressing this that can be done in the mid-end?  I 
> thought the latter would be the more sensible approach in that it would then 
> potentially apply to all targets saving the need to recognize specific idioms 
> in every vec_perm implementation.

I see.  Still this will be a target specific thing and a extract +
duplicate vs. permute RTL expansion
costing compare would be the usual thing to do.  On the GIMPLE side the permute
is more canonical (it's a single operation).  I do not remember why we needed
VEC_DUPLICATE - IIRC we discussed allowing VEC_PERM_EXPR with
non-vector sources.  What we do allow is a VIEW_CONVERT of a scalar to
a vector(1) and then a VEC_PERM_EXPR of that representing a duplicate.
But there
might have been VLA vector issues that prevented this.

I do think that having a single representation is better because it will in the
end simplify RTL expansion and target description.

Richard.

>
>
> R.
>
> >> gcc/ChangeLog:
> >>
> >>         * match.pd: Simplify vec_perm to vec_duplicate when all outputs
> >>         come from the same input lane.
> >> ---
> >>  gcc/match.pd | 24 +++++++++++++++++++++++-
> >>  1 file changed, 23 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/gcc/match.pd b/gcc/match.pd
> >> index 3912f9f7d5438..52ebf642677e8 100644
> >> --- a/gcc/match.pd
> >> +++ b/gcc/match.pd
> >> @@ -12910,6 +12910,28 @@ and,
> >>       (view_convert @0)
> >>       (if (known_eq (nelts, nelts_in) && sel.series_p (0, 1, nelts_in, 1))
> >>        (view_convert @1)
> >> +      (if (canonicalize_math_after_vectorization_p ()
> >> +          && known_eq (nelts, nelts_in) && sel.series_p (0, 1, sel[0], 0)
> >> +          && (known_lt ((poly_uint64) sel[0], nelts)
> >> +              || known_ge ((poly_uint64) sel[0], nelts)))
> >> +       (with
> >> +       {
> >> +         poly_uint64 index = sel[0];
> >> +         tree vec = op0;
> >> +         poly_uint64 lane = index;
> >> +         if (known_ge (index, nelts))
> >> +           {
> >> +             vec = op1;
> >> +             lane -= nelts;
> >> +           }
> >> +         tree elem_type = TREE_TYPE (TREE_TYPE (vec));
> >> +         unsigned HOST_WIDE_INT bits = tree_to_uhwi (TYPE_SIZE 
> >> (elem_type));
> >> +       }
> >> +       (
> >> +         vec_duplicate
> >> +          (BIT_FIELD_REF:elem_type { vec; }
> >> +                                   { bitsize_int (bits); }
> >> +                                   { bitsize_int (lane * bits); })))
> >>        (with
> >>         {
> >>           if (!single_arg)
> >> @@ -13022,7 +13044,7 @@ and,
> >>           (bit_insert { op0; } { ins; }
> >>            { bitsize_int (at * vector_element_bits (type)); })
> >>           (if (changed)
> >> -          (vec_perm { op0; } { op1; } { op2; }))))))))))))
> >> +          (vec_perm { op0; } { op1; } { op2; })))))))))))))
> >>
> >>  /* VEC_PERM_EXPR (v, v, mask) -> v where v contains same element.  */
> >>
> >> --
> >> 2.56.0
> >>
>

Reply via email to