To handle vector permutations, extracts, inserts and constructors in SLP we
need to map each result element to an element of a source operand. The
operands can have different lane counts, so they cannot all be described using
the result width.
e.g.
typedef unsigned V8 __attribute__((vector_size(32)));
void insert_lane (V8 *out, const V8 *a, unsigned value)
{
V8 x = *a;
x[5] = value;
*out = x;
}
slp_oprnds records the lane count of each operand and decodes constant
permutations once per statement. SLP builds VEC_PERM_EXPR nodes from
those mappings. For the BIT_INSERT_EXPR at bit 160 above, the map is:
lane permutation { 0[0] 0[1] 0[2] 0[3] 0[4] 1[0] 0[6] 0[7] }
With -O3 -fno-vect-cost-model -march=armv8-a we generated before:
ldp x4, x5, [x1]
ldp x1, x3, [x1, 16]
stp x4, x5, [x0]
bfi x1, x2, 32, 32
stp x1, x3, [x0, 16]
ret
After these patches we get:
ldp q30, q31, [x1]
ins v31.s[1], w2
stp q30, q31, [x0]
ret
Matching and failure handling use the child lane positions. This also lets e.g.
a 4 lane extract discover the eight-lane source without changing the
source's 32-byte memory stride. Constructor roots use the same operand mapping
when building the SLP tree so are handled similarly.
If SLP declines an insert into an emulated vector, lower_vec_insert provides
the generic lowering needed to expand the remaining operation.
Bootstrapped Regtested on aarch64-none-linux-gnu,
arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
-m32, -m64 and no issues.
Any feedback?
Thanks,
Tamar
Assisted-by: Codex GPT 6.1 Sol
AI was used to generate test cases for various types and inputs.
gcc/ChangeLog:
PR tree-optimization/119187
* tree-vect-slp.cc (vect_get_operand_map, slp_oprnds,
vect_get_and_check_slp_defs): Map vector operands and their lane counts.
(slp_oprnds::get_op_nunits, slp_oprnds::get_permutation): New.
(vect_build_slp_tree_3, vect_build_slp_tree_2): Build lane-routing
operations as ordinary SLP permutation nodes.
(vect_slp_check_for_roots): Recognize vector-valued constructors.
* tree-vect-generic.cc (lower_vec_insert): New.
(expand_vector_operations_1): Lower inserts into emulated vectors.
* tree-vect-slp.cc (slp_oprnds::get_op_for_slp_child): Share constructor
operand mapping.
(vect_slp_is_lane_insert): Use the common constructor operand count.
gcc/testsuite/ChangeLog:
PR tree-optimization/119187
* gcc.dg/vect/vect-revectorize-permute.c: New test.
* g++.target/aarch64/vect-revectorize-permute.C: New test.
* gcc.dg/vect/vect-revectorize-constructor.c: New test.
* gcc.dg/vect/vect-revectorize-subvectors.c: New test.
* gcc.dg/vect/vect-revectorize-insert-lower.c: New test.
* gcc.dg/vect/vect-revectorize-memory-lanes.c: New test.
* gcc.target/aarch64/vect-revectorize-memory-lanes.c: New test.
* gcc.target/aarch64/vect-revectorize-invariant-cost.c: New test.
* gcc.target/aarch64/sve/vect-revectorize-perm-const-2048-run.c: New
XFAIL test.
* gcc.dg/vect/vect-revectorize-constructor-loop.c: New test.
* gcc.dg/vect/vect-revectorize-memory-lanes-2.c: New test.
* gcc.dg/vect/vect-revectorize-memory-lanes-3.c: New test.
* gcc.dg/vect/vect-revectorize-memory-lanes.h: New test support.
* gcc.target/aarch64/vect-revectorize-memory-lanes-2.c: New test.
* gcc.target/aarch64/vect-revectorize-memory-lanes-3.c: New test.
---
--
diff --git a/gcc/testsuite/g++.target/aarch64/vect-revectorize-permute.C
b/gcc/testsuite/g++.target/aarch64/vect-revectorize-permute.C
new file mode 100644
index
0000000000000000000000000000000000000000..69de9244c9a93bbc56c37975b5256a75462713d6
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/vect-revectorize-permute.C
@@ -0,0 +1,33 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-options "-O3 -march=armv8-a -fdump-tree-slp-details -save-temps" } */
+
+#include "../../gcc.dg/vect/tree-vect.h"
+
+typedef unsigned V __attribute__ ((vector_size (32)));
+
+__attribute__ ((noipa)) void
+shuffle (V &x)
+{
+ x ^= 1;
+ x = (V) { x[4], x[0], x[5], x[1], x[6], x[2], x[7], x[3] };
+}
+
+__attribute__ ((optimize ("no-tree-vectorize")))
+int
+main ()
+{
+ check_vect ();
+ V x = { 0, 101, 202, 303, 404, 505, 606, 707 };
+ const V expected = { 405, 1, 504, 100, 607, 203, 706, 302 };
+ shuffle (x);
+ if (__builtin_memcmp (&x, &expected, sizeof (x)))
+ __builtin_abort ();
+ return 0;
+}
+
+/* Retain the cost model: the original unsupported shuffle is lowered
+ elementwise, whereas SLP needs two native permutations. */
+/* { dg-final { scan-tree-dump "basic block part vectorized using 16 byte
vectors" "slp2" } } */
+/* { dg-final { scan-assembler {zip1\tv[0-9]+\.4s} } } */
+/* { dg-final { scan-assembler {zip2\tv[0-9]+\.4s} } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-constructor-loop.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-constructor-loop.c
new file mode 100644
index
0000000000000000000000000000000000000000..9c86696831e33c99a451c599be4d996f531e00ea
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-constructor-loop.c
@@ -0,0 +1,75 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fgimple -fno-vect-cost-model -Wno-psabi" } */
+/* { dg-additional-options "-fdump-tree-slp-details -fdump-tree-vect-details"
} */
+
+#include "tree-vect.h"
+
+typedef unsigned V4 __attribute__ ((vector_size (16)));
+typedef unsigned V8 __attribute__ ((vector_size (32)));
+
+__attribute__ ((always_inline)) static inline V8 __GIMPLE ()
+concat (V4 a, V4 b)
+{
+ V8 result;
+ result = _Literal (V8) { a, b };
+ return result;
+}
+
+/* Nested constructors, including an invariant operand. */
+__attribute__ ((noipa)) void
+nested (V8 *restrict out, const V4 *restrict in, V4 bias, unsigned n)
+{
+ for (unsigned i = 0; i < n; ++i)
+ {
+ V4 a = in[2 * i] + (V4) { 1, 3, 5, 7 };
+ V4 b = in[2 * i + 1] ^ (V4) { 9, 11, 13, 15 };
+ out[2 * i] = concat (a, b) + 1;
+ out[2 * i + 1] = concat (b, bias) ^ 17;
+ }
+}
+
+__attribute__ ((noipa)) void
+scalars (V4 *restrict out, const unsigned *restrict in, unsigned n)
+{
+ for (unsigned i = 0; i < n; ++i)
+ out[i] = (V4) { in[4 * i] + 1, in[4 * i + 1] + 3,
+ in[4 * i + 2] + 5, in[4 * i + 3] + 7 } ^ 17;
+}
+
+__attribute__ ((optimize ("no-tree-vectorize"))) int
+main (void)
+{
+ check_vect ();
+ V4 in[34], scalar_out[34], expected_scalars[17];
+ V8 out[34], expected_nested[34];
+ const V4 bias = { 31, 37, 41, 43 };
+ for (unsigned i = 0; i < 34; ++i)
+ in[i] = (V4) { 0, 7, 14, 21 } + 101 * i;
+
+ /* Scalar references keep the two halves and the invariant lanes separate.
*/
+ for (unsigned k = 0; k < 17 * 4; ++k)
+ {
+ unsigned i = k / 4, j = k % 4;
+ expected_nested[2 * i][j] = 202 * i + 9 * j + 2;
+ expected_nested[2 * i][j + 4]
+ = ((202 * i + 101 + 7 * j) ^ (9 + 2 * j)) + 1;
+ expected_nested[2 * i + 1][j]
+ = (202 * i + 101 + 7 * j) ^ (9 + 2 * j) ^ 17;
+ expected_nested[2 * i + 1][j + 4] = bias[j] ^ 17;
+ expected_scalars[i][j] = (101 * i + 9 * j + 1) ^ 17;
+ }
+
+ for (unsigned n = 0; n <= 17; ++n)
+ {
+ nested (out, in, bias, n);
+ scalars (scalar_out, (const unsigned *) in, n);
+ if (__builtin_memcmp (out, expected_nested, 2 * n * sizeof (V8))
+ || __builtin_memcmp (scalar_out, expected_scalars, n * sizeof (V4)))
+ __builtin_abort ();
+ }
+
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 2 "vect"
{ target aarch64*-*-* } } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-constructor.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-constructor.c
new file mode 100644
index
0000000000000000000000000000000000000000..37071c6d350fe35d9f9a8433753e504c67a90a80
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-constructor.c
@@ -0,0 +1,110 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fgimple -fno-vect-cost-model -Wno-psabi" } */
+/* { dg-additional-options "-fdump-tree-slp-details -fdump-tree-vect-details"
} */
+
+#include "tree-vect.h"
+
+typedef unsigned V2 __attribute__ ((vector_size (8)));
+typedef unsigned V4 __attribute__ ((vector_size (16)));
+typedef unsigned V8 __attribute__ ((vector_size (32)));
+
+/* Start at SLP to test constructor roots without intervening lowering. */
+__attribute__ ((noipa)) V4 __GIMPLE (ssa, startwith ("slp"))
+root (const V2 *p)
+{
+ V2 a;
+ V2 b;
+ V2 low;
+ V2 high;
+ V4 result;
+
+ __BB(2):
+ a_2 = __MEM <V2> (p_1(D));
+ b_3 = __MEM <V2> (p_1(D) + _Literal (const V2 *) 8);
+ low_4 = a_2 + _Literal (V2) { 1u, 3u };
+ high_5 = b_3 + _Literal (V2) { 5u, 7u };
+ result_6 = _Literal (V4) { low_4, high_5 };
+ return result_6;
+}
+
+__attribute__ ((noipa)) V8 __GIMPLE (ssa, startwith ("slp"))
+wide_root (const V2 *p)
+{
+ V2 a;
+ V2 b;
+ V2 c;
+ V2 d;
+ V2 low;
+ V2 high;
+ V2 low2;
+ V2 high2;
+ V8 result;
+
+ __BB(2):
+ a_2 = __MEM <V2> (p_1(D));
+ b_3 = __MEM <V2> (p_1(D) + _Literal (const V2 *) 8);
+ c_4 = __MEM <V2> (p_1(D) + _Literal (const V2 *) 16);
+ d_5 = __MEM <V2> (p_1(D) + _Literal (const V2 *) 24);
+ low_6 = a_2 + _Literal (V2) { 1u, 3u };
+ high_7 = b_3 + _Literal (V2) { 5u, 7u };
+ low2_8 = c_4 + _Literal (V2) { 9u, 11u };
+ high2_9 = d_5 + _Literal (V2) { 13u, 15u };
+ result_10 = _Literal (V8) { low_6, high_7, low2_8, high2_9 };
+ return result_10;
+}
+
+/* Keep the ordinary lowering for constructors with implicit zero lanes. */
+__attribute__ ((noipa)) V4 __GIMPLE (ssa, startwith ("slp"))
+partial (unsigned x)
+{
+ unsigned low;
+ unsigned high;
+ V4 result;
+
+ __BB(2):
+ low_2 = x_1(D) + 1u;
+ high_3 = x_1(D) + 3u;
+ result_4 = _Literal (V4) { low_2, high_3 };
+ return result_4;
+}
+
+__attribute__ ((noipa)) V4 __GIMPLE (ssa, startwith ("slp"))
+empty (void)
+{
+ V4 result;
+
+ __BB(2):
+ result_1 = _Literal (V4) {};
+ return result_1;
+}
+
+__attribute__ ((optimize ("no-tree-vectorize"))) int
+main (void)
+{
+ check_vect ();
+ V4 in[] = { { 0, 7, 14, 21 }, { 101, 108, 115, 122 } };
+ V4 expected = { 1, 10, 19, 28 };
+ V8 expected_wide = { 1, 10, 19, 28, 110, 119, 128, 137 };
+ V4 r = root ((const V2 *) in);
+ if (__builtin_memcmp (&r, &expected, sizeof (r)))
+ __builtin_abort ();
+
+ V8 wide = wide_root ((const V2 *) in);
+ if (__builtin_memcmp (&wide, &expected_wide, sizeof (wide)))
+ __builtin_abort ();
+
+ V4 p = partial (123);
+ V4 expected_p = { 124, 126, 0, 0 };
+ V4 zero = empty ();
+ for (unsigned j = 0; j < 4; ++j)
+ if (p[j] != expected_p[j] || zero[j] != 0)
+ __builtin_abort ();
+
+ return 0;
+}
+
+/* Check root discovery and vectorization of both kinds of constructor. */
+/* { dg-final { scan-tree-dump "Analyzing vectorizable constructor: result_6
=" "slp2" { target aarch64*-*-* } } } */
+/* { dg-final { scan-tree-dump "Analyzing vectorizable constructor: result_10
=" "slp2" { target aarch64*-*-* } } } */
+/* { dg-final { scan-tree-dump-times {basic block part vectorized using
[^\n]*vectors} 2 "slp2" { target aarch64*-*-* } } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-insert-lower.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-insert-lower.c
new file mode 100644
index
0000000000000000000000000000000000000000..36370473a75929601381ba739927ec3d368585db
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-insert-lower.c
@@ -0,0 +1,40 @@
+/* Inserts into unsupported vectors must also lower without vectorization. */
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O2 -fgimple -fno-tree-vectorize -Wno-psabi" } */
+
+#include "tree-vect.h"
+
+typedef unsigned V8 __attribute__ ((vector_size (32)));
+typedef unsigned V2 __attribute__ ((vector_size (8)));
+__attribute__ ((noipa)) V8 __GIMPLE
+insert_pair (V8 x, V2 y)
+{
+ V8 res;
+ res = __BIT_INSERT (x, y, 64);
+ return res;
+}
+
+__attribute__ ((noipa)) V8 __GIMPLE
+insert_scalar (V8 x, unsigned y)
+{
+ V8 res;
+ res = __BIT_INSERT (x, y, 160);
+ return res;
+}
+
+int
+main (void)
+{
+ check_vect ();
+ V8 x = { 0, 1, 2, 3, 4, 5, 6, 7 };
+ V2 pair = { 99, 101 };
+ V8 expected_a = { 0, 1, 99, 101, 4, 5, 6, 7 };
+ V8 expected_b = { 0, 1, 2, 3, 4, 23, 6, 7 };
+ V8 a = insert_pair (x, pair), b = insert_scalar (x, 23);
+ if (__builtin_memcmp (&a, &expected_a, sizeof (a))
+ || __builtin_memcmp (&b, &expected_b, sizeof (b)))
+ __builtin_abort ();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes-2.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes-2.c
new file mode 100644
index
0000000000000000000000000000000000000000..9fb407030156ef617fd1fc071f0ae994e9a9bf0e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes-2.c
@@ -0,0 +1,7 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fno-vect-cost-model -Wno-psabi" } */
+/* { dg-xfail-run-if "SVE 2048-bit two-input permutation" { aarch64_sve } {
"-msve-vector-bits=2048" } } */
+
+typedef unsigned V __attribute__ ((vector_size (32)));
+#include "vect-revectorize-memory-lanes.h"
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes-3.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes-3.c
new file mode 100644
index
0000000000000000000000000000000000000000..b59c04e283d8715d56a63bb1addd9a5341c0d2db
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes-3.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fno-vect-cost-model -Wno-psabi" } */
+
+typedef unsigned V __attribute__ ((vector_size (64)));
+#include "vect-revectorize-memory-lanes.h"
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes.c
new file mode 100644
index
0000000000000000000000000000000000000000..59346ecb4b56bc751ecf2a27f09d0a4ee1983d5f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes.c
@@ -0,0 +1,7 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fno-vect-cost-model -Wno-psabi" } */
+/* { dg-xfail-run-if "SVE 2048-bit two-input permutation" { aarch64_sve } {
"-msve-vector-bits=2048" } } */
+
+typedef unsigned V __attribute__ ((vector_size (16)));
+#include "vect-revectorize-memory-lanes.h"
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes.h
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes.h
new file mode 100644
index
0000000000000000000000000000000000000000..c1d8c4e848847814e8fe464b76db2d86b25d0688
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-memory-lanes.h
@@ -0,0 +1,77 @@
+/* Source loads have more lanes than the prefix or repeated-lane results. */
+#include "tree-vect.h"
+
+typedef unsigned V2 __attribute__ ((vector_size (8)));
+typedef unsigned V4 __attribute__ ((vector_size (16)));
+
+__attribute__ ((noipa)) void
+prefix_load (V2 *restrict out, const V *restrict in, unsigned n)
+{
+ for (unsigned i = 0; i < n; ++i)
+ out[i] = __builtin_shufflevector (in[i], in[i], 0, 1);
+}
+
+__attribute__ ((noipa)) void
+repeat_load (V4 *restrict out, const V *restrict in, unsigned n)
+{
+ for (unsigned i = 0; i < n; ++i)
+ out[i] = __builtin_shufflevector (in[i], in[i], 1, 0, 1, 0);
+}
+
+__attribute__ ((noipa)) void
+group_load (V4 *restrict out, const V *restrict in, unsigned n)
+{
+ for (unsigned i = 0; i < n; ++i)
+ out[i]
+ = __builtin_shufflevector (in[2 * i], in[2 * i + 1], 1, 0,
+ sizeof (V) / sizeof (unsigned) + 1,
+ sizeof (V) / sizeof (unsigned));
+}
+
+__attribute__ ((optimize ("no-tree-vectorize"))) int
+main (void)
+{
+ check_vect ();
+ V in[130];
+ unsigned input[130][2];
+ V4 expected_repeat[65], expected_group[65];
+ V2 prefix[66];
+ V4 repeated[66], grouped[66];
+ const V2 guard2 = { -1, -1 };
+ const V4 guard4 = { -1, -1, -1, -1 };
+
+ /* Only two source lanes are used. Distinct rows check source strides. */
+ for (unsigned i = 0; i < 130; ++i)
+ {
+ input[i][0] = 37 * i;
+ input[i][1] = 37 * i + 11;
+ in[i] = (V) { input[i][0], input[i][1] };
+ }
+
+ for (unsigned i = 0; i < 65; ++i)
+ {
+ expected_repeat[i] = (V4) { 37 * i + 11, 37 * i,
+ 37 * i + 11, 37 * i };
+ expected_group[i] = (V4) { 74 * i + 11, 74 * i,
+ 74 * i + 48, 74 * i + 37 };
+ }
+
+ /* Cross vector boundaries even at the largest SVE vector length. */
+ for (unsigned n = 0; n <= 65; ++n)
+ {
+ prefix[n] = guard2;
+ repeated[n] = grouped[n] = guard4;
+ prefix_load (prefix, in, n);
+ repeat_load (repeated, in, n);
+ group_load (grouped, in, n);
+ if (__builtin_memcmp (prefix, input, n * sizeof (V2))
+ || __builtin_memcmp (repeated, expected_repeat, n * sizeof (V4))
+ || __builtin_memcmp (grouped, expected_group, n * sizeof (V4))
+ || __builtin_memcmp (&prefix[n], &guard2, sizeof (V2))
+ || __builtin_memcmp (&repeated[n], &guard4, sizeof (V4))
+ || __builtin_memcmp (&grouped[n], &guard4, sizeof (V4)))
+ __builtin_abort ();
+ }
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-permute.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-permute.c
new file mode 100644
index
0000000000000000000000000000000000000000..add6aa413ef8d4ecd84663b81e82885c9b4dae8f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-permute.c
@@ -0,0 +1,54 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fno-vect-cost-model" } */
+
+#include "tree-vect.h"
+
+typedef unsigned V __attribute__ ((vector_size (32)));
+
+__attribute__ ((noipa)) void
+permute (V *out, const V *a, const V *b)
+{
+ out[0] = __builtin_shuffle (*a, *b, (V) { 7, 8, 2, 13, 4, 11, 6, 9 });
+ out[1] = __builtin_shuffle (*a, *b,
+ (V) { -1, -8, 18, 29, 36, 43, 54, 57 });
+ out[2] = __builtin_shuffle (*a, (V) { 7, 6, 5, 4, 3, 2, 1, 0 });
+}
+
+__attribute__ ((noipa)) void
+permute_variable (V *out, const V *a, const V *b, const V *mask)
+{
+ V t = __builtin_shuffle (*a, *b, *mask);
+ out[0] = __builtin_shuffle (t, (V) { 7, 6, 5, 4, 3, 2, 1, 0 });
+ out[1] = __builtin_shuffle (*a, *mask);
+ out[2] = t + *b;
+}
+
+int
+main (void)
+{
+ check_vect ();
+ V a = { 0, 9, 18, 27, 36, 45, 54, 63 };
+ V b = { 61, 58, 55, 52, 49, 46, 43, 40 };
+ V expected[] = {
+ { 63, 61, 18, 46, 36, 52, 54, 58 },
+ { 40, 61, 18, 46, 36, 52, 54, 58 },
+ { 63, 54, 45, 36, 27, 18, 9, 0 }
+ };
+ V out[3];
+ permute (out, &a, &b);
+ if (__builtin_memcmp (out, expected, sizeof (out)))
+ __builtin_abort ();
+
+ V variable_mask = { -1, -8, 18, 29, 36, 43, 54, 57 };
+ V expected_variable[] = {
+ { 58, 54, 52, 36, 46, 18, 61, 40 },
+ { 63, 0, 18, 45, 36, 27, 54, 9 },
+ { 101, 119, 73, 98, 85, 98, 97, 98 }
+ };
+ permute_variable (out, &a, &b, &variable_mask);
+ if (__builtin_memcmp (out, expected_variable, sizeof (out)))
+ __builtin_abort ();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-subvectors.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-subvectors.c
new file mode 100644
index
0000000000000000000000000000000000000000..da2306298f297c94bbe0b36825b630f31e6f2097
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-subvectors.c
@@ -0,0 +1,133 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fgimple -fno-vect-cost-model -Wno-psabi
-fdump-tree-slp-details -fdump-tree-vect-details" } */
+
+#include "tree-vect.h"
+
+typedef unsigned V2 __attribute__ ((vector_size (8)));
+typedef unsigned V4 __attribute__ ((vector_size (16)));
+typedef unsigned V8 __attribute__ ((vector_size (32)));
+
+/* Preserve shared subvector extracts until SLP. */
+__attribute__ ((noipa)) void __GIMPLE (ssa, startwith ("slp"))
+extracts (V4 *out, const V8 *in)
+{
+ V8 x;
+ V8 y;
+ V2 low;
+ V2 high;
+ V2 a;
+ V2 b;
+ V4 result;
+
+ __BB(2):
+ x_3 = __MEM <V8> (in_2(D));
+ y_4 = x_3 ^ _Literal (V8) { 17u, 17u, 17u, 17u, 17u, 17u, 17u, 17u };
+ low_5 = __BIT_FIELD_REF <V2> (y_4, 64, 64);
+ high_6 = __BIT_FIELD_REF <V2> (y_4, 64, 192);
+ a_7 = low_5 + _Literal (V2) { 1u, 3u };
+ b_8 = high_6 + _Literal (V2) { 5u, 7u };
+ result_9 = _Literal (V4) { a_7, b_8 };
+ __MEM <V4> (out_1(D)) = result_9;
+ return;
+}
+
+/* Chained subvector and scalar inserts at nonzero offsets. */
+__attribute__ ((noipa)) void __GIMPLE (ssa, startwith ("slp"))
+inserts (V8 *out, const V8 *in, const V2 *pair, unsigned value)
+{
+ V8 x;
+ V8 y;
+ V2 p;
+ V2 q;
+ unsigned v;
+ V8 tmp;
+ V8 result;
+
+ __BB(2):
+ x_5 = __MEM <V8> (in_2(D));
+ p_6 = __MEM <V2> (pair_3(D));
+ y_7 = x_5 ^ _Literal (V8) { 17u, 17u, 17u, 17u, 17u, 17u, 17u, 17u };
+ q_8 = p_6 + _Literal (V2) { 1u, 3u };
+ v_9 = value_4(D) + 1u;
+ tmp_10 = __BIT_INSERT (y_7, q_8, 64);
+ result_11 = __BIT_INSERT (tmp_10, v_9, 160);
+ __MEM <V8> (out_1(D)) = result_11;
+ return;
+}
+
+__attribute__ ((always_inline)) static inline V8 __GIMPLE ()
+update (V8 x)
+{
+ V2 p;
+ V2 q;
+ V8 result;
+ p = __BIT_FIELD_REF <V2> (x, 64, 64);
+ q = p + _Literal (V2) { 1u, 3u };
+ result = __BIT_INSERT (x, q, 64);
+ return result;
+}
+
+__attribute__ ((noipa)) void
+updates (V8 *p, unsigned n)
+{
+ for (unsigned i = 0; i < n; ++i)
+ p[i] = update (p[i]);
+}
+
+__attribute__ ((noipa)) V8
+recurrence (V8 x, unsigned n)
+{
+ for (unsigned i = 0; i < n; ++i)
+ x = update (x);
+ return x;
+}
+
+__attribute__ ((optimize ("no-tree-vectorize"))) int
+main (void)
+{
+ check_vect ();
+ for (unsigned n = 0; n < 17; ++n)
+ {
+ V8 in = (V8) { 0, 7, 14, 21, 28, 35, 42, 49 } + 101 * n;
+ V8 b, data[17], want_recurrence = in, want_update = in;
+ V2 pair = { 3 * n, 7 * n };
+ V4 a;
+
+ /* Compute the references by scalar lane, independently of the
+ subvector extracts, inserts and constructors under test. */
+ want_recurrence[2] += n;
+ want_recurrence[3] += 3 * n;
+ want_update[2] += 1;
+ want_update[3] += 3;
+ V4 want_extract = { (in[2] ^ 17) + 1, (in[3] ^ 17) + 3,
+ (in[6] ^ 17) + 5, (in[7] ^ 17) + 7 };
+ V8 want_insert = in ^ 17;
+ want_insert[2] = 3 * n + 1;
+ want_insert[3] = 7 * n + 3;
+ want_insert[5] = 11 * n + 1;
+
+ for (unsigned i = 0; i < 17; ++i)
+ data[i] = in;
+ updates (data, n);
+ V8 repeated = recurrence (in, n);
+ if (__builtin_memcmp (&repeated, &want_recurrence, sizeof (V8)))
+ __builtin_abort ();
+
+ for (unsigned i = 0; i < 17; ++i)
+ if (__builtin_memcmp (&data[i], i < n ? &want_update : &in,
+ sizeof (V8)))
+ __builtin_abort ();
+
+ extracts (&a, &in);
+ inserts (&b, &in, &pair, 11 * n);
+ if (__builtin_memcmp (&a, &want_extract, sizeof (V4))
+ || __builtin_memcmp (&b, &want_insert, sizeof (V8)))
+ __builtin_abort ();
+ }
+
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump "basic block part vectorized" "slp2" { target
aarch64*-*-* } } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 2 "vect"
{ target aarch64*-*-* } } } */
diff --git
a/gcc/testsuite/gcc.target/aarch64/sve/vect-revectorize-perm-const-2048-run.c
b/gcc/testsuite/gcc.target/aarch64/sve/vect-revectorize-perm-const-2048-run.c
new file mode 100644
index
0000000000000000000000000000000000000000..5ef039e9a10f46f71ff9c47a2b049f13a91dab57
--- /dev/null
+++
b/gcc/testsuite/gcc.target/aarch64/sve/vect-revectorize-perm-const-2048-run.c
@@ -0,0 +1,56 @@
+/* { dg-do run { target aarch64_sve2048_hw } } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-options "-O3 -march=armv8-a+sve -msve-vector-bits=2048
-fno-tree-vectorize" } */
+/* { dg-xfail-run-if "SVE 2048-bit two-input permutation" { *-*-* } } */
+
+/* Check selection from both inputs without involving the vectorizer. */
+#include "../../../gcc.dg/vect/tree-vect.h"
+
+typedef unsigned V __attribute__ ((vector_size (256)));
+
+__attribute__ ((noipa)) void
+perm (V *out, const V *a, const V *b)
+{
+ *out
+ = __builtin_shufflevector (*a, *b, 1, 0, 65, 64, 5, 4, 69, 68, 9, 8, 73,
+ 72, 13, 12, 77, 76, 17, 16, 81, 80, 21, 20, 85,
+ 84, 25, 24, 89, 88, 29, 28, 93, 92, 33, 32, 97,
+ 96, 37, 36, 101, 100, 41, 40, 105, 104, 45, 44,
+ 109, 108, 49, 48, 113, 112, 53, 52, 117, 116,
+ 57, 56, 121, 120, 61, 60, 125, 124);
+}
+
+static const V inputs[] = {
+ { 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011,
+ 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023,
+ 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035,
+ 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047,
+ 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059,
+ 1060, 1061, 1062, 1063 },
+ { 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
+ 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023,
+ 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035,
+ 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047,
+ 2048, 2049, 2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059,
+ 2060, 2061, 2062, 2063 }
+};
+static const V expected[] = {
+ { 1001, 1000, 2001, 2000, 1005, 1004, 2005, 2004, 1009, 1008, 2009, 2008,
+ 1013, 1012, 2013, 2012, 1017, 1016, 2017, 2016, 1021, 1020, 2021, 2020,
+ 1025, 1024, 2025, 2024, 1029, 1028, 2029, 2028, 1033, 1032, 2033, 2032,
+ 1037, 1036, 2037, 2036, 1041, 1040, 2041, 2040, 1045, 1044, 2045, 2044,
+ 1049, 1048, 2049, 2048, 1053, 1052, 2053, 2052, 1057, 1056, 2057, 2056,
+ 1061, 1060, 2061, 2060 }
+};
+
+int
+main (void)
+{
+ check_vect ();
+ V out;
+ perm (&out, &inputs[0], &inputs[1]);
+ if (__builtin_memcmp (&out, &expected[0], sizeof (out)))
+ __builtin_abort ();
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/vect-revectorize-invariant-cost.c
b/gcc/testsuite/gcc.target/aarch64/vect-revectorize-invariant-cost.c
new file mode 100644
index
0000000000000000000000000000000000000000..08efc70da403edeeabcb720bac09fc3e2a641d2b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/vect-revectorize-invariant-cost.c
@@ -0,0 +1,38 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-require-effective-target vect_float } */
+/* { dg-options "-O3 -march=armv8-a+sve -Wno-psabi -fdump-tree-slp-details
-save-temps" } */
+
+#include "../../gcc.dg/vect/tree-vect.h"
+
+typedef float V4 __attribute__ ((vector_size (16)));
+typedef float V8 __attribute__ ((vector_size (32)));
+
+__attribute__ ((noipa)) V8
+swap_halves (V8 v)
+{
+ V4 d[2];
+ __builtin_memcpy (d, &v, sizeof v);
+ V4 t = d[0];
+ d[0] = d[1];
+ d[1] = t;
+ __builtin_memcpy (&v, d, sizeof v);
+ return v;
+}
+
+__attribute__ ((optimize ("no-tree-vectorize")))
+int
+main (void)
+{
+ check_vect ();
+ V8 x = { 0, 101, 202, 303, 404, 505, 606, 707 };
+ const V8 expected = { 404, 505, 606, 707, 0, 101, 202, 303 };
+ V8 y = swap_halves (x);
+ if (__builtin_memcmp (&y, &expected, sizeof (y)))
+ __builtin_abort ();
+
+ return 0;
+}
+
+/* Existing native vector pieces require no construction in the prologue. */
+/* { dg-final { scan-tree-dump "basic block part vectorized using 16 byte
vectors" "slp2" } } */
+/* { dg-final { scan-tree-dump-not "times scalar_to_vec" "slp2" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/vect-revectorize-memory-lanes-2.c
b/gcc/testsuite/gcc.target/aarch64/vect-revectorize-memory-lanes-2.c
new file mode 100644
index
0000000000000000000000000000000000000000..9a313c8f61509f176691649529d4d37b134c4cd0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/vect-revectorize-memory-lanes-2.c
@@ -0,0 +1,8 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-options "-O3 -march=armv8-a+sve -fno-vect-cost-model -Wno-psabi
-fdump-tree-vect-details" } */
+/* { dg-xfail-run-if "SVE 2048-bit two-input permutation" { *-*-* } {
"-msve-vector-bits=2048" } } */
+
+#include "../../gcc.dg/vect/vect-revectorize-memory-lanes-2.c"
+
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 3 "vect"
} } */
diff --git a/gcc/testsuite/gcc.target/aarch64/vect-revectorize-memory-lanes-3.c
b/gcc/testsuite/gcc.target/aarch64/vect-revectorize-memory-lanes-3.c
new file mode 100644
index
0000000000000000000000000000000000000000..40fdadcb009191c8ffcf8aa4ae2173aeda5dd74c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/vect-revectorize-memory-lanes-3.c
@@ -0,0 +1,7 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-options "-O3 -march=armv8-a+sve -fno-vect-cost-model -Wno-psabi
-fdump-tree-vect-details" } */
+
+#include "../../gcc.dg/vect/vect-revectorize-memory-lanes-3.c"
+
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 3 "vect"
} } */
diff --git a/gcc/testsuite/gcc.target/aarch64/vect-revectorize-memory-lanes.c
b/gcc/testsuite/gcc.target/aarch64/vect-revectorize-memory-lanes.c
new file mode 100644
index
0000000000000000000000000000000000000000..4eda51c04b0ddd1e979a7da63598aad1969769b4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/vect-revectorize-memory-lanes.c
@@ -0,0 +1,8 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-options "-O3 -march=armv8-a+sve -fno-vect-cost-model -Wno-psabi
-fdump-tree-vect-details" } */
+/* { dg-xfail-run-if "SVE 2048-bit two-input permutation" { *-*-* } {
"-msve-vector-bits=2048" } } */
+
+#include "../../gcc.dg/vect/vect-revectorize-memory-lanes.c"
+
+/* { dg-final { scan-tree-dump-times "vectorized 1 loops in function" 3 "vect"
} } */
diff --git a/gcc/tree-vect-generic.cc b/gcc/tree-vect-generic.cc
index
f9cd17db8b49a85a8b770b402faa32f101fed881..8df7ffc148ebbe4272139de8ad8c538556e54a45
100644
--- a/gcc/tree-vect-generic.cc
+++ b/gcc/tree-vect-generic.cc
@@ -2167,6 +2167,37 @@ expand_vector_conversion (gimple_stmt_iterator *gsi)
gsi_replace (gsi, g, false);
}
+/* Lower an insert into an emulated vector using its source subvector size.
+ This also provides the fallback for whole-vector values exposed by the
+ vectorizer when SLP does not replace the insert. */
+
+static void
+lower_vec_insert (gimple_stmt_iterator *gsi)
+{
+ gassign *stmt = as_a<gassign *> (gsi_stmt (*gsi));
+ tree type = TREE_TYPE (gimple_assign_lhs (stmt));
+ tree old = gimple_assign_rhs1 (stmt);
+ tree value = gimple_assign_rhs2 (stmt);
+ tree size = TYPE_SIZE (TREE_TYPE (value));
+ unsigned HOST_WIDE_INT bits = tree_to_uhwi (size);
+ unsigned HOST_WIDE_INT first = tree_to_uhwi (gimple_assign_rhs3 (stmt));
+ unsigned HOST_WIDE_INT total = tree_to_uhwi (TYPE_SIZE (type));
+ vec<constructor_elt, va_gc> *elts = nullptr;
+ for (unsigned HOST_WIDE_INT pos = 0; pos < total; pos += bits)
+ {
+ tree part = value;
+ if (pos != first)
+ part
+ = gimplify_build3 (gsi, BIT_FIELD_REF, TREE_TYPE (value), old, size,
+ bitsize_int (pos));
+
+ CONSTRUCTOR_APPEND_ELT (elts, NULL_TREE, part);
+ }
+
+ gimple_assign_set_rhs_from_tree (gsi, build_constructor (type, elts));
+ update_stmt (gsi_stmt (*gsi));
+}
+
/* Process one statement. If we identify a vector operation, expand it. */
static void
@@ -2191,6 +2222,14 @@ expand_vector_operations_1 (gimple_stmt_iterator *gsi)
rhs_class = get_gimple_rhs_class (code);
lhs = gimple_assign_lhs (stmt);
+ if (code == BIT_INSERT_EXPR
+ && VECTOR_TYPE_P (TREE_TYPE (lhs))
+ && TYPE_MODE (TREE_TYPE (lhs)) == BLKmode)
+ {
+ lower_vec_insert (gsi);
+ return;
+ }
+
if (code == VEC_PERM_EXPR)
{
lower_vec_perm (gsi);
diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
index
109cac7142f94f22dd35546e21882dc84be87ed3..c6e94bd422b4d4b811c20cac4407c4aaa09fc318
100644
--- a/gcc/tree-vect-slp.cc
+++ b/gcc/tree-vect-slp.cc
@@ -618,6 +618,7 @@ vect_get_operand_map (const gimple *stmt, bool
gather_scatter_p,
{
static const int no_arg_map[] = { 0 };
static const int arg0_map[] = { 1, 0 };
+ static const int arg0_arg1_map[] = { 2, 0, 1 };
static const int arg2_map[] = { 1, 2 };
static const int arg2_arg3_map[] = { 2, 2, 3 };
static const int arg2_arg4_map[] = { 2, 2, 4 };
@@ -656,8 +657,17 @@ vect_get_operand_map (const gimple *stmt, bool
gather_scatter_p,
|| commutative_tree_code (code))
&& swap)
return op1_op0_map;
- else if (code == VIEW_CONVERT_EXPR)
+ else if (code == VIEW_CONVERT_EXPR
+ || (code == BIT_FIELD_REF
+ && vect_can_revectorize_type_p
+ (TREE_TYPE (gimple_assign_lhs (assign)))))
return op00_map;
+ else if ((code == BIT_INSERT_EXPR
+ || (code == VEC_PERM_EXPR
+ && TREE_CODE (gimple_assign_rhs3 (assign)) == VECTOR_CST))
+ && vect_can_revectorize_type_p
+ (TREE_TYPE (gimple_assign_lhs (assign))))
+ return arg0_arg1_map;
else if (gather_scatter_p)
return (TREE_CODE (gimple_assign_lhs (assign)) != SSA_NAME
? off_op0_map : off_map);
@@ -738,16 +748,37 @@ vect_slp_child_index_for_operand (const stmt_vec_info
stmt, int op)
vect_get_operand_map. Adjust all consumers. */
struct slp_oprnds {
- slp_oprnds (stmt_vec_info);
+ slp_oprnds (stmt_vec_info, unsigned char = 0);
tree get_op_for_slp_child (stmt_vec_info, unsigned);
+ unsigned get_op_nunits (tree) const;
+ bool get_permutation (stmt_vec_info, unsigned, auto_lane_permutation_t &);
const int *opmap;
const unsigned int num_slp_children;
+
+ /* Output lanes per statement for a permutation, or zero otherwise. */
+ unsigned int num_lanes = 0;
};
-slp_oprnds::slp_oprnds (stmt_vec_info stmt_info)
- : opmap (vect_get_operand_map (stmt_info)),
- num_slp_children (opmap ? opmap[0] : gimple_num_args (stmt_info->stmt))
+slp_oprnds::slp_oprnds (stmt_vec_info stmt_info, unsigned char swap)
+ : opmap (vect_get_operand_map (stmt_info, swap)),
+ num_slp_children (opmap ? opmap[0]
+ : (is_gimple_assign (stmt_info->stmt)
+ && gimple_assign_rhs_code (stmt_info->stmt)
+ == CONSTRUCTOR)
+ ? CONSTRUCTOR_NELTS (gimple_assign_rhs1 (stmt_info->stmt))
+ : gimple_num_args (stmt_info->stmt))
{
+ if (gassign *stmt = dyn_cast <gassign *> (stmt_info->stmt))
+ {
+ tree type = TREE_TYPE (gimple_assign_lhs (stmt));
+ tree_code code = gimple_assign_rhs_code (stmt);
+ if (vect_can_revectorize_type_p (type)
+ && (code == CONSTRUCTOR || code == BIT_FIELD_REF
+ || code == BIT_INSERT_EXPR
+ || (code == VEC_PERM_EXPR
+ && TREE_CODE (gimple_assign_rhs3 (stmt)) == VECTOR_CST)))
+ num_lanes = TYPE_VECTOR_SUBPARTS (type).to_constant ();
+ }
}
/* For SLP child number N get the corresponding tree operand from GIMPLE
@@ -757,6 +788,10 @@ tree
slp_oprnds::get_op_for_slp_child (stmt_vec_info stmt_info, unsigned n)
{
gcc_assert (n < num_slp_children);
+ if (is_gimple_assign (stmt_info->stmt)
+ && gimple_assign_rhs_code (stmt_info->stmt) == CONSTRUCTOR)
+ return CONSTRUCTOR_ELT (gimple_assign_rhs1 (stmt_info->stmt), n)->value;
+
int opno = opmap ? opmap[n + 1] : (int) n;
if (opno == GATHER_SCATTER_OFFSET)
gcc_unreachable (); // TODO
@@ -766,6 +801,101 @@ slp_oprnds::get_op_for_slp_child (stmt_vec_info
stmt_info, unsigned n)
return gimple_arg (stmt_info->stmt, opno);
}
+/* Return the number of lanes contributed by OP to its SLP child. */
+
+unsigned
+slp_oprnds::get_op_nunits (tree op) const
+{
+ if (!num_lanes || !VECTOR_TYPE_P (TREE_TYPE (op)))
+ return 1;
+
+ unsigned HOST_WIDE_INT count;
+ return TYPE_VECTOR_SUBPARTS (TREE_TYPE (op)).is_constant (&count) ? count :
0;
+}
+
+/* Append the permutation for statement STMT_INFO at position INDEX. */
+
+bool
+slp_oprnds::get_permutation (stmt_vec_info stmt_info, unsigned index,
+ auto_lane_permutation_t &perm)
+{
+ if (!num_slp_children)
+ return false;
+
+ auto_vec<unsigned, 4> op_lanes (num_slp_children);
+ for (unsigned i = 0; i < num_slp_children; ++i)
+ {
+ unsigned count = get_op_nunits (get_op_for_slp_child (stmt_info, i));
+ if (!count)
+ return false;
+
+ op_lanes.quick_push (count);
+ }
+
+ gassign *stmt = as_a <gassign *> (stmt_info->stmt);
+ tree_code code = gimple_assign_rhs_code (stmt);
+ vec_perm_indices indices;
+ unsigned HOST_WIDE_INT first = 0;
+ if (code == CONSTRUCTOR)
+ {
+ if (num_lanes != num_slp_children * op_lanes[0])
+ return false;
+ }
+ else if (code == VEC_PERM_EXPR)
+ {
+ if (!tree_to_vec_perm_indices (&indices, gimple_assign_rhs1 (stmt),
+ gimple_assign_rhs2 (stmt),
+ gimple_assign_rhs3 (stmt)))
+ return false;
+ }
+ else
+ {
+ tree type = TREE_TYPE (gimple_assign_lhs (stmt));
+ auto offset
+ = code == BIT_FIELD_REF
+ ? bit_field_offset (gimple_assign_rhs1 (stmt))
+ : tree_to_poly_uint64 (gimple_assign_rhs3 (stmt));
+ if (!constant_multiple_p (offset,
+ tree_to_poly_uint64
+ (TYPE_SIZE (TREE_TYPE (type))), &first)
+ || first >= op_lanes[0])
+ return false;
+ }
+
+ for (unsigned i = 0; i < num_lanes; ++i)
+ {
+ unsigned input = 0, lane = i;
+ if (code == CONSTRUCTOR)
+ {
+ input = i / op_lanes[0];
+ lane = i % op_lanes[0];
+ }
+ else if (code == VEC_PERM_EXPR)
+ {
+ HOST_WIDE_INT idx;
+ if (!indices[i].is_constant (&idx))
+ return false;
+
+ input = idx / op_lanes[0];
+ lane = idx % op_lanes[0];
+ }
+ else if (code == BIT_INSERT_EXPR)
+ {
+ input = i >= first && i - first < op_lanes[1];
+ lane = input ? i - first : i;
+ }
+ else
+ lane += first;
+
+ if (lane >= op_lanes[input])
+ return false;
+
+ perm.safe_push (std::make_pair (input, index * op_lanes[input] + lane));
+ }
+
+ return true;
+}
+
/* Get the defs for the rhs of STMT (collect them in OPRNDS_INFO), check that
they are of a valid type and that they match the defs of the first stmt of
the SLP group (stored in OPRNDS_INFO). This function tries to match stmts
@@ -809,10 +939,11 @@ vect_get_and_check_slp_defs (vec_info *vinfo, tree
vectype, unsigned char swap,
&& !is_a<gphi *> (stmt_info->stmt))
return -1;
- number_of_oprnds = gimple_num_args (stmt_info->stmt);
- const int *map = vect_get_operand_map (stmt_info, swap);
- if (map)
- number_of_oprnds = *map++;
+ slp_oprnds child_ops (stmt_info, swap);
+ number_of_oprnds = child_ops.num_slp_children;
+ if (number_of_oprnds != oprnds_info->length ())
+ return 1;
+
if (gcall *stmt = dyn_cast <gcall *> (stmt_info->stmt))
{
if (gimple_call_internal_p (stmt))
@@ -842,7 +973,7 @@ vect_get_and_check_slp_defs (vec_info *vinfo, tree vectype,
unsigned char swap,
for (i = 0; i < number_of_oprnds; i++)
{
oprnd_info = (*oprnds_info)[i];
- int opno = map ? map[i] : int (i);
+ int opno = child_ops.opmap ? child_ops.opmap[i + 1] : int (i);
if (opno == GATHER_SCATTER_OFFSET)
{
gcc_assert (STMT_VINFO_GATHER_SCATTER_P (stmt_info));
@@ -864,11 +995,9 @@ vect_get_and_check_slp_defs (vec_info *vinfo, tree
vectype, unsigned char swap,
oprnd = gs_info.offset;
}
}
- else if (opno < 0)
- oprnd = TREE_OPERAND (gimple_arg (stmt_info->stmt, 0), -1 - opno);
else
{
- oprnd = gimple_arg (stmt_info->stmt, opno);
+ oprnd = child_ops.get_op_for_slp_child (stmt_info, i);
if (gphi *stmt = dyn_cast <gphi *> (stmt_info->stmt))
{
edge e = gimple_phi_arg_edge (stmt, opno);
@@ -898,9 +1027,7 @@ vect_get_and_check_slp_defs (vec_info *vinfo, tree
vectype, unsigned char swap,
continue;
}
- oprnd_info->def_stmts.quick_push (def_stmt_info);
- oprnd_info->ops.quick_push (oprnd);
-
+ tree op = oprnd;
if (def_stmt_info
&& is_pattern_stmt_p (def_stmt_info))
{
@@ -909,8 +1036,14 @@ vect_get_and_check_slp_defs (vec_info *vinfo, tree
vectype, unsigned char swap,
oprnd_info->any_pattern = true;
else
/* If we promote this to external use the original stmt def. */
- oprnd_info->ops.last ()
- = gimple_get_lhs (vect_orig_stmt (def_stmt_info)->stmt);
+ op = gimple_get_lhs (vect_orig_stmt (def_stmt_info)->stmt);
+ }
+
+ unsigned count = child_ops.get_op_nunits (oprnd);
+ for (unsigned lane = 0; lane < count; ++lane)
+ {
+ oprnd_info->def_stmts.safe_push (def_stmt_info);
+ oprnd_info->ops.safe_push (op);
}
/* If there's a extern def on a backedge make sure we can
@@ -986,8 +1119,11 @@ vect_get_and_check_slp_defs (vec_info *vinfo, tree
vectype, unsigned char swap,
oprnd_info = (*oprnds_info)[i];
dt = dts[i];
- stmt_vec_info def_stmt_info = oprnd_info->def_stmts[stmt_num];
- oprnd = oprnd_info->ops[stmt_num];
+
+ /* Child operands can contribute a different number of lanes from
+ the output statement, so STMT_NUM is not an operand index. */
+ stmt_vec_info def_stmt_info = oprnd_info->def_stmts.last ();
+ oprnd = oprnd_info->ops.last ();
tree type = TREE_TYPE (oprnd);
if (!types_compatible_p (oprnd_info->first_op_type, type))
@@ -1059,10 +1195,11 @@ vect_get_and_check_slp_defs (vec_info *vinfo, tree
vectype, unsigned char swap,
dump_printf_loc (MSG_NOTE, vect_location,
"trying swapped operands\n");
std::swap (dts[i], dts[i+1]);
- std::swap ((*oprnds_info)[i]->def_stmts[stmt_num],
- (*oprnds_info)[i+1]->def_stmts[stmt_num]);
- std::swap ((*oprnds_info)[i]->ops[stmt_num],
- (*oprnds_info)[i+1]->ops[stmt_num]);
+ std::swap ((*oprnds_info)[i]->def_stmts.last (),
+ (*oprnds_info)[i+1]->def_stmts.last ());
+ std::swap ((*oprnds_info)[i]->ops.last (),
+ (*oprnds_info)[i+1]->ops.last ());
+
/* After swapping some operands we lost track whether an
operand has any pattern defs so be conservative here. */
if ((*oprnds_info)[i]->any_pattern
@@ -1109,8 +1246,8 @@ vect_get_and_check_slp_defs (vec_info *vinfo, tree
vectype, unsigned char swap,
&& (REDUC_GROUP_FIRST_ELEMENT (def_stmt_info)
== REDUC_GROUP_FIRST_ELEMENT (stmt_info)))
{
- oprnd_info->def_stmts[stmt_num] = oprnd_info->def_stmts[0];
- oprnd_info->ops[stmt_num] = oprnd_info->ops[0];
+ oprnd_info->def_stmts.last () = oprnd_info->def_stmts[0];
+ oprnd_info->ops.last () = oprnd_info->ops[0];
}
++i;
@@ -1370,6 +1507,8 @@ vect_build_slp_tree_3 (vec_info *vinfo,
vec<stmt_vec_info> stmts,
&& rhs_code != VIEW_CONVERT_EXPR
&& rhs_code != CALL_EXPR
&& rhs_code != BIT_FIELD_REF
+ && !(rhs_code == CONSTRUCTOR
+ && vect_can_revectorize_type_p (TREE_TYPE (lhs)))
&& rhs_code != SSA_NAME)
{
if (dump_enabled_p ())
@@ -1410,17 +1549,30 @@ vect_build_slp_tree_3 (vec_info *vinfo,
vec<stmt_vec_info> stmts,
if (!ldst_p && rhs_code == BIT_FIELD_REF)
{
- tree vec = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
- if (!is_a <bb_vec_info> (vinfo)
+ tree ref = gimple_assign_rhs1 (stmt);
+ tree vec = TREE_OPERAND (ref, 0);
+ tree source_type = TREE_TYPE (vec);
+ bool same_elements_p
+ = (VECTOR_TYPE_P (source_type)
+ && types_compatible_p (TREE_TYPE (vectype),
+ TREE_TYPE (source_type)));
+ bool supported_p;
+ if (VECTOR_TYPE_P (TREE_TYPE (lhs)))
+ /* Vector slices keep the source element type and lane order. */
+ supported_p
+ = (vect_can_revectorize_type_p (source_type) && same_elements_p);
+ else
+ /* BB SLP also allows scalar extracts after reinterpreting a
+ source of the same size as VECTYPE. */
+ supported_p
+ = (is_a <bb_vec_info> (vinfo)
+ && (same_elements_p
+ || operand_equal_p (TYPE_SIZE (vectype),
+ TYPE_SIZE (source_type))));
+
+ if (!supported_p
|| TREE_CODE (vec) != SSA_NAME
- || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (vec)
- /* When the element types are not compatible we pun the
- source to the target vectype which requires equal size. */
- || ((!VECTOR_TYPE_P (TREE_TYPE (vec))
- || !types_compatible_p (TREE_TYPE (vectype),
- TREE_TYPE (TREE_TYPE (vec))))
- && !operand_equal_p (TYPE_SIZE (vectype),
- TYPE_SIZE (TREE_TYPE (vec)))))
+ || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (vec))
{
if (dump_enabled_p ())
dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
@@ -1561,6 +1713,7 @@ vect_build_slp_tree_3 (vec_info *vinfo,
vec<stmt_vec_info> stmts,
if (!ldst_p
&& first_stmt_code == BIT_FIELD_REF
+ && !vect_can_revectorize_type_p (TREE_TYPE (lhs))
&& (TREE_OPERAND (gimple_assign_rhs1 (first_stmt_info->stmt), 0)
!= TREE_OPERAND (gimple_assign_rhs1 (stmt_info->stmt), 0)))
{
@@ -2247,9 +2400,7 @@ vect_build_slp_tree_2 (vec_info *vinfo, slp_tree node,
}
}
- nops = gimple_num_args (stmt_info->stmt);
- if (const int *map = vect_get_operand_map (stmt_info))
- nops = map[0];
+ nops = slp_oprnds (stmt_info).num_slp_children;
/* If the SLP node is a PHI (induction or reduction), terminate
the recursion. */
@@ -2478,7 +2629,8 @@ vect_build_slp_tree_2 (vec_info *vinfo, slp_tree node,
}
else if (gimple_assign_single_p (stmt_info->stmt)
&& !gimple_vuse (stmt_info->stmt)
- && gimple_assign_rhs_code (stmt_info->stmt) == BIT_FIELD_REF)
+ && gimple_assign_rhs_code (stmt_info->stmt) == BIT_FIELD_REF
+ && !vect_can_revectorize_type_p (type))
{
if (soft_fail)
{
@@ -2927,14 +3079,28 @@ out:
}
/* Get at the operands, verifying they are compatible. */
+ slp_oprnds operands (stmt_info);
+ unsigned stmt_lanes = MAX (1, operands.num_lanes);
+ auto_lane_permutation_t lane_perm;
+ if (operands.num_lanes)
+ for (i = 0; i < group_size; i += stmt_lanes)
+ {
+ slp_oprnds ops (stmts[i]);
+ if (ops.num_slp_children != nops
+ || !ops.get_permutation (stmts[i], i / stmt_lanes, lane_perm))
+ {
+ matches[0] = -1;
+ return NULL;
+ }
+ }
+
vec<slp_oprnd_info> oprnds_info = vect_create_oprnd_info (nops, group_size);
slp_oprnd_info oprnd_info;
- FOR_EACH_VEC_ELT (stmts, i, stmt_info)
+ for (i = 0; i < group_size; i += stmt_lanes)
{
- int res = vect_get_and_check_slp_defs (vinfo, vectype,
- swap[i], skip_args,
- stmts, i, &oprnds_info,
- soft_fail);
+ int res
+ = vect_get_and_check_slp_defs (vinfo, vectype, swap[i], skip_args,
+ stmts, i, &oprnds_info, soft_fail);
if (res != 0)
/* ??? This puts -1 back into matches[] and the cache. */
matches[(res == -1) ? 0 : i] = -1;
@@ -3066,9 +3232,22 @@ out:
int gs_scale = 0;
tree gs_base = NULL_TREE;
+ match_elt_t *parent_matches = matches;
+
/* Create SLP_TREE nodes for the definition node/s. */
FOR_EACH_VEC_ELT (oprnds_info, i, oprnd_info)
{
+ unsigned group_size = oprnd_info->ops.length ();
+ auto_vec<match_elt_t> child_matches;
+ match_elt_t *matches = parent_matches;
+
+ /* Child matches use the child's lane coordinates. */
+ if (group_size != stmts.length ())
+ {
+ child_matches.safe_grow (group_size);
+ matches = child_matches.address ();
+ }
+
slp_tree child = nullptr;
unsigned int j;
unsigned old_swap_distance;
@@ -3240,6 +3419,7 @@ out:
}
can_swap = (i == 0
+ && group_size == stmts.length ()
&& (nops == 2 || nops == 3)
&& oprnds_info.length () > 1
&& oprnds_info[1]->first_dt == vect_internal_def
@@ -3402,6 +3582,9 @@ fail:
}
gcc_assert (child == NULL);
+ if (group_size != stmts.length ())
+ parent_matches[0] = -1;
+
FOR_EACH_VEC_ELT (children, j, child)
if (child)
vect_free_slp_tree (child);
@@ -3544,6 +3727,14 @@ fail:
node = vect_create_new_slp_node (node, stmts, nops);
SLP_TREE_VECTYPE (node) = vectype;
+
+ if (!lane_perm.is_empty ())
+ {
+ SLP_TREE_CODE (node) = VEC_PERM_EXPR;
+ SLP_TREE_LANE_PERMUTATION (node) = lane_perm.copy ();
+ SLP_TREE_REPRESENTATIVE (node) = NULL;
+ }
+
SLP_TREE_CHILDREN (node).splice (children);
SLP_TREE_GS_SCALE (node) = gs_scale;
SLP_TREE_GS_BASE (node) = gs_base;
@@ -10493,8 +10684,7 @@ vect_slp_is_lane_insert (gimple *use_stmt, tree vec,
unsigned *this_lane)
return true;
}
-/* Find any vectorizable constructors and add them to the grouped_store
- array. */
+/* Find vectorizable constructors and other SLP roots. */
static void
vect_slp_check_for_roots (bb_vec_info bb_vinfo)
@@ -10516,28 +10706,38 @@ vect_slp_check_for_roots (bb_vec_info bb_vinfo)
if (code == CONSTRUCTOR)
{
if (!VECTOR_TYPE_P (TREE_TYPE (rhs))
- || maybe_ne (TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs)),
- CONSTRUCTOR_NELTS (rhs))
- || VECTOR_TYPE_P (TREE_TYPE (CONSTRUCTOR_ELT (rhs, 0)->value))
+ || !TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs)).is_constant ()
|| uniform_vector_p (rhs))
continue;
+ slp_oprnds operands (bb_vinfo->lookup_stmt (assign));
+ vec<stmt_vec_info> stmts = vNULL;
unsigned j;
- tree val;
- FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), j, val)
- if (TREE_CODE (val) != SSA_NAME
- || !bb_vinfo->lookup_def (val))
- break;
- if (j != CONSTRUCTOR_NELTS (rhs))
- continue;
+ for (j = 0; j < operands.num_slp_children; ++j)
+ {
+ tree val
+ = operands.get_op_for_slp_child (bb_vinfo->lookup_stmt (assign),
+ j);
+ stmt_vec_info def = bb_vinfo->lookup_def (val);
+ tree type = TREE_TYPE (val);
+ if (!def || (VECTOR_TYPE_P (type)
+ && !vect_can_revectorize_type_p (type)))
+ break;
+
+ unsigned nelts = operands.get_op_nunits (val);
+ for (unsigned k = 0; k < nelts; ++k)
+ stmts.safe_push (vect_stmt_to_vectorize (def));
+ }
+
+ if (j != operands.num_slp_children
+ || stmts.length () != operands.num_lanes)
+ {
+ stmts.release ();
+ continue;
+ }
vec<stmt_vec_info> roots = vNULL;
roots.safe_push (bb_vinfo->lookup_stmt (assign));
- vec<stmt_vec_info> stmts;
- stmts.create (CONSTRUCTOR_NELTS (rhs));
- FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), j, val)
- stmts.quick_push
- (vect_stmt_to_vectorize (bb_vinfo->lookup_def (val)));
bb_vinfo->roots.safe_push (slp_root (slp_inst_kind_ctor,
stmts, roots));
}