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

            Bug ID: 127406
           Summary: Wrong code: `(X + M*N) / N` fold introduces a trapping
                    `INT_MIN / -1` division
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: zyx2024 at sjtu dot edu.cn
  Target Milestone: ---

Created attachment 65596
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65596&action=edit
The triggering test case

GCC folds `(X + M*N) / N` to `X / N + M`.  The source expression below is
fully defined, but the replacement evaluates `INT_MIN / -1`.  On x86_64 the
generated `idivl` raises SIGFPE.

Testcase (`div-intmin.c`, attached):

```c
static int __attribute__((noipa)) f(int x, int m, int n)
{
  if (x > -6 || m < -3 || m > -2 || n < -2 || n > -1)
    return 0;
  return (x + m * n) / n;
}

int main(void)
{
  return f(-2147483647 - 1, -2, -1) != 2147483646;
}
```

The testcase is standalone and includes no headers.

Compiler under test: GCC Git commit
`a426e0a8c5ee9cdcf842ea44dc396069e890a4ca`.

`gcc -v` output (local filesystem paths redacted):

```
Reading specs from <gcc-build>/gcc/specs
COLLECT_GCC=<gcc-build>/gcc/xgcc
COLLECT_LTO_WRAPPER=<gcc-build>/gcc/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: <gcc-source>/configure --prefix=<install-prefix>
--disable-bootstrap --disable-multilib --enable-languages=c
--enable-checking=release --disable-werror CFLAGS=-O2 CXXFLAGS=-O2
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 17.0.0 20260914 (experimental) (GCC)
```

Reproducer command (local build path redacted):

```
$ BUILD=<gcc-build>
$ $BUILD/gcc/xgcc -B$BUILD/gcc -B$BUILD/x86_64-pc-linux-gnu/libgcc
-B$BUILD/x86_64-pc-linux-gnu/libatomic/.libs -std=c11 -Wall -Wextra -O2
div-intmin.c -o test
$ ./test
Floating point exception (core dumped)
$ echo $?
136
```

There are no compiler diagnostics.  The testcase returns 0 at `-O0` and
`-O1`; it raises SIGFPE at `-O2` and `-O3`.

`main` calls `f` with `x = INT_MIN`, `m = -2`, and `n = -1`.  The source
computes:

```
m * n          = 2
x + m * n      = INT_MIN + 2 = -2147483646
(x + m * n)/n  = 2147483646
```

All source intermediates are representable as `int`, and `n` is nonzero.

The `evrp` dump shows the invalid replacement:

```
_14 = x_10(D) / n_12(D);
_13 = m_11(D) + _14;
```

The `match.pd` rule around lines 1163-1181 checks the multiplication and the
original addition for overflow, but does not check whether the newly introduced
`X/N` has a representable quotient.  The sibling
`(X - M*N) / N -> X / N - M` rule has the same issue; `div-minus.c` is attached
as a second testcase.

The failure remains with
`-fno-strict-aliasing -fwrapv -fno-aggressive-loop-optimizations`.  With
`-fsanitize=address,undefined`, the testcase is clean at `-O0`; at `-O2`, ASan
catches the same optimizer-generated FPE without a UBSan source-operation
diagnostic.  GCC 11.4.0, Clang 14.0.0, and CompCert 3.18 return 0 at `-O2`.
GCC 11 and Clang also return 0 with `-fsanitize=address,undefined`.

Reply via email to