ARM Load-Exclusive/Store-Exclusive (LDXR/STXR): How ARM Implements Atomics Without a LOCK Prefix

2026-08-19

x86 does atomics with a single instruction: LOCK CMPXCHG holds the cache line in exclusive state for the duration of the read-modify-write. ARM took a fundamentally different path — load-linked / store-conditional (LL/SC). Instead of one indivisible instruction, you get two cooperating ones: LDXR (Load Exclusive Register) and STXR (Store Exclusive Register).

The mechanism: LDXR X0, [X1] reads a word and tells the CPU's exclusive monitor to watch that address. You do arbitrary computation. Then STXR W2, X3, [X1] attempts the store — but only succeeds if nothing else touched that cache line since your LDXR. Success returns 0 in W2; failure returns 1, and you retry the loop.

A typical atomic increment on AArch64:

What clears the monitor? Another core writing that cache line (via coherence traffic), a context switch, an exception, another LDXR from the same core, or exceeding the reservation granule (typically the cache line, 64B). Even a random interrupt between your LDXR and STXR will cause the STXR to fail — the kernel's exception entry clears the monitor.

Why this matters practically: LL/SC is optimistic. Uncontended atomics never bus-lock anything; only the coherence protocol's normal invalidations matter. But under heavy contention, LL/SC can livelock — every core keeps invalidating every other core's reservation. This is why ARMv8.1 added LSE (Large System Extensions) with true atomic instructions like CAS, LDADD, SWP. On a Graviton3 or Apple M-series, LSE atomics can be 4-10x faster than LDXR/STXR loops on a contended counter.

Rule of thumb: An uncontended LDXR/STXR pair costs ~3-5 ns. Under N-core contention on a hot line, throughput collapses to roughly 1 / (N × cache_miss_latency) ≈ one atomic per 100 ns per core at N=16. If you see ldxr/stxr in a hot profile on modern ARM, check whether glibc was built with -moutline-atomics or your compiler emitted the casal family instead.

Real-world gotcha: The Linux kernel's arch/arm64/include/asm/atomic_ll_sc.h keeps LL/SC as a fallback for pre-v8.1 CPUs, but production binaries increasingly ship casal unconditionally. AWS Graviton2 was the last major server CPU where the choice mattered performance-wise.

Key Takeaway: ARM implements atomics with an optimistic reservation (LDXR/STXR) rather than x86's pessimistic bus lock — elegant when uncontended, prone to livelock under pressure, which is why ARMv8.1's LSE atomics replaced it for hot paths.

All newsletters