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


Reply via email to