[llvm-dev] RFC: non-temporal fencing in LLVM IR

Hal Finkel via llvm-dev llvm-dev at lists.llvm.org
Thu Jan 14 12:51:01 PST 2016


Hi JF, Philip,

Clang currently has __builtin_nontemporal_store and __builtin_nontemporal_load. How will the usage model for those change?

Thanks again,
Hal

----- Original Message ----- 

> From: "Philip Reames via llvm-dev" <llvm-dev at lists.llvm.org>
> To: "JF Bastien" <jfb at google.com>, "llvm-dev"
> <llvm-dev at lists.llvm.org>
> Cc: "Hans Boehm" <hboehm at google.com>
> Sent: Wednesday, January 13, 2016 11:45:35 AM
> Subject: Re: [llvm-dev] RFC: non-temporal fencing in LLVM IR

> On 01/12/2016 11:16 PM, JF Bastien wrote:

> > Hello, fencing enthusiasts!
> 

> > TL;DR: We'd like to propose an addition to the LLVM memory model
> > requiring non-temporal accesses be surrounded by non-temporal load
> > barriers and non-temporal store barriers, and we'd like to add such
> > orderings to the fence IR opcode.
> 

> > We are open to different approaches, hence this email instead of a
> > patch.
> 

> > Who's "we"?
> 

> > Philip Reames brought this to my attention, and we've had numerous
> > discussions with Hans Boehm on the topic. Any mistakes below are my
> > own, all the clever bits are theirs.
> 

> > Why?
> 

> > Ignore non-temporals for a moment, on most x86 targets LLVM
> > generates
> > an mfence for seq_cst atomic fencing. One could instead use a
> > locked
> > idempotent atomic accesses to top-of-stack such as lock or4i
> > [RSP-8]
> > 0 . Philip has measured this as equivalent on micro-benchmarks, but
> > as ~25% faster in macro-benchmarks (other codebases confirm this).
> > There's one problem with this approach: non-temporal accesses on
> > x86
> > are only ordered by fence instructions! This means that code using
> > non-temporal accesses can't rely on LLVM's fence opcode to do the
> > right thing, they instead have to rely on architecture-specific
> > _mm*fence intrinsics.
> 
> Just for clarify: the proposal to change the implementation of
> ceq_cst is arguable separate from this proposal. It will go through
> normal patch review once the semantics are addressed. Whatever we
> end up doing with ceq_cst, we currently have a semantic hole in our
> specification around non-temporals that needs addressed.

> Another approach would be to define the current fences as fencing
> non-temporals and introducing new ones that don't. Either approach
> is workable. I believe that new fences for non-temporals are the
> appropriate choice given that would more closely match existing
> practice.

> We could also consider forward serialize bitcode to the stronger form
> whichever choice we made. That would be conservatively correct thing
> to do for older bitcode which might be assuming strong semantics
> than our barriers explicitly provided.

> > But wait! Who said developers need to issue any type of fence when
> > using non-temporals?
> 

> > Well, the LLVM memory model sure didn't. The x86 memory model does
> > (volume 3 section 8.2.2 Memory Ordering) but LLVM targets more than
> > x86 and the backends are free to ignore the !nontemporal metadata,
> > and AFAICT the x86 backend doesn't add those fences.
> 

> > Therefore even without the above optimization the LLVM language
> > reference is incorrect: non-temporals should be bracketed by
> > barriers. This applies even without threading! Non-temporal
> > accesses
> > aren't guaranteed to interact well with regular accesses, which
> > means that regular loads cannot move "down" a non-temporal barrier,
> > and regular stores cannot move "up" a non-temporal barrier.
> 

> > Why not just have the compiler add the fences?
> 

> > LLVM could do this, either as a per-backend thing or a hookable
> > pass
> > such as AtomicExpandPass . It seems more natural to ask the
> > programmer to express intent, just as is done with atomics. In
> > fact,
> > a backend is current free to ignore !nontemporal on load and store
> > and could therefore generate only half of what's requested, leading
> > to incorrect code. That would of course be silly, backends should
> > either honor all !nontemporal or none of them but who knows what
> > the
> > middle-end does.
> 

> > Put another way: some optimized C library use non-temporal accesses
> > (when string instructions aren't du jour) and they terminate their
> > copying with an sfence . It's a de-facto convention, the ABI
> > doesn't
> > say anything, but let's avoid divergence.
> 

> > Aside: one day we may live in the fence elimination promised land
> > where fences are exactly where they need to be, no more, no less.
> 

> > Isn't x86's lfence just a no-op?
> 

> > Yes, but we're proposing the addition of a target-independent
> > non-temporal load barrier. It'll be up to the x86 backend to make
> > it
> > an X86ISD::MEMBARRIER and other backends to get it right (hint:
> > it's
> > not always a no-op).
> 

> > Won't this optimization cause coherency misses? C++ access the
> > thread
> > stack concurrently all the time!
> 

> > Maybe, but then it isn't much of an optimization if it's slowing
> > code
> > down. LLVM doesn't just target C++, and it's really up to the
> > backend to decide whether one fence type is better than another (on
> > x86, whether a locked top-of-stack idempotent operation is better
> > than mfence ). Other languages have private stacks where this isn't
> > an issue, and where the stack top can reasonably be assumed to be
> > in
> > cache.
> 

> > How will this affect non-user-mode code (i.e. kernel code)?
> 

> > Kernel code still has to ask for _mm_ mfence if it wants mfence :
> > C11
> > and C++11 barriers aren't specified as a specific instruction.
> 

> > Is it safe to access top-of-stack?
> 

> > AFAIK yes, and the ABI-specified red zone has our back (or front if
> > the stack grows up ☻).
> 

> > What about non-x86 architectures?
> 

> > Architectures such as ARMv8 support non-temporal instructions and
> > require barriers such as DMB nshld to order loads and DMB nshst to
> > order stores.
> 

> > Even ARM's address-dependency rule (a.k.a. the ill-fated
> > std::memory_order_consume ) fails to hold with non-temporals:
> 

> > > LDR X0, [X3]
> > 
> 

> > > LDNP X2, X1, [X0] // X0 may not be loaded when the instruction
> > > executes!
> > 
> 
> > Who uses non-temporals anyways?
> 

> > That's an awfully personal question!
> 
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev

-- 

-- 
Hal Finkel
Assistant Computational Scientist
Leadership Computing Facility
Argonne National Laboratory


More information about the llvm-dev mailing list