int x = 2;
volatile int y = 2;
const int z = x/y;
int main(){
int x = 2 + 3;
double d = 7 / 3;
}
I have three questions here:
Firstly, can the compiler calculate the value of the 'z' at compile time to be 1 in this case?
Secondly, I observed that the compiler does not generate assembly instructions for adding 2 and 3 to initialize x. It directly initializes x with 5. Can the same be done with 'd'?
Thirdly, Is there any good book to read on these two concepts? Any quotes from the Standard would be helpful (The standard document seems to be an interesting place though very scary)
Firstly, can the compiler calculate the value of the 'z' at compile time to be 1 in this case?
No. reading or writing to volatile variables considered having side effects, so the compiler is not allowed to do this.
Secondly, I observed that the compiler does not generate assembly instructions for adding 2 and 3 to initialize x. It directly initializes x with 5. Can the same be done with 'd'?
Yes. As long as the compiler can prove that there are no side effect. E.g. if overflow or a divide by zero happens during the computation, it can't compute it at compile time since the computation should trigger a CPU exception at runtime.
Thirdly, Is there any good book to read on these two concepts?
Yes. C++ ISO standard describes exactly what you're asking. Books are good to learn the basics or the theory. It makes no sense writing books that rephrase all the technical details described in the standard.
As for "first" - y
must be accessed, when z
is initialized, but I don't think the result of that access has to be used to calculate z
, if the implementation somehow knows that it must be 2. For this program there are (I think) only 2 ways it can have any other value:
Both of those are things that the implementation can rule out - in the second case by knowing how the loader behaves, in the first place by placing limitations on what you can hope to achieve with a debugger ("writing volatile variables results in surprising behavior"). Disappointing for the user of the debugger, but the standard doesn't constrain how debuggers work, or what memory "actually" contains, it just constrains what valid C++ implementations and programs do, and what C++ "sees".
In practice you'd think the compiler wouldn't bother treating a volatile object as subject to optimisations. It's a non-const object, and you've got to suspect that the only reason for defining a non-const volatile object is because it's going to change in ways the compiler doesn't expect[*]. You'd hope it will just read y
and do the division, but I reckon a case could be made for optimisation being legal.
As for "second" - in the case of your program, the compiler can initialize d
with a pre-computed value under the "as-if" rule, provided it knows what value division produces. For that matter in your program it can remove d
entirely.
"Provided it knows what value division produces" depends on the implementation - if it supports changes to IEEE rounding modes or equivalent, and if it doesn't know what mode should be in force, then in general it doesn't know in advance the result of even simple arithmetic.
The "as-if" rule covers, say, 85% of compiler optimisations. It's covered in section 1.9 of the standard, which is worth a look. I agree that the document as a whole is pretty intimidating, and the language it uses is sometimes impenetrable, but you have to start somewhere, so start with whatever you're currently interested in ;-)
[*] Specifically, and this is not something that's addressed in the C++03 standard in any way, some compilers (Microsoft) involve volatile
in their definition of threading semantics.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With