Re: [PATCH v14 12/19] vect: Extend BB SLP vectorization to use predicated tails
Richard Biener <[email protected]> Mon, 3 Aug 2026 13:30:50 +0200
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <CAFiYyc2zQn692jL0Vdp_UOqoKArFutbyOyr0D1i205-29TK3Fg@mail.gmail.com> |
On Thu, Jul 30, 2026 at 4:58 PM Christopher Bazley <[email protected]> wrote: > > This enables use of a predicate mask or length limit for > vectorization of basic blocks in cases where previously only the > equivalent rolled (i.e. loop) form of some source code would have > been vectorized. Predication is used for groups whose size > is not neatly divisible into vectors of lengths that can be > supported directly by the target. > > The initial vector mode for an SLP region is "autodetected" by calling > aarch64_preferred_simd_mode, which prefers SVE modes if supported > unless configured otherwise (e.g. VNx4SI for int). If at least one > profitable subgraph can be scheduled then GCC does not try to vectorize > the region using any other modes, even though their estimated costs > might otherwise have been lower. > > For example, if analysis of a 24-byte group succeeds with vector mode > V16QI (using types vector(16) and vector(8) char) then the estimated > cost of the vectorized code is 11+11=22. If analysis of the same group > succeeds with vector mode VNx16QI (using type vector([16,16]) char for > both subtrees) then the estimated cost is 15+15=30. In both cases, the > estimated vectorized cost would beat the estimated scalar cost of > 96+48=144, but vector([16,16]) is chosen because VNx16QI is tried > first. > > This is mitigated by the fact that a sequence of GIMPLE stmts such as: > > vectp.14_86 = x_50(D) + 16; > slp_mask_87 = .WHILE_ULT (0, 8, { 0, ... }); > .MASK_STORE (vectp.14_86, 8B, slp_mask_87, vect__34.12_85); > > are lowered to a fixed-length vector store (e.g., str d30, [x0, 16]) if > possible, instead of a more literal interpretation such as: > > add x0, x0, 16 > ptrue p7.b, vl8 > st1b z30.b, p7, [x0] > > The vect_record_max_nunits function used during building of an SLP > tree is updated to prevent it returning failure for BB SLP if the > group size is not an integral multiple of the number of lanes in the > vector type; it now allows such cases if the group size is known to > be less than or equal to the minimum number of lanes in the vector type. > > For BB SLP, vect_analyze_slp_instance previously gave up after > building an SLP tree if it could not prove that the group size was > at least the maximum lane count across all of the vector types in > the SLP tree (which is unprovable for scalable vector types), or > attempted to split the group if it could prove that the group size > was greater than this maximum but not exactly divisible by it > (which is also unprovable for scalable vector types). > > This function will now provisionally create a new SLP instance if the > group size definitely does not exceed the minimum number of lanes, > even if the group size otherwise satisfies conditions that would > require a loop to be unrolled (e.g., a group of size 3 that uses a > mixture of V4SI and V8HI types). If the group size lies between the > minimum and maximum number of lanes then vectorization is still > abandoned (e.g., a group of size 3 that uses a mixture of > V2DI and V4SI types). > > The vect_prologue_cost_for_slp and vect_create_constant_vectors > functions expect to be able to get the number of vectors needed for > an SLP node by dividing the number of scalar elements needed for the > vectorized statements by the number of subparts in the vector type. > This relies on the scalar elements occupying a whole number of vectors > or else one partial vector, which is true of nodes created during > building of the SLP tree but not always true when a vector type is > assigned to an external definition node during analysis of statements. > Update vect_maybe_update_slp_op_vectype to reject late attempts to > assign a vector type that would result in an external definition > that occupies "one and half" vectors or similar. > > Update test expectations for gcc.dg/vect/vect-over-widen-*.c, > gcc.target/aarch64/sve/slp_6.c and > gcc.target/aarch64/sve/vec_construct_*.c. > > The vec_construct_*.c tests previously expected their output > to use Advanced SIMD instead of SVE despite their use of > vector length agnostic types such as svint16_t and despite > the fact that they are in the aarch64/sve directory. Since > BB SLP can now vectorize these tests using VLA types such > as 'vector([8,8]) char', and because (with one exception) the > resultant code is deemed profitable relative to scalar code, > GCC no longer considers vectorizing using non-VLA types such > as 'vector(8) char' (although the estimated cost with non-VLA > types might have been lower, had it been calculated). > Instruction selection is not the focus of these tests, therefore > I updated them to expect SVE instead (e.g. st1b instead of str) > and added --param=aarch64-autovec-preference=sve-only to reduce > future churn. > > Because the cost model takes into account predicate mask > generation for BB SLP with VLA types, the threshold at which > vectorized code wins against scalar code is higher than > before. The number of elements stored by vec_construct_3.c was > increased just enough to allow for that. > > gcc/ChangeLog: > > * tree-vect-slp.cc (vect_record_max_nunits): For BB SLP > vectorization, allow group sizes that are indivisible by the > vector length if they are known not to exceed it. > (vect_build_slp_tree_1): If building SLP failed because of an > unsupported data type or because unrolling was required then use the > known minimum number of subparts when choosing a splitting point for > a VLA vector type. > (vect_build_slp_tree_2): Don't call > can_duplicate_and_interleave_p when doing basic block SLP > vectorization. > (vect_update_slp_min_nunits_for_node): New recursive function. > Update min_nunits to reflect the minimum number of subparts for > all of the vector types used by an SLP subgraph. > (vect_slp_tree_min_nunits): New function. Initialize min_nunits > then call vect_update_slp_min_nunits_for_node. > (vect_analyze_slp_instance): For BB SLP vectorization, create > a new SLP instance if the group size definitely does not exceed > the minimum number of subparts for all of the vector types used > in the SLP tree, even if the group size otherwise satisfies > conditions that would require a loop to be unrolled. > (vectorizable_slp_permutation_1): Instead of asserting that an > SLP tree node's number of lanes is compatible with the chosen > vector width, return a failure indication if incompatible. > * tree-vect-stmts.cc (get_vectype_for_scalar_type): Execute the > old algorithm to find a narrower vector type that can be used to > carve an SLP group into pieces only if partial vector support is > unavailable for either loads or stores, and only if the number of > subparts may be greater than the group size rather than also if > the number of subparts may be equal to the group size. > Otherwise, assume that using a partial vector is more efficient > than carving the group into smaller pieces. If the natural > vector type is not VLA and it has more subparts than the group > size then find the smallest vector type that contains the group. > (vect_maybe_update_slp_op_vectype): Reject external definitions > when the number of scalar elements represented by the vectorized > operation is not divisible by the number of subparts in a vector > type inferred from the scalar type. This takes into account the > vectorization factor like vect_get_num_copies although it should > be 1 for BB SLP vectorization. > (vect_get_vector_types_for_stmt): Update the description. > > gcc/testsuite/ChangeLog: > > * gcc.dg/vect/vect-over-widen-10.c: Update test expectations to > avoid spurious matching of scan-tree-dump-not pattern. > * gcc.dg/vect/vect-over-widen-13.c: As above. > * gcc.dg/vect/vect-over-widen-14.c: As above. > * gcc.dg/vect/vect-over-widen-17.c: As above. > * gcc.dg/vect/vect-over-widen-18.c: As above. > * gcc.dg/vect/vect-over-widen-5.c: As above. > * gcc.dg/vect/vect-over-widen-6.c: As above. > * gcc.dg/vect/vect-over-widen-7.c: As above. > * gcc.dg/vect/vect-over-widen-8.c: As above. > * gcc.dg/vect/vect-over-widen-9.c: As above. > * gcc.target/aarch64/sve/slp_6.c: Require ld1b, ld1h and ld1w > instructions in the output instead of forbidding them because > discovery of reduction groups now succeeds. Also require > the corresponding st1b, st1h and st1w instructions. > * gcc.target/aarch64/sve/vec_construct_1.c: > Expect SVE instead of ASIMD instructions and add > --param=aarch64-autovec-preference=sve-only to stop > flip-flopping. > * gcc.target/aarch64/sve/vec_construct_2.c: Forbid SVE > instructions as well as ASIMD instructions and add > --param=aarch64-autovec-preference=sve-only to stop > flip-flopping. > * gcc.target/aarch64/sve/vec_construct_3.c: Expect SVE instead > of ASIMD instructions and add > --param=aarch64-autovec-preference=sve-only to avoid > flip-flopping. Increase the number of elements stored to > ensure vectorization using SVE is deemed profitable despite > predicate mask costs. > * gcc.target/aarch64/sve/vec_construct_4.c: > Forbid SVE instructions as well as ASIMD instructions and add > --param=aarch64-autovec-preference=sve-only to stop > flip-flopping. Fix a missing end of comment delimiter. > * gcc.target/aarch64/sve/vec_construct_5.c: As above. > --- > .../gcc.dg/vect/vect-over-widen-10.c | 2 +- > .../gcc.dg/vect/vect-over-widen-13.c | 2 +- > .../gcc.dg/vect/vect-over-widen-14.c | 2 +- > .../gcc.dg/vect/vect-over-widen-17.c | 2 +- > .../gcc.dg/vect/vect-over-widen-18.c | 2 +- > gcc/testsuite/gcc.dg/vect/vect-over-widen-5.c | 2 +- > gcc/testsuite/gcc.dg/vect/vect-over-widen-6.c | 2 +- > gcc/testsuite/gcc.dg/vect/vect-over-widen-7.c | 2 +- > gcc/testsuite/gcc.dg/vect/vect-over-widen-8.c | 2 +- > gcc/testsuite/gcc.dg/vect/vect-over-widen-9.c | 2 +- > gcc/testsuite/gcc.target/aarch64/sve/slp_6.c | 18 ++- > .../gcc.target/aarch64/sve/vec_construct_1.c | 6 +- > .../gcc.target/aarch64/sve/vec_construct_2.c | 4 +- > .../gcc.target/aarch64/sve/vec_construct_3.c | 20 +++- > .../gcc.target/aarch64/sve/vec_construct_4.c | 5 +- > .../gcc.target/aarch64/sve/vec_construct_5.c | 6 +- > gcc/tree-vect-slp.cc | 113 +++++++++++++++--- > gcc/tree-vect-stmts.cc | 97 ++++++++++++--- > 18 files changed, 228 insertions(+), 61 deletions(-) > > diff --git a/gcc/testsuite/gcc.dg/vect/vect-over-widen-10.c b/gcc/testsuite/gcc.dg/vect/vect-over-widen-10.c > index f0140e4ef6d..6efcf739db9 100644 > --- a/gcc/testsuite/gcc.dg/vect/vect-over-widen-10.c > +++ b/gcc/testsuite/gcc.dg/vect/vect-over-widen-10.c > @@ -16,5 +16,5 @@ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* >> 1} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* >> 2} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_cast_forwprop_pattern: detected:[^\n]* \(unsigned char\)} "vect" } } */ > -/* { dg-final { scan-tree-dump-not {vector[^ ]* int} "vect" } } */ > +/* { dg-final { scan-tree-dump-not {vector[^ ]* int vect__} "vect" } } */ > /* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" } } */ > diff --git a/gcc/testsuite/gcc.dg/vect/vect-over-widen-13.c b/gcc/testsuite/gcc.dg/vect/vect-over-widen-13.c > index 08a65ea5518..720353716cf 100644 > --- a/gcc/testsuite/gcc.dg/vect/vect-over-widen-13.c > +++ b/gcc/testsuite/gcc.dg/vect/vect-over-widen-13.c > @@ -48,5 +48,5 @@ main (void) > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* \+} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* / 2} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_cast_forwprop_pattern: detected:[^\n]* = \(signed char\)} "vect" } } */ > -/* { dg-final { scan-tree-dump-not {vector[^ ]* int} "vect" } } */ > +/* { dg-final { scan-tree-dump-not {vector[^ ]* int vect__} "vect" } } */ > /* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" } } */ > diff --git a/gcc/testsuite/gcc.dg/vect/vect-over-widen-14.c b/gcc/testsuite/gcc.dg/vect/vect-over-widen-14.c > index dfa09f5d2ca..f1d5f95c543 100644 > --- a/gcc/testsuite/gcc.dg/vect/vect-over-widen-14.c > +++ b/gcc/testsuite/gcc.dg/vect/vect-over-widen-14.c > @@ -15,5 +15,5 @@ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* \+} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* >> 1} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_cast_forwprop_pattern: detected:[^\n]* = \(unsigned char\)} "vect" } } */ > -/* { dg-final { scan-tree-dump-not {vector[^ ]* int} "vect" } } */ > +/* { dg-final { scan-tree-dump-not {vector[^ ]* int vect__} "vect" } } */ > /* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" } } */ > diff --git a/gcc/testsuite/gcc.dg/vect/vect-over-widen-17.c b/gcc/testsuite/gcc.dg/vect/vect-over-widen-17.c > index 53fcfd0c06c..ac1a0f86727 100644 > --- a/gcc/testsuite/gcc.dg/vect/vect-over-widen-17.c > +++ b/gcc/testsuite/gcc.dg/vect/vect-over-widen-17.c > @@ -46,5 +46,5 @@ main (void) > adopts realign_load scheme. It requires rs6000_builtin_mask_for_load to > generate mask whose return type is vector char. */ > /* { dg-final { scan-tree-dump-not {vector[^\n]*char} "vect" { target vect_hw_misalign } } } */ > -/* { dg-final { scan-tree-dump-not {vector[^ ]* int} "vect" } } */ > +/* { dg-final { scan-tree-dump-not {vector[^ ]* int vect__} "vect" } } */ > /* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" } } */ > diff --git a/gcc/testsuite/gcc.dg/vect/vect-over-widen-18.c b/gcc/testsuite/gcc.dg/vect/vect-over-widen-18.c > index aa58cd1c957..3ebfaa78270 100644 > --- a/gcc/testsuite/gcc.dg/vect/vect-over-widen-18.c > +++ b/gcc/testsuite/gcc.dg/vect/vect-over-widen-18.c > @@ -47,5 +47,5 @@ main (void) > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* |} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* <<} "vect" } } */ > /* { dg-final { scan-tree-dump {vector[^\n]*char} "vect" } } */ > -/* { dg-final { scan-tree-dump-not {vector[^ ]* int} "vect" } } */ > +/* { dg-final { scan-tree-dump-not {vector[^ ]* int vect__} "vect" } } */ > /* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" } } */ > diff --git a/gcc/testsuite/gcc.dg/vect/vect-over-widen-5.c b/gcc/testsuite/gcc.dg/vect/vect-over-widen-5.c > index c2ab11a9d32..1d89789a86d 100644 > --- a/gcc/testsuite/gcc.dg/vect/vect-over-widen-5.c > +++ b/gcc/testsuite/gcc.dg/vect/vect-over-widen-5.c > @@ -49,5 +49,5 @@ main (void) > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* \+ } "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* >> 1} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_cast_forwprop_pattern: detected:[^\n]* \(signed char\)} "vect" } } */ > -/* { dg-final { scan-tree-dump-not {vector[^ ]* int} "vect" } } */ > +/* { dg-final { scan-tree-dump-not {vector[^ ]* int vect__} "vect" } } */ > /* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" } } */ > diff --git a/gcc/testsuite/gcc.dg/vect/vect-over-widen-6.c b/gcc/testsuite/gcc.dg/vect/vect-over-widen-6.c > index bda92c965e0..62d5a52587e 100644 > --- a/gcc/testsuite/gcc.dg/vect/vect-over-widen-6.c > +++ b/gcc/testsuite/gcc.dg/vect/vect-over-widen-6.c > @@ -13,5 +13,5 @@ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* \+ } "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* >> 1} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_cast_forwprop_pattern: detected:[^\n]* \(unsigned char\)} "vect" } } */ > -/* { dg-final { scan-tree-dump-not {vector[^ ]* int} "vect" } } */ > +/* { dg-final { scan-tree-dump-not {vector[^ ]* int vect__} "vect" } } */ > /* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" } } */ > diff --git a/gcc/testsuite/gcc.dg/vect/vect-over-widen-7.c b/gcc/testsuite/gcc.dg/vect/vect-over-widen-7.c > index 1d55e13fb1f..6e09631009a 100644 > --- a/gcc/testsuite/gcc.dg/vect/vect-over-widen-7.c > +++ b/gcc/testsuite/gcc.dg/vect/vect-over-widen-7.c > @@ -51,5 +51,5 @@ main (void) > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* \+ } "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* >> 2} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_cast_forwprop_pattern: detected:[^\n]* \(signed char\)} "vect" } } */ > -/* { dg-final { scan-tree-dump-not {vector[^ ]* int} "vect" } } */ > +/* { dg-final { scan-tree-dump-not {vector[^ ]* int vect__} "vect" } } */ > /* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" } } */ > diff --git a/gcc/testsuite/gcc.dg/vect/vect-over-widen-8.c b/gcc/testsuite/gcc.dg/vect/vect-over-widen-8.c > index 553c0712a79..b6d650beab4 100644 > --- a/gcc/testsuite/gcc.dg/vect/vect-over-widen-8.c > +++ b/gcc/testsuite/gcc.dg/vect/vect-over-widen-8.c > @@ -16,5 +16,5 @@ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* \+ } "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* >> 2} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_cast_forwprop_pattern: detected:[^\n]* \(unsigned char\)} "vect" } } */ > -/* { dg-final { scan-tree-dump-not {vector[^ ]* int} "vect" } } */ > +/* { dg-final { scan-tree-dump-not {vector[^ ]* int vect__} "vect" } } */ > /* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" } } */ > diff --git a/gcc/testsuite/gcc.dg/vect/vect-over-widen-9.c b/gcc/testsuite/gcc.dg/vect/vect-over-widen-9.c > index 36bfc68e053..e82f8a571da 100644 > --- a/gcc/testsuite/gcc.dg/vect/vect-over-widen-9.c > +++ b/gcc/testsuite/gcc.dg/vect/vect-over-widen-9.c > @@ -56,5 +56,5 @@ main (void) > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* >> 1} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_over_widening_pattern: detected:[^\n]* >> 2} "vect" } } */ > /* { dg-final { scan-tree-dump {vect_recog_cast_forwprop_pattern: detected:[^\n]* \(signed char\)} "vect" } } */ > -/* { dg-final { scan-tree-dump-not {vector[^ ]* int} "vect" } } */ > +/* { dg-final { scan-tree-dump-not {vector[^ ]* int vect__} "vect" } } */ > /* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/slp_6.c b/gcc/testsuite/gcc.target/aarch64/sve/slp_6.c > index 44d128477d2..0ac3f09cbdb 100644 > --- a/gcc/testsuite/gcc.target/aarch64/sve/slp_6.c > +++ b/gcc/testsuite/gcc.target/aarch64/sve/slp_6.c > @@ -36,11 +36,21 @@ vec_slp_##TYPE (TYPE *restrict a, TYPE *restrict b, int n) \ > > TEST_ALL (VEC_PERM) > > -/* These loops can't use SLP. */ > -/* { dg-final { scan-assembler-not {\tld1b\t} } } */ > -/* { dg-final { scan-assembler-not {\tld1h\t} } } */ > -/* { dg-final { scan-assembler-not {\tld1w\t} } } */ > +/* When this test was written, these loops couldn't use SLP. Since changes to > + enable tail predication, SLP discovery of size 3 reduction groups instead > + succeeds, producing predicated loads such as ld1b z25.b, p7/z, [x1] for > + {b[0], b[1], b[2]}. */ > + > +/* { dg-final { scan-assembler {\tld1b\t} } } */ > +/* { dg-final { scan-assembler {\tld1h\t} } } */ > +/* { dg-final { scan-assembler {\tld1w\t} } } */ > /* { dg-final { scan-assembler-not {\tld1d\t} } } */ > + > +/* { dg-final { scan-assembler {\tst1b\t} } } */ > +/* { dg-final { scan-assembler {\tst1h\t} } } */ > +/* { dg-final { scan-assembler {\tst1w\t} } } */ > +/* { dg-final { scan-assembler-not {\tst1d\t} } } */ > + > /* { dg-final { scan-assembler {\tld3b\t} } } */ > /* { dg-final { scan-assembler {\tld3h\t} } } */ > /* { dg-final { scan-assembler {\tld3w\t} } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_1.c b/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_1.c > index 2f8ce6808a9..eea13c28e49 100644 > --- a/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_1.c > +++ b/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_1.c > @@ -1,5 +1,5 @@ > /* { dg-do compile } */ > -/* { dg-options "-O2 -ftree-slp-vectorize" } */ > +/* { dg-options "-O2 -ftree-slp-vectorize --param=aarch64-autovec-preference=sve-only" } */ > > /* Test that a group of stores of 8 elements derived from a horizontal > reduction is vectorized by constructing a vector and storing it. > @@ -30,8 +30,8 @@ foo (svint8_t src0, svint8_t src1, svint8_t src2, svint8_t src3, svint8_t src4, > s.h = svaddv_s8 (all, src7); > } > > -/* { dg-final { scan-assembler-times {\tins\tv[0-9]+\.b\[[0-9]+\], v[0-9]+\.b\[[0-9]+\]\n} 7 } } */ > -/* { dg-final { scan-assembler-times {\tstr\td[0-9]+, } 1 } } */ > +/* { dg-final { scan-assembler-times {\tinsr\tz[0-9]+\.h, h[0-9]+\n} 7 } } */ > +/* { dg-final { scan-assembler-times {\tst1b\tz[0-9]+\.h, p[0-9]+, \[x[0-9]+\]\n} 1 } } */ > > /* { dg-final { scan-assembler-not {\tstr\tb[0-9]+, } } } */ > /* { dg-final { scan-assembler-not {\tstrb\tw[0-9]+, } } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_2.c b/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_2.c > index 6715118d7b0..2bf537e13e2 100644 > --- a/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_2.c > +++ b/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_2.c > @@ -1,5 +1,5 @@ > /* { dg-do compile } */ > -/* { dg-options "-O2 -ftree-slp-vectorize" } */ > +/* { dg-options "-O2 -ftree-slp-vectorize --param=aarch64-autovec-preference=sve-only" } */ > > /* Test that a group of stores of 8 elements derived from the results of calls > to a function that has only vector parameters and returns a scalar result is > @@ -40,3 +40,5 @@ foo (svint8_t src0, svint8_t src1, svint8_t src2, svint8_t src3, svint8_t src4, > > /* { dg-final { scan-assembler-not {\tins\tv[0-9]+\.b\[[0-9]+\], w[0-9]+\n} } } */ > /* { dg-final { scan-assembler-not {\tstr\td[0-9]+, } } } */ > +/* { dg-final { scan-assembler-not {\tfmov\th[0-9]+, h[0-9]+\n} } } */ > +/* { dg-final { scan-assembler-not {\tinsr\tz[0-9]+\.b, w[0-9]+\n} } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_3.c b/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_3.c > index 8143d0050ad..ccadaccbcb4 100644 > --- a/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_3.c > +++ b/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_3.c > @@ -1,7 +1,7 @@ > /* { dg-do compile } */ > -/* { dg-options "-O2 -ftree-slp-vectorize" } */ > +/* { dg-options "-O2 -ftree-slp-vectorize --param=aarch64-autovec-preference=sve-only" } */ > > -/* Test that a group of stores of 8 elements derived from a horizontal > +/* Test that a group of stores of 14 elements derived from a horizontal > reduction is vectorized by constructing a vector and storing it > even if the results of the reductions are narrowed. > Since there are no GPR-to-SIMD register transfers, there is no > @@ -13,12 +13,14 @@ > > struct S > { > - char a, b, c, d, e, f, g, h; > + char a, b, c, d, e, f, g, h, i, j, k, l, m, n; > } s; > > void > foo (svint16_t src0, svint32_t src1, svint16_t src2, svint32_t src3, > - svint32_t src4, svint16_t src5, svint32_t src6, svint16_t src7) > + svint32_t src4, svint16_t src5, svint32_t src6, svint16_t src7, > + svint16_t src8, svint32_t src9, svint16_t src10, svint32_t src11, > + svint32_t src12, svint16_t src13) > { > svbool_t all16 = svptrue_b16 (); > svbool_t all32 = svptrue_b32 (); > @@ -30,10 +32,16 @@ foo (svint16_t src0, svint32_t src1, svint16_t src2, svint32_t src3, > s.f = svminv_s16 (all16, src5); > s.g = svlastb_s32 (svptrue_pat_b32 (SV_VL1), src6); > s.h = svaddv_s16 (all16, src7); > + s.i = svmaxv_s16 (all16, src8); > + s.j = svminv_s32 (all32, src9); > + s.k = svlastb_s16 (svptrue_pat_b16 (SV_VL1), src10); > + s.l = svaddv_s32 (all32, src11); > + s.m = svmaxv_s32 (all32, src12); > + s.n = svminv_s16 (all16, src13); > } > > -/* { dg-final { scan-assembler-times {\tins\tv[0-9]+\.b\[[0-9]+\], v[0-9]+\.b\[[0-9]+\]\n} 7 } } */ > -/* { dg-final { scan-assembler-times {\tstr\td[0-9]+, } 1 } } */ > +/* { dg-final { scan-assembler-times {\tinsr\tz[0-9]+\.b, b[0-9]+\n} 13 } } */ > +/* { dg-final { scan-assembler-times {\tst1b\tz[0-9]+\.b, p[0-9]+, \[x[0-9]\]\n} 1 } } */ > > /* { dg-final { scan-assembler-not {\tstr\tb[0-9]+, } } } */ > /* { dg-final { scan-assembler-not {\tstrb\tw[0-9]+, } } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_4.c b/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_4.c > index 49f8114b64c..3d41af684a3 100644 > --- a/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_4.c > +++ b/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_4.c > @@ -1,5 +1,5 @@ > /* { dg-do compile } */ > -/* { dg-options "-O2 -ftree-slp-vectorize" } */ > +/* { dg-options "-O2 -ftree-slp-vectorize --param=aarch64-autovec-preference=sve-only" } */ > > /* Test that a group of stores of 8 elements derived from a horizontal > reduction is not vectorized by constructing a vector and storing it > @@ -33,5 +33,6 @@ foo (svint16_t src0, svint8_t src1, svint16_t src2, svint8_t src3, > /* { dg-final { scan-assembler-times {\tstp\tw[0-9]+, w[0-9]+,} 4 } } */ > > /* { dg-final { scan-assembler-not {\tins\tv[0-9]+\.s\[[0-9]+\], w[0-9]+\n} } } */ > -/* { dg-final { scan-assembler-not {\tfmov\ts[0-9]+, w[0-9]+\n} } } > +/* { dg-final { scan-assembler-not {\tfmov\ts[0-9]+, w[0-9]+\n} } } */ > /* { dg-final { scan-assembler-not {\tstp\tq[0-9]+, q[0-9]+,} } } */ > +/* { dg-final { scan-assembler-not {\tinsr\tz[0-9]+.s, w[0-9]+\n} } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_5.c b/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_5.c > index 983d6c69ebc..89e57406c0e 100644 > --- a/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_5.c > +++ b/gcc/testsuite/gcc.target/aarch64/sve/vec_construct_5.c > @@ -1,5 +1,5 @@ > /* { dg-do compile } */ > -/* { dg-options "-O2 -ftree-slp-vectorize" } */ > +/* { dg-options "-O2 -ftree-slp-vectorize --param=aarch64-autovec-preference=sve-only" } */ > > /* Test that a group of stores of 8 elements derived from lane extractions is > vectorized by constructing a vector and storing it. Since there are no > @@ -30,8 +30,8 @@ foo (svint8_t src0, svint8_t src1, svint8_t src2, svint8_t src3, svint8_t src4, > s.h = svlastb_s8 (p, src7); > } > > -/* { dg-final { scan-assembler-times {\tins\tv[0-9]+\.b\[[0-9]+\], v[0-9]+\.b\[[0-9]+\]\n} 7 } } */ > -/* { dg-final { scan-assembler-times {\tstr\td[0-9]+, } 1 } } */ > +/* { dg-final { scan-assembler-times {\tinsr\tz[0-9]+\.h, h[0-9]+\n} 7 } } */ > +/* { dg-final { scan-assembler-times {\tst1b\tz[0-9]+\.h, p[0-9]+, \[x[0-9]+\]\n} 1 } } */ > > /* { dg-final { scan-assembler-not {\tstr\tb[0-9]+, } } } */ > /* { dg-final { scan-assembler-not {\tstrb\tw[0-9]+, } } } */ > diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc > index 7bb3d400ca0..ca9bae97e7e 100644 > --- a/gcc/tree-vect-slp.cc > +++ b/gcc/tree-vect-slp.cc > @@ -1114,8 +1114,12 @@ vect_record_max_nunits (vec_info *vinfo, stmt_vec_info stmt_info, > } > > /* If populating the vector type requires unrolling then fail > - before adjusting *max_nunits for basic-block vectorization. */ > + before adjusting *max_nunits for basic-block vectorization. > + Allow group sizes that are indivisible by the vector length only if they > + are known not to exceed the vector length. We may be able to support such > + cases by generating constant masks. */ > if (is_a <bb_vec_info> (vinfo) > + && maybe_gt (group_size, TYPE_VECTOR_SUBPARTS (vectype)) > && !multiple_p (group_size, TYPE_VECTOR_SUBPARTS (vectype))) > { > if (dump_enabled_p ()) > @@ -1693,16 +1697,22 @@ vect_build_slp_tree_1 (vec_info *vinfo, unsigned char *swap, > > if (maybe_soft_fail) > { > - unsigned HOST_WIDE_INT const_nunits; > - if (!TYPE_VECTOR_SUBPARTS > - (soft_fail_nunits_vectype).is_constant (&const_nunits) > - || const_nunits > group_size) > + /* Use the known minimum number of subparts for VLA because we still need > + to choose a splitting point although the choice is more arbitrary. */ > + unsigned HOST_WIDE_INT const_nunits = constant_lower_bound ( > + TYPE_VECTOR_SUBPARTS (soft_fail_nunits_vectype)); > + > + if (const_nunits > group_size) > matches[0] = false; > else > { > /* With constant vector elements simulate a mismatch at the > point we need to split. */ > + gcc_assert ((const_nunits & (const_nunits - 1)) == 0); > unsigned tail = group_size & (const_nunits - 1); > + if (tail == 0) > + tail = const_nunits; > + gcc_assert (group_size >= tail); > memset (&matches[group_size - tail], 0, sizeof (bool) * tail); > } > return false; > @@ -2442,13 +2452,21 @@ vect_build_slp_tree_2 (vec_info *vinfo, slp_tree node, > /* Check whether we can build the invariant. If we can't > we never will be able to. */ > tree type = TREE_TYPE (chains[0][n].op); > - if (!GET_MODE_SIZE (vinfo->vector_mode).is_constant () > - && (TREE_CODE (type) == BOOLEAN_TYPE > - || !can_duplicate_and_interleave_p (vinfo, group_size, > - type))) > + if (!GET_MODE_SIZE (vinfo->vector_mode).is_constant ()) > { > - matches[0] = false; > - goto out; > + if (TREE_CODE (type) == BOOLEAN_TYPE) > + { > + matches[0] = false; > + goto out; > + } > + > + if (!is_a<bb_vec_info> (vinfo) > + && !can_duplicate_and_interleave_p (vinfo, group_size, > + type)) > + { > + matches[0] = false; > + goto out; > + } > } > } > else if (dt != vect_internal_def) > @@ -2881,7 +2899,7 @@ out: > uniform_val = NULL_TREE; > break; > } > - if (!uniform_val > + if (!uniform_val && !is_a<bb_vec_info> (vinfo) > && !can_duplicate_and_interleave_p (vinfo, > oprnd_info->ops.length (), > TREE_TYPE (op0))) > @@ -5079,6 +5097,53 @@ vect_analyze_slp_reductions (loop_vec_info loop_vinfo, > return true; > } > > +/* Update MIN_NUNITS to reflect the minimum number of subparts for all of the > + vector types used by the SLP subgraph rooted at NODE. VISITED is used to > + avoid reevaluating any node in the subgraph; it thereby prevents infinite > + recursion should a cycle be encountered. The value of MIN_NUNITS will only be > + updated if any node in the subgraph has a vector type with a number of > + subparts that is smaller than the passed-in value of MIN_NUNITS. Before > + calling this function for the first time, initialize MIN_NUNITS to > + UINT64_MAX. */ > + > +static void > +vect_update_slp_min_nunits_for_node (slp_tree node, poly_uint64 &min_nunits, > + hash_set<slp_tree> &visited) > +{ > + if (!node || SLP_TREE_DEF_TYPE (node) != vect_internal_def) > + return; > + > + if (visited.add (node)) > + return; > + > + for (slp_tree child : SLP_TREE_CHILDREN (node)) > + vect_update_slp_min_nunits_for_node (child, min_nunits, visited); > + > + tree vectype = SLP_TREE_VECTYPE (node); > + if (!vectype) > + return; > + > + /* All unit counts have the form vec_info::vector_size * X for some > + rational X, therefore we know the values are ordered. */ > + poly_uint64 nunits = TYPE_VECTOR_SUBPARTS (vectype); > + min_nunits = known_eq (min_nunits, UINT64_MAX) > + ? nunits > + : ordered_min (min_nunits, nunits); > +} > + > +/* For NODE, return the minimum number of subparts for all of the vector > + types used in the given SLP subgraph. */ > + > +static poly_uint64 > +vect_slp_tree_min_nunits (slp_tree node) > +{ > + poly_uint64 min_nunits = UINT64_MAX; > + hash_set<slp_tree> visited; > + vect_update_slp_min_nunits_for_node (node, min_nunits, visited); > + gcc_checking_assert (known_ne (min_nunits, UINT64_MAX)); > + return min_nunits; > +} > + > /* Analyze an SLP instance starting from a group of grouped stores. Call > vect_build_slp_tree to build a tree of packed stmts if possible. > Return FALSE if it's impossible to SLP any stmt in the group. */ > @@ -5148,8 +5213,8 @@ vect_analyze_slp_instance (vec_info *vinfo, > poly_uint64 unrolling_factor > = calculate_unrolling_factor (max_nunits, group_size); > > - if (maybe_ne (unrolling_factor, 1U) > - && is_a <bb_vec_info> (vinfo)) > + if (maybe_ne (unrolling_factor, 1U) && is_a<bb_vec_info> (vinfo) > + && !known_ge (vect_slp_tree_min_nunits (node), group_size)) > { > unsigned HOST_WIDE_INT const_max_nunits; > if (!max_nunits.is_constant (&const_max_nunits) > @@ -5235,8 +5300,10 @@ vect_analyze_slp_instance (vec_info *vinfo, > tree vectype = get_vectype_for_scalar_type (vinfo, scalar_type, > 1 << floor_log2 (i)); > unsigned HOST_WIDE_INT const_nunits; > + poly_uint64 nunits = TYPE_VECTOR_SUBPARTS (vectype); > if (vectype > - && TYPE_VECTOR_SUBPARTS (vectype).is_constant (&const_nunits)) > + && (const_nunits = constant_lower_bound (nunits)) > 1 > + && (i % const_nunits) == 0) > { > /* Split into two groups at the first vector boundary. */ > gcc_assert ((const_nunits & (const_nunits - 1)) == 0); > @@ -11863,7 +11930,21 @@ vectorizable_slp_permutation_1 (vec_info *vinfo, gimple_stmt_iterator *gsi, > unpack_factor = 1; > } > unsigned olanes = unpack_factor * ncopies * SLP_TREE_LANES (node); > - gcc_assert (repeating_p || multiple_p (olanes, nunits)); > + > + /* With fully-predicated BB-SLP, an external node's number of lanes can be > + incompatible with the chosen vector width (e.g., lane packs of 3 with a > + natural 2-lane vector type). */ > + if (!repeating_p && !multiple_p (olanes, nunits)) > + { > + if (dump_p) > + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, > + "unsupported permutation %p: vector type %T," > + " nunits=" HOST_WIDE_INT_PRINT_UNSIGNED > + " ncopies=%" PRIu64 ", lanes=%u and unpack=%u\n", > + (void *) node, vectype, estimated_poly_value (nunits), > + ncopies, SLP_TREE_LANES (node), unpack_factor); > + return -1; > + } > > /* Compute the { { SLP operand, vector index}, lane } permutation sequence > from the { SLP operand, scalar lane } permutation as recorded in the > diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc > index bcc8581b4cc..168d79c91d0 100644 > --- a/gcc/tree-vect-stmts.cc > +++ b/gcc/tree-vect-stmts.cc > @@ -13826,9 +13826,11 @@ get_related_vectype_for_scalar_type (machine_mode prevailing_mode, > /* Function get_vectype_for_scalar_type. > > Returns the vector type corresponding to SCALAR_TYPE as supported > - by the target. If GROUP_SIZE is nonzero and we're performing BB > - vectorization, make sure that the number of elements in the vector > - is no bigger than GROUP_SIZE. */ > + by the target, or NULL_TREE if none. If GROUP_SIZE is nonzero and we're > + performing BB vectorization, ensure that the number of subparts in the > + returned type is no bigger than GROUP_SIZE, or no smaller than GROUP_SIZE if > + a vector mask or length can be used to limit the number of lanes loaded and > + stored. */ > > tree > get_vectype_for_scalar_type (vec_info *vinfo, tree scalar_type, > @@ -13853,11 +13855,23 @@ get_vectype_for_scalar_type (vec_info *vinfo, tree scalar_type, > if (vectype) > vinfo->used_vector_modes.add (TYPE_MODE (vectype)); > > - /* If the natural choice of vector type doesn't satisfy GROUP_SIZE, > - try again with an explicit number of elements. */ > - if (vectype > - && group_size > - && maybe_ge (TYPE_VECTOR_SUBPARTS (vectype), group_size)) > + if (!vectype || !group_size) > + return vectype; > + > + /* The natural choice of vector type satisfies GROUP_SIZE if it can be used to > + store the whole group without a mask or length limit (known_eq) or if it > + can be used to carve the group into pieces (known_lt). */ > + const poly_uint64 natural_nelts = TYPE_VECTOR_SUBPARTS (vectype); > + if (known_le (natural_nelts, group_size)) > + return vectype; > + > + /* We might be able to use a partial vector to handle a GROUP_SIZE that is not > + satisfied by the natural choice of vector type. Assume this is more > + efficient than carving the group into pieces. */ > + if (vect_get_load_store_partial_vector_style (vectype, true) > + == vect_load_store_partial_vectors_none > + || vect_get_load_store_partial_vector_style (vectype, false) > + == vect_load_store_partial_vectors_none) > { > /* Start with the biggest number of units that fits within > GROUP_SIZE and halve it until we find a valid vector type. > @@ -13879,8 +13893,32 @@ get_vectype_for_scalar_type (vec_info *vinfo, tree scalar_type, > nunits /= 2; > } > while (nunits > 1 && !vectype); > + > + return vectype; > + } > + > + /* Interoperability between modes requires one to be a constant multiple of > + the other, so do not try to substitute a narrower fixed-length vector type > + for a variable-length vector type. Do not try to minimize the number of > + elements in a variable-length type either because it can increase the size > + of vector constants (e.g., if QImode values are stored in HImode). */ > + if (!natural_nelts.is_constant ()) > + return vectype; > + > + /* Try to minimize the number of subparts in the partial vector type. Start > + with the smallest number of units that contains GROUP_SIZE and double it > + until we find a valid vector type. */ > + unsigned int nunits = 1 << ceil_log2 (group_size); > + do > + { > + vectype = get_related_vectype_for_scalar_type (vinfo->vector_mode, > + scalar_type, nunits); > + nunits *= 2; > } > + while (known_le (nunits, natural_nelts) && !vectype); > > + /* We should have rediscovered the natural vector type if nothing else. */ > + gcc_assert (vectype != NULL); > return vectype; > } > > @@ -13900,10 +13938,10 @@ get_vectype_for_scalar_type (vec_info *vinfo, tree scalar_type, slp_tree node) > /* Function get_mask_type_for_scalar_type. > > Returns the mask type corresponding to a result of comparison > - of vectors of specified SCALAR_TYPE as supported by target. > - If GROUP_SIZE is nonzero and we're performing BB vectorization, > - make sure that the number of elements in the vector is no bigger > - than GROUP_SIZE. */ > + of vectors of specified SCALAR_TYPE as supported by target or NULL_TREE if > + none. If GROUP_SIZE is nonzero and we're performing BB vectorization, ensure > + that the returned mask type is suitable for use with the vector type that > + would be chosen by get_vectype_for_scalar_type. */ > > tree > get_mask_type_for_scalar_type (vec_info *vinfo, tree scalar_type, > @@ -14173,7 +14211,33 @@ vect_maybe_update_slp_op_vectype (vec_info *vinfo, slp_tree op, tree vectype) > && SLP_TREE_DEF_TYPE (op) == vect_external_def > && SLP_TREE_LANES (op) > 1) > return false; > - (void) vinfo; /* FORNOW */ > + > + /* When the vectorizer falls back to building vector operands from scalars, > + it can create SLP trees with external defs that have a number of lanes not > + divisible by the number of subparts in a vector type naively inferred from > + the scalar type. Reject such types to avoid ICE when later computing the > + prologue cost for invariant operands. */ > + if (SLP_TREE_DEF_TYPE (op) == vect_external_def) > + { > + poly_uint64 vf = vinfo->vectorization_factor; > + > + vf *= SLP_TREE_LANES (op); > + > + if (maybe_lt (TYPE_VECTOR_SUBPARTS (vectype), vf) > + && !multiple_p (vf, TYPE_VECTOR_SUBPARTS (vectype))) Coming here from 4/n, the patch to pass vinfo to all callers (the split patch makes it a bit awkward to review separately). The comment suggests that the caller passes, say, vectype V8SI but for a SLP node with 4 lanes and VF == 1? It would be the callers fault to do so, and we should reject it there instead. Or is it that we fail to zero-pad the external defs for predicated tails? I think we discussed this during review last year? Without now searching for the patch patching vect_get_num_copies, you edit that to /* Return the number of vectors in the context of vectorization region VINFO, needed for a group of statements and a vector type as specified by NODE. */ inline unsigned int vect_get_num_copies (vec_info *vinfo, slp_tree node) { tree vectype = SLP_TREE_VECTYPE (node); unsigned int group_size = SLP_TREE_LANES (node); poly_uint64 nunits; if (loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (vinfo)) nunits = LOOP_VINFO_VECT_FACTOR (loop_vinfo) * group_size; else { /* The group size is not necessarily an integral multiple of the number of subparts in the vector type if doing BB SLP vectorization with predicated tails. */ if (known_ge (TYPE_VECTOR_SUBPARTS (vectype), group_size)) return 1; nunits = group_size; } return vect_get_num_vectors (nunits, vectype); } meaning iff there are less SLP lanes than vector lanes we have a single vector. We discussed SLP splitting constraints here, I don't remember exactly, but I think this constaint doesn't work on x86 if you consider a V3SI -> V3DI promotion which gets you a V4SI and two V2DI vectors, the 2nd with a predicated tail, so vect_get_num_copies would be 2 for the V3DI node. I suppose that we could instead change vect_get_num_vectors to use can_div_away_from_zero_p (which is DIV_CEIL?)? That still fulfills the documented semantics. > + { > + if (dump_enabled_p ()) > + dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location, > + "lanes=" HOST_WIDE_INT_PRINT_UNSIGNED > + " is not divisible by " > + "subparts=" HOST_WIDE_INT_PRINT_UNSIGNED ".\n", > + estimated_poly_value (vf), > + estimated_poly_value ( > + TYPE_VECTOR_SUBPARTS (vectype))); > + return false; > + } > + } > + > SLP_TREE_VECTYPE (op) = vectype; > return true; > } > @@ -14858,9 +14922,10 @@ vect_gen_while_not (gimple_seq *seq, tree mask_type, tree start_index, > > /* Try to compute the vector types required to vectorize STMT_INFO, > returning true on success and false if vectorization isn't possible. > - If GROUP_SIZE is nonzero and we're performing BB vectorization, > - take sure that the number of elements in the vectors is no bigger > - than GROUP_SIZE. > + If GROUP_SIZE is nonzero and we're performing BB vectorization, ensure that > + the number of subparts in *STMT_VECTYPE_OUT is no bigger than GROUP_SIZE, or > + no smaller than GROUP_SIZE if a vector mask or length can be used to limit > + the number of lanes loaded and stored. > > On success: > > -- > 2.43.0 >