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

            Bug ID: 128608
           Summary: wrong code at -O2: ipa-bit-cp turns max(x, 1) of a
                    non-negative parameter into the constant 1
           Product: gcc
           Version: 16.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: ipa
          Assignee: unassigned at gcc dot gnu.org
          Reporter: kris.fabmoos at gmail dot com
  Target Milestone: ---

Created attachment 66130
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=66130&action=edit
Testcase

Hi,

the attached test is miscompiled at -O2 and -O3 by gcc 15.2.0 and 16.2.0 on
x86_64-w64-mingw32. It has no includes, so it is already in preprocessed form.

    static volatile long long sink;

    __attribute__((noinline)) static long long
    callee (long long lb)
    {
      sink = lb;
      return lb;
    }

    __attribute__((noinline)) static long long
    clamp_then_call (long long lb)
    {
      if (lb < 1)
        lb = 1;
      return callee (lb);
    }

    int
    main (int argc, char **argv)
    {
      long long want = argc;

      (void) argv;
      if (want < 1)
        want = 1;
      want += 249999;               /* 250000 when run without arguments */

      if (clamp_then_call (1) != 1)
        __builtin_abort ();
      if (clamp_then_call (want) != want)
        __builtin_abort ();         /* aborts here */
      return 0;
    }

    $ gcc -O2 bug.c -o bug.exe && ./bug.exe
    (aborts)
    $ gcc -O2 -fno-ipa-bit-cp bug.c -o bug.exe && ./bug.exe
    (exit status 0)

clamp_then_call() raises its argument to at least 1 and passes it on to
callee(). main() calls it twice, with the constant 1 and with 250000. In the
-O2 build callee() receives 1 both times, so the second check aborts.

-O3 fails the same way. -O1 is fine, and so is -O2 with -fno-ipa-bit-cp or
with -fno-ipa-cp. There are no warnings with -Wall -Wextra.


What -fdump-ipa-cp-details shows (15.2.0)

The jump function for the call is an arithmetic pass-through with max_expr,
and the value range recorded next to it is correct:

    callsite  clamp_then_call/3 -> callee/2 :
       param 0: PASS THROUGH: 0, op max_expr 1 (in type long long int)
    [irange] long long int [1, +INF] MASK 0x7fffffffffffffff VALUE 0x0

The propagated bits, however, say that callee's parameter is exactly 1:

    Propagated bits info for function clamp_then_call/3:
     param 0: value = 0x0, mask = 0xf..f00000000ffffffff
    Propagated bits info for function callee/2:
     param 0: value = 0x1, mask = 0xf..f0000000000000000

So the range is right and the bits are wrong. It looks as if the bits lattice
evaluates max_expr (x, 1) for an x that is "non-negative, low bits unknown"
and concludes that the result is the constant 1. We have not read the code
closely enough to say why. One thing we noticed is that the mask has every
bit above the type's precision set, which might make the largest possible x
look negative in a signed comparison - but that is only a guess.


Other things we tried

A min version of the same test (if (x > 1000) x = 1000; with callers passing
1000 and a value in [0, 1000]) is compiled correctly by both versions.

We only have x86_64-w64-mingw32 here and nothing older than 15.2.0, so we
cannot say whether this is a regression or whether other targets are
affected. As far as we can tell nothing in the test depends on Windows.


How we found it

In a larger C program a function clamps a lower bound like this and hands it
to a checking routine, which then started from 1 on every call. Nothing
crashed. One of our test runs simply took about twenty times as long as it
should have, and that is what made us look. We now build with
-fno-ipa-bit-cp.


Compilers

    gcc.exe (Rev13, Built by MSYS2 project) 15.2.0    fails
    gcc.exe (Rev6, Built by MSYS2 project) 16.2.0     fails

gcc -v of the 16.2.0 build:

    Target: x86_64-w64-mingw32
    Configured with: ../gcc-16.2.0/configure --prefix=/ucrt64
--with-sysroot=/ucrt64 --with-build-sysroot=D:/M/msys64/ucrt64
--with-local-prefix=/local --with-native-system-header-dir=/include
--libexecdir=/ucrt64/lib --enable-bootstrap --enable-checking=release
--with-arch=nocona --with-tune=generic --enable-mingw-wildcard
--enable-languages=c,c++,ada,fortran,lto,objc,obj-c++,jit --enable-shared
--enable-static --enable-libatomic --enable-threads=posix --enable-tls
--enable-graphite --enable-fully-dynamic-string
--enable-libstdcxx-backtrace=yes --enable-libstdcxx-filesystem-ts
--enable-libstdcxx-time --disable-libstdcxx-pch --enable-libgomp
--disable-libssp --disable-multilib --disable-rpath --disable-win32-registry
--disable-nls --disable-werror --disable-symvers --with-libiconv
--with-system-zlib --with-gmp=/ucrt64 --with-mpfr=/ucrt64 --with-mpc=/ucrt64
--with-isl=/ucrt64 --with-pkgversion='Rev6, Built by MSYS2 project'
--with-bugurl=https://github.com/msys2/MINGW-packages/issues --with-gnu-as
--with-gnu-ld --with-libstdcxx-zoneinfo=yes --disable-libstdcxx-debug
--enable-lto --enable-plugin --with-boot-ldflags=-static-libstdc++
--with-stage1-ldflags=-static-libstdc++
    Thread model: posix
    gcc version 16.2.0 (Rev6, Built by MSYS2 project)

Thanks for taking a look.

Reply via email to