Reputation: 1898
This is a general question, but since I mostly deal with gcc/g++/VStudio, I have tagged it as c/c++. The question came to my mind when I was messing around with optimization options. In simplest form, consider an arithmetic operation such as i / 6 * 8
. If a human faces this expression, he will most likely simplify it to something like i / 3 * 4
. And if he is more comfortable with multiplying by 4, he will first do that, i.e. (i * 4) / 3
. I have to emphasize again that this is just a simple example.
Now what about the compilers? Are there any possibilities that they do the same with such operations? And since we know that in the above example, if i
is an integer, then simplifying and changing the order of operations may lead to completely different results, the question can be changed to: do the compilers completely avoid such actions?
If we want the program to do some arithmetic operations exactly as we stated and without changing the order of operations, should we be worried about the compiler's behavior?
Upvotes: 3
Views: 915
Reputation: 47923
As other answers have explained, valid compilers must be conservative and must not employ any optimizations which would change the behavior of well-defined programs. But it's important to remember that this conservatism applies only to valid, properly-written, well-defined programs. If the code being compiled depends on undefined behavior, modern compilers can be downright radical in the optimizations they employ, and in the real world, this means that the answer to the question as stated is actually, "Yes, there are cases where an arithmetic operation can be affected by compiler optimization."
Here are two great web pages describing some of the meaning-changing optimizations which compilers sometimes apply when faced with undefined behavior:
A programming language definition is often described as a "contract" between the programmer and the program as one party, and the compiler and its implementors as the other. As long as your code follows all the rules, the compiler is obligated to generate an executable with behavior which exactly matches the language definition and the "abstract machine". But if you break any rules, and in particular if your code wanders into any undefined behavior, all bets are off, the contract is void, and the compiler can basically do whatever it wants.
For example, if you write
int i = 1;
printf("%d\n", i++ + i++); /* WRONG */
you will very likely find that the value of the expression changes as you change the optimization level.
(Needless to say, the moral of the story is not, "If you write undefined code, you'll have to be careful which optimization settings you use." The right lesson to learn is, "Don't write code that depends on undefined behavior.")
Upvotes: 3
Reputation: 213458
There is very little in common between equation simplification and compiler optimization. The former aims to make an expression more readable to humans, the latter aims to make a program as efficient as possible. To simplify the equation like you have done would not yield a faster program, so the compiler won't bother with such.
The compiler can't re-order the expression into i * 8 / 6
because that could change the meaning of the code. Basically, a compiler is much smarter than a human mathematician, because the compiler is fully aware of types, while a human might lack such awareness. When programming, i / 6 * 8
is not equivalent to i * 8 / 6
! Because there is the issue of potential integer overflow. If the compiler doesn't know what value i
will have, then re-ordering could cause an overflow if i * 8
can't fit inside an integer.
For the same reason, the compiler can't change to code to i / 3 * 4
either. What if the programmer wanted an overflow? The program could be an attempt to demonstrate undefined behavior, or there could be compiler behavior implemented for the case of overflow. If the compiler would change the values, there might no longer be an overflow, and the program behavior would be changed, which is not allowed.
More likely, the compiler will look for a way to remove one of the operations by pre-calculating it at compile-time. And it will perhaps also look for a way to replace the division with bit shifts, since division is traditionally a slow operation. What optimizations that actually will be done depends on the whole surrounding code.
Upvotes: 4
Reputation: 1273
Compilers are very conservative when it comes to optimising the code. They may change the order in which operations are executed, and even pre-calculate arithmetic operations which operands are known at compile time (this is called constant folding), but they will never change the computed result.
Floating-point operations are a bit more problematic. You cannot in general change the order of calculation or pre-calculate without changing the computed result. Therefore most compilers leave them as-is by default. Yet it is possible to ask the compiler to optimise aggressively; in this case the computed result is likely to change but the user asked for it. This is for example of the case of the gcc option -Ofast
(because it internally sets options -ffast-math
). Note that it may lead to strange side-effects such as unexpected "random" division by zeros.
** Edit: note on non-arithmetic operations **
Optimisations get even harder when the code contains pointer and function calls. It is impossible, in general, to predict side-effects (think of pointer aliasing and global variables). Therefore compilers always give-up in a very conservative way: a good compiled program should be correct at least, being fast is a luxury.
** Edit: some examples **
This SO question gives a very detailed example of what can happen with floating point: Different floating point result with optimization enabled - compiler bug?
Upvotes: 5
Reputation: 1314
Most likely, the compiler would apply 'constant folding' and 'constant propagation' optimisations to constant expression.
In the case above, the compiler can't apply such optimisations.
Imagine
i = i * (4/2)
The compiler would generate
i= i * 2
This is because of constant folding.
Upvotes: 6