-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: .NET memory model and lightweight barrier for writes #6257
Comments
The volatile read in your example prevents reordering of the writes. However, making the second write volatile instead should have about the same effect:
If your context is more complex, it may be useful to show the code for the other processor and then ask whether certain result is possible. Kind of like like the examples in "Examples Illustrating the Memory-Ordering Principles" chapter in the Intel manual. |
It all started here Volatile.Write(ref length, 1);
// bunch of normal writes to other memory locations
Volatile.Write(ref length, 2); The idea is to try not to use heavyweight MemoryBarrier between first write to |
I can't check what code is generated for ARM. static volatile int a;
static int b, dummy;
[MethodImpl(MethodImplOptions.NoInlining)]
static void LightweightStoreFence()
{
a = 1;
dummy = a;
b = 2;
} mov dword ptr [7FE988F47A0h],1
mov eax,dword ptr [000007FE988F47A0h]
mov dword ptr [000007FE988F47A8h],eax
mov dword ptr [7FE988F47A4h],2
ret Everything seems ok. But will it be the same on other CPUs? For example, if we read to a local var a = 1;
var dummy = a;
b = 2; JIT removes the volatile read completely. mov dword ptr [7FE989047A0h],1
mov dword ptr [7FE989047A4h],2
ret And it is not clear if JIT did it because it realized it can remove the dummy read still preserving the order dictated by CLI rules or the order just happened to be preserved. |
The JIT does not do any reordering over volatile ops today - https://github.com/dotnet/coreclr/blob/master/src/jit/importer.cpp#L11296 |
Unless it happens to be buggy :) |
I think so. |
@jkotas |
cc @dotnet/jit-contrib There are number of tests that are validating this in the repo (look for Fully validating it is hard problem. It would need to prove that no combination of different optimizations violates the invariants ... https://en.wikipedia.org/wiki/Formal_methods or https://en.wikipedia.org/wiki/Model_checking are there to help. |
@omariom: the ECMA volatile semantics are read-acquire and write-release, so the volatile read followed by a volatile write will do what you want. To be more explicit, memory operations that occur after a volatile read in program order cannot be moved above that volatile read, and memory operations that occur before a volatile write in program order cannot be moved after a volatile write. So if you have a took the sequence you mentioned:
And performed the transformation you suggested:
(3) implies that the read-acquire behavior of the volatile read applies transitively to the preceding volatile write, which means that the bunch of normal writes would not be allowed to move above the first volatile write. IIRC, if a different memory location was used for the volatile read, the third property would not be in play and the bunch of normal writes could still be reordered w.r.t. the first volatile write. HTH. |
(@ericeil can point out any flaws in my understanding and/or logic 😄) |
@pgavlin - I don't believe that you need condition 3 to ensure that the volatile read is not moved above the volatile write. The first paragraph of I.12.6.4 in the spec ensures that:
|
Ah, perfect! I couldn't find that language under I.12.6.7, which covers volatile read and write semantics specifically. Thanks! In that case, any volatile read will do. |
@omariom: @RussKeldorph pointed out that I managed to skim over the assembly dump you posted. That assembly (which I've reproduced locally) indicates that there is a bug in the JIT that is causing us to drop the volatile read, as ECMA specifically disallows removing or coalescing volatile operations. I've opened dotnet/coreclr#6172 to track. |
Does it mean a volatile write can't be reordered with a following volatile read from a different location? |
@pgavlin wow! I thought that was legal. |
@omariom - yes, the way I would read that section is that it would be illegal to reorder any volatile operations relative to one another, even if they involve different locations. Though, to be fair, it is often been observed that the section in question is not as rigorous as it could be. |
This is not what happens currently. The JIT doesn't use any fences for volatile load/stores and x86 allows some load/store reordering. That said, on x86 stores aren't reordered so in this particular case the load isn't actually needed. |
@stephentoub @jkotas
.NET
.volatile
writes don't prevent any writes from floating above them - that's the semantics of store-release. So in the following scenario..those 2 writes can be reordered.
@JPWatson suggested putting a dummy volatile read from the just written location to form a single barrier that doesn't pass reads/writes below itself nor allows writes to float above it.
The reasoning is this:
It looks legitimate according to ECMA CLI
For reads this trick won't work because in such scenario, thanks to store buffer forwarding, reads can be reordered with the dummy read. @joeduffy explained it here and here.
Is it a legitimate way to create a barrier for following writes or we have to use full memory barrier?
The text was updated successfully, but these errors were encountered: