On 19/07/2026 19:56, Christopher Bazley wrote:
On 24/06/2026 11:42, Richard Biener wrote:
On Wed, Jun 3, 2026 at 5:21 PM Christopher Bazley
<[email protected]> wrote:
group_size = 0;
tree vectype = get_related_vectype_for_scalar_type (vinfo-
>vector_mode,
@@ -13787,10 +13832,18 @@ get_vectype_for_scalar_type (vec_info
*vinfo, tree scalar_type,
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))
+ try again with an explicit number of elements. A vector type
satisfies
+ GROUP_SIZE if it is definitely not too long to store the whole
group,
+ or we are able to generate masks to handle the unknown number
of excess
+ lanes that might exist. Otherwise, we must substitute a vector
type that
+ can be used to carve up the group.
+ */
+ if (vectype && group_size
+ && maybe_gt (TYPE_VECTOR_SUBPARTS (vectype), group_size)
+ && (vect_get_partial_vector_style (vectype, true)
+ == vect_partial_vectors_none
+ || vect_get_partial_vector_style (vectype, false)
+ == vect_partial_vectors_none))
this changes _ge to _gt - why? I don't like a partial vector style
query here.
In v1 of this patch series, the equivalent hunk was:
@@ -13270,13 +13315,31 @@ get_vectype_for_scalar_type (vec_info *vinfo, ...
/* If the natural choice of vector type doesn't satisfy GROUP_SIZE,
- try again with an explicit number of elements. */
+ try again with an explicit number of elements. A vector type
satisfies
+ GROUP_SIZE if it could be long enough to store the whole group but
we don't
+ know for sure. (If we know that the vector type is long enough,
then we
+ can generate masks to handle the excess lanes; if we aren't sure
then we
+ must substitute a vector type that can be used to carve up the
group.)
+ */
if (vectype
&& group_size
- && maybe_ge (TYPE_VECTOR_SUBPARTS (vectype), group_size))
+ && maybe_ge (TYPE_VECTOR_SUBPARTS (vectype), group_size)
+ && !known_ge (TYPE_VECTOR_SUBPARTS (vectype), group_size))
{
/* Start with the biggest number of units that fits within
GROUP_SIZE and halve it until we find a valid vector type.
'Changes in v2' in the cover letter also provides relevant context:
- A check in get_vectype_for_scalar_type for whether the natural
choice of vector type satisfies the group size was too simplistic.
Instead of choosing a narrower vector type if the natural vector
type could be long enough but not definitely (variable length, by
proxy), get_len_load_store_mode is now used to explicitly query
whether the target supports mask- or length-limited loads and
stores. With the previous version, GCC preferred the natural vector
type if it was known to be long enough; sometimes that resulted in
better output than a narrower type, but it also caused some
bb-slp-* tests to fail.
The following expression (from the v1 patch) evaluates to false for
TYPE_VECTOR_SUBPARTS (vectype) == group_size:
maybe_ge (TYPE_VECTOR_SUBPARTS (vectype), group_size)
&& !known_ge (TYPE_VECTOR_SUBPARTS (vectype), group_size)
Whereas the following expression evaluates to true for
TYPE_VECTOR_SUBPARTS (vectype) == group_size:
maybe_ge (TYPE_VECTOR_SUBPARTS (vectype), group_size)
So I probably changed the condition to maybe_gt for that reason.
In any case, the adjacent comment says "If the natural choice of vector
type doesn't satisfy GROUP_SIZE..." but a vector type whose number of
subparts is equal to "group_size" does satisfy GROUP_SIZE. In those
circumstances, I expect rounding "group_size" down to the nearest power
of two and calling get_related_vectype_for_scalar_type with the
resultant value of "nunits" to return the same vector type, which seems
redundant.
I know we're setting the actual type up only late, but shouldn't we get
proper failures when analyzing this as partial vectors always but we
cannot
support them later?
Following your suggestion to one possible conclusion, perhaps
get_vectype_for_scalar_type does not need a "group_size" parameter at
all. However, I think that removing the "group_size" parameter might
also remove the opportunity to choose the narrowest type that is at
least as wide as "group_size".
After testing on both x86 and AArch64, it seems to me that "group_size"
is an essential input to normal BB-SLP vector-type selection. It cannot
safely be removed globally. There were a huge number of test
regressions on x86, but few enough on AArch64 that it is possible to
analyse them:
gcc.dg/vect/bb-slp-17.c
gcc.dg/vect/bb-slp-4.c
gcc.target/aarch64/vect_mixed_sizes_14.c
Looking at one of those failures, bb-slp-17.c, it becomes clear that
selecting a vector type without paying attention to "group_size" can
prevent vectorisation altogether:
note: === vect_analyze_slp ===
note: Starting SLP discovery for
note: b[0] = a0_34;
note: b[1] = a1_35;
note: starting SLP discovery for node 0x5347050
note: get vectype for scalar type (group size 2): unsigned int
note: vectype: vector(4) unsigned int
note: nunits = 4
note: Build SLP for b[0] = a0_34;
note: Build SLP for b[1] = a1_35;
note: vect_is_simple_use: operand _1 + 23, type of def: internal
note: vect_is_simple_use: operand _2 + 142, type of def: internal
but later:
note: === vectorizable_operation ===
note: vect_model_simple_cost: inside_cost = 1, prologue_cost = 0 .
note: ==> examining statement: b[0] = a0_34;
note: vect_is_simple_use: operand _1 + 23, type of def: internal
note: the target doesn't have the appropriate partial vectorization
store for vector data type vector(4) unsigned int.
missed: can't operate on partial vectors because the target doesn't
have the appropriate partial vectorization load or store.
missed: SLP store needs but cannot use partial vectors.
missed: not vectorized: relevant stmt not supported: b[0] = a0_34;
note: removing SLP instance operations starting from: b[0] = a0_34;
Whereas, with my proposed patchset, V2SI is chosen to vectorise b[0 ..
1] because:
note: === vect_analyze_slp ===
note: Starting SLP discovery for
note: b[0] = a0_34;
note: b[1] = a1_35;
note: starting SLP discovery for node 0x5347050
note: get vectype for scalar type (group size 2): unsigned int
note: the target doesn't have the appropriate partial vectorization
load for vector data type vector(4) unsigned int.
note: vectype: vector(2) unsigned int
note: nunits = 2
I have been testing a hybrid solution that almost works, but still
causes at least one regression on x64.
Instead of removing "group_size" as a parameter of
get_vectype_for_scalar_type and allowing the natural/prevailing vector
type to be chosen, I keep "group_size".
Instead of using the "group_size" to find "... the biggest number of
units that fits within GROUP_SIZE...", I use it to make sure that the
number of elements in the vector is as small as possible but no smaller
than "group_size":
/* If a group size was specified and the natural choice of vector
type has
more elements than required for a group of that size then try to find
the related vector type that has as few elements as possible
whilst still
having enough. Do not try to reduce the number of elements in
scalable
vector types because it can increase the size of vector constants
(e.g.
if QImode values are stored in HImode containers). */
unsigned HOST_WIDE_INT nunits;
if (vectype && group_size
&& TYPE_VECTOR_SUBPARTS (vectype).is_constant (&nunits))
{
const unsigned int min_nunits = 1 << ceil_log2 (group_size);
while (nunits > min_nunits)
{
nunits /= 2;
tree candidate
= get_related_vectype_for_scalar_type (vinfo->vector_mode,
scalar_type, nunits);
if (!candidate)
break;
vectype = candidate;
}
}
The point of this new heuristic is that, for partial vectors to be
effective, as much of the group as possible should be represented by a
partial vector. I assume that, even if they exist and might be usable,
vector types with more elements than the natural/prevailing vector type
would not be efficient for use as partial vectors.
In the bb-slp-17.c case (and a huge number of others, judging by the
reduced number of regressions), this new heuristic avoids the problem of
"can't operate on partial vectors because the target doesn't have the
appropriate partial vectorisation load or store" during operations
analysis, when it is too late to split the SLP node.
The natural vector type for bb-slp-17.c is V4SI (because 4 * 32 = 128).
V4SI has more subparts than required, so nunits is halved by the new
heuristic and V2SI is chosen instead. Types with fewer subparts are not
considered because min_nunits == 2. V2SI exactly matches "group_size"
because "group_size" is an integral power of 2, therefore bb-slp-17.c
passes.
However, the gcc.target/i386/zext-sse-2.c test regresses on x86. The
test expects output like this:
cvttsd2sil 16(%rdi), %ecx
movupd (%rdi), %xmm1
cvttpd2dq %xmm1, %xmm0
movq %xmm0, %rax
movq %rcx, %rdx
ret
But with the new heuristic outlined above to allow partial vectors, the
following (presumably unacceptable) code is generated instead:
cvttsd2sil 8(%rdi), %eax
cvttsd2sil (%rdi), %edx
cvttsd2sil 16(%rdi), %ecx
movl %eax, -20(%rsp)
movl %edx, -24(%rsp)
movq -24(%rsp), %rax
movq %rcx, %rdx
ret
The reason for the regression is that the SLP group is no longer split.
Previously, get_vectype_for_scalar_type chose V2SI to carve up a group
of 3 ints because the natural vector type of V4SI had more subparts than
required:
note: === vect_analyze_slp ===
note: Starting SLP discovery for
note: D.2986.i = _2;
note: D.2986.j = _4;
note: D.2986.k = _6;
note: starting SLP discovery for node 0x6125b50
note: get vectype for scalar type (group size 3): int
note: vectype: vector(2) int
note: nunits = 2
missed: Build SLP failed: unrolling required in basic block SLP
note: Build SLP for D.2986.i = _2;
note: Build SLP for D.2986.j = _4;
note: Build SLP for D.2986.k = _6;
note: SLP discovery for node 0x6125b50 failed
note: Splitting SLP group at stmt 2
note: Split group into 2 and 1
note: Starting SLP discovery for
note: D.2986.i = _2;
note: D.2986.j = _4;
note: starting SLP discovery for node 0x6125c00
note: get vectype for scalar type (group size 2): int
note: vectype: vector(2) int
note: nunits = 2
note: Build SLP for D.2986.i = _2;
note: Build SLP for D.2986.j = _4;
note: vect_is_simple_use: operand (int) _1, type of def: internal
note: vect_is_simple_use: operand (int) _3, type of def: internal
note: starting SLP discovery for node 0x6125cb0
note: get vectype for scalar type (group size 2): int
note: vectype: vector(2) int
note: nunits = 2
note: Build SLP for _2 = (int) _1;
note: Build SLP for _4 = (int) _3;
note: vect_is_simple_use: operand x_8(D)->x, type of def: internal
note: vect_is_simple_use: operand x_8(D)->y, type of def: internal
This split allowed SLP discovery for the node that feeds three "double"
members into the conversion to "int" to pass despite the fact that the
natural vector type is V2DF, because the group had already been split
into 2 + 1:
note: starting SLP discovery for node 0x6125d60
note: get vectype for scalar type (group size 2): double
note: vectype: vector(2) double
note: nunits = 2
note: Build SLP for _1 = x_8(D)->x;
note: Build SLP for _3 = x_8(D)->y;
note: SLP discovery for node 0x6125d60 succeeded
note: SLP discovery for node 0x6125cb0 succeeded
note: SLP discovery for node 0x6125c00 succeeded
note: SLP size 3 vs. limit 10.
note: Final SLP tree for instance 0x607aed0:
note: node 0x6125c00 (max_nunits=2, refcnt=2) vector(2) int
note: op template: D.2986.i = _2;
note: stmt 0 D.2986.i = _2;
note: stmt 1 D.2986.j = _4;
note: children 0x6125cb0
note: node 0x6125cb0 (max_nunits=2, refcnt=2) vector(2) int
note: op template: _2 = (int) _1;
note: stmt 0 _2 = (int) _1;
note: stmt 1 _4 = (int) _3;
note: children 0x6125d60
note: node 0x6125d60 (max_nunits=2, refcnt=2) vector(2) double
note: op template: _1 = x_8(D)->x;
note: stmt 0 _1 = x_8(D)->x;
note: stmt 1 _3 = x_8(D)->y;
note: load permutation { 0 1 }
With the heuristic outlined above, get_vectype_for_scalar_type instead
allowed the natural vector type of V4SI for the same group of 3 ints, on
the assumption that partial vectors could be available. Consequently
SLP discovery for the node succeeded and the group was not split:
note: === vect_analyze_slp ===
note: Starting SLP discovery for
note: D.2986.i = _2;
note: D.2986.j = _4;
note: D.2986.k = _6;
note: starting SLP discovery for node 0x6128b50
note: get vectype for scalar type (group size 3): int
note: vectype: vector(4) int
note: nunits = 4
note: Build SLP for D.2986.i = _2;
note: Build SLP for D.2986.j = _4;
note: Build SLP for D.2986.k = _6;
note: vect_is_simple_use: operand (int) _1, type of def: internal
note: vect_is_simple_use: operand (int) _3, type of def: internal
note: vect_is_simple_use: operand (int) _5, type of def: internal
note: starting SLP discovery for node 0x6128c00
note: get vectype for scalar type (group size 3): int
note: vectype: vector(4) int
note: nunits = 4
note: Build SLP for _2 = (int) _1;
note: Build SLP for _4 = (int) _3;
note: Build SLP for _6 = (int) _5;
note: vect_is_simple_use: operand x_8(D)->x, type of def: internal
note: vect_is_simple_use: operand x_8(D)->y, type of def: internal
note: vect_is_simple_use: operand x_8(D)->z, type of def: internal
Because the group was not split, SLP discovery for the node that feeds
three "double" members into the conversion to "int" failed, because
get_vectype_for_scalar_type allowed the natural vector type of V2DF and
V2DF cannot hold three scalars:
note: starting SLP discovery for node 0x6128cb0
note: get vectype for scalar type (group size 3): double
note: vectype: vector(2) double
note: nunits = 2
missed: Build SLP failed: unrolling required in basic block SLP
note: Build SLP for _1 = x_8(D)->x;
note: Build SLP for _3 = x_8(D)->y;
note: Build SLP for _5 = x_8(D)->z;
note: SLP discovery for node 0x6128cb0 failed
note: Building vector operands from scalars
note: SLP discovery for node 0x6128c00 succeeded
note: SLP discovery for node 0x6128b50 succeeded
note: SLP size 3 vs. limit 10.
note: Final SLP tree for instance 0x607ded0:
note: node 0x6128b50 (max_nunits=4, refcnt=2) vector(4) int
note: op template: D.2986.i = _2;
note: stmt 0 D.2986.i = _2;
note: stmt 1 D.2986.j = _4;
note: stmt 2 D.2986.k = _6;
note: children 0x6128c00
note: node 0x6128c00 (max_nunits=4, refcnt=2) vector(4) int
note: op template: _2 = (int) _1;
note: stmt 0 _2 = (int) _1;
note: stmt 1 _4 = (int) _3;
note: stmt 2 _6 = (int) _5;
note: children 0x6128d60
note: node (external) 0x6128d60 (max_nunits=1, refcnt=1)
note: { _1, _3, _5 }
Finally, analysis of the vectorised operations failed when lack of
support for partial vectors was discovered when it was too late to do
anything about it:
note: === vect_slp_analyze_operations ===
note: ==> examining statement: _2 = (int) _1;
missed: lanes=3 is not divisible by subparts=2.
missed: incompatible vector types for invariants
missed: not vectorized: relevant stmt not supported: _2 = (int) _1;
note: Building vector operands of 0x6128c00 from scalars instead
note: ==> examining statement: D.2986.i = _2;
note: the target doesn't have the appropriate partial vectorization
store for vector data type vector(4) int.
missed: can't operate on partial vectors because the target doesn't
have the appropriate partial vectorization load or store.
missed: SLP store needs but cannot use partial vectors.
missed: not vectorized: relevant stmt not supported: D.2986.i = _2;
note: removing SLP instance operations starting from: D.2986.i = _2;
missed: not vectorized: bad operation in basic block.
By the time vect_slp_analyze_operations discovered that partial stores
are unsupported, vect_analyze_slp_instance had already returned and the
previous 2 + 1 SLP split was no longer available.
This is why I added the early partial vector style query during SLP
discovery in v2 of this patch series. Would you be willing to accept
that query to avoid the regression outlined above, or accept the
regression, or do you want me to try something different?
Thanks,
--
Christopher Bazley
Staff Software Engineer, GNU Tools Team.
Arm Ltd, 110 Fulbourn Road, Cambridge, CB1 9NJ, UK.
http://www.arm.com/