As explained, the only values ever written to x
are 0 and 42. Thread 1:
r3 = x; // here we read either 0 or 42
if (r3 == 0)
x = 42;
// at this point x is definitely 42
r1 = x;
Therefore the JIT compiler can rewrite r1 = x
as r1 = 42
, and further y = 42
. The point is, Thread 1 will always, unconditionally write 42 to y
. The r3
variable is in fact redundant and could be completely eliminated from the machine code. So the code in the example only gives the appearance of a causal arrow from x
to y
, but detailed analysis shows that there is in fact no causality. The surprising consequence is that the write to y
can be committed early.
A general note on optimization: I take it you are familiar with performance penalties involved in reading from the main memory. That is why the JIT compiler is bent on refusing to do it whenever possible, and in this example it turns out that it doesn't in fact need to read x
in order to know what to write to y
.
A general note on notation: r1
, r2
, r3
are local variables (they could be on the stack or in CPU registers); x
, y
are shared variables (these are in the main memory). Without taking this into account, the examples will not make sense.