Register forwarding supplies a later instruction with a computed value before that value has reached the register file. It removes some read-after-write hazards without changing the program's meaning. Consider these symbolic machine instructions, with the destination first:In the five-stage in-order pipeline, the addition produces its result at the end of EX. In the next cycle the subtraction is in EX, although the addition has not yet reached WB. A bypass from the addition's EX/MEM pipeline register to the subtraction's arithmetic logic unit input supplies the new
ADD r1, r2, r3
SUB r4, r1, r5r1; the old value read in ID must be overridden.The bypass control compares source and destination processor register identifiers and selects the youngest applicable older producer. It must also check that the producer actually writes that processor register. Forward the newest available result instead of waiting for its writeback. A load instruction is different: its value is normally available only at the end of MEM, so an immediately following dependent instruction still needs a pipeline stall in this five-stage arrangement.
A store buffer holds the memory address and value of a store instruction while the memory cache or backing computer memory completes the write. The in-order pipeline can continue after enqueueing the write, rather than waiting for a possibly long memory access latency. A full store buffer eventually causes a pipeline stall; buffering hides finite delays, not an indefinitely inadequate service rate.
Without a memory management unit, the comparison can use the actual physical addresses; there is no address translation or synonym problem to solve. Suppose If the write is still in the store buffer, reading only computer memory could incorrectly return zero. The load instruction searches older buffered store instructions for an overlapping memory address. If the newest matching write covers the requested bytes and its data are ready, store-to-load forwarding returns seven directly. With several pending writes to
x initially contains zero and these operations execute in order:STORE [x], 7
LOAD r1, [x]x, the youngest older matching write wins. If the relevant data are not ready, the load instruction waits; partial overlaps require merging the covered bytes or conservatively waiting for the writes to drain.A load instruction from an unrelated address
y can bypass those buffered writes, subject to the architecture's memory model. Buffer writes to overlap them with useful execution, but preserve each load's dependence on older writes to the same bytes. Device accesses through memory-mapped input-output need their specified ordering and should not be treated as ordinary cacheable computer memory.In IF, fetch
PREFETCH normally. In ID, recognize its opcode, read the base processor register and immediate offset, and set controls selecting a prefetch request rather than a load instruction or store instruction. Ordinary register forwarding and data hazard interlocks still apply to that base processor register. In EX, use the arithmetic logic unit to form the effective memory address.Latch that memory address and a prefetch-valid control into EX/MEM. In MEM, put the memory address and any hint information on the dedicated request computer bus, or enqueue them in a small prefetch-request queue. The cache prefetching engine handles lookup, allocation and the later cache line return independently. Do not wait for the fill or select the returned data as a register file input. In WB, disable architectural processor register and processor condition flag writes; the instruction simply leaves the in-order pipeline.
If the request path is busy, dropping the hint is compatible with the contract in part (ii), and preserves its nonblocking purpose. A design that instead stalls until queue space exists can still be correct but reduces the performance benefit. A memory management unit must not turn a speculative hint into an ordinary protection fault: discard an invalid target, or use an appropriately checked nonfaulting translation path. Suppress device transactions for memory-mapped input-output and avoid speculative writes. Use the existing address-generation datapath, add a prefetch request/control path, and disable demand-load waiting and writeback.
Define
PREFETCH [a] as a software prefetch hint: initiate a request for the cache line containing memory address a, then continue without waiting for that cache line to arrive. It produces no value in a destination processor register, changes no program data and creates no data-result dependence for later instructions. It may be ignored if resources are unavailable. A useful hint is nonfaulting for an inaccessible or untranslated target: a later real load instruction must still perform its ordinary access checks. This is a proposed contract for the instruction, rather than a guarantee that the patent abstract alone fixes every detail of its semantics.For example, after reading the next pointer of a linked list node, prefetch that next linked list node while processing the current one. Replacing the hint by an otherwise unused ordinary load instruction on this in-order pipeline would wait in MEM on a cache miss before the current computation could start. It would also consume a destination processor register and might raise a synchronous processor exception. A harmless hint can be scheduled earlier, even on a path where the data will never be demanded. The crucial semantic difference is that cache filling may continue after the prefetch instruction itself has finished.
A software prefetch is not a memory barrier and does not establish acquire-release synchronization or promise that later accesses will hit. Its purpose is performance; correctness must survive the hint being ignored.
Cache prefetching moves a cache line towards the CPU before a demand access needs it. A correctly timed request overlaps the cache miss penalty with earlier computation, so a subsequent load instruction can find a cache hit instead of stopping the in-order pipeline in MEM. Requests directed at several levels of a cache hierarchy can prepare nearby data in a small fast memory cache while staging more distant future data in larger levels. The separate request computer bus avoids making those address requests compete directly with returning data; returned cache lines still consume transfer capacity.
For a store instruction, the gain depends on the write policy. With a write-allocate cache, fetching its target cache line early can avoid the allocation cache miss and help the store buffer drain. On a multicore processor, a write-oriented prefetch may additionally obtain cache coherence ownership; an ordinary read prefetch alone does not guarantee write permission. A store buffer can already hide an individual write's latency, so the visible gain may appear only when it would otherwise fill. With a no-write-allocate cache, fetching the cache line just to overwrite it can be unnecessary. Loads benefit directly from earlier data arrival; stores benefit when prefetching removes a required allocation or ownership delay.
The pointer to the next linked list node is unavailable until it has been read from the current linked list node; a software prefetch cannot invent that pointer. Once it is available, however, the current node's computation is useful independent work. Assuming the Here
next field shares the already loaded cache line, use this symbolic loop:p := head
while p != null:
q := LOAD [p + next_offset]
if q != null:
PREFETCH [q]
process_payload(p)
p := qprocess_payload must not change the next pointer, and its results must not be needed to determine q. Move the hint before this computation. The real load instruction for the next node still occurs in the next iteration and waits if the prefetch has not finished. The null test can be omitted when the hint's contract safely ignores that target; its cost, when retained, belongs in the loop overhead.Let be the additional demand cache miss penalty per node, the independent processing time after discovering
q, and the cost of issuing one hint. Ignore common loop and cache hit overhead, assume requests do not contend, and assume the prefetched cache line remains resident. Without prefetching, a long traversal takes approximately cycles per node. A hint followed by cycles of work leaves at most cycles of waiting, givingConsequently the ideal steady-state speedup isFor , and , the comparison is . Each hint has enough independent work behind it to hide the entire additional cache miss penalty. For instead, the comparison is : most of the wait remains. With negligible the one-node-ahead model approaches a factor of two when , not an unlimited speedup.One-node-ahead software prefetch overlaps the next linked list node's transfer with processing of the current node; synthetic costs , , cycles, with the final processing interval shown only in part
. Details. The first linked list node still has a startup miss. A node occupying several cache lines may need several hints, and a separately missing
next field adds a genuine pointer chasing delay. Very short node computations, cache pollution or saturation of the data-transfer path can eliminate the benefit. The speedup comes from overlap with work on the current node; a prefetch does not remove the linked list's address dependence.Consider a slow load instruction controlling one conditional addition, followed by independent work. The following is schematic A32 assembly language; the plain Without branch prediction, the unresolved conditional branch prevents fetching beyond it until the load instruction, comparison and branch resolution complete. The multiplication does not depend on
ADD and MUL forms shown do not update processor condition flags:LDR r0, [r1]
CMP r0, #0
BNE skip
ADD r2, r2, #1
skip:
MUL r8, r9, r10r0, yet the out-of-order execution machinery cannot execute it if it has not been fetched and issued.Conditional execution removes that branch:
LDR r0, [r1]
CMP r0, #0
ADDEQ r2, r2, #1
MUL r8, r9, r10ADDEQ adds one exactly when the zero flag from CMP is set; otherwise it preserves r2. The instruction stream has a known sequential continuation, so the CPU can fetch and issue the multiplication while the load instruction is outstanding. With available reservation stations and a free multiplier, independent multiplication overlaps the load instruction's latency.Predication removes a control-flow obstacle to finding independent work; it does not resolve the slow load or its dependent condition earlier. The conditional addition must still select the correct result after the processor condition flags become known. The gain is especially useful for a short conditional region, since instructions whose predicates are false still occupy fetch and issue resources. On a strictly blocking in-order pipeline, a slow demand load instruction would still stop later work in MEM; the overlap here uses the question's out-of-order execution setting and a nonblocking memory path.
Use two different shared locations
x and y, initially zero, and two CPU cores. The following are architectural memory operations, with each result kept in its own processor register:| Core 0 | Core 1 |
|---|---|
STORE [x], 1 | STORE [y], 1 |
LOAD r0, [y] | LOAD r1, [x] |
Each CPU core can retire its write into its own store buffer. Before either write drains, Core 0's load instruction misses its own store buffer because it asks for
y, while Core 1's asks for x. Both can read the old shared values. Afterwards the buffers drain and both locations become one. ThusThere is no contradiction with cache coherence: each location still has an agreed write order. The unusual observation concerns ordering across two different locations. This is the standard store-buffering example, also permitted by total store order.[ref]On a single CPU core executing the two logical participants in an ordinary thread interleaving, the same pair of zero observations is impossible under the usual shared-buffer and own-write-forwarding behavior. To read
y=0, Core-0's logical read must precede the other participant's write of y; to read x=0, the other read must precede the write of x. Combining these requirements with each participant's program order gives the impossible cycleIf a write is still buffered across a context switch on that single CPU core, a later read of the same address sees it through store-to-load forwarding rather than returning an older value. Hence separate store buffers on different CPU cores enable the observation; the comparison assumes ordinary coherent cacheable computer memory and no device accesses or additional writes. A locally completed store need not yet be visible to another core.Separate the two required orders. A writer must make all initialization store instructions to a fresh version visible before another CPU core can use the publication of its address. A reader must load
p before the load instructions of fields addressed through the saved pointer. Assume ordinary coherent cacheable computer memory, atomic aligned pointer accesses, immutable published versions, and the lifetime guarantee from part (ii). The sequences below are architectural operations; a compiler must preserve the stated accesses and dependencies.TSO. Total store order preserves store-to-store order, so initialization writes cannot become visible after the later pointer publication. It also preserves load-to-load order, so reading the pointer precedes reading its fields. Its store-to-load relaxation, illustrated in part (b), does not break either required ordering. ThereforeNo drain of every reader's store buffer and no explicit cache flush is required. This statement is about hardware ordering; unsynchronized ordinary C or C++ accesses to
p would still need the language's atomic operation and acquire-release synchronization facilities to avoid a data race and unwanted compiler transformations.Power/ARM. These weaker memory models can expose a new pointer before all the writer's initialization store instructions are visible. Put a write-ordering memory barrier between the last initialization write and the store of The scope must include every participating CPU core. A broader full barrier is sufficient but can cost more. Without the writer barrier, observing the new
p. A sufficient sequence uses lwsync on POWER architecture or an appropriately scoped DMB, such as DMB ISH, on the ARMv7 ARM architecture family:writer:
STORE [new + field1], value1
STORE [new + field2], value2
publication_barrier
STORE [p], newp and stale initialization data is exactly the unwanted message-passing outcome.[ref]Understanding POWER Multiprocessors (2011). §§2.1–2.2, message passing and ordering by
sync/lwsync, printed pp. 2–4For the reader as actually specified, the saved pointer is an address dependency: the loaded value directly determines each subsequent field's memory address.Both POWER architecture and the relevant ARM memory models respect such dependent reads. Thus no additional reader hardware fence is needed when every data access retains this real address dependency. The writer's publication barrier together with the dependent reads supplies the necessary ordering. A mere test of
reader:
q := LOAD [p]
a := LOAD [q + field1_offset]
b := LOAD [q + field2_offset]p followed by unrelated fixed-address reads would be a control dependency, which is not sufficient here.[ref]Understanding POWER Multiprocessors (2011). §2.2, “Using dependencies” and “Control dependencies, observable speculative reads, and isync”, printed p. 3
If that address dependency is not retained, use an acquire operation or an explicit read-ordering memory barrier after loading
p and before reading fields. DMB ISH on ARMv7 or lwsync on POWER architecture is a conservative sufficient choice for this load-to-load ordering; a suitable conditional branch followed by isync is another POWER acquire implementation. On later ARM instruction sets an acquire load instruction can express the reader requirement directly. Such stronger forms are often convenient even though they are not minimal for the dependent sequence in this question.| Model | Before publishing p | After reading p |
|---|---|---|
| Total store order | No extra hardware fence | No extra hardware fence |
| Power/ARM, genuine dependent field reads | Write-ordering publication barrier | Address dependency suffices |
| Power/ARM, general reads without the dependency | Write-ordering publication barrier | Acquire or sufficient read-ordering barrier |
The minimal answer is a writer publication barrier on Power/ARM, with the stated reader's address dependence doing the read-side ordering; TSO needs neither extra hardware fence. None of these choices repairs an invalid pointer, premature memory reclamation, or a lost update between writers.
O1 supplies mutual exclusion over each complete operation. A reader sees the protected state from a serial order of readers and writers; a writer can safely mutate the existing object while holding the common mutex. If an update reads the old state and modifies it, the entire read-modify-write can be protected against a lost update.
O2 has different semantics. Assume an indivisible aligned access to
p, correct publication ordering, and no later mutation of a published version. A reader chooses one version when it loads p; the subsequent load instructions use the same saved pointer, so an overlapping writer need not block that reader. The reader may continue observing the old complete version after the writer has published the new one. A sufficiently specified snapshot read can still be linearizable, with the load of p as its linearization point: a read overlapping a write is allowed to come before that write in the abstract order. Thus reading an old version during overlap does not by itself prove failure of linearizability.Multiple writers can each construct a valid replacement and race to publish it. If the operation is simply “replace the whole object”, atomic pointer stores give a last-publication order. If each writer instead computes a change from the previously published state, O2 alone does not preserve both changes. Two writers can both read a counter value zero, independently build a version containing one, and publish their respective versions; the final counter is one. A writer mutex or compare-and-swap with retry is needed when the required operation must avoid that lost update. Memory barriers alone do not provide this exclusion.
Releasing the old object's computer memory immediately after publishing a replacement is unsafe. For example:The final operation is a use-after-free: it may crash, see allocator metadata, or read a different object that reuses the same memory address. An atomic update of
reader: q := LOAD [p] // gets the old object
writer: STORE [p], new_object
writer: free(old_object)
reader: LOAD [q + field] // accesses freed storagep does not update the reader's already copied pointer. Freeing the new object immediately would of course also invalidate p itself.Publication and reclamation are separate: publish a fully initialized version, and retain every old version until all readers that might hold it have finished. A read-copy-update grace period or a suitable hazard pointer protocol can provide that lifetime guarantee; naive reference-count acquisition after loading an unprotected pointer can itself race with freeing. Correct memory barriers in part (iii) ensure visibility, not object lifetime.
With O1, even readers must acquire and release the same mutex. Acquiring it normally uses an atomic read-modify-write on the lock's cache line; releasing writes that cache line again. As different CPU cores repeatedly read the data, ownership of the lock's cache line moves among them, causing cache coherence traffic and serializing the protected operations. If the lock shares a cache line with otherwise stable data, false sharing can also invalidate useful reader data. The actual data need not change for this lock traffic to occur.
With O2 and correctly ordered publication, readers perform only load instructions: copy
p once into a processor register, then read that version of the data. Multiple CPU cores can keep read-only shared copies of its cache lines. No per-reader exclusive ownership transfer is needed. During an update, a writer fills newly allocated computer memory privately; the old version's cache lines remain unchanged for readers already using it. Publishing the new pointer writes p and invalidates cached copies of that pointer's cache line, but this occurs only on updates.For many reads and rare updates, O2 avoids lock-line contention and permits concurrent readers, so it can be substantially faster. The costs are heap allocation, construction or copying of a whole replacement, first accesses to its newly populated cache lines, and deferred memory reclamation. Frequent updates or very large replacements can reverse the comparison. O1 can modify a small field in place and may then save copying and computer memory. These are workload-dependent comparisons, not a claim that removing every mutex always accelerates an operation.
Pointer chasing 2026-10-31
Following a chain of stored pointers, as in a linked list. A later memory address may depend on the result of an earlier load instruction, limiting how far ahead cache prefetching can run.
Store buffer 2026-10-31
Storage for pending writes, usually with their memory addresses, data and status. A younger load instruction must account for older overlapping buffered store instructions, often by store-to-load forwarding.
Store-to-load forwarding 2026-10-31
Satisfying a load instruction from an older pending store instruction rather than stale backing computer memory. The youngest applicable older write supplies each requested byte; unresolved or partial overlaps require additional handling.
