10

I had to code some checking routines and they appear to behave differently if one uses -O0, -O1, -O2 or -O3.

Below I created a minimal example that works fine for -O0 and -O1. But using -O2 or -O3 the behavior changed. In the -O0 and -O1 case, the for-loop increments the integer and the first time it gets to the maximum, the overflow happens and the check routine triggers. In the other case the for-loop never breaks, although the integer gets negative.

Code

#include <iostream>

inline bool check(const int i) {
  if (i < 0)
    return false;
  else
    return true;
}

int main() {
  for (int i = 0;; i += 50000000) {
    std::cout << i << std::endl;
    const bool succ = check(i);
    if (succ == false) {
      std::cout << "Overflow: " << i << std::endl;
      break;
    }
  }
  return 0;
}

Why is the compiler allowed to optimize this away?

Trying with gcc, clang and icc, only the icc does it correct in all optimization variants the other two did not.

Community
  • 1
  • 1
Chris
  • 103
  • 4

1 Answers1

19

Signed integer overflow gives undefined behavior. Thus, the compiler has free reign to implement this case as they like.

Community
  • 1
  • 1
Joe Z
  • 17,413
  • 3
  • 28
  • 39
  • 7
    In particular, the compiler may well be assuming that `i` can never go negative, thus optimising the check away. – Oliver Charlesworth Dec 20 '13 at 15:09
  • 1
    @OliCharlesworth : Yep, I suspect that's what's happening here. – Joe Z Dec 20 '13 at 15:10
  • 3
    Re "the compiler has free reign", that's only *with respect to the C++ standard*. In addition there are platform-specific conventions and programmer expectations that a compiler better adhere to if it is to be used seriously on such a platform. In the case of this compiler (it's g++) the main argument for doing the silliness is that it helps it optimize, yet it produces far less efficient code than the main Windows compiler, that doesn't do surprise surprise. – Cheers and hth. - Alf Dec 20 '13 at 15:15
  • 7
    @Cheersandhth.-Alf: Undefined behavior is undefined behavior. Editorializing on the design decisions of different compiler writers really isn't helpful. There are plenty of places _most_ compilers (and I'd venture even MSVC) rely on subtleties like this. Granted, this example is rather blatant. Usually it's more subtle, like changing `x < y + 5` into `x - y < 5`. – Joe Z Dec 20 '13 at 15:21
  • This article, BTW, is fascinating and relevant: https://lwn.net/Articles/575563/ – Joe Z Dec 20 '13 at 15:23
  • 1
    @joez: No, as I wrote it's only undefined behavior wrt. to the ISO standard. There are plenty of cases of formal UB that could not be UB in practice. The simplest is perhaps to use any local variable, since the standard doesn't put a limit on the size of such, and doesn't specify a minimum stack size. Programmers are free to choose practical, predictable tools. And GNU hackers are free to be as impractical and unpredictable as they want to: the result of that is as it is today. It's that simple. – Cheers and hth. - Alf Dec 20 '13 at 15:30