(cc Mike - TL;DR MemoryFence() is broken on MSVC/x64 with LTO)

Hello Jay,

On Fri, 6 Mar 2026, at 23:48, Jay Krell wrote:
> MemoryFence:
>
> I know basically the comments and the code.
>
> The comments say that loads/stores *before* it, are completed *before* 
> it. It says nothing about loads/stores *after* it.
>
> Is the exact intent, therefore, that loads/stores after it, can 
> complete before it?
>

I think 'completion' is not the right idiom here. Memory ordering is about a 
CPU issuing loads and stores in a certain order (program order), and other 
observers (other CPUs, DMA devices) seeing the results in a different order.

To me, it seems obvious that the intent of a MemoryFence() is to ensure that 
this order is respected for loads and stores issued before vs. after the fence. 
However, another observer must use a MemoryFence() too if it cares about the 
ordering as well.

For example, the producer side of a ring buffer might do

- write the data
- fence
- update the pointer

and the consumer side would do

- read the pointer
- fence
- read the data

This ensures that an observer reading the new pointer value will also see all 
the new data. It might see a stale value of the pointer, but that is fine - 
that is just the nature of concurrency. For correctness, it doesn't matter 
whether the pointer update has become visible or not, it only matters that if 
it has, all preceding stores have become visible too.

In the arm64 architecture, this model has been mostly superseded by using 
acquire/release semantics on the actual accesses, e.g.

- write the data
- update the pointer using store-release

vs.

- read the pointer using load-acquire
- read the data

This gives the same guarantees as above, but with clearer semantics regarding 
the purpose/direction of the barriers.

> See, I have some code, with a similar name and meaning: MemoryBarrier().
>
> It starts out like MemoryFence: all loads and stores before it, 
> complete before it.
> But more so, all loads and stores after it, complete after it.
>
> The x86/amd64 implementation of MemoryBarrier with Visual C++ 
> __faststorefence, which is lock or [rsp], 0 on AMD64.
>
> Should x86/amd64 MemoryFence do that, or it is meant exactly as commented?
>

Only if this is needed to ensure that other observers see the correct memory 
ordering. On x86, this is already guaranteed AFAIK.

> Furthermore, the existing EDK2 x86/amd64 MemoryFence for Visual C++ is 
> an empty function.
>   Was the thinking there, that call stores the return address, ret 
> reads it, these being memory operations, force ordering before/after 
> them?
>

No, the presence of memory operations is irrelevant here. x86 already 
guarantees that stores become visible in program order to other observers.

> But isn’t LTO/LTCG supposed to work? If so, this is entirely 
> unconvincing. It will be optimized to nothing, including no scheduling 
> inhibitions to compiler or processor.
> It should, probably, at least _ReadWriteBarrier.
> Which is stronger than intended, but still incredibly weak.
> It could even then be extern __forceinline, if that helps.
>

OK, so now we are no longer talking about concurrency, but about compiler 
reordering. This is a completely different matter, as it affects all 
architectures in the same way, and affects the actual program order, rather 
than what is seen by other observers.

You are right that the VC++ implementation is broken: MemoryFence() has no 
effect whatsoever, and under LTO, the compiler is free to modify the order in 
which the loads and stores around it are issued. This could affect other 
observers, but also the CPU itself, e.g., when it takes an interrupt and 
observes data structures in an inconsistent state, despite the use of fences to 
prevent this.

So what is needed here is a compiler barrier. GCC just uses asm("":::"memory"); 
for this, which informs the compiler that the empty inline asm has memory side 
effects, and this is sufficient to prevent the compiler from reordering other 
memory operations around it.

Not sure what the VC++ equivalent is, but it should be a compiler directive 
rather than an actual instruction or asm opcode.

> EDK2 ARM/ARM64 MemoryFence use dmb so are full barriers already.
> Should/can ARM/ARM64 be weakened, in fact?

No. The memory barriers are there to ensure that other observers see memory 
operations occurring in the correct order. Also, it is not clear to me why 
there is a need to weaken MemoryFence() on arm64.

> Is there a way? With LSE?

LSE does not materially differ from LL/SC when it comes to concurrency 
semantics.

> Really my question is the opposite though.
> There are many options. Maybe add a new function with the stronger guarantee.
>

What exactly is this stronger guarantee, and why do we need it?



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#121828): https://edk2.groups.io/g/devel/message/121828
Mute This Topic: https://groups.io/mt/118201494/21656
Group Owner: [email protected]
Unsubscribe: https://edk2.groups.io/g/devel/unsub [[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to