views:

574

answers:

4

I just found a bug that, strangely, occurred only when optimization was turned on (g++ -O2). It was an Arithmetic exception in the following code, when interval was set to zero (from a command line argument):

for(int i = 0; i < n; ++i) {
  if((i + 1) % interval == 0) { // exception here
    DoSomething();
  }
}

It's obvious that modulo zero operation threw a division-by-zero exception, but why did this only occur when the code was compiled with optimization turned on?

+12  A: 

Divide by zero is always undefined behavior. The fact that you get different results with different optimization settings still fits within the definition of undefined behavior.

TokenMacGuy
Okay, but I'd like to know more. Why is it that the optimized version actually threw an exception, and the non-optimized version didn't seem to care at all?
Try this is GCC, try compiling with the `-S` option, to get assembly language output, and then compare the differences.
TokenMacGuy
The language spec is not the final authority on what the CPU does. It's fair to say the language requires the operation to be performed, and only the result is UB, and it's fair to say the microarchitecture guides say how to implement the operation, and that the ISA spec says these operands to this operation raise an exception. So, there's still a story to be told.
Potatoswatter
The language doesn't specify that the divide by zero must occur. Undefined behavior means that, for example, a compiler may emit machine code for input that would result in a divide by zero that causes all of the data on your hard drive to be erased and replaced with animated gifs of ponies, and still be in perfect compliance with the standard. Undefined behavior means that the language does not define the behavior, at any point in the lifecycle of such input. See also (http://foxtrotters.tripod.com/icelnd.gif)
TokenMacGuy
@user60628: Your optimized version did not "throw an exception". C++ does not throw exceptions when division by zero happens. Instead, it produces undefined behavior.
AndreyT
@AndreyT: You statement does not make sense. "Throwing an exception" is a subset of "Undefined Behavior". You can't refute user60628 specific observation based on the general knowledge that UB should occur.
MSalters
@MSalters: I didn't mean it that way. Judging by the OP's comment above, they seem to expect a specific behavior - an exception - which takes place in one case and doesn't take place in the other. I'm just saying that the correct description of the observed behavior is just "undefined behavior". Even if the code "throws an exception" in some *particular* test, it is incorrect to say that it "throws an exception" in *general*. The latter is my point.
AndreyT
The point i was trying to make is that there's no real answer to the reason why a compiler produces one kind of undefined behavior in one case, and another in different cases
TokenMacGuy
A: 

You don't show us where 'interval' gets set. The optimizer may be doing something that sets 'interval' to 0. Change your code to

for(int i = 0; i < n; ++i) {
  if (0==interval) { break; }
  if((i + 1) % interval == 0) { // exception here
    DoSomething();
  }
}

And see if you still get the error. Or better yet, show us where 'interval' is getting its value.

selfsimilar
`interval` is a `const int` that is set early in the program, from reading the command line arguments. So it is basically set to zero from the command line.
error: assignment of read-only variable 'interval'
dwelch
@dwelch: You're a bad compiler if you throw an assignment error on `const int interval = x;`
@user60628: Bad compiler! No biscuit!
Jon Purdy
A: 

Constant folding.

You declared interval as a global const int and compiler took you at your word.

Joshua
What do you mean? Note that the compiler cannot know if `interval` will be zero or not since I set it depending on a command line switch.
@Joshua: please check the difference between `const int` and Integral Constant Expressions. Not every `const int` is an ICE.
MSalters
He said it was a "const int" in another comment. A const int in the same source file is allowed to be an Integral Constant Expression.
Joshua
A: 
dwelch
No no, the `interval` value wasn't set that way using `-D`, it was set by parsing and processing the program runtime arguments.
if you declare a variable const then you cannot change it in the program, your compiler should give you a warning telling you that it is wrong for example: error: assignment of read-only variable 'interval', you need to post a complete example, without such mistakes for us to work out the real problem.
dwelch
@dwelch: You can change the value with this code: *(int *) Don't do that for globals: the compiler is allowed to place global const variables in readonly memory.
Joshua