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
> >>
>