Scope: MSI on the course model, the MP test, and the SC and TSO models, with the weak-model differences stated once in the last section.
Term | Meaning in this topic |
|---|---|
Performed | The load has obtained its value, from the cache, a buffer or a response. |
Committed (retired) | The load's result is architecturally final. Commit happens in program order. |
Squash | Discard the instruction and every younger instruction, including their results. |
Replay | Re-execute a squashed load, which performs it again and obtains a fresh value. |
A load can be performed but not yet committed for many cycles. In that window its value is tentative: the core may use it for younger work, but must be able to squash it if keeping it would break the memory model.
X (payload) and Y (flag) are in different lines, both 0. C1 executes W(X)=1 then W(Y)=1. C0 executes r1=R(Y) then r2=R(X). The roles are reversed from the litmus lesson (there C0 writes and C1 reads); the register order (r1, r2) = (flag, payload) is the same. SC and TSO forbid (1, 0): the reader saw the flag, so it must see the payload written before it.
C0 already holds X's line in S / 0 from earlier work. Its R Y misses, so the younger R X hits and performs first with 0. That is allowed only if nothing can make the committed pair (1, 0).
Loads may perform out of order. Each performed, uncommitted load keeps its line address in the load queue.
An incoming Inv or Fwd-GetM for line X, or C0's own eviction of X, is compared against every performed, uncommitted load. On a match, if an older load is still unperformed, the core squashes and re-executes from the matching load. If no older load is unperformed, no squash is needed: the value can commit in order.
Without this check, MP can commit flag = 1, payload = 0. C1's GetM(X) must invalidate C0's S copy before C1 may write X, so the Inv reaches C0 before C1's store to X, and therefore before its store to Y. If C0's R Y later returns 1 and C0 kept its early 0, the forbidden pair commits. The Inv hitting the load queue is what prevents it.
Own evictions count because after C0 evicts X it is no longer a sharer, so C1's GetM sends it no Inv. The eviction is C0's last chance to notice.
The check is conservative: it squashes whenever the line changed while an older load was outstanding, even when the early value would have been legal. This scheme goes back to Gharachorloo, Gupta and Hennessy (ICPP 1991), who used speculative loads with invalidation checks to run strict models quickly. The alternative is value-based re-execution at commit: re-read each speculative load before it commits and squash only if the value changed (Cain and Lipasti, ISCA 2004), which needs no snoop of the load queue but costs a second cache access.
View | What it can establish | What it cannot establish alone |
|---|---|---|
Performed | A value was obtained at that point. | That the value may commit. |
Coherent source | The value was the newest for X at the moment it was read. | That older loads to other lines are consistent with it. |
Committed | The result passed the core's ordering checks and is final. | That it stays the newest value: later writes do not change a committed register. |
SC and TSO (x86-TSO, RVTSO / Ztso) order every load-load pair, so they need it for all of them. RVWMO and AArch64 allow different-address load-load reordering, so MP's (1, 0) is allowed there with plain loads. They still need the check for same-address read-read pairs (CoRR: two reads of X must not see X go backwards) and wherever an address dependency, an acquire or a fence orders the loads.
No. Once committed, the result is final. The Inv only matters for loads that are performed but not committed.