Hi again,
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:
@@ -14106,7 +14159,36 @@ 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 = 1;
+
+ if (loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (vinfo))
+ vf = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
+
+ vf *= SLP_TREE_LANES (op);
+
+ if (maybe_lt (TYPE_VECTOR_SUBPARTS (vectype), vf)
+ && !multiple_p (vf, TYPE_VECTOR_SUBPARTS (vectype)))
So this seems to be a guard for vect_get_num_copies? It seems to me
that if this is only a problem to costing we should fix up there, not
here.
After digging out my notes from October last year, I can confirm that I
added that guard to prevent vect_get_num_copies -> vect_get_num_vectors
from crashing when called from vect_prologue_cost_for_slp:
Final SLP tree for instance 0x4cee590:
node 0x4d6a5d0 (nunits.min=4, nunits.max=4, refcnt=2) vector(4) unsigned
int
op template: MEM[(struct inflate_state *)_1].ncode = _45;
stmt 0 MEM[(struct inflate_state *)_1].ncode = _45;
stmt 1 MEM[(struct inflate_state *)_1].nlen = _39;
stmt 2 MEM[(struct inflate_state *)_1].ndist = _42;
children 0x4d6a690
node 0x4d6a690 (nunits.min=4, nunits.max=4, refcnt=2) vector(4) unsigned
int
op template: _45 = _44 + 4;
stmt 0 _45 = _44 + 4;
stmt 1 _39 = _38 + 257;
stmt 2 _42 = _41 + 1;
children 0x4d6a750 0x4d6ab10
node 0x4d6a750 (nunits.min=4, nunits.max=4, refcnt=2) vector(4) unsigned
int
op template: _44 = _43 & 15;
stmt 0 _44 = _43 & 15;
stmt 1 _38 = _37 & 31;
stmt 2 _41 = _40 & 31;
children 0x4d6a810 0x4d6aa50
node 0x4d6a810 (nunits.min=4, nunits.max=4, refcnt=2) vector(4) unsigned
int
op template: _43 = (unsigned int) hold_463;
stmt 0 _43 = (unsigned int) hold_463;
stmt 1 _37 = (unsigned int) hold_1020;
stmt 2 _40 = (unsigned int) hold_460;
children 0x4d6a990
node (external) 0x4d6a990 (nunits.min=18446744073709551615,
nunits.max=1, refcnt=1)
{ hold_463, hold_1020, hold_460 }
node (constant) 0x4d6aa50 (nunits.min=18446744073709551615,
nunits.max=1, refcnt=1)
{ 15, 31, 31 }
node (constant) 0x4d6ab10 (nunits.min=18446744073709551615,
nunits.max=1, refcnt=1)
{ 4, 257, 1 }
(gdb)
#5 0x0000000001f9c498 in vect_prologue_cost_for_slp (
vinfo=0x4e4e2e0, node=0x4d6a990, cost_vec=0xffffffffdca0)
at ../../gcc/tree-vect-slp.cc:8269
8269 unsigned nvectors = vect_get_num_copies (vinfo, node);
(gdb) print *node
$42 = {
children = {m_vec = 0x0},
stmts = {m_vec = 0x0},
ops = {m_vec = 0x4cbf1b0},
representative = 0x0,
cycle_info = {id = -1, reduc_idx = -1},
load_permutation = {m_vec = 0x0},
lane_permutation = {m_vec = 0x0},
vectype = 0xfffff7436738,
vec_defs = {m_vec = 0x0},
refcnt = 1,
nunits = {
min = {coeffs = {18446744073709551615, 0}},
max = {coeffs = {1, 0}}
},
def_type = vect_external_def,
lanes = 3,
code = ERROR_MARK,
gs_scale = 0,
gs_base = 0x0,
ldst_lanes = false,
avoid_stlf_fail = false,
can_use_mask = false,
can_use_len = false,
can_use_partial_vectors = true,
vertex = 4,
type = undef_vec_info_type,
data = 0x0,
failed = 0x0,
next_node = 0x4d6a810,
prev_node = 0x4d6aa50
}
during GIMPLE pass: slp
dump file: libz_a-infback.c.192t.slp1
../../zlib/infback.c: In function ‘inflateBack’:
../../zlib/infback.c:242:13: internal compiler error: in exact_div, at
poly-int.h:2179
242 | int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR
*in_desc,
| ^~~~~~~~~~~
0x3a162bb internal_error(char const*, ...)
../../gcc/diagnostic-global-context.cc:786
0x3a25c2f fancy_abort(char const*, int, char const*)
../../gcc/diagnostics/context.cc:1803
0x151916f poly_int<2u, poly_result<unsigned long, if_nonpoly<unsigned
long, unsigned long, poly_int_traits<unsigned long>::is_poly>::type,
poly_coeff_pair_traits<unsigned long, if_nonpoly<unsigned long, unsigned
long, poly_int_traits<unsigned
long>::is_poly>::type>::result_kind>::type> exact_div<2u, unsigned long,
unsigned long>(poly_int<2u, unsigned long> const&, unsigned long)
../../gcc/poly-int.h:2179
0x151494f poly_int<2u, poly_result<unsigned long, unsigned long,
poly_coeff_pair_traits<unsigned long, unsigned
long>::result_kind>::type> exact_div<2u, unsigned long, unsigned
long>(poly_int<2u, unsigned long> const&, poly_int<2u, unsigned long>
const&)
../../gcc/poly-int.h:2192
0x1f36dcb vect_get_num_vectors(poly_int<2u, unsigned long>, tree_node*)
../../gcc/tree-vectorizer.h:2315
0x1f36ed3 vect_get_num_copies(vec_info*, _slp_tree*)
../../gcc/tree-vectorizer.h:2336
0x1f9c497 vect_prologue_cost_for_slp
../../gcc/tree-vect-slp.cc:8269
0x1f9cccf vect_slp_analyze_node_operations
../../gcc/tree-vect-slp.cc:8432
0x1f9c8c7 vect_slp_analyze_node_operations
../../gcc/tree-vect-slp.cc:8355
0x1f9c8c7 vect_slp_analyze_node_operations
../../gcc/tree-vect-slp.cc:8355
0x1f9c8c7 vect_slp_analyze_node_operations
../../gcc/tree-vect-slp.cc:8355
0x1f9de5b vect_slp_analyze_operations(vec_info*)
../../gcc/tree-vect-slp.cc:8782
0x1fa1e13 vect_slp_analyze_bb_1
../../gcc/tree-vect-slp.cc:9829
0x1fa1feb vect_slp_region
../../gcc/tree-vect-slp.cc:9876
0x1fa3127 vect_slp_bbs
../../gcc/tree-vect-slp.cc:10099
0x1fa38c3 vect_slp_function(function*)
../../gcc/tree-vect-slp.cc:10221
0x1fd18bb execute
../../gcc/tree-vectorizer.cc:1568
As I understand for SVE you're always having a single vector given
you want to use AdvSIMD for full vector parts, like with 7 int lanes
you force split during SLP discovery to get 4 lanes AdvSIMD and
3 lanes with predicated tail and SVE? On x86 we can share the
At the assembly language level, that is right:
adrp x1, .LANCHOR0
add x2, x1, :lo12:.LANCHOR0
ldr q29, [x0] ; ASIMD load of 4 ints
ptrue p7.s, vl3
ldr q30, [x2, 16]
ldr q28, [x1, #:lo12:.LANCHOR0]
add z28.s, z28.s, z29.s
str q28, [x0], 16 ; ASIMD store of 4 ints
ld1w z31.s, p7/z, [x0] ; SVE load of 7 ints
add z30.s, z31.s, z30.s
st1w z30.s, p7, [x0] ; SVE store of 7 ints
But in GIMPLE, the same (VLA) vector type is used for both groups after
the split:
note: starting SLP discovery for node 0x5390530
note: get vectype for scalar type (group size 7): uint32_t
note: vectype: vector([4,4]) unsigned int
note: nunits = [4,4]
missed: Build SLP failed: unrolling required in basic block SLP
note: Build SLP for *x_16(D) = _2;
note: Build SLP for MEM[(uint32_t *)x_16(D) + 4B] = _4;
note: Build SLP for MEM[(uint32_t *)x_16(D) + 8B] = _6;
note: Build SLP for MEM[(uint32_t *)x_16(D) + 12B] = _8;
note: Build SLP for MEM[(uint32_t *)x_16(D) + 16B] = _10;
note: Build SLP for MEM[(uint32_t *)x_16(D) + 20B] = _12;
note: Build SLP for MEM[(uint32_t *)x_16(D) + 24B] = _14;
note: SLP discovery for node 0x5390530 failed
note: Splitting SLP group at stmt 4
note: Split group into 4 and 3
...
note: Final SLP tree for instance 0x52ceac0:
note: node 0x53905e8 (max_nunits=4, refcnt=2) vector([4,4]) unsigned int
note: op template: *x_16(D) = _2;
note: stmt 0 *x_16(D) = _2;
note: stmt 1 MEM[(uint32_t *)x_16(D) + 4B] = _4;
note: stmt 2 MEM[(uint32_t *)x_16(D) + 8B] = _6;
note: stmt 3 MEM[(uint32_t *)x_16(D) + 12B] = _8;
note: children 0x53906a0
note: node 0x53906a0 (max_nunits=4, refcnt=2) vector([4,4]) unsigned int
note: op template: _2 = _1 + 1;
note: stmt 0 _2 = _1 + 1;
note: stmt 1 _4 = _3 + 2;
note: stmt 2 _6 = _5 + 1;
note: stmt 3 _8 = _7 + 2;
note: children 0x5390758 0x5390810
note: node 0x5390758 (max_nunits=4, refcnt=2) vector([4,4]) unsigned int
note: op template: _1 = *x_16(D);
note: stmt 0 _1 = *x_16(D);
note: stmt 1 _3 = MEM[(uint32_t *)x_16(D) + 4B];
note: stmt 2 _5 = MEM[(uint32_t *)x_16(D) + 8B];
note: stmt 3 _7 = MEM[(uint32_t *)x_16(D) + 12B];
note: load permutation { 0 1 2 3 }
note: node (constant) 0x5390810 (max_nunits=1, refcnt=1)
note: { 1, 2, 1, 2 }
...
note: Final SLP tree for instance 0x52e95d0:
note: node 0x53908c8 (max_nunits=4, refcnt=2) vector([4,4]) unsigned int
note: op template: MEM[(uint32_t *)x_16(D) + 16B] = _10;
note: stmt 0 MEM[(uint32_t *)x_16(D) + 16B] = _10;
note: stmt 1 MEM[(uint32_t *)x_16(D) + 20B] = _12;
note: stmt 2 MEM[(uint32_t *)x_16(D) + 24B] = _14;
note: children 0x5390980
note: node 0x5390980 (max_nunits=4, refcnt=2) vector([4,4]) unsigned int
note: op template: _10 = _9 + 1;
note: stmt 0 _10 = _9 + 1;
note: stmt 1 _12 = _11 + 2;
note: stmt 2 _14 = _13 + 1;
note: children 0x5390a38 0x5390af0
note: node 0x5390a38 (max_nunits=4, refcnt=2) vector([4,4]) unsigned int
note: op template: _9 = MEM[(uint32_t *)x_16(D) + 16B];
note: stmt 0 _9 = MEM[(uint32_t *)x_16(D) + 16B];
note: stmt 1 _11 = MEM[(uint32_t *)x_16(D) + 20B];
note: stmt 2 _13 = MEM[(uint32_t *)x_16(D) + 24B];
note: load permutation { 4 5 6 }
note: node (constant) 0x5390af0 (max_nunits=1, refcnt=1)
note: { 1, 2, 1 }
vector type so technically we do not need to force split there
(but I think there's no harm done if doing so). So -- why does
if (known_ge (TYPE_VECTOR_SUBPARTS (vectype), vf))
return 1;
in vect_get_num_vectors not trigger and solve the issue?
Before my change in vect_maybe_update_slp_op_vectype, I think the call
that provoked the ICE must have been vect_get_num_vectors(3, V4SI),
which failed because 3 % 4 != 0. I agree that known_ge
This was a false hypothesis. I can find no evidence for it.
(TYPE_VECTOR_SUBPARTS (V4SI), 3) should have been true in that
circumstance. That suggests that I observed the ICE in exact_div (and
then modified vect_maybe_update_slp_op_vectype to fix it) before I made
an unrelated change to make vect_get_num_copies more robust (probably
for other reasons). I will try reverting my fix in
vect_maybe_update_slp_op_vectype to confirm that it is now redundant. If
it is redundant then I will remove it; otherwise, I will look into
modifying vect_prologue_cost_for_slp to avoid the ICE in a different way.
Having experimentally reverted my change to
vect_maybe_update_slp_op_vectype, I discovered that the ICE in
vect_get_num_copies reoccurred with vect_get_num_vectors (3, V2DI) on
x64 when called from the same place: vect_prologue_cost_for_slp. Note
that the group size is greater than the number of subparts in the vector
type. That means known_ge (TYPE_VECTOR_SUBPARTS (vectype), vf) is false
in vect_get_num_copies, therefore that 'if' statement that you asked
about cannot solve this issue.
This is the problematic part of the source code (in zlib/inflate.c):
#define NEEDBITS(n) \
do { \
while (bits < (unsigned)(n)) \
PULLBYTE(); \
} while (0)
/* Return the low n bits of the bit accumulator (n < 16) */
#define BITS(n) \
((unsigned)hold & ((1U << (n)) - 1))
/* Remove n bits from the bit accumulator */
#define DROPBITS(n) \
do { \
hold >>= (n); \
bits -= (unsigned)(n); \
} while (0)
...
unsigned long hold; /* bit buffer */
...
case TABLE:
NEEDBITS(14);
state->nlen = BITS(5) + 257;
DROPBITS(5);
state->ndist = BITS(5) + 1;
DROPBITS(5);
state->ncode = BITS(4) + 4;
DROPBITS(4);
which operates on these struct members:
unsigned ncode; /* number of code length code lengths */
unsigned nlen; /* number of length code lengths */
unsigned ndist; /* number of distance code lengths */
The relevant part of the slp1 dump is:
note: starting SLP discovery for node 0x62444e0
note: get vectype for scalar type (group size 3): unsigned int
note: vectype: vector(4) unsigned int
note: nunits = 4
note: Build SLP for state_848->ncode = _220;
note: Build SLP for state_848->nlen = _214;
note: Build SLP for state_848->ndist = _217;
note: vect_is_simple_use: operand _219 + 4, type of def: internal
note: vect_is_simple_use: operand _213 + 257, type of def: internal
note: vect_is_simple_use: operand _216 + 1, type of def: internal
note: starting SLP discovery for node 0x6244590
note: get vectype for scalar type (group size 3): unsigned int
note: vectype: vector(4) unsigned int
note: nunits = 4
note: Build SLP for _220 = _219 + 4;
note: Build SLP for _214 = _213 + 257;
note: Build SLP for _217 = _216 + 1;
note: vect_is_simple_use: operand _218 & 15, type of def: internal
note: vect_is_simple_use: operand 4, type of def: constant
note: vect_is_simple_use: operand _212 & 31, type of def: internal
note: vect_is_simple_use: operand 257, type of def: constant
note: vect_is_simple_use: operand _215 & 31, type of def: internal
note: vect_is_simple_use: operand 1, type of def: constant
note: starting SLP discovery for node 0x6244640
note: get vectype for scalar type (group size 3): unsigned int
note: vectype: vector(4) unsigned int
note: nunits = 4
note: Build SLP for _219 = _218 & 15;
note: Build SLP for _213 = _212 & 31;
note: Build SLP for _216 = _215 & 31;
note: vect_is_simple_use: operand (unsigned int) hold_892, type of
def: internal
note: vect_is_simple_use: operand 15, type of def: constant
note: vect_is_simple_use: operand (unsigned int) hold_1726, type of
def: internal
note: vect_is_simple_use: operand 31, type of def: constant
note: vect_is_simple_use: operand (unsigned int) hold_889, type of
def: internal
note: vect_is_simple_use: operand 31, type of def: constant
note: starting SLP discovery for node 0x62446f0
note: get vectype for scalar type (group size 3): unsigned int
note: vectype: vector(4) unsigned int
note: nunits = 4
note: Build SLP for _218 = (unsigned int) hold_892;
note: Build SLP for _212 = (unsigned int) hold_1726;
note: Build SLP for _215 = (unsigned int) hold_889;
note: vect_is_simple_use: operand hold_1726 >> 10, type of def: internal
note: vect_is_simple_use: operand hold_1726 = PHI <hold_903(178),
hold_641(19)>, type of def: internal
note: vect_is_simple_use: operand hold_1726 >> 5, type of def: internal
note: starting SLP discovery for node 0x62447a0
note: get vectype for scalar type (group size 3): long unsigned int
note: vectype: vector(2) long unsigned int
note: nunits = 2
missed: Build SLP failed: unrolling required in basic block SLP
note: Build SLP for hold_892 = hold_1726 >> 10;
note: Build SLP for hold_1726 = PHI <hold_903(178), hold_641(19)>
missed: Build SLP failed: different operation in stmt hold_1726 = PHI
<hold_903(178), hold_641(19)>
missed: original stmt hold_892 = hold_1726 >> 10;
note: Build SLP for hold_889 = hold_1726 >> 5;
missed: Build SLP failed: different shift arguments in hold_889 =
hold_1726 >> 5;
note: SLP discovery for node 0x62447a0 failed
note: Building vector operands from scalars
note: SLP discovery for node 0x62446f0 succeeded
note: SLP discovery for node 0x6244640 succeeded
note: SLP discovery for node 0x6244590 succeeded
note: SLP discovery for node 0x62444e0 succeeded
note: SLP size 5 vs. limit 1240.
note: Final SLP tree for instance 0x62808b0:
note: node 0x62444e0 (max_nunits=4, refcnt=2) vector(4) unsigned int
note: op template: state_848->ncode = _220;
note: stmt 0 state_848->ncode = _220;
note: stmt 1 state_848->nlen = _214;
note: stmt 2 state_848->ndist = _217;
note: children 0x6244590
note: node 0x6244590 (max_nunits=4, refcnt=2) vector(4) unsigned int
note: op template: _220 = _219 + 4;
note: stmt 0 _220 = _219 + 4;
note: stmt 1 _214 = _213 + 257;
note: stmt 2 _217 = _216 + 1;
note: children 0x6244640 0x62449b0
note: node 0x6244640 (max_nunits=4, refcnt=2) vector(4) unsigned int
note: op template: _219 = _218 & 15;
note: stmt 0 _219 = _218 & 15;
note: stmt 1 _213 = _212 & 31;
note: stmt 2 _216 = _215 & 31;
note: children 0x62446f0 0x6244900
note: node 0x62446f0 (max_nunits=4, refcnt=2) vector(4) unsigned int
note: op template: _218 = (unsigned int) hold_892;
note: stmt 0 _218 = (unsigned int) hold_892;
note: stmt 1 _212 = (unsigned int) hold_1726;
note: stmt 2 _215 = (unsigned int) hold_889;
note: children 0x6244850
note: node (external) 0x6244850 (max_nunits=1, refcnt=1)
note: { hold_892, hold_1726, hold_889 }
note: node (constant) 0x6244900 (max_nunits=1, refcnt=1)
note: { 15, 31, 31 }
note: node (constant) 0x62449b0 (max_nunits=1, refcnt=1)
note: { 4, 257, 1 }
The node that fails in vect_prologue_cost_for_slp has:
def_type = vect_external_def
lanes = 3
vectype = V2DI
It is a three-element external definition pack:
{ hold_892, hold_1726, hold_889 }
Where each element is defined as:
hold_892 = hold_1726 >> 10;
hold_1726 = PHI <hold_903(178), hold_641(19)>
hold_889 = hold_1726 >> 5
The problematic vectype of V2DI is assigned to the external definition
node by vect_maybe_update_slp_op_vectype (vinfo, slp_op0, vectype_in).
At this point, the parent SLP node (passed to vectorizable_conversion) has:
lanes = 3
vectype = V4SI
def_type = vect_internal_def
And, before its vectype is updated, the operand SLP node has:
lanes = 3
vectype = NULL initially
def_type = vect_external_def
vectorizable_conversion derives:
lhs_type (from TREE_TYPE (scalar_dest)) = unsigned int
vectype_out (from SLP_TREE_VECTYPE (slp_node)) = unsigned V4SI
vectype_in (from vect_is_simple_use) = NULL_TREE
rhs_type (from TREE_TYPE (op0)) = unsigned DI
vectype_in (from get_vectype_for_scalar_type (.., rhs_type, slp_node)) =
unsigned V2DI
It then classifies the operation as:
nunits_out (from TYPE_VECTOR_SUBPARTS (vectype_out)) = 4
nunits_in (from TYPE_VECTOR_SUBPARTS (vectype_in)) = 2
multiple_p (4, 2) == true
modifier = NARROW_DST
Given that get_vectype_for_scalar_type is supposed to select a vector
type compatible with slp_node, and given that the general rule for my
patch series is that SLP nodes must either occupy a whole number of
vectors or one partial vector, I am inclined to think that
get_vectype_for_scalar_type is at fault for choosing unsigned V2DI. I
think it should have chosen NULL_TREE instead.
Does that sound reasonable?
Thanks,
--
Christopher Bazley
Staff Software Engineer, GNU Tools Team.
Arm Ltd, 110 Fulbourn Road, Cambridge, CB1 9NJ, UK.
http://www.arm.com/