5.6Relaxed Memory Model Case Studies: RISC-V and IBM Power

book pp. 75–81 · ~7 min read

  • RVWMO
  • acquire/release annotations
  • dependency-induced ordering
  • performed w.r.t.
  • cumulative FENCE
  • HWSYNC/LWSYNC

Two real memory models, in increasing order of difficulty: RISC-V’s RVWMO, then IBM Power — the hardest model in the book.

5.6.1 RISC-V Weak Memory Order (RVWMO)

RVWMO can be understood as a mix of release consistency and XC. Like XC, it is defined in terms of a global memory order (a total order of all memory operations) and offers FENCE instructions. Like RC, loads and stores can carry annotations: a load can be an ACQUIRE, a store a RELEASE, and an RMW either or both. (RISC-V also specifies a TSO variant, zTSO, not covered here.)

RELEASE/ACQUIRE orderings. Each annotation comes in two strengths — RCsc and RCpc (named for sequential and processor consistency; PC is TSO’s less-formal precursor without write atomicity). Loads and stores may carry either strength; RMWs only RCsc. The annotations preserve:

  • ACQUIRE → Load, Store (both ACQUIRE-RCsc and ACQUIRE-RCpc);
  • Load, Store → RELEASE (both strengths); and
  • RELEASE-RCsc → ACQUIRE-RCsc.

FENCE orderings. Six non-trivial variants (R = load, W = store):

FENCEEnforces
FENCE RW,RWeverything: Load, Store → Load, Store (XC’s full FENCE)
FENCE RW,WLoad, Store → Store
FENCE R,RWLoad → Load, Store
FENCE R,RLoad → Load only
FENCE W,WStore → Store only
FENCE.TSOLoad → Load, Store → Store, Load → Store — not Store → Load

An example (Table 5.11). Both mechanisms at once — C1 orders S1 → L1 with RCsc annotations; C2 orders S2 → L2 with a full FENCE; the combination makes (r1, r2) = (0, 0) impossible:

Core C1Core C2Comments
S1: RELEASE-RCsc x = NEW;
L1: ACQUIRE-RCsc r1 = y;
S2: y = NEW;
FENCE RW,RW;
L2: r2 = x;
/* Initially x = y = 0. S1 → L1 via RELEASE-RCsc → ACQUIRE-RCsc; S2 → L2 via the FENCE */

Dependency-induced orderings. Here RVWMO is subtly stronger than XC: syntactic address, data, and control dependencies induce order.

  • Address (Table 5.12): C1 stores data2 = NEW, FENCE W,W, then points pointer at data2; C2 loads the pointer (L1) and dereferences it (L2). Though no fence separates C2’s loads, L1 → L2 is enforced because L1’s value computes L2’s address — so (r1, r2) = (&data2, 0) is impossible.
  • Data (Table 5.13, “load buffering”): each core loads one variable into a register and stores that register to the other variable. Can both registers read 42, out of thin air ? XC, surprisingly, does not prohibit it — with no FENCE between load and store, a speculative cycle is legal:
Program Order (<p) of Core C1Program Order (<p) of Core C2Memory Order (<m)S1: y = r1; /* 42 (speculated!) */L2: r2 = y; /* 42 */S2: x = r2; /* 42 */L1: r1 = x; /* 42 */data dependency: r1 feeds S1data dependency: r2 feeds S2Outcome: (r1, r2) = (42, 42) — out of thin air; legal XC, forbidden RVWMO

Table 5.13’s cycle: S1 speculates that L1 will read 42 and writes 42 to y; L2 reads it; S2 writes it to x; L1 reads it — “confirming” the guess. RVWMO’s data-dependency rule (red arcs must point downward) breaks the cycle.

  • Control (Table 5.14): a store that executes only if a branch on an earlier load’s value is not taken is ordered after that load — preventing a store from (indirectly) affecting the very load that decides whether it executes.

All these dependencies are syntactic (a function of register identities), not semantic — and RVWMO adds “pipeline dependencies” beyond the book’s scope.

RMWs. RISC-V has two kinds: AMOs — single instructions (e.g., fetch-and-increment), atomic iff load and store are consecutive in the global memory order, like an XC RMW — and load-reserved / store-conditional pairs, whose atomicity is subtly weaker: if the LdR reads a value produced by store s, the pair is atomic as long as no same-address store appears between s and the StC in memory order.

Summary: a recent relaxed model combining XC’s total-order, FENCE-based skeleton with RC’s acquire/release annotations — plus dependency rules XC lacks.

5.6.2 IBM Power

Power looks superficially like XC, with four deep differences.

First — stores are performed with respect to cores, not memory. A store by C1 is performed w.r.t. C2 once C2’s loads can no longer read the clobbered value. With FENCEs, C1’s stores S1, S2, S3 perform w.r.t. every core in the same order; without them, S1 may be performed w.r.t. C2 but not yet C3. Power is therefore not guaranteed to create a total memory order — the assumption every model so far shared, and the reason §5.5’s IRIW outcome becomes reachable on Power hardware without the right fences.

Second — FENCEs are cumulative . For a FENCE with pre-set X and post-set Y: (a) X absorbs other cores’ accesses ordered before the FENCE (e.g., a store already performed w.r.t. this core); (b) Y absorbs accesses ordered after it by data or control dependence or later FENCEs; (c) both grow recursively across cores. (XC’s FENCEs are cumulative too — but the total memory order provides it automatically.)

Third — three FENCEs (more exist for I/O space):

HWSYNC

”Heavyweight sync”: all of X before all of Y — including Store→Load. Cumulative. The only fence strong enough for Dekker and IRIW.

LWSYNC

”Lightweight sync”: loads-in-X → Y, X → stores-in-Y — everything except Store→Load. Cumulative. Enough for the flag idiom and causality chains.

ISYNC

Orders INSTRUCTIONS, not memory accesses — despite the name, not a memory fence, and not cumulative. The book avoids it.

Fourth — dependency-induced orderings, even without FENCEs: a load feeding a later load’s address orders the pair, as does a load feeding a later store’s address or data value.

The classic programs on Power

Flag idiom (Table 5.15) — LWSYNC suffices. C1: S1, S2, LWSYNC, S3 (flag); C2: spin on flag, LWSYNC, L2, L3. C1’s fence orders stores-in-X before stores-in-Y (S1, S2 before S3 — note it does not order S1 and S2 with each other, which isn’t needed); C2’s orders its spin load before L2 and L3. No Store→Load order needed anywhere.

Dekker (Table 5.16) — HWSYNC required. Each core needs its store ordered before its subsequent load — exactly the one ordering LWSYNC lacks. With HWSYNC between each store/load pair, (r1, r2) = (0, 0) is prevented.

Causality (Table 5.17) — cumulative LWSYNCs. Step through how the performed-w.r.t. sets grow and why r3 is always NEW:

C1data1 = 0data2 = 0C1's private viewC2data1 = 0data2 = 0C2's private viewC3data1 = 0data2 = 0C3's private viewS1 (data1=NEW) performed w.r.t.:C1C2C3S2 (data2=NEW) performed w.r.t.:C1C2C3all views start at 0

1 / 7Power has no single memory order

Unlike XC, a Power store is not ordered by one total <m. It is "performed with respect to" (w.r.t.) each core INDIVIDUALLY: performed w.r.t. Ci once Ci's loads can no longer read the old value. Without fences, C1's store may be performed w.r.t. C2 long before C3. Watch each core's private view below.

IRIW (Table 5.18) — HWSYNCs, and cumulativity is the point. Replace Table 5.10’s FENCEs with HWSYNC and the forbidden r1 = NEW, r2 = 0, r3 = NEW, r4 = 0 outcome is disallowed: C3’s HWSYNC must cumulatively order C1’s S1 before C3’s L2 — for every core, including C4. LWSYNCs are not sufficient here.

Finally, a thought experiment: Power restricted to an HWSYNC between every pair of memory-accessing instructions gives SC executions. It is definitely not a recommended way to achieve performance — and a footnote warns that FENCEs cannot restore SC on Power when multiple access sizes are in play.

Check yourself

1.RVWMO is described as a mix of XC and RC. Which ingredients come from where?

2.Which orderings does RVWMO's FENCE.TSO enforce?

3.In the load-buffering example (Table 5.13), XC technically allows r1 = r2 = 42 "out of thin air." How does RVWMO forbid it?

4.When does RVWMO enforce same-address Load→Load ordering?

5.On Power, why does Dekker (Table 5.16) need HWSYNC when the flag idiom (Table 5.15) gets by with LWSYNC?

6.What makes a Power FENCE CUMULATIVE?

6 questions