[Bug c/102800] New: Incorrect UB warning with aggressive-loop-optimizations

phil at phord dot com gcc-bugzilla@gcc.gnu.org
Sun Oct 17 03:49:52 GMT 2021


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

            Bug ID: 102800
           Summary: Incorrect UB warning with
                    aggressive-loop-optimizations
           Product: gcc
           Version: 11.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: phil at phord dot com
  Target Milestone: ---

I found this spurious warning occurs since gcc 11.1.  It does not occur in 10.3
or previous versions that I tested.

I believe this is a different bug from PR100801 because that bug started
appearing in 9.1.

This bug seems to be very sensitive to changes from this minimal code:
----------------[ Source ]-----------------
/* Compiled with: gcc-11 -O2 -c test.c   */
void bar(char x);
void foo(char * begin)
{
    // end = ALIGN(begin, 8);
    char * end = begin + 7 - ((((unsigned long)begin) - 1) % 8);

    if (begin != end) {
        long long x = end - begin;
        do bar(x);
        while(--x != 0);
    }
}

static char buff[1024] = { 0 };
void test(int i)
{
    foo(buff + i * 8);
}

------------[ Compiler output ]-------------

In function 'void foo(char*)',
    inlined from 'void test(int)' at <source>:16:8:
<source>:9:19: warning: iteration 0x8000000000000000 invokes undefined behavior
[-Waggressive-loop-optimizations]
    9 |         while(--x != 0);
      |               ~~~~^~~~
<source>:9:19: note: within this loop
--------------------------------------------

Notice we don't enter the loop unless x will be greater than zero because of
the `begin != end` check, and because end >= begin due to the ALIGN math.

The compiled code appears to be correct. Only the spurious warning is a
problem. (In fact, the compiled code is optimized out where the warning is
generated because the inlined function has nothing to do. But the code is
called from elsewhere, so size and correctness matter, of course.)

I found several workarounds:

> Change x to unsigned.  Produces exact same obj output but no warning:
        unsigned long long x = end - begin;

> Change x to short (int16_t). Produces exact same obj output but no warning:
        short x = end - begin;

> Compare with > 0 instead of != 0. Compiled output is 1 opcode longer:
        while(--x > 0);

> Use calculated x value to determine whether to enter loop.
    long long x = end - begin;
    if (x > 0)
        do bar(x);
        while(--x != 0);

----

I just noticed this bug (spurious warning) appears in 9.3 and earlier if I
change this line from != to <:

    if (begin < end) {

I guess it is this subtle difference that I think is a new bug. But maybe this
is a dup of PR100801 after all.


More information about the Gcc-bugs mailing list