> -----Original Message----- > From: Pengfei Li <[email protected]> > Sent: 07 July 2026 16:10 > To: [email protected] > Cc: [email protected]; [email protected]; > [email protected]; Tamar Christina <[email protected]>; Pengfei Li > <[email protected]> > Subject: [PATCH] match.pd: Eliminate dead operand in vector permute and > insert patterns > > When shifting a vector by one element and then inserting a new element > into the vacated lane, GIMPLE can contain a VEC_PERM_EXPR followed by a > BIT_INSERT_EXPR. For example, the testcase in this patch has: > > _1 = VEC_PERM_EXPR <v_2(D), { 0, 0, 0, 0 }, { 1, 2, 3, 4 }>; > shifted_4 = BIT_INSERT_EXPR <_1, val_3(D), 96 (32 bits)>; > > On AArch64, GCC currently generates: > > movi v31.4s, 0 > ext v0.16b, v0.16b, v31.16b, #4 > ins v0.s[3], w0 > > Here the zero vector is only used to fill the lane that is immediately > overwritten by the insertion. > > This patch adds a match.pd pattern to detect when all live lanes of the > VEC_PERM_EXPR result come from a single source operand, and replaces the > other operand in that case. This helps avoid materializing a zero filler > vector. After this patch, GCC will generate: > > ext v0.16b, v0.16b, v0.16b, #4 > ins v0.s[3], w0 > > Bootstrapped and tested on aarch64-linux-gnu and x86_64-linux-gnu. > > gcc/ChangeLog: > > * match.pd: Simplify vector permute and insert patterns. > > gcc/testsuite/ChangeLog: > > * gcc.dg/fold-vecperm-insert-1.c: New test. > --- > gcc/match.pd | 55 ++++++++++++++++++++ > gcc/testsuite/gcc.dg/fold-vecperm-insert-1.c | 19 +++++++ > 2 files changed, 74 insertions(+) > create mode 100644 gcc/testsuite/gcc.dg/fold-vecperm-insert-1.c > > diff --git a/gcc/match.pd b/gcc/match.pd > index a7cec25dbad..ca6b63a3395 100644 > --- a/gcc/match.pd > +++ b/gcc/match.pd > @@ -12275,6 +12275,61 @@ and, > (if (can_fold) > (view_convert (vec_perm @0 @1 @2))))) > > +/* Simplify > + v = VEC_PERM_EXPR <op0, op1, sel>; > + res = BIT_INSERT_EXPR <v, new_elt, bit_pos>; > + by replacing either op0 or op1 with the other one when > + 1) BIT_INSERT_EXPR inserts exactly one full lane of v. > + 2) All other lanes come from a single source, either op0 or op1. > + > + The vector operand that contributes only to the overwritten lane is dead. > + Replacing it avoids materializing a filler vector. */ > + > +(simplify > + (bit_insert (vec_perm @0 @1 VECTOR_CST@2) @3 INTEGER_CST@4) > + (with
I don't think the bit_insert should be part of the match. The operation should just be to simplify VEC_PERM_EXPR that use only 1 vector to that one vector. Since e.g. https://godbolt.org/z/GTb3vYT6d is also a missed optimization. > + { > + bool from0 = false; > + bool from1 = false; > + tree single_src = NULL_TREE; > + unsigned elt_size = vector_element_bits (type); > + > + unsigned HOST_WIDE_INT nelts; > + vec_perm_builder builder; > + unsigned ins_lane_idx; > + > + /* Require fixed-length vectors, different operands to VEC_PERM_EXPR > and > + BIT_INSERT_EXPR to insert exactly one full lane. */ > + if (TYPE_VECTOR_SUBPARTS (type).is_constant (&nelts) > + && @0 != @1 > + && tree_to_vec_perm_builder (&builder, @2) > + && tree_to_uhwi (TYPE_SIZE (TREE_TYPE (@3))) == elt_size > + && multiple_p (tree_to_uhwi (@4), elt_size, &ins_lane_idx)) > + { > + vec_perm_indices sel (builder, 2, nelts); > + for (unsigned i = 0; i < nelts; i++) > + { > + /* Skip the overwritten lane. */ > + if (i == ins_lane_idx) > + continue; > + > + /* Set FROM0 / FROM1 if current lane comes from @0 / @1. */ > + unsigned HOST_WIDE_INT elt = sel[i].to_constant (); > + if (elt < nelts) > + from0 = true; > + else > + from1 = true; > + } > + > + /* If only one of FROM0 and FROM1 is true, all live lanes come from > + a single source. */ > + if (from0 ^ from1) > + single_src = from0 ? @0 : @1; > + } > + } > + (if (single_src != NULL_TREE) > + (bit_insert (vec_perm { single_src; } { single_src; } @2) @3 @4)))) > + Before you do the rewrite you have to check with the target if it can do it. Some targets are deficient in not being able to do a permute with just 1 src vector but can with 2. I think you want to extend the existing patterns in (simplify (vec_perm @0 @1 VECTOR_CST@2) (with { tree op0 = @0, op1 = @1, op2 = @2; machine_mode result_mode = TYPE_MODE (type); machine_mode op_mode = TYPE_MODE (TREE_TYPE (op0)); here, which already tries to do similar optimizations and already has code to detect whether only one vector is needed. Thanks, Tamar > #if GIMPLE > /* Simplify (a >> 1) + (b >> 1) + ((a | b) & 1) to .AVG_CEIL (a, b). > Similar for (a | b) - ((a ^ b) >> 1). */ > diff --git a/gcc/testsuite/gcc.dg/fold-vecperm-insert-1.c > b/gcc/testsuite/gcc.dg/fold-vecperm-insert-1.c > new file mode 100644 > index 00000000000..121f9999d89 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/fold-vecperm-insert-1.c > @@ -0,0 +1,19 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-forwprop3" } */ > + > +typedef int __attribute__((vector_size(16))) v4si; > + > +/* Shift vector v by one element and insert the value val. The vector shift > + typically requires a zero vector operand in VEC_PERM_EXPR, but it can be > + optimized away in this case. */ > + > +v4si shift_and_insert (v4si v, int val) > +{ > + v4si zero = { 0, 0, 0, 0 }; > + v4si sel = { 1, 2, 3, 4 }; > + v4si shifted = __builtin_shuffle (v, zero, sel); > + shifted[3] = val; > + return shifted; > +} > + > +/* { dg-final { scan-tree-dump-times "VEC_PERM_EXPR.*v_\[0-9\]+.*v_\[0- > 9\]+" 1 "forwprop3" } } */ > -- > 2.43.0
