Home > other >  Why is GCC 11 compiler producing weird output when optimization is enabled?
Why is GCC 11 compiler producing weird output when optimization is enabled?

Time:03-10

Please take a look at this code:

#include <stdio.h>
#include <stdint.h>

int main()
{
    uint8_t run = 1;

    /* Define variable of interest and set to random value */
    uint32_t dwTime = 0xdeadc0de;

    /* Define uint16_t pointer */
    uint16_t* tmpU16;
    /* Assign least 16 bits from dwTime to tmpU16 by downcasting from uint32_t to uint16_t */
    tmpU16 = (uint16_t*)&dwTime;

    /* Print content of tmpU16 */
    fprintf(stderr, "TEST: x\n", *tmpU16); /* Will print "TEST: c0de" here */

    /* This loop will run exactly once */
    while (run)
    {
        /* Print content of tmpU16 AGAIN (content of tmpU16 should not have changed here!) */
        /* Will print "TEST: 0000" here when optimization is enabled */
        /* Will print "TEST: c0de" here when optimization is disabled */
        fprintf(stderr, "TEST: x\n", *tmpU16);

        /* Increment tmpU16 pointer by 1 (yes, this does not make sense but it should not do any harm
        either, unless something gets written to its address AFTER incrementing */
        tmpU16  ;
        run--;
    }

    return 0;
}

When compiling this code with GCC 11 and optimization enabled (for example -O2) it will produce following output:

TEST: c0de
TEST: 0000

When this code is compiled without optimization (-O0) it will produce:

TEST: c0de
TEST: c0de

I assumed after the first fprintf is executed, the next fprintf inside the while loop will be executed immediately. In between these two fprintfs nothing should happen, hence content of tmpU16 stay unchanged.

However, when I comment out the tmpU16 pointer incrementation, it produces correct output with optimization.

Why is this, what's happening here?

CodePudding user response:

On this line:

tmpU16 = (uint16_t*)&dwTime;

You're committing a strict aliasing violation by treating an object of one type as if it were another incompatible type.

Section 6.5p7 of the C standard lists the conditions in which aliasing may occur:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88)

  • a type compatible with the effective type of the object,
  • a qualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
  • a character type.

88 ) The intent of this list is to specify those circumstances in which an object may or may not be aliased

Using a pointer to a uint16_t to access a uint32_t does not fall under any of these categories. Such a violation triggers undefined behavior.

With gcc in particular, enforcement of strict aliasing rules isn't enabled unless you either use -O2 or greater, or explicitly use -fstrict-aliasing. By enforcing this rule, the compiler is able to make optimizations it wouldn't be able to make otherwise.

  • Related