[gcc r17-2315] match.pd: Eliminate dead operand in vector permute and insert patterns

Pengfei Li via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
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" } } */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.