On 5/28/26 12:36, Jeffrey Law wrote:
On 5/28/2026 4:21 AM, Abhishek Kaushik wrote:
determine_value_range used ranger information only when the expression
being bounded was itself an SSA_NAME. This missed useful range
information when the expression was a value-preserving conversion of an
SSA_NAME, such as a conversion from uint8_t to int.
This can make niter analysis fall back to the full range of the
converted
type. In turn, vectorization can fail to prove that related induction
variables do not wrap.
Look through chains of integral conversions while each conversion
preserves every value of the source type. Use ranger information for
the
peeled SSA_NAME when available, and otherwise use the static bounds
of the
peeled type. Stop at the first non-value-preserving conversion so that
cases such as uint8_t -> int8_t -> int are not treated as having the
original uint8_t range.
This patch was bootstrapped and regression tested on aarch64-linux-gnu.
gcc/ChangeLog:
* tree-ssa-loop-niter.cc (determine_value_range): Look through
value-preserving integral conversions when determining the range
of VAR. Use the peeled SSA_NAME for ranger and PHI range queries.
Use the peeled type bounds when no ranger range is available.
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/sve2/niter-convert-range.c: New test.
So we very much want to look through those conversions and use the
narrower range. But it seems like ranger should have associated the
narrower range with output SSA_NAME for those conversions and we
should have been using that output SSA_NAME rather chasing things down
through a CONVERT_EXPR.
So I guess the question is how do we get a CONVERT_EXPR in here rather
than an SSA_NAME? That may point to a cleaner solution.
I vaguley recall some issues with convert_expr that were never fully
resolved, and there was a PR for it.. perator_view appears to mostly be
the same as operator_cast that I can see..
Ranger certainly sees through conversions fine.. ie
void foo (int x)
{
if (x < -500 || x > 500)
return;
int y = (char) x;
int z = y;
foo (z);
}
void foo (int x)
{
int z;
int y;
unsigned int x.0_1;
unsigned int _2;
char _3;
<bb 2> :
x.0_1 = (unsigned int) x_5(D);
_2 = x.0_1 + 500;
if (_2 > 1000)
goto <bb 3>; [INV]
else
goto <bb 4>; [INV]
<bb 3> :
// predicted unlikely by early return (on trees) predictor.
goto <bb 5>; [INV]
<bb 4> :
_3 = (char) x_5(D);
y_6 = (int) _3;
foo (y_6);
Non-varying global ranges:
=========================:
y_6 : [irange] int [-128, 127]
as you can see, y_6 has the truncated range from the character cast....
Andrew