This small program: // built using g++ -o test -O2 main.cpp
#include <iostream> struct stuff { int x; }; class MyException : public std::exception { public: MyException() { } }; // make this global so conditional below doesn't get eliminated bool should_throw = false; void calc_x(stuff& s, int n) { // set s.x to max(s.x, n, 2) s.x = std::max(n, s.x); s.x = std::max(2, s.x); // bogus throw needed to generate error if(should_throw) { // throw MyException() won't trigger bug - must be separate lines // also, something like std::runtime_error won't trigger either MyException ex; throw ex; } } int main(int argc, char* argv[]) { stuff s = { 0 }; int n = atoi(argv[1]); calc_x(s, n); std::cout << s.x << "\n"; std::cout << (s.x == n ? "SUCCESS" : "FAILURE") << "\n"; } will fail when passed any value greater than 2. calc_x should be returning the maximum of s.x, n and 2, but for values of n > 2, always returns the original value of s.x. Output: ------------- % ./test 0 2 % ./test 1 2 % ./test 2 2 % ./test 3 0 I've attempted to distill it to a smaller example than this, but eliminating almost anything causes it to start functioning again. Looking at the generated assembly, gcc is generating two conditional moves, corresponding to the two std::max calls. In the bad code, the final move is moving the address of s.x into a register, which then gets dereferenced and assigned into s.x. However, the intermediate result of the first comparison was not stored in s.x, but a scratch temporary on the stack. Therefore, s.x is being dereferenced and assigned to itself. movl %esi, 12(%rsp) <--- tmp1 = n cmpl (%rdi), %esi <--- compare s.x and n leaq 12(%rsp), %rax <--- rax = &tmp1 cmovl %rdi, %rax <--- rax = &s if n < s.x movl (%rax), %edx <--- edx = *rax leaq 28(%rsp), %rax <--- rax = &tmp2 movl $2, 28(%rsp) <--- tmp2 = 2 cmpl $2, %edx cmovg %rdi, %rax <--- rax = &s.x (!!!) if edx > 2 cmpb $0, should_throw(%rip) movl (%rax), %eax <--- eax = *rax movl %eax, (%rdi) <--- s.x = eax This is using gcc 4.2.3 as distributed with Ubuntu 8.04, however I've also verified the same results using an unpatched gcc 4.2.3, as well as the latest gcc-4_2-branch branch from subversion. Thanks, Brett Polivka % g++ -v Using built-in specs. Target: x86_64-linux-gnu Configured with: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.2 --program-suffix=-4.2 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix gcc version 4.2.3 (Ubuntu 4.2.3-2ubuntu7) -- Summary: wrong code generated with optimization on x86-64 Product: gcc Version: 4.2.3 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ AssignedTo: unassigned at gcc dot gnu dot org ReportedBy: brett dot polivka at magnetar dot com GCC build triplet: x86_64-linux-gnu GCC host triplet: x86_64-linux-gnu GCC target triplet: x86_64-linux-gnu http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36115