From ISO/IEC 9899:201x section 5.1.2.3 Program execution:
2 Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. Evaluation of an expression in general includes both value computations and initiation of side effects. Value computation for an lvalue expression includes determining the identity of the designated object.
So, accessing (read\write) a volatile is called a side effect. Lets continue:
4 In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).
So if I read a volatile (which is a side effect) but doing nothing with the fetched value I read, this access is actually not needed and can be optimized out.
OK, also:
6 The least requirements on a conforming implementation are:
Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine.
At program termination, all data written into files shall be identical to the result that execution of the program according to the abstract semantics would have produced.
The input and output dynamics of interactive devices shall take place as specified in 7.21.3. The intent of these requirements is that unbuffered or line-buffered output appear as soon as possible, to ensure that prompting messages actually appear prior to a program waiting for input. This is the observable behavior of the program.
So, to my understanding, section 4 is a rule of the abstract machine, and section 6 says that volatiles needs to be evaluated strictly according to the abstract machine rules (which section 4 is). So the read access (without further using the fetched value) can be optimized out.
So, take a look at this code:
volatile int *timer_reg = REGISTER_ADDRESS_CONST;
while (*timer_reg == 0) {/*empty loop*/}
This code, blocks the execution until the HW raises a bit at the address pointed by timer_reg
. This register acts as a kind of timer which was configured to raise a bit after certain time period elapsed. When the timer elapses the busy wait ends and execution continues.
Now, can this while
loop be optimized out? I have tried this with many compilers online with different optimization levels, and it seems that it does not.
Is there something I'm missing here? to my understanding, standard says explicitly it can be optimized.
EDIT: My point is that because of the fact that the while loop is empty, maybe the compiler can consider the fetched volatile value as unneeded .
So if I read a volatile (which is a side effect) without using the value I read, this access is actually not needed and can be optimized out.
No. The text says "its value is not used and that no needed side effects are produced". It does not say "or". Regardless if you are using the value, there is a side effect present - an access to a volatile variable.
So the read access (without further using the fetched value) can be optimized out.
No.
Now, can this while loop be optimized out?
No. The data pointed at is volatile-qualified. At each lap in the loop it must read *timer_reg
directly from memory. It may not optimize away the read. It may not re-order it. It may not cache it/pre-fetch it.