Introduction/confirmation of basic facts
It is well known that with GCC style C and C++ compilers, you can use inline assembly with a "memory" clobber:
asm("":::"memory");
to prevent reordering of (most) code past it, acting as a (thread local) "memory barrier" (for example for the purpose of interacting with async signals).
Note: these "compiler barriers" do NOT accomplish inter-threads synchronization.
It does the equivalent of a call to a non inline function, potentially reading all objects that can be read outside of the current scope and altering all those that can be altered (non const objects):
int i;
void f() {
int j = i;
asm("":::"memory"); // can change i
j += i; // not j *= 2
// ... (assume j isn't unused)
}
Essentially it's the same as calling a NOP function that's separately compiled, except that the non inline NOP function call is later (1) inlined so nothing survives from it.
(1) say, after compiler middle pass, after analysis
So here j
cannot be changed as it's local, and is still the copy of the old i
value, but i
might have changed, so the compilation is pretty much the same as:
volatile int vi;
int f2() {
int j = vi;
; // can "change" vi
j += vi; // not j *= 2
return j;
}
Both reads of vi
are needed (for a different reason) so the compiler doesn't change that into 2*vi
.
Is my understanding correct up to that point? (I presume it is. Otherwise the question doesn't make sense.)
The real issue: extern or static
The above was just the preamble. The issue I have is with static variables, possible calls to static functions (or the C++ equivalent, anonymous namespaces):
Can a memory clobber access static data that isn't otherwise accessible via non static functions, and call static functions that aren't otherwise callable, as none of these are visible at link stage, from other modules, if they aren't named explicitly in the input arguments of the asm directive?
static int si;
int f3() {
int j = si;
asm("":::"memory"); // can access si?
j += si; // optimized to j = si*2; ?
return j;
}
In other words, is that "clobber" the equivalent of a call to:
- an external NOP function, which wouldn't be able to name
si
directly, nor to access it in any indirect way, as no function in the TU either communicates the address ofsi
, or makessi
indirectly modifiable - a locally defined NOP function that can access
si
?
Bonus question: global optimization
If the answer is that static variables aren't treated like extern variables in that case, what is the impact when compiling the program at once? More specifically:
During global compilation of the whole program, with global analysis and inference over variables values, is the knowledge of the fact that for example a global variable is never modified (or never assigned a negative value...), except possibly in an asm
"clobber", an input of the optimizer?
In other words, if non static i
is only named in one TU, can it be optimized as if it was a static int
even if there are asm
statements? Should global variables be explicitly listed as clobbers in that case?