Master the fundamental concepts of pipelining & out-of-order execution through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksCPUs and compilers reorder memory operations for speed. Memory barriers (fences) restrict reordering so other cores observe writes in intended order. Lock implementations depend on acquire/release semantics.
cLoading…
MFENCE, SFENCE, LFENCE; locked ops imply strong orderingDMB, DSB, ISBmemory_order_seq_cst maps to fences on weak ISAsFor this exercise, you will demonstrate a litmus test where missing barriers lets a second thread observe ready==1 but stale data. This task asks you to fix ordering with atomics, matching what the Linux kernel documents for spinlock release.
Keep the relevant datasheet, ISA manual, or architecture textbook chapter open while you implement. When your output disagrees with the reference trace on the same program, the bug is usually a mis-decoded opcode, a stale register read, or a flag bit left unchanged after arithmetic.
For this exercise, you will use those habits while implementing the requirement in the starter code. Microarchitectural product names change across CPU generations, but the control ideas (fetch, bypass, cache lines, vector lanes) stay stable enough to debug from first principles.
Run litmus tests for memory ordering. Two threads each execute a few loads and stores, and a memory model decides which interleavings, and which reorderings, are allowed. Enumerate every execution and report which results can be observed. You'll see the classic outcomes: store-buffer (r1 = r2 = 0) and message-passing (reading the flag but not the data), and how a barrier removes them.
cLoading…
Operations, where X, Y are shared variables (initially 0) and rN is thread-local:
| Operation | Meaning |
|---|---|
store X=V | write V to X |
load rN=X | read X into rN |
fence | full barrier |
Each thread's operations are considered in program order, except that a thread may locally reorder two adjacent operations when the model allows it and neither is a fence (a fence may never move, and nothing may move across one):
Enumerate every reordered program per thread (apply swaps repeatedly until no new orders appear), then every interleaving of the two threads' sequences. Execute each on a single shared memory.
Per test:
cLoading…
List every combination of the final register values, in ascending order (registers sorted by name, values ascending, first register most significant), over the value range 0..max, where max is the largest value stored anywhere in the test. Registers are the ones loaded into, sorted by name. Print a blank line between tests.
Input:
cLoading…
Output:
cLoading…
Under sc the same test forbids r1=0 r2=0: no interleaving produces it, and no reordering is allowed. Under tso each thread may float its load ahead of its store, and both read 0.
Hidden tests cover message passing (store data; store flag against load flag; load data) under relaxed, the same test with fences, two threads accessing one location with three possible values, and the load-buffer test.