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