Chris Torek said:
[lots of earlier stuff omitted]
This is not what I would consider "good style" but will achieve
the effect:
int x;
...
void f(void) {
int y = *(volatile int *)&x;
... use y as needed ...
}
Equivalently, and perhaps slightly better (but still not "great")
style:
void f(void) {
/*
* NOTE TO MAINTAINERS: references through *xp read or write
* x as if x were declared volatile, even though it is not.
* This can be used to trick the compiler into doing operations
* with partial ordering constraints without having total
* ordering constraints.
*/
volatile int *xp = &x;
int y = *xp;
... use y and/or *xp as needed ...
}
Thank you Chris for these helpful replies.
I'd thought of the 'y = *(volatile int *)&x;' before, but I concluded
it had the same problem as 'y = (volatile int)x;' -- because the
subexpression '(volatile int *)&x' isn't an lvalue (by which I mean,
can't be on the left hand side of an assignment operator), the
'volatile' type qualifier is discarded. Or do I misunderstand
what the standard means by 'lvalue'?
Yes, you did understand what is meant by 'lvalue' in this case.
The cast is on the address of x. The result of the cast is indeed an
lvalue of type 'pointer to volatile int'. The pointer itself is not
volatile, which would indeed be meaningless. But the type pointed to
by the pointer is not an lvalue, and that is what the 'volatile' in
the cast applies to.
The other solution, with 'volatile int *xp = &x;', seems to
work just fine. That's great, I hadn't thought of that.
Notice that the overall effect of this statement is no different than
the other one. In fact, you could emphasize the similarity by adding
a dreaded redundant cast to this expression:
volatile int *xp = (volatile int *)&x;
Note that the automatic conversion performed by the assignment is
exactly the same as that of the first cast expression.
Incidentally, what about this idea:
int * volatile xp = &x, y = *xp;
No, this does not do the same thing at all. Type qualifiers 'const'
and 'volatile' (and probably 'restrict', but I haven't checked) modify
what appears to their left. In this case you are defining 'xp' as a
volatile pointer to a non-volatile int.
Technically that should mean that the compiler must read the value of
the pointer xp every time you dereference it, rather than relying on a
cached value in a register or whatever. Common sense would indicate
that a compiler, needing to reread xp each time, would then
dereference it to access memory each time, but the standard does not
require this behavior.
You are unlikely to find one, but since the integer pointed to by xp
is not volatile, a compiler would be within its rights to read the
value of xp each time, compare it to a cached value, and use a cached
value for '*xp' if 'xp' itself is still the same as the last time it
was dereferenced.
Seems like that also would force actual evaluation into a
'y' temporary, even imagining a compiler smart enough to
figure out that 'y' is getting the value in 'x'. Yes?
I don't think of this as a memory barrier issue. I want to capture
the value that the global 'x' has at the beginning of the function,
and guarantee that exactly that same value is used later in the
function. Any changes that happen to 'x', for whatever reason, while
the function is running must not be allowed to affect what the
function does, which should depend on the value that 'x' has at function
start and not any subsequent values.
Any compiler that did not behave the way you describe would be
horribly broken. Don't worry about it.
Is it? I thought one of the reasons for having 'volatile' is
to enable such things as this in operating system code, which
is in fact the area of application in this case.
What makes you think that the compiler is trying to optimize away a
local variable that stores the value of a file-scope object? In any
case, if the value of 'x' is subject to change while a function is
executing, and that function does not write to 'x', write via a
pointer that might alias to 'x', or call another function that might
modify 'x', that means that the value of 'x' might change
asynchronously and beyond the scope of the compiler.
In that case, 'x' should be defined as volatile in the first place.