int a;
...
int b = a
if (a + 1 > b){
foo();
}
Then compilers will use the fact that integer overflow is undefined behavior. This allows the compiler to assume the `if` statement will always be true. And hence it removes this check.This isn't just the compiler removing a check the programmer wanted. This could occur because of constants, where other constants make the check meaningful. It could also occur after in-lining a specific function call.
Really, the only case where this optimization would be bad is if the author was trying to detect overflow. Sadly, detecting if something would cause undefined behavior in C code is hard. Because you cannot try the thing.
Turning optimizations on doesn't suddenly undefine any behavior, it just changes what the compiler does when encountering some of those behaviors (among other, non-UB based optimizations). A non-optimizing compiler and an optimizing compiler both have to conform to the standard.
It is simply not possible to write an optimizing compiler in which the evaluation order is truly indeterminate yet always the same.
{
auto& a = <expression>
auto& b = <expression>
foo(a, b);
}
To foo(<expression>, <expression>);
Could be an optimization.Edit: modified the code a bit to make the two samples more similar.
[0] http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0145r3....
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p040...
> We do not believe that such a nondeterminism brings any substantial added optimization benefit, but it does perpetuate the confusion and hazards around order of evaluations in function calls.It perpetuates unnecessary confusion around brace-initialization vs. direct initialization using parenthesis.
> We found that some entries in the benchmark suite ran slower, others ran faster compared to the scenario where the evaluation of the argument list is left unspecified. The variation is between -4% and +4%. It is worth noting that these results are for the worst case scenario where the optimizers have not yet been updated to be aware of, and take advantage of the new evaluation rules and they are blindly forced to evaluate function calls from left to right. It is clear that the left-to-right evaluation strategy is triggering new optimization paths (different inlining decisions and different register allocation) affecting the variations in the benchmark performance. It appears those opportunities have not traditionally been exploited, even though permitted under the unspecified order regime.
Unfortunately, they seem to have been overruled by the Core Working Group [1], for reasons I have not been able to dig up.
[0] http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0145r3....
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p040...
> In this case, it's pretty easy to see that 3 will have to be printed after both 1 and 2, but it's unclear whether 1 or 2 will be printed first. The reason for this is evaluation order: in order to evaluate one + two, we'll need to evaluate both one and two. But there is nothing telling GHC which of those thunks should be evaluated first, and therefore GHC is at full liberty to choose whichever thunk to evaluate first.
Note that in Haskell evaluation order is not guaranteed anywhere, even between separate statements, whereas in C++ it is basically guaranteed everywhere except function calls.
As for paranoia about undefined behavior? Compilers won't emit a program to call you a pizza or format your disk. There are risks, but those aren't real ones.
Another example is when undefined behavior occurs, like (iirc) signed integer overflow. Compilers can technically do anything they want, including weird things. But how often does that actually happen?