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

            Bug ID: 95202
           Summary: Assignment to a member is wrongly optimized away by
                    g++ with -fstrict-aliasing
           Product: gcc
           Version: 9.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: syomalina at gmail dot com
  Target Milestone: ---

Created attachment 48559
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=48559&action=edit
the preprocessed file (*.i*) that triggers the bug

The following code produces a wrong ERROR print with -fstrict-aliasing enabled.
It sounds like a commonly reported false-positive bug with aliasing rules
violation from the FAQ, but it's not (imho :-)), as no type-casting and memory
manipulations are involved. 

The problem could be related to the following bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93246, as "the symptoms" are quite
similar and type aliases are involved also here. But for the code below
-fno-ipa-sra doesn't help, only -fno-strict-aliasing does. 

The problem appears with GCC 9.2.0, 9.1.0. It doesn't appear with GCC 9.3 and
10.1. The reason I've thought it would be still useful to report the bug is: a
very fragile code constellation which allows to reproduce the problem. I've
marked in comments below what could be done to prevent the problem from
appearing. It looks like that even a slight change in the optimizer could
"hide" the problem back, what could have happened in the later versions of GCC.
So, it would be great to check what is the root cause of the problem, and if it
was actually fixed in later GCC versions. 

The code below models variable sized array allocated on stack. Then 2x
dimension  array is created with sizes 32 and 4 respectively. The bug happens
in this line:
outerArrItem.innerArray.setSize(innerArraySize);
body.numElements is not updated with the new size (4) for the inner array, and
following access to an element [0] of the inner array raises the error. 

// --------------------------------
#include <iostream>

template <typename T, uint32_t CAPACITY>
class VariableSizedArray
{
public:
    void setSize(uint32_t num) { body.numElements = num; }

    T& operator[](uint32_t idx)
    {
        if (idx >= CAPACITY or idx >= body.numElements)
        {
            raiseIndexErr("", 42, "ERROR: Invalid idx: ", idx, 42,
body.numElements);
        }
        return body.elems[idx];
    }

    // Changes in parameters make the problem to disappear
    static void raiseIndexErr(const char*, uint32_t, const char* error,
uint32_t idx, uint32_t, uint32_t num)
    {
        // If printf is used, problem disappears
        std::cerr << error << idx << "! Capacity: " << CAPACITY << ", number
elements: " << num << ::std::endl;
    }

    // private: // If private -- problem disappears
    struct Data
    {
        uint32_t numElements;
        T elems[CAPACITY];
    };
    using Body = Data; 
    Body body; // If Data is used directly here w/o "using", problem disappears
};

static constexpr int outerArraySize = 32;
static constexpr int innerArraySize = 4;

class InnerArrayItem
{
public:
    void use(){};
    uint8_t dummy[1]{}; // when removed or not an array, problem disappears
};

class OuterArrayItem
{
public:
    uint8_t dummy{0}; // When removed, problem disappears
    VariableSizedArray<InnerArrayItem, innerArraySize> innerArray{};
};

struct TestProblem
{
    // When parameter removed (its always 0) -- problem disappears
    static void run(const uint8_t index0)
    {
        VariableSizedArray<OuterArrayItem, outerArraySize> outerArray{};

        outerArray.setSize(outerArraySize);
        auto& outerArrItem = outerArray[index0];
        outerArrItem.innerArray.setSize(innerArraySize);

        // ERROR: invalid idx is reported for outerArray[0].innerArray[0]
access!
        outerArrItem.innerArray[index0].use();
    }
};

int main()
{
    TestProblem::run(0);
    std::cout << "Test finished." << std::endl;
    return 0;
}

/*
Execution results:

$ g++ --version # same happens for 9.1.0 as well
g++ (GCC) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.

# Just -O1
$ rm gccBug; g++ -o gccBug -Wall -Werror -O1 TestGccBug.cpp && ./gccBug
Test finished.

# -O1 with -fstrict-aliasing
$ rm gccBug; g++ -o gccBug -Wall -Werror -O1 -fstrict-aliasing TestGccBug.cpp
&& ./gccBug 
ERROR: Invalid idx: 0! Capacity: 4, number elements: 0
Test finished.

# Just -O2
$ rm gccBug; g++ -o gccBug -Wall -Werror -O2 TestGccBug.cpp && ./gccBug
ERROR: Invalid idx: 0! Capacity: 4, number elements: 0
Test finished.

# Just -O3
$ rm gccBug; g++ -o gccBug -Wall -Werror -O3 TestGccBug.cpp && ./gccBug
ERROR: Invalid idx: 0! Capacity: 4, number elements: 0
Test finished.

# -O3 with -fno-ipa-sra as recommended in
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93246
$ rm gccBug; g++ -o gccBug -Wall -Werror -O3 -fno-ipa-sra TestGccBug.cpp &&
./gccBug
ERROR: Invalid idx: 0! Capacity: 4, number elements: 0
Test finished.

# -O3 with -fno-ipa-sra and -fno-strict-aliasing
$ rm gccBug; g++ -o gccBug -Wall -Werror -O3 -fno-ipa-sra -fno-strict-aliasing
TestGccBug.cpp && ./gccBug
Test finished.

# -O3 with -fno-strict-aliasing
$ rm gccBug; g++ -o gccBug -Wall -Werror -O3 -fno-strict-aliasing
TestGccBug.cpp && ./gccBug
Test finished.
*/

Verbose output of GCC compilation:

$ g++ -v -save-temps -o gccBug -Wall -Werror -Wextra -O3 TestGccBug.cpp
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/opt/gcc/x86_64/9.2.0a-1/libexec/gcc/x86_64-redhat-linux/9.2.0/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/opt/gcc/x86_64/9.2.0a-1
--exec-prefix=/opt/gcc/x86_64/9.2.0a-1 --bindir=/opt/gcc/x86_64/9.2.0a-1/bin
--sbindir=/opt/gcc/x86_64/9.2.0a-1/sbin
--sysconfdir=/opt/gcc/x86_64/9.2.0a-1/etc
--datadir=/opt/gcc/x86_64/9.2.0a-1/share
--includedir=/opt/gcc/x86_64/9.2.0a-1/include
--libdir=/opt/gcc/x86_64/9.2.0a-1/lib
--libexecdir=/opt/gcc/x86_64/9.2.0a-1/libexec --localstatedir=/var
--sharedstatedir=/var/lib --mandir=/opt/gcc/x86_64/9.2.0a-1/man
--infodir=/opt/gcc/x86_64/9.2.0a-1/info
--with-docdir=/opt/gcc/x86_64/9.2.0a-1/doc --enable-shared
--enable-languages=c,c++ --enable-threads=posix --enable-checking=release
--with-system-zlib --disable-libunwind-exceptions --enable-libssp --enable-lto
--with-gnu-ld --verbose --disable-gtktest --target=x86_64-redhat-linux
--build=x86_64-redhat-linux --host=x86_64-redhat-linux --enable-__cxa_atexit
--enable-pic
Thread model: posix
gcc version 9.2.0 (GCC) 
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-o' 'gccBug' '-Wall' '-Werror'
'-Wextra' '-O3' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /opt/gcc/x86_64/9.2.0a-1/libexec/gcc/x86_64-redhat-linux/9.2.0/cc1plus -E
-quiet -v -D_GNU_SOURCE TestGccBug.cpp -mtune=generic -march=x86-64 -Wall
-Werror -Wextra -O3 -fpch-preprocess -o TestGccBug.ii
ignoring nonexistent directory
"/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/../../../../x86_64-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:

/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/../../../../include/c++/9.2.0

/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/../../../../include/c++/9.2.0/x86_64-redhat-linux

/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/../../../../include/c++/9.2.0/backward
 /opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/include
 /usr/local/include
 /opt/gcc/x86_64/9.2.0a-1/include
 /opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/include-fixed
 /usr/include
End of search list.
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-o' 'gccBug' '-Wall' '-Werror'
'-Wextra' '-O3' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /opt/gcc/x86_64/9.2.0a-1/libexec/gcc/x86_64-redhat-linux/9.2.0/cc1plus
-fpreprocessed TestGccBug.ii -quiet -dumpbase TestGccBug.cpp -mtune=generic
-march=x86-64 -auxbase TestGccBug -O3 -Wall -Werror -Wextra -version -o
TestGccBug.s
GNU C++14 (GCC) version 9.2.0 (x86_64-redhat-linux)
        compiled by GNU C version 9.2.0, GMP version 5.0.0, MPFR version 3.1.0,
MPC version 1.1.0, isl version none
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
GNU C++14 (GCC) version 9.2.0 (x86_64-redhat-linux)
        compiled by GNU C version 9.2.0, GMP version 5.0.0, MPFR version 3.1.0,
MPC version 1.1.0, isl version none
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 4b6a28fa973d06df756608f08fdba542
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-o' 'gccBug' '-Wall' '-Werror'
'-Wextra' '-O3' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 as -v --64 -o TestGccBug.o TestGccBug.s
GNU assembler version 2.32 (x86_64-redhat-linux) using BFD version (GNU
Binutils) 2.32
COMPILER_PATH=/opt/gcc/x86_64/9.2.0a-1/libexec/gcc/x86_64-redhat-linux/9.2.0/:/opt/gcc/x86_64/9.2.0a-1/libexec/gcc/x86_64-redhat-linux/9.2.0/:/opt/gcc/x86_64/9.2.0a-1/libexec/gcc/x86_64-redhat-linux/:/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/:/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/:/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-o' 'gccBug' '-Wall' '-Werror'
'-Wextra' '-O3' '-shared-libgcc' '-mtune=generic' '-march=x86-64'
 /opt/gcc/x86_64/9.2.0a-1/libexec/gcc/x86_64-redhat-linux/9.2.0/collect2
-plugin
/opt/gcc/x86_64/9.2.0a-1/libexec/gcc/x86_64-redhat-linux/9.2.0/liblto_plugin.so
-plugin-opt=/opt/gcc/x86_64/9.2.0a-1/libexec/gcc/x86_64-redhat-linux/9.2.0/lto-wrapper
-plugin-opt=-fresolution=TestGccBug.res -plugin-opt=-pass-through=-lgcc_s
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc
-plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc
--eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o
gccBug /lib/../lib64/crt1.o /lib/../lib64/crti.o
/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/crtbegin.o
-L/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0
-L/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/../../../../lib64
-L/lib/../lib64 -L/usr/lib/../lib64
-L/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/../../..
TestGccBug.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc
/opt/gcc/x86_64/9.2.0a-1/lib/gcc/x86_64-redhat-linux/9.2.0/crtend.o
/lib/../lib64/crtn.o
COLLECT_GCC_OPTIONS='-v' '-save-temps' '-o' 'gccBug' '-Wall' '-Werror'
'-Wextra' '-O3' '-shared-libgcc' '-mtune=generic' '-march=x86-64'

Reply via email to