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'