Here the pointer is volatile, not the value that is pointed to:
int* volatile ptr;
Here the value that is pointed to is volatile, not the pointer:
volatile int* p;
Here both are volatile:
volatile int* volatile ptr;
In the first case where the pointer is volatile can the compiler optimize the value pointed?
Knowing that we can also have both pointer and value volatile makes me think that the compiler may optimize the value that is pointed to when only the pointer is volatile. Is this true? Is there any good application or good example where this may be required?
CodePudding user response:
Consider this code:
int * volatile ptr = SomeAddress;
printf("%d\n", *ptr);
printf("%d\n", *ptr);
The compiler cannot optimize the second printf
to use the same argument as the first printf
. It must recompute the argument by accessing ptr
, in case ptr
changed.
Consider this code:
int * volatile ptr = SomeAddress;
int x = *ptr;
printf("%d\n", x);
printf("%d\n", x);
The compiler can optimize the second printf
to use the same argument as the first printf
, because they both use the non-volatile x
. The fact that the value of x
was previously derived from a volatile pointer is irrelevant.
CodePudding user response:
In the first case where the pointer is volatile can the compiler optimize the value pointed?
By "optimize", I take you to mean avoiding a read from main memory. In principle, yes, that can be done in the case of a volatile pointer to a non-volatile object. The pointer value must be read, but the implementation could test that freshly-read value to decide whether it needs to read the pointed-to object from memory.
In practice, however, such an approach is likely to be more costly than just performing the read from memory unconditionally, so I do not expect C implementations to "optimize" in such a way.
Knowing that we can also have both pointer and value volatile makes me think that the compiler may optimize the value pointed when only the pointer is volatile. Is this true?
Sort of. See above.
Is there any good application or good example where this may be required?
volatile
is not about suppressing optimization as a goal in itself. It is about instructing the compiler about the nature and use of an object, so that the implementation does not make faulty assumptions.
If the storage of the pointer is subject to change outside the scope of the program's own C-language semantics, then it should be declared volatile
. If the pointer's value may point to an object that is subject to such changes then the pointed to type should be volatile
. If both, then both. In practice, it is merely uncommon to require a pointer to volatile data, but exceedingly rare to require a volatile pointer.
CodePudding user response:
In the first case where the pointer is volatile can the compiler optimize the value pointed?
In theory it could, for example if it can deduct that the pointed-at value is the same as pointed at by some other non-qualified pointer.
int* b = ...;
int* volatile a = b;
// here `*b` can be optimized regardless of `a`.
In practice, how do you optimize something at an unknown address? The compiler would have to somehow know what was in stored at that specific address the last time it was accessed. Each access through the volatile pointer involves reading that pointer.
Similarly, non-qualified pointers cannot alias *volatile
pointers, because you can never assume that they point at the same address.
volatile
for pointers mostly makes sense in case of hardware registers that in turn contain an address to another register. But those rare cases would be of the volatile type* volatile
flavour, or more likely volatile uint32_t*
which is then de-referenced to get an integer, which in turn is converted to a volatile uint32_t*
pointer if needed.
I can't recall ever using type* volatile
or volatile type* volatile
in any real-world system, and then I mostly program the kind of low level drivers where you would be most likely to encounter such, if anywhere.
CodePudding user response:
Consider this code:
int test(int * volatile p)
{
int x = *p;
int y = *p;
return x;
}
The compiler needs to generate code for reading p
twice, but it only needs to dereference p
once to compute x
. y
is a dead value and can be eliminated, together with the dereference needed to compute it.
Live demo (slightly modified to make keeping track of the dereference easier).