Vector PHIs must stay inside the SLP graph when we split a vector recurrence.
We also need to reconstruct the original vector type when its value is used
outside the graph.
e.g.
typedef unsigned V8 __attribute__((vector_size(32)));
void recurrence (V8 *out, V8 x, unsigned n)
{
for (unsigned i = 0; i < n; ++i)
x = x * 2 + (V8) {1,3,5,7,9,11,13,15};
*out = x;
}
We mark these PHIs as internal definitions and vectorize the cycle at loop
VF 1: each transformed loop iteration processes one original loop iteration.
The SLP node still has eight element lanes for the V8 statement. On Adv. SIMD
we split those lanes into two four-element vectors, with a PHI for each piece:
Before splitting:
x = PHI <initial_x, next_x>;
next_x = x * {2, ...} + {1,3,5,7,9,11,13,15};
After splitting:
x_lo = PHI <initial_lo, next_lo>;
x_hi = PHI <initial_hi, next_hi>;
next_lo = x_lo * {2,2,2,2} + {1,3,5,7};
next_hi = x_hi * {2,2,2,2} + {9,11,13,15};
The scheduler creates the PHIs with their initial values and fills in each
backedge after scheduling its definition. Scalar PHI-to-PHI cycles use the
same path. Induction-variable advancement excludes these internal PHIs so
epilogue setup does not advance them as scalar induction variables.
With -O3 -march=armv8-a we generated before:
sshl v28.4s, v30.4s, v26.4s
sshl v29.4s, v31.4s, v26.4s
add v28.4s, v27.4s, v28.4s
add v29.4s, v25.4s, v29.4s
mov v30.16b, v28.16b
mov v31.16b, v29.16b
After these patches we get:
sshl v30.4s, v30.4s, v28.4s
sshl v31.4s, v31.4s, v28.4s
add v30.4s, v29.4s, v30.4s
add v31.4s, v27.4s, v31.4s
The recurrence is carried directly in the arithmetic result registers,
removing the copies between iterations.
For a whole-vector live-out we wait until all pieces are available, then build
{result_lo, result_hi}, or extract the source-sized range from a wider result.
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
gcc/ChangeLog:
PR tree-optimization/119187
* tree-vect-loop-manip.cc (iv_phi_p): Exclude internal PHI cycles.
(vect_can_advance_ivs_p): Diagnose non-induction PHIs.
(vect_update_ivs_after_vectorizer): Likewise.
* tree-vect-loop.cc (vect_analyze_scalar_cycles_1): Recognize vector
PHIs and scalar PHI cycles.
(vect_compute_single_scalar_iteration_cost): Cost emulated vector PHIs.
(vectorizable_phi): Handle internal loop PHIs at VF equal to one.
(vectorizable_live_operation_1): Reconstruct whole vectors using the
existing loop-exit PHI path.
(vectorizable_live_operation): Track one live lane per source vector and
assemble split live-outs after all their pieces are available.
* tree-vect-slp.cc (vect_get_slp_live_lc_phi_def,
vect_analyze_slp_live_lanes): New.
(vect_analyze_slp): Build complete vector live-out roots and group
lowered lanes by bit position in one pass per exit edge, preserving
source destination order.
* tree-vect-stmts.cc (vect_analyze_stmt): Allow internal loop PHIs.
(vect_transform_stmt): Likewise.
* tree-vectorizer.h (vectorizable_phi): Handle internal loop PHIs at VF
equal to one.
gcc/testsuite/ChangeLog:
PR tree-optimization/119187
* gcc.dg/vect/vect-revectorize-phi-cycle.c: New test.
* gcc.dg/vect/vect-revectorize-bb-live.c: New test.
* gcc.dg/vect/vect-revectorize-loop-live.c: Likewise.
* gcc.dg/vect/vect-revectorize-wide-recurrence.c: New test.
* gcc.target/aarch64/vect-revectorize-sra-unsupported-vector-1.c: New
test.
---
--
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-bb-live.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-bb-live.c
new file mode 100644
index
0000000000000000000000000000000000000000..2615b2eace1991817bfbfa55c8a4d424345785e9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-bb-live.c
@@ -0,0 +1,34 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fno-vect-cost-model -Wno-psabi" } */
+
+#include "tree-vect.h"
+
+typedef unsigned V __attribute__ ((vector_size (32)));
+
+/* The store seeds SLP, while the return keeps the whole source vector live.
*/
+__attribute__ ((noipa)) V
+store_and_return (V *out, const V *a, const V *b)
+{
+ V value = (*a + *b) ^ (V) { 1, 3, 5, 7, 9, 11, 13, 15 };
+ *out = value;
+ return value;
+}
+
+int
+main (void)
+{
+ check_vect ();
+ V a, b, stored;
+ for (unsigned i = 0; i < 8; ++i)
+ {
+ a[i] = 13 * i;
+ b[i] = 100 - 7 * i;
+ }
+ V returned = store_and_return (&stored, &a, &b);
+ for (unsigned i = 0; i < 8; ++i)
+ if (stored[i] != ((100 + 6 * i) ^ (2 * i + 1))
+ || returned[i] != stored[i])
+ __builtin_abort ();
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-loop-live.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-loop-live.c
new file mode 100644
index
0000000000000000000000000000000000000000..27ec5bfc6611a042ac00f8ecbd878b7abbb9f213
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-loop-live.c
@@ -0,0 +1,85 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fno-vect-cost-model -Wno-psabi" } */
+
+#include "tree-vect.h"
+
+typedef unsigned V __attribute__ ((vector_size (64)));
+
+__attribute__ ((noipa)) V
+live (const V *a, V bias, unsigned n)
+{
+ V x = bias;
+ for (unsigned i = 0; i < n; ++i)
+ x = (x + a[i]) ^ bias;
+ return x;
+}
+
+/* Early exits must retain every lane of the last completed iteration. */
+__attribute__ ((noipa)) V
+live_break (const V *a, const unsigned *stop, V bias, unsigned n)
+{
+ V x = bias;
+ for (unsigned i = 0; i < n; ++i)
+ {
+ if (stop[i])
+ break;
+
+ x = (x + a[i]) ^ bias;
+ }
+
+ return x;
+}
+
+static const V a[] = {
+ { 0, 11, 22, 33, 44, 55, 66, 77, 88, 99, 110, 121, 132, 143, 154, 165 },
+ { 7, 18, 29, 40, 51, 62, 73, 84, 95, 106, 117, 128, 139, 150, 161, 172 },
+ { 14, 25, 36, 47, 58, 69, 80, 91, 102, 113, 124, 135, 146, 157, 168, 179 },
+ { 21, 32, 43, 54, 65, 76, 87, 98, 109, 120, 131, 142, 153, 164, 175, 186 },
+ { 28, 39, 50, 61, 72, 83, 94, 105, 116, 127, 138, 149, 160, 171, 182, 193 },
+ { 35, 46, 57, 68, 79, 90, 101, 112, 123, 134, 145, 156, 167, 178, 189, 200 },
+ { 42, 53, 64, 75, 86, 97, 108, 119, 130, 141, 152, 163, 174, 185, 196, 207 },
+ { 49, 60, 71, 82, 93, 104, 115, 126, 137, 148, 159, 170, 181, 192, 203, 214
+ },
+ { 56, 67, 78, 89, 100, 111, 122, 133, 144, 155, 166, 177, 188, 199, 210, 221
+ }
+};
+static const V expected[] = {
+ { 3, 20, 37, 54, 71, 88, 105, 122, 139, 156, 173, 190, 207, 224, 241, 258 },
+ { 0, 11, 30, 97, 52, 215, 194, 189, 104, 99, 438, 393, 412, 399, 378, 165 },
+ { 4, 9, 30, 191, 32, 333, 354, 363, 76, 81, 646, 695, 744, 709, 746, 83 },
+ { 17, 54, 103, 216, 29, 458, 475, 444, 57, 94, 943, 896, 949, 898, 867, 4 },
+ { 37, 66, 183, 312, 25, 590, 603, 612, 45, 74, 1183, 1200, 1153, 1222, 1251,
+ 444 },
+ { 66, 125, 204, 323, 38, 761, 720, 695, 42, 85, 1412, 1531, 1518, 1425,
+ 1384, 895 },
+ { 102, 191, 288, 433, 50, 779, 860, 861, 46, 71, 1720, 1577, 1626, 1699,
+ 1748, 1349 },
+ { 147, 224, 325, 458, 207, 820, 929, 942, 59, 72, 2045, 1650, 1991, 1980,
+ 1897, 1814 },
+ { 199, 264, 425, 554, 363, 964, 1149, 1110, 79, 64, 2097, 1954, 2227, 2204,
+ 2245, 1774 },
+ { 252, 351, 466, 693, 392, 1131, 1182, 1185, 84, 71, 2170, 2285, 2464, 2435,
+ 2406, 1737 }
+};
+
+int
+main (void)
+{
+ check_vect ();
+ for (unsigned n = 0; n <= 9; ++n)
+ for (unsigned end = 0; end <= n; ++end)
+ {
+ unsigned stop[9] = { 0 };
+ if (end < 9)
+ stop[end] = 1;
+
+ V result = live (a, expected[0], n);
+ V early = live_break (a, stop, expected[0], n);
+ if (__builtin_memcmp (&result, &expected[n], sizeof (V))
+ || __builtin_memcmp (&early, &expected[end], sizeof (V)))
+ __builtin_abort ();
+ }
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-phi-cycle.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-phi-cycle.c
new file mode 100644
index
0000000000000000000000000000000000000000..dc16092d4c9b4e3984cab420c4060398e1d0a672
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-phi-cycle.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-additional-options "-O3 -fno-vect-cost-model" } */
+
+#include "tree-vect.h"
+
+__attribute__ ((noipa)) void
+f (unsigned *out, unsigned n)
+{
+ unsigned a = 1, b = 2, c = 3;
+ for (unsigned i = 0; i < n; ++i)
+ {
+ unsigned t = a;
+ a = b;
+ b = c;
+ c = t;
+ }
+
+ out[0] = a;
+ out[1] = b;
+ out[2] = c;
+}
+
+static const unsigned expected[][3] = {
+ { 1, 2, 3 },
+ { 2, 3, 1 },
+ { 3, 1, 2 }
+};
+
+int
+main (void)
+{
+ check_vect ();
+ for (unsigned n = 0; n < 19; ++n)
+ {
+ unsigned out[3];
+ f (out, n);
+ if (__builtin_memcmp (out, expected[n % 3], sizeof (out)))
+ __builtin_abort ();
+ }
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/vect/vect-revectorize-wide-recurrence.c
b/gcc/testsuite/gcc.dg/vect/vect-revectorize-wide-recurrence.c
new file mode 100644
index
0000000000000000000000000000000000000000..ce578201cb61ea34adb8d28c7032a1337faec56b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-revectorize-wide-recurrence.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
+f (V *out, unsigned n)
+{
+ V x = (V) { 1, 2, 3, 4, 5, 6, 7, 8 };
+ V y = (V) { 19, 20, 21, 22, 23, 24, 25, 26 };
+ for (unsigned i = 0; i < n; ++i)
+ {
+ y = x;
+ x = x * 2 + (V) { 1, 3, 5, 7, 9, 11, 13, 15 };
+ }
+
+ out[0] = x;
+ out[1] = y;
+}
+
+static const unsigned expected[][16] = {
+ { 1, 2, 3, 4, 5, 6, 7, 8, 19, 20, 21, 22, 23, 24, 25, 26 },
+ { 3, 7, 11, 15, 19, 23, 27, 31, 1, 2, 3, 4, 5, 6, 7, 8 },
+ { 7, 17, 27, 37, 47, 57, 67, 77, 3, 7, 11, 15, 19, 23, 27, 31 },
+ { 15, 37, 59, 81, 103, 125, 147, 169, 7, 17, 27, 37, 47, 57, 67, 77 },
+ { 31, 77, 123, 169, 215, 261, 307, 353, 15, 37, 59, 81, 103, 125, 147, 169 },
+ { 63, 157, 251, 345, 439, 533, 627, 721, 31, 77, 123, 169, 215, 261, 307,
+ 353 },
+ { 127, 317, 507, 697, 887, 1077, 1267, 1457, 63, 157, 251, 345, 439, 533,
+ 627, 721 },
+ { 255, 637, 1019, 1401, 1783, 2165, 2547, 2929, 127, 317, 507, 697, 887,
+ 1077, 1267, 1457 },
+ { 511, 1277, 2043, 2809, 3575, 4341, 5107, 5873, 255, 637, 1019, 1401, 1783,
+ 2165, 2547, 2929 },
+ { 1023, 2557, 4091, 5625, 7159, 8693, 10227, 11761, 511, 1277, 2043, 2809,
+ 3575, 4341, 5107, 5873 }
+};
+
+int
+main (void)
+{
+ check_vect ();
+ for (unsigned n = 0; n < 10; ++n)
+ {
+ V out[2];
+ f (out, n);
+ if (__builtin_memcmp (out, expected[n], sizeof (out)))
+ __builtin_abort ();
+ }
+
+ return 0;
+}
diff --git
a/gcc/testsuite/gcc.target/aarch64/vect-revectorize-sra-unsupported-vector-1.c
b/gcc/testsuite/gcc.target/aarch64/vect-revectorize-sra-unsupported-vector-1.c
new file mode 100644
index
0000000000000000000000000000000000000000..8369eaf68b622982fc1540e3f60fd8284b59d37f
--- /dev/null
+++
b/gcc/testsuite/gcc.target/aarch64/vect-revectorize-sra-unsupported-vector-1.c
@@ -0,0 +1,22 @@
+/* Check lowering of a loop-carried 256-bit vector on AdvSIMD. */
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=armv8-a" } */
+
+typedef float __attribute__ ((vector_size (32))) TYPE;
+
+void
+foo (TYPE *a, TYPE *b, unsigned n)
+{
+ TYPE x = *a;
+ TYPE y = *b;
+ for (int i = 0; i < n; i++)
+ {
+ y = x;
+ x = x * 2.0;
+ }
+
+ *a = x;
+ *b = y;
+}
+
+/* { dg-final { scan-assembler-times {\tmov\tv[0-9]+\.16b, v[0-9]+\.16b} 2 } }
*/
diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc
index
51c8b42deff08be6951534941fd6afcb0076b52f..5764744c233cb59411716c59cfeaed2977ad9ffc
100644
--- a/gcc/tree-vect-loop-manip.cc
+++ b/gcc/tree-vect-loop-manip.cc
@@ -2284,7 +2284,10 @@ iv_phi_p (stmt_vec_info stmt_info)
return false;
if (STMT_VINFO_DEF_TYPE (stmt_info) == vect_reduction_def
- || STMT_VINFO_DEF_TYPE (stmt_info) == vect_double_reduction_def)
+ || STMT_VINFO_DEF_TYPE (stmt_info) == vect_double_reduction_def
+ /* Internal PHIs include whole vectors and scalar PHI cycles. They
+ have no induction step to advance after vectorization. */
+ || STMT_VINFO_DEF_TYPE (stmt_info) == vect_internal_def)
return false;
return true;
@@ -2407,12 +2410,13 @@ vect_can_advance_ivs_p (loop_vec_info loop_vinfo)
/* Skip virtual phi's. The data dependences that are associated with
virtual defs/uses (i.e., memory accesses) are analyzed elsewhere.
- Skip reduction phis. */
+ Skip reduction and internal cycle phis. */
if (!iv_phi_p (phi_info))
{
if (dump_enabled_p ())
dump_printf_loc (MSG_NOTE, vect_location,
- "reduc or virtual phi. skip.\n");
+ "non-induction phi. skip.\n");
+
continue;
}
@@ -2545,12 +2549,13 @@ vect_update_ivs_after_vectorizer (loop_vec_info
loop_vinfo,
"vect_update_ivs_after_vectorizer: phi: %G",
(gimple *) phi);
- /* Skip reduction and virtual phis. */
+ /* Skip reduction, internal cycle and virtual phis. */
if (!iv_phi_p (phi_info))
{
if (dump_enabled_p ())
dump_printf_loc (MSG_NOTE, vect_location,
- "reduc or virtual phi. skip.\n");
+ "non-induction phi. skip.\n");
+
continue;
}
diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
index
fb28596006a16b558ac1779345b6f7457cdf8438..a6e6d3ab4a4813c0a70946973d3a3ee938c1489c
100644
--- a/gcc/tree-vect-loop.cc
+++ b/gcc/tree-vect-loop.cc
@@ -485,6 +485,46 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo,
class loop *loop)
}
else if (vect_phi_first_order_recurrence_p (loop_vinfo, loop, phi))
STMT_VINFO_DEF_TYPE (stmt_vinfo) = vect_first_order_recurrence;
+ else if (VECTOR_TYPE_P (TREE_TYPE (def)))
+ {
+ tree type = TREE_TYPE (def);
+ unsigned HOST_WIDE_INT nelts;
+ if (TYPE_VECTOR_SUBPARTS (type).is_constant (&nelts)
+ && vect_emulated_vector_p (type)
+ && get_vectype_for_scalar_type (loop_vinfo, type,
+ nelts))
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_NOTE, vect_location,
+ "Detected splittable vector PHI.\n");
+
+ STMT_VINFO_DEF_TYPE (stmt_vinfo) = vect_internal_def;
+ }
+ else if (dump_enabled_p ())
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+ "Unknown def-use cycle pattern.\n");
+ }
+ else if (gimple_bb (phi) == loop->header)
+ {
+ edge latch_e = loop_latch_edge (loop);
+ tree latch_arg = PHI_ARG_DEF_FROM_EDGE (phi, latch_e);
+ gimple *def_stmt
+ = (TREE_CODE (latch_arg) == SSA_NAME
+ ? SSA_NAME_DEF_STMT (latch_arg) : NULL);
+ if (def_stmt
+ && gimple_code (def_stmt) == GIMPLE_PHI
+ && gimple_bb (def_stmt) == loop->header)
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_NOTE, vect_location,
+ "Detected splittable scalar PHI cycle.\n");
+
+ STMT_VINFO_DEF_TYPE (stmt_vinfo) = vect_internal_def;
+ }
+ else if (dump_enabled_p ())
+ dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+ "Unknown def-use cycle pattern.\n");
+ }
else
if (dump_enabled_p ())
dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
@@ -1429,6 +1469,23 @@ vect_compute_single_scalar_iteration_cost (loop_vec_info
loop_vinfo)
gimple_stmt_iterator si;
basic_block bb = bbs[i];
+ /* Match the PHI costs charged by vectorizable_phi. An emulated
+ input PHI represents one PHI for each piece after vector lowering. */
+ for (auto gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gphi *phi = gsi.phi ();
+ stmt_vec_info stmt_info = loop_vinfo->lookup_stmt (phi);
+ tree type = TREE_TYPE (gimple_phi_result (phi));
+ if (vect_can_revectorize_type_p (type)
+ && vect_emulated_vector_p (type)
+ && gimple_phi_num_args (phi) > 1
+ && (STMT_VINFO_RELEVANT_P (stmt_info)
+ || STMT_VINFO_LIVE_P (stmt_info)))
+ record_stmt_cost (&LOOP_VINFO_SCALAR_ITERATION_COST (loop_vinfo),
+ vect_scalar_stmt_count (phi),
+ scalar_stmt, stmt_info, 0, vect_body);
+ }
+
for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
{
gimple *stmt = gsi_stmt (si);
@@ -8720,7 +8777,7 @@ vect_transform_lc_phi (loop_vec_info loop_vinfo,
/* Vectorizes PHIs. */
bool
-vectorizable_phi (bb_vec_info vinfo,
+vectorizable_phi (vec_info *vinfo,
stmt_vec_info stmt_info,
slp_tree slp_node, stmt_vector_for_cost *cost_vec)
{
@@ -8730,6 +8787,14 @@ vectorizable_phi (bb_vec_info vinfo,
if (STMT_VINFO_DEF_TYPE (stmt_info) != vect_internal_def)
return false;
+ /* At VF 1, a PHI cycle such as A <- B <- C <- A still advances once
+ per iteration when its values are packed into vector lanes. A larger
+ VF would need to advance the cycle by VF steps, which this generic
+ PHI transformation does not implement. */
+ if (loop_vec_info loop_vinfo = dyn_cast <loop_vec_info> (vinfo))
+ if (!known_eq (LOOP_VINFO_VECT_FACTOR (loop_vinfo), 1U))
+ return false;
+
tree vectype = SLP_TREE_VECTYPE (slp_node);
if (cost_vec) /* transformation not required. */
@@ -10265,8 +10330,13 @@ vectorizable_live_operation_1 (loop_vec_info
loop_vinfo, basic_block exit_bb,
gimple_seq stmts = NULL;
tree new_tree;
+ if (VECTOR_TYPE_P (lhs_type))
+ new_tree
+ = gimple_build (&stmts, BIT_FIELD_REF, lhs_type, vec_lhs_phi,
+ TYPE_SIZE (lhs_type), bitstart);
+
/* If bitstart is 0 then we can use a BIT_FIELD_REF */
- if (integer_zerop (bitstart))
+ else if (integer_zerop (bitstart))
{
tree scalar_res = gimple_build (&stmts, BIT_FIELD_REF, TREE_TYPE
(vectype),
vec_lhs_phi, bitsize, bitstart);
@@ -10457,6 +10527,27 @@ vectorizable_live_operation (vec_info *vinfo,
stmt_vec_info stmt_info,
return false;
}
+ /* Use the lhs of the original scalar statement. */
+ gimple *stmt = vect_orig_stmt (stmt_info)->stmt;
+ if (dump_enabled_p () && vec_stmt_p)
+ dump_printf_loc (MSG_NOTE, vect_location, "extracting lane for live "
+ "stmt %G", stmt);
+
+ lhs = gimple_get_lhs (stmt);
+ lhs_type = TREE_TYPE (lhs);
+
+ /* Each source vector occupies consecutive lanes. Reconstruct it once,
+ including when it is not the first vector packed into the node. */
+ if (VECTOR_TYPE_P (lhs_type)
+ && slp_index % TYPE_VECTOR_SUBPARTS (lhs_type).to_constant () != 0)
+ return true;
+
+ /* A whole-vector live-out needs the final value of every source lane.
+ The scalar extraction path does not handle early exits for this case. */
+ if (VECTOR_TYPE_P (lhs_type) && loop_vinfo
+ && LOOP_VINFO_EARLY_BREAKS (loop_vinfo))
+ return false;
+
if (!vec_stmt_p)
{
/* No transformation required. */
@@ -10509,15 +10600,6 @@ vectorizable_live_operation (vec_info *vinfo,
stmt_vec_info stmt_info,
return true;
}
- /* Use the lhs of the original scalar statement. */
- gimple *stmt = vect_orig_stmt (stmt_info)->stmt;
- if (dump_enabled_p ())
- dump_printf_loc (MSG_NOTE, vect_location, "extracting lane for live "
- "stmt %G", stmt);
-
- lhs = gimple_get_lhs (stmt);
- lhs_type = TREE_TYPE (lhs);
-
bitsize = vector_element_bits_tree (vectype);
/* Get the vectorized lhs of STMT and the lane to use (counted in bits). */
@@ -10532,6 +10614,53 @@ vectorizable_live_operation (vec_info *vinfo,
stmt_vec_info stmt_info,
/* In case we need to early break vectorize also get the first stmt. */
tree vec_lhs0 = SLP_TREE_VEC_DEFS (slp_node)[0];
+ /* Assemble split source vectors in the ordinary IL. The existing
+ live-out path then needs only one value, and cleanup can forward its
+ pieces through the exit PHI. */
+ if (VECTOR_TYPE_P (lhs_type)
+ && known_lt (nunits, TYPE_VECTOR_SUBPARTS (lhs_type)))
+ {
+ gimple_seq stmts = NULL;
+ gimple *last_def = nullptr;
+ unsigned nparts
+ = exact_div (TYPE_VECTOR_SUBPARTS (lhs_type), nunits).to_constant ();
+ vec<constructor_elt, va_gc> *v = NULL;
+ vec_alloc (v, nparts);
+ auto defs = SLP_TREE_VEC_DEFS (slp_node);
+ for (unsigned i = vec_entry; i < vec_entry + nparts; ++i)
+ {
+ CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, defs[i]);
+ if (TREE_CODE (defs[i]) != SSA_NAME
+ || SSA_NAME_IS_DEFAULT_DEF (defs[i]))
+ continue;
+
+ /* Permutation optimization can reorder the pieces. Place the
+ constructor after the last definition, rather than the last
+ lane. */
+ gimple *def = SSA_NAME_DEF_STMT (defs[i]);
+ if (!last_def || vect_stmt_dominates_stmt_p (last_def, def))
+ last_def = def;
+ }
+
+ vec_lhs
+ = force_gimple_operand (build_constructor (lhs_type, v), &stmts, true,
+ NULL_TREE);
+ if (!last_def)
+ vinfo->insert_seq_on_entry (stmt_info, stmts);
+ else if (is_a <gphi *> (last_def))
+ {
+ auto si = gsi_after_labels (gimple_bb (last_def));
+ gsi_insert_seq_before (&si, stmts, GSI_SAME_STMT);
+ }
+ else
+ {
+ auto si = gsi_for_stmt (last_def);
+ gsi_insert_seq_after (&si, stmts, GSI_SAME_STMT);
+ }
+
+ vec_index = 0;
+ }
+
/* Get entry to use. */
tree bitstart = bitsize_int (vec_index);
bitstart = int_const_binop (MULT_EXPR, bitsize, bitstart);
@@ -10603,19 +10732,33 @@ vectorizable_live_operation (vec_info *vinfo,
stmt_vec_info stmt_info,
}
else
{
- /* For basic-block vectorization simply insert the lane-extraction. */
- tree bftype = TREE_TYPE (vectype);
- if (VECTOR_BOOLEAN_TYPE_P (vectype))
- bftype = build_nonstandard_integer_type (tree_to_uhwi (bitsize), 1);
- tree new_tree = build3 (BIT_FIELD_REF, bftype,
- vec_lhs, bitsize, bitstart);
gimple_seq stmts = NULL;
- new_tree = force_gimple_operand (fold_convert (lhs_type, new_tree),
- &stmts, true, NULL_TREE);
+ tree new_tree;
+ if (VECTOR_TYPE_P (lhs_type))
+ new_tree
+ = gimple_build (&stmts, BIT_FIELD_REF, lhs_type, vec_lhs,
+ TYPE_SIZE (lhs_type), bitstart);
+ else
+ {
+ /* Extract the live lane for basic-block vectorization. */
+ tree bftype = TREE_TYPE (vectype);
+ if (VECTOR_BOOLEAN_TYPE_P (vectype))
+ bftype = build_nonstandard_integer_type (tree_to_uhwi (bitsize), 1);
+
+ new_tree
+ = build3 (BIT_FIELD_REF, bftype, vec_lhs, bitsize, bitstart);
+ new_tree
+ = force_gimple_operand (fold_convert (lhs_type, new_tree), &stmts,
+ true, NULL_TREE);
+ }
+
if (TREE_CODE (new_tree) == SSA_NAME
&& SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_tree) = 1;
- gimple *vec_stmt = SSA_NAME_DEF_STMT (vec_lhs);
+
+ gimple *vec_stmt
+ = TREE_CODE (vec_lhs) == SSA_NAME
+ ? SSA_NAME_DEF_STMT (vec_lhs) : nullptr;
if (TREE_CODE (vec_lhs) != SSA_NAME
|| SSA_NAME_IS_DEFAULT_DEF (vec_lhs)
|| (vinfo->bbs[0] != gimple_bb (vec_stmt)
diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
index
4990852cbe6795accb0b6d273bff489938b38447..7723bc1b681aee6fe80bc8aeb57a11c2a12ee081
100644
--- a/gcc/tree-vect-slp.cc
+++ b/gcc/tree-vect-slp.cc
@@ -52,6 +52,7 @@ along with GCC; see the file COPYING3. If not see
#include "alloc-pool.h"
#include "sreal.h"
#include "predict.h"
+#include "ordered-hash-map.h"
#define REDUC_GROUP_FIRST_ELEMENT(S) \
(gcc_checking_assert (!(S)->dr_aux.dr), (S)->first_element)
@@ -6049,6 +6050,112 @@ vect_lower_load_permutations (loop_vec_info loop_vinfo,
/* Check if there are stmts in the loop can be vectorized using SLP. Build SLP
trees of packed scalar stmts if SLP is possible. */
+/* If LC_PHI is a loop-closed PHI for exit edge E and its incoming value is
+ an only-live scalar statement that SLP can vectorize, return that statement.
+ Otherwise return null. */
+
+static stmt_vec_info
+vect_get_slp_live_lc_phi_def (loop_vec_info loop_vinfo, gphi *lc_phi, edge e)
+{
+ tree def = gimple_phi_arg_def_from_edge (lc_phi, e);
+ stmt_vec_info stmt_info;
+ if (TREE_CODE (def) == SSA_NAME
+ && !virtual_operand_p (def)
+ && (stmt_info = loop_vinfo->lookup_def (def))
+ && ((stmt_info = vect_stmt_to_vectorize (stmt_info)), true)
+ && STMT_VINFO_RELEVANT (stmt_info) == vect_used_only_live
+ && STMT_VINFO_LIVE_P (stmt_info)
+ && !VECTORIZABLE_CYCLE_DEF (STMT_VINFO_DEF_TYPE (stmt_info))
+ && STMT_VINFO_REDUC_IDX (stmt_info) == -1)
+ return stmt_info;
+
+ return NULL;
+}
+
+/* Group scalar live-outs that rebuild a source vector in E's destination.
+ Vector lowering stores each LC PHI into a BIT_FIELD_REF of the original
+ vector. Collect these stores once, indexed by destination and lane. */
+
+static void
+vect_analyze_slp_live_lanes (loop_vec_info loop_vinfo,
+ scalar_stmts_to_slp_tree_map_t *bst_map, edge e,
+ unsigned max_tree_size, unsigned *limit,
+ bool force_single_lane,
+ hash_set<stmt_vec_info> &grouped_live_stmts)
+{
+ ordered_hash_map<tree, vec<stmt_vec_info>> live_vectors;
+ for (auto gsi = gsi_after_labels (e->dest); !gsi_end_p (gsi); gsi_next
(&gsi))
+ {
+ auto assign = dyn_cast <gassign *> (gsi_stmt (gsi));
+ if (!assign)
+ continue;
+
+ tree lhs = gimple_assign_lhs (assign);
+ if (TREE_CODE (lhs) != BIT_FIELD_REF)
+ continue;
+
+ tree obj = TREE_OPERAND (lhs, 0);
+ tree type = TREE_TYPE (obj);
+ if (!vect_can_revectorize_type_p (type))
+ continue;
+
+ bool existed;
+ auto &stmts = live_vectors.get_or_insert (obj, &existed);
+ if (!existed)
+ stmts.safe_grow_cleared (TYPE_VECTOR_SUBPARTS (type).to_constant ());
+
+ /* An empty entry marks an object that cannot be grouped. */
+ if (stmts.is_empty ())
+ continue;
+
+ unsigned elt_bits = vector_element_bits (type);
+ unsigned HOST_WIDE_INT lane;
+ if (!known_eq (bit_field_size (lhs), elt_bits)
+ || !constant_multiple_p (bit_field_offset (lhs), elt_bits, &lane)
+ || lane >= stmts.length ())
+ {
+ stmts.release ();
+ continue;
+ }
+
+ tree rhs = gimple_assign_rhs1 (assign);
+ auto phi
+ = TREE_CODE (rhs) == SSA_NAME
+ ? dyn_cast <gphi *> (SSA_NAME_DEF_STMT (rhs)) : nullptr;
+ stmt_vec_info def
+ = phi && gimple_bb (phi) == e->dest
+ ? vect_get_slp_live_lc_phi_def (loop_vinfo, phi, e) : NULL;
+ if (!def || stmts[lane])
+ stmts.release ();
+ else
+ stmts[lane] = def;
+ }
+
+ for (auto entry : live_vectors)
+ {
+ auto stmts = entry.second;
+ bool complete = !stmts.is_empty ();
+ for (auto stmt : stmts)
+ if (!stmt || grouped_live_stmts.contains (stmt))
+ complete = false;
+
+ if (complete)
+ {
+ auto lane_stmts = stmts.copy ();
+ vec<stmt_vec_info> roots = vNULL;
+ vec<tree> remain = vNULL;
+ if (vect_build_slp_instance (loop_vinfo, slp_inst_kind_reduc_group,
+ lane_stmts, roots, remain,
+ max_tree_size, limit, bst_map,
+ force_single_lane))
+ for (auto stmt : stmts)
+ grouped_live_stmts.add (stmt);
+ }
+
+ stmts.release ();
+ }
+}
+
opt_result
vect_analyze_slp (vec_info *vinfo, unsigned max_tree_size,
bool force_single_lane)
@@ -6154,28 +6261,38 @@ vect_analyze_slp (vec_info *vinfo, unsigned
max_tree_size,
return opt_result::failure_at (vect_location, "SLP build failed.\n");
}
+ hash_set<stmt_vec_info> grouped_live_stmts;
+
+ for (edge e : get_loop_exit_edges (LOOP_VINFO_LOOP (loop_vinfo)))
+ vect_analyze_slp_live_lanes (loop_vinfo, bst_map, e, max_tree_size,
+ &limit, force_single_lane,
+ grouped_live_stmts);
+
/* Make sure to vectorize only-live stmts, usually inductions. */
for (edge e : get_loop_exit_edges (LOOP_VINFO_LOOP (loop_vinfo)))
for (auto gsi = gsi_start_phis (e->dest); !gsi_end_p (gsi);
gsi_next (&gsi))
{
gphi *lc_phi = *gsi;
- tree def = gimple_phi_arg_def_from_edge (lc_phi, e);
- stmt_vec_info stmt_info;
- if (TREE_CODE (def) == SSA_NAME
- && !virtual_operand_p (def)
- && (stmt_info = loop_vinfo->lookup_def (def))
- && ((stmt_info = vect_stmt_to_vectorize (stmt_info)), true)
- && STMT_VINFO_RELEVANT (stmt_info) == vect_used_only_live
- && STMT_VINFO_LIVE_P (stmt_info)
- && !VECTORIZABLE_CYCLE_DEF (STMT_VINFO_DEF_TYPE (stmt_info))
- && STMT_VINFO_REDUC_IDX (stmt_info) == -1)
+ stmt_vec_info stmt_info
+ = vect_get_slp_live_lc_phi_def (loop_vinfo, lc_phi, e);
+ if (stmt_info && !grouped_live_stmts.contains (stmt_info))
{
vec<stmt_vec_info> stmts;
vec<stmt_vec_info> roots = vNULL;
vec<tree> remain = vNULL;
- stmts.create (1);
- stmts.quick_push (vect_stmt_to_vectorize (stmt_info));
+ unsigned HOST_WIDE_INT nelts = 1;
+ tree lhs = gimple_get_lhs (stmt_info->stmt);
+ if (lhs && vect_can_revectorize_type_p (TREE_TYPE (lhs)))
+ {
+ if (!TYPE_VECTOR_SUBPARTS (TREE_TYPE (lhs))
+ .is_constant (&nelts))
+ continue;
+ }
+
+ stmts.create (nelts);
+ for (unsigned HOST_WIDE_INT j = 0; j < nelts; ++j)
+ stmts.quick_push (vect_stmt_to_vectorize (stmt_info));
if (! vect_build_slp_instance (vinfo,
slp_inst_kind_reduc_group,
stmts, roots, remain,
diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
index
4a459e53ccdb68b0f327309febc35133e680bbd8..6f0201e9efd7c5909adf6cfcd0ab031beecd6497
100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -13198,8 +13198,7 @@ vect_analyze_stmt (vec_info *vinfo,
|| vectorizable_shift (vinfo, stmt_info, NULL, node, cost_vec)
|| vectorizable_condition (vinfo, stmt_info, NULL, node, cost_vec)
|| vectorizable_comparison (vinfo, stmt_info, NULL, node, cost_vec)
- || (bb_vinfo
- && vectorizable_phi (bb_vinfo, stmt_info, node, cost_vec))
+ || vectorizable_phi (vinfo, stmt_info, node, cost_vec)
|| (is_a <loop_vec_info> (vinfo)
&& (vectorizable_lane_reducing (as_a <loop_vec_info> (vinfo),
stmt_info, node, cost_vec)
@@ -13342,8 +13341,7 @@ vect_transform_stmt (vec_info *vinfo,
break;
case phi_info_type:
- done = vectorizable_phi (as_a <bb_vec_info> (vinfo),
- stmt_info, slp_node, NULL);
+ done = vectorizable_phi (vinfo, stmt_info, slp_node, NULL);
gcc_assert (done);
break;
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index
7be54e3343daa1ca55b9bcf79bc363902b374445..b97140b65bf407dbbbaafeacc0609a8c9bb5e58e
100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -2756,7 +2756,7 @@ extern bool vect_transform_cycle_phi (loop_vec_info,
stmt_vec_info,
slp_tree, slp_instance);
extern bool vectorizable_lc_phi (loop_vec_info, stmt_vec_info, slp_tree);
extern bool vect_transform_lc_phi (loop_vec_info, stmt_vec_info, slp_tree);
-extern bool vectorizable_phi (bb_vec_info, stmt_vec_info, slp_tree,
+extern bool vectorizable_phi (vec_info *, stmt_vec_info, slp_tree,
stmt_vector_for_cost *);
extern bool vectorizable_recurr (loop_vec_info, stmt_vec_info,
slp_tree, stmt_vector_for_cost *);