Analysis of x86 output generated by JIT in the context of volatility

I am writing this post in connection with a deep understanding of Java variability

public class Main { private int x; private volatile int g; public void actor1(){ x = 1; g = 1; } public void actor2(){ put_on_screen_without_sync(g); put_on_screen_without_sync(x); } } 

Now I analyze that I created a JIT for the above code snippet. From our discussion in my previous post, we know that the conclusion 1, 0 impossible, because:


write to volatile v causes each action of a preceding v to cause a to be visible (will be flushed to memory) before v is displayed.


  .................(I removed not important body of method)..... 0x00007f42307d9d5e: c7460c01000000 (1) mov dword ptr [rsi+0ch],1h ;*putfield x ; - package.Main:: actor1@2 (line 14) 0x00007f42307d9d65: bf01000000 (2) mov edi,1h 0x00007f42307d9d6a: 897e10 (3) mov dword ptr [rsi+10h],edi 0x00007f42307d9d6d: f083042400 (4) lock add dword ptr [rsp],0h ;*putfield g ; - package.Main:: actor1@7 (line 15) 0x00007f42307d9d72: 4883c430 add rsp,30h 0x00007f42307d9d76: 5d pop rbp 0x00007f42307d9d77: 850583535116 test dword ptr [7f4246cef100h],eax ; {poll_return} 0x00007f42307d9d7d: c3 ret 

Do I understand correctly that it works because x86 cannot perform reordering of StoreStore ? If this could require an additional memory barrier, right?


EDIT AFTER EXCELLENT @Eugene answer:

  int tmp = i; // volatile load // [LoadStore] // [LoadLoad] 

Here I understand what you mean - it is clear: every action below (after) volatile read ( int tmp = i ) is not reordered.

  // [StoreLoad] -- this one int tmp = i; // volatile load // [LoadStore] // [LoadLoad] 

Here you put one more barrier. This ensures that no action is reordered with int tmp = i . But why is this important? Why do I have doubts? From what I know, volatile load guarantees:

Each action after a volatile load will not be reordered until the volatile load is visible.

I see what you write:

There must be consistent consistency

But I do not understand why consistent consistency is required.

+1
java volatile jvm memory-barriers
source share
1 answer

A few things, first will be flushed to memory - this is pretty wrong. It almost never sticks to main memory - it usually drains StoreBuffer to L1 , and it connects to the cache consistency protocol to synchronize data between all caches, but if you find it easier to understand this concept in these terms, it's fine - just know that it slightly different and faster.

Good question about why [StoreLoad] exists, maybe this will clarify things a bit. volatile really all about fences. Here is an example of what will happen:

  int tmp = i; // volatile load // [LoadStore] // [LoadLoad] 

What happens for volatile load and below, what happens for volatile storage:

  // [StoreStore] // [LoadStore] i = tmp; // volatile store 

But it’s not him, there is more. There should be sequential consistency , so any normal implementation ensures that volatile itself will not be reordered, thus adding two more fences:

  // [StoreLoad] -- this one int tmp = i; // volatile load // [LoadStore] // [LoadLoad] 

And one more here:

 // [StoreStore] // [StoreLoad] i = tmp; // volatile store // [StoreLoad] -- and this one 

Now, it turns out that on x86 3 out of 4 memory barriers are free - since this is a strong memory model . The only one that needs to be implemented is StoreLoad .

Usually, mfence is a good option for StoreLoad on x86 , but the same thing can be guaranteed with lock add , so you see it. this is basically the StoreLoad barrier. And yes, you are right in your last sentence, a weaker memory model will require the StoreStore barrier. On a side note, this is what is used when you safely publish a link through the final fields inside the constructor. After exiting the designer, two fences are inserted: LoadStore and StoreStore .

EDIT

Suppose you have this case:

 [StoreStore] [LoadStore] int x = i; // volatile store int j = 3; // plain actions j = x; // volatile load [LoadLoad] [LoadStore] 

In principle, there is no barrier that would prevent the volatile store from recharging with the volatile load (i.e., the volatile load would be performed in the first place), and this can cause problems; thus, the sequential sequence is broken.

You seem to lose the point here btw (if I'm not mistaken) through Every action after volatile load won't be reordered before volatile load is visible . Reordering is not possible with changing oneself - other operations can be redirected. Let me give you an example:

  int y = 0; int tmp = i; // volatile load // [LoadStore] // [LoadLoad] int x = 3; // plain load y = 4; // plain store 

The last two operations x = 3 and y = 4 absolutely free so that they can be reordered, they cannot float over mutable ones, but they can be redirected through them. The above example would be completely legal:

  int y = 0; int tmp = i; // volatile load // [LoadStore] // [LoadLoad] // see how they have been inverted here... y = 4; // plain store int x = 3; // plain load 
+3
source share

All Articles