https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128482

            Bug ID: 128482
           Summary: [14/15/16/17 Regression] wrong code at -O1: SCEV
                    negates an INT_MIN step in C - {a, +, INT_MIN} and
                    niter records a bound of one execution
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: wrong-code
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: lzy20000419 at outlook dot com
  Target Milestone: ---
              Host: x86_64-pc-linux-gnu
            Target: x86_64-pc-linux-gnu
             Build: x86_64-pc-linux-gnu

```c
__attribute__((noipa)) int f (int n)
{
  int b = 5, d = 0, k = 0;
  for (;;)
    {
      d = -10 - b;
      if (++k >= n)
        break;
      b += -2147483647 - 1;
    }
  return d;
}
int main (void)
{
  if (f (2) != 2147483633)
    __builtin_abort ();
  return 0;
}
```

```
$ gcc -O0 t.c && ./a.out          # ok
$ gcc -O1 t.c && ./a.out          # Aborted
$ gcc -O1 -fwrapv t.c && ./a.out  # ok
$ gcc -O1 -fno-aggressive-loop-optimizations t.c && ./a.out  # ok
```

No undefined behaviour: b takes the values 5 and 5 + INT_MIN, d takes -15 and
2147483633; the addition to b runs once. -fsanitize=undefined (gcc) reports
nothing and clang -O2 is correct. The same happens with long long and
LLONG_MIN, and with other forms that negate the step: d = ~b (fails at -O1 on
trunk too), d = -b + 7, d = C - ~b. Plain d = -b is handled correctly. The
INT_MIN step can also come from a multiplication whose result step is exactly
INT_MIN (no overflow there): d = -414612191 - b * 2 with b += -1073741824 fails
on trunk too.

```c
/* second testcase: bitwise not */
__attribute__((noipa)) int f (int n)
{
  int b = 20, d = 0, k = 0;
  for (;;)
    {
      d = ~b;
      if (++k >= n)
        break;
      b += -2147483647 - 1;
    }
  return d;
}
int main (void)
{
  if (f (2) != 2147483627)
    __builtin_abort ();
  return 0;
}
```

```c
/* third testcase: step of b*2 is exactly INT_MIN, then C - (b*2) negates it */
__attribute__((noipa)) int f (int n)
{
  int b = 13, d = 0, k = 0;
top:
  d = -414612191 - b * 2;
  if (++k < n)
    {
      b += -1073741824;
      goto top;
    }
  return d;
}
int main (void)
{
  if (f (2) != 1732871431)
    __builtin_abort ();
  return 0;
}
```

Rechecked on trunk 17.0.0 20261105 (local b76d14f773; godbolt cgsnapshot
bd921def14a4). Wrong at -O1/-O2/-O3 on 14.2.0, 15.3.0, 16.2.0/16.2.1 and trunk
for all three; -O0 and the two workarounds above are fine. At -O1 this is a 14+
regression: 5.4.0 through 13.4.0 are correct, 14.2.0 and later abort. At
-O2/-O3 the same test already aborts from 7.5.0 through trunk (6.3.0 OK).
Godbolt (first testcase at -O1 with execution on trunk, 16.2, 14.2 and 13.2;
13.2 works): https://godbolt.org/z/39noxWc15 On 14.2.0 / trunk -O1 the
optimized dump already has "return -15;" for f.

tree-chrec.cc chrec_fold_plus_1, cst - POLYNOMIAL_CHREC case (:322-340 on
trunk) builds {cst - a, +, chrec_fold_multiply (type, s, -1)}. With s = INT_MIN
the constant product wraps to INT_MIN with TREE_OVERFLOW, so the derived IV d
gets step -2147483648(OVF) although its real increment is +2^31.
infer_loop_bounds_from_signedness (tree-ssa-loop-niter.cc:4519) /
record_nonwrapping_iv (:4218) then take the sign of that wrapped step and
record that the statement is executed at most once; the loop exit is folded and
f returns -15. Trunk added overflow checks for poly+poly step accumulation
(PR125652) and for chrec * cst (PR114074 remainder), but here
chrec_fold_multiply is called with two INTEGER_CSTs, so neither applies.

A possible one-line fix (not tested) is to return chrec_dont_know in the cst -
chrec case when negating the step overflows for a type with undefined overflow
(or do the negation in unsigned), and/or make infer_loop_bounds_from_signedness
reject a step with TREE_OVERFLOW set.

Related: PR114074 (SCEV/chrec folding that ignores signed overflow; still NEW;
c14 noted no remaining GCC 14+ testcase) and PR125652 (poly+poly step overflow)
are nearby, but a fix for either does not cover this cst - chrec INTEGER_CST
negation path.

Disclaimer: This test case was generated by a fuzzer, and the report was
written with LLM assistance.

Reply via email to