https://gcc.gnu.org/g:0ac16fdbee1509efe9cbdee89ae9fa7be9b87e78
commit r17-2315-g0ac16fdbee1509efe9cbdee89ae9fa7be9b87e78 Author: Pengfei Li <[email protected]> Date: Tue Jul 7 15:09:53 2026 +0000 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 if the updated VEC_PERM_EXPR is still supported by the target. This avoids 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. Diff: --- gcc/match.pd | 63 ++++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/fold-vecperm-insert-1.c | 19 +++++++++ 2 files changed, 82 insertions(+) diff --git a/gcc/match.pd b/gcc/match.pd index 96156fe564f4..891f15fb69b7 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -12389,6 +12389,69 @@ 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:s @0 @1 VECTOR_CST@2) @3 INTEGER_CST@4) + (with + { + bool from0 = false; + bool from1 = false; + tree single_op = NULL_TREE; + unsigned elt_size = vector_element_bits (type); + + unsigned HOST_WIDE_INT nelts; + unsigned ins_lane_idx; + vec_perm_indices sel; + + /* 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_indices (&sel, @0, @1, @2) + && tree_to_uhwi (TYPE_SIZE (TREE_TYPE (@3))) == elt_size + && multiple_p (tree_to_uhwi (@4), elt_size, &ins_lane_idx)) + { + 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_op = from0 ? @0 : @1; + vec_perm_indices new_sel; + if (!tree_to_vec_perm_indices (&new_sel, single_op, single_op, @2) + || !can_vec_perm_const_p (TYPE_MODE (type), + TYPE_MODE (TREE_TYPE (single_op)), + new_sel, + false)) + single_op = NULL_TREE; + } + } + } + (if (single_op != NULL_TREE) + (bit_insert (vec_perm { single_op; } { single_op; } @2) @3 @4)))) + #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 000000000000..b1a9772dc2c3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/fold-vecperm-insert-1.c @@ -0,0 +1,19 @@ +/* { dg-do compile { target aarch64*-*-* arm*-*-* x86_64-*-* } } */ +/* { 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" } } */
