Thanks for review comments.
Changes in v2:
- Use the new tree_to_vec_perm_indices to get vec_perm_indices.
- Check can_vec_perm_const_p for the updated VEC_PERM_EXPR.
- Rename single_src to single_op.
- Restrict the test case to a few targets.
Ok for trunk?
Thanks,
Pengfei
-- >8 --
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.
---
gcc/match.pd | 63 ++++++++++++++++++++
gcc/testsuite/gcc.dg/fold-vecperm-insert-1.c | 19 ++++++
2 files changed, 82 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..78c94b6ba2b 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -12275,6 +12275,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 @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 00000000000..b1a9772dc2c
--- /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" } } */
--
2.43.0