Is it allowed for a compiler to optimize away a local volatile variable?

Solution 1:

No. Access to volatile objects is considered observable behavior, exactly as I/O, with no particular distinction between locals and globals.

The least requirements on a conforming implementation are:

  • Access to volatile objects are evaluated strictly according to the rules of the abstract machine.

[...]

These collectively are referred to as the observable behavior of the program.

N3690, [intro.execution], ¶8

How exactly this is observable is outside the scope of the standard, and falls straightly into implementation-specific territory, exactly as I/O and access to global volatile objects. volatile means "you think you know everything going on here, but it's not like that; trust me and do this stuff without being too smart, because I'm in your program doing my secret stuff with your bytes". This is actually explained at [dcl.type.cv] ¶7:

[ Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. Furthermore, for some implementations, volatile might indicate that special hardware instructions are required to access the object. See 1.9 for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. — end note ]

Solution 2:

This loop can be optimised away by the as-if rule because it has no observable behaviour:

for (unsigned i = 0; i < n; ++i) { bool looped = true; }

This one cannot:

for (unsigned i = 0; i < n; ++i) { volatile bool looped = true; }

The second loop does something on every iteration, which means the loop takes O(n) time. I have no idea what the constant is, but I can measure it and then I have a way of busy looping for a (more or less) known amount of time.

I can do that because the standard says that access to volatiles must happen, in order. If a compiler were to decide that in this case the standard didn't apply, I think I would have the right to file a bug report.

If the compiler chooses to put looped into a register, I suppose I have no good argument against that. But it still must set the value of that register to 1 for every loop iteration.

Solution 3:

I beg to dissent with the majority opinion, despite the full understanding that volatile means observable I/O.

If you have this code:

{
    volatile int x;
    x = 0;
}

I believe the compiler can optimize it away under the as-if rule, assuming that:

  1. The volatile variable is not otherwise made visible externally via e.g. pointers (which is obviously not a problem here since there is no such thing in the given scope)

  2. The compiler does not provide you with a mechanism for externally accessing that volatile

The rationale is simply that you couldn't observe the difference anyway, due to criterion #2.

However, in your compiler, criterion #2 may not be satisfied! The compiler may try to provide you with extra guarantees about observing volatile variables from the "outside", such as by analyzing the stack. In such situations, the behavior really is observable, so it cannot be optimized away.

Now the question is, is the following code any different than the above?

{
    volatile int x = 0;
}

I believe I've observed different behavior for this in Visual C++ with respect to optimization, but I'm not entirely sure on what grounds. It may be that initialization does not count as "access"? I'm not sure. This may be worth a separate question if you're interested, but otherwise I believe the answer is as I explained above.