The speculative Tomasulo algorithm, stage by stage — and the ROB exam table

After this lesson you will run a loop through the four stages of the speculative Tomasulo machine and fill the ROB table and the rename table of the February 2025 paper row by row — including the two rows that may never exist.

On 1 April 2026 the class took a short online self-assessment on "ILP, dynamic scheduling, reorder buffer". Fifty-seven students took part, about ten minutes each, and the course page records the average: 5.9 out of 10. Nothing here is hard arithmetic; what is hard is bookkeeping — who holds a ticket, who holds a value, who may leave — and that is what the exam tests: in February 2025 a whole exercise was a 10-entry ROB table to complete, and in June 2026 four of the eight sub-points of the 8-point open question were speculation, register renaming, out-of-order execution and the reorder buffer.

Here is the puzzle. Take a five-instruction loop, unroll it twice, and run it on a machine with a 10-entry reorder buffer until, in the words of the paper, "the ROB becomes full while the first instruction is still in execution due to a cache miss". Ten instructions sit in the buffer and not one of them may retire, because the very first load is waiting for memory. Two of them belong to a loop iteration that may never exist. Which ticket does each one hold, which register names did the last two steal — and what exactly is torn up if the guess behind them turns out wrong?

Keep the question. First the four stages on the loop of the notes, then the notes' eight-entry example, then the paper's table — filled in by you, one row at a time.

One paragraph of recall, one loop, four stages

Lesson 17 built the reorder buffer: a circular buffer with one entry per instruction in flight, allocated at the tail when the instruction issues and freed at the head when it commits. An entry holds Busy, the instruction and its type, the destination (a register; a memory address for a store; nothing for a branch), the Value once it exists, a Ready bit and the course's Speculative flag. Head and tail are two pointers; head = tail means full if that entry is busy and empty if it is not. Beside the buffer stands the rename table — register name → the entry that will produce its newest value — and the reservation stations stay where Tomasulo put them. That is all you need from lesson 17.

The algorithm has four stages, and the whole lesson walks them on one loop: the notes' Example 14 (pages 242–245), written here in the mnemonics of the exam papers. The notes write MULTD and #8; BNEZ (branch if not zero) stays, because the papers' BNE needs a second register the loop does not have; the registers are written without the dollar sign, as the 2026 paper does (older papers write $F0).

Loop: I0: LD   F0, 0(R1)
      I1: FMUL F4, F0, F2
      I2: SD   F4, 0(R1)
      I3: SUBI R1, R1, 8
      I4: BNEZ R1, Loop

Assumptions of the walk: one instruction issues per cycle, in program order, from cycle 1; the first LD misses in the cache and takes many cycles; FMUL takes several; SUBI takes one; BNEZ is predicted taken; the ROB has 8 entries. The notes do not say how many stations there are, so take two load buffers (Load1, Load2), two multiplier stations (Mult1, Mult2) and two integer stations (Int1, Int2) — enough for the iteration and a half the example covers.

The dependences, as the exam's dependence table would list them: I1 reads F0 from I0 (RAW), I2 reads F4 from I1 (RAW), I4 reads R1 from I3 (RAW); I3 writes R1 after I0 and I2 have read it (WAR); and across iterations the next LD writes F0 while this FMUL still needs it (WAR), while F0, F4 and R1 are written again (WAW). Renaming through ROB entries removes every WAR and WAW; the three RAWs are what the common data bus serves.

CDB Instr. queue Load1 Load unit RS Mult1 Qj = ROB0 ROB0 head ROB1 ROB2 ROB3 tail F0 F2 F4 registers Memory ⟨ROB0, value⟩ 1 1 2 3 3 3 4 CDB to the ROB, not to the registers
Fig. 1. The four stages in one picture. 1 — issue: an instruction leaves the queue for a station or buffer of its class (a load buffer here) and takes the ROB entry at the tail, ROB3: its ticket. 2 — execute on the unit. 3 — write result: the load that holds ROB0 puts ⟨ROB0, value⟩ on the CDB; entry ROB0 fills, and the station Mult1 that was waiting for the tag ROB0 captures the value. 4 — commit: the head alone writes the register file (solid) or, for a store, memory (dashed). The registers are the dashed column: nothing but commit ever writes them.

Read the picture once as a sentence per stage: issue allocates, execute computes, write result publishes to the buffer, commit publishes to the world. Now each stage in the detail the exam asks for.

Issue: a station and a ticket, or nothing

Issue takes the next instruction from the instruction queue and checks two things before it may enter: is a reservation station of its class free, and is a ROB entry free? Both must be. If either is missing the instruction stalls in the queue — and since issue is in order, everything behind it stalls too. This is a structural hazard, and the only thing "ROB full" closes is the entrance: the stations, the units and the CDB keep working.

When both are free, three things happen in the same cycle. First, the entry at the tail is taken: Busy = Yes, the instruction and its destination are written in, Ready = No, Value empty, and Spec = Yes if an older branch in the buffer is still unresolved; the tail advances. Second, the destination register is renamed: the rename table now says F4 → ROB1, whatever it said before. The newest producer always wins, even while an older writer of the same register is still in flight. A load also takes a load buffer; a branch takes an integer station and has no destination; a store, in the paper's table, takes none of the listed stations — its address and its data will live in its entry, which is why the speculative machine has no store buffer. Third, the ROB number goes to the station: it is the tag the result will carry on the CDB — "this ROB number replaces the register name during speculative execution", page 239.

The source operands are fetched by a rule with three cases, and the notes give only parts of it: page 239 says "Ready in the Register File → send it immediately to the Reservation Station; Pending in the ROB → send the ROB number (tag)", page 232 says "If an ROB entry is mapped: the instruction reads the pending value from the ROB". Both are right, and together they make the rule.

Operand rule at issue — for each source register: if the rename table has no mapping, the value comes from the register file; if it maps to entry \(h\) and ROB[\(h\)] is Ready, the value comes from the entry's Value field — completed but not yet committed; if it maps to \(h\) and ROB[\(h\)] is not Ready, the station receives the tag \(h\) and waits for the CDB to broadcast it.

The middle case is the one the exam's scenario produces all the time. A head stuck on a cache miss means that everything behind it finishes without committing, so later instructions find their operands complete in the ROB and absent from the registers. The widget runs the rule on the loop.

Where does the operand come from?

Pick the instruction being issued (cycles from the walk of this lesson: one issue per cycle, the first LD missing, SUBI writing R1 at cycle 6). For each of its sources the stage shows the rename table, the producer's ROB entry and the register file, and draws what the reservation station receives: a value (blue arrow) or a tag (violet arrow). The second control forces the producers of the mapped sources into a state of your choice. The insight to take away: three cases, and the middle one — a value read from the ROB — is where the paper's "head stuck on a miss" scenario lives.

  1. Cycle 2, I1 FMUL F4, F0, F2. Before you look: what does the station get for F0, and what for F2?
    What you should see

    F0 is mapped to ROB0, the missing load, which is not Ready: the station gets the tag ROB0 in Qj and waits. F2 has no mapping: its value comes from the register file into Vk. Two sources, two different cases in one instruction.

  2. Cycle 8, the second iteration's I2 SD F4, 0(R1). R1 is mapped to ROB3 (the SUBI), which finished at cycle 6 — but the head is still stuck, so ROB3 has not committed. Predict the source of R1, then switch to this instruction.
    What you should see

    The value comes from ROB3's Value field: the third case. The store gets its base register at once and can compute its address; nobody waits for a commit. F4, mapped to ROB6, the second multiply, is not ready: tag ROB6 — the store's data will arrive on the CDB.

  3. Stay on that store and force the producers "committed". What changes in the picture, and what does not?
    What you should see

    Both mappings are gone — the entries ROB3 and ROB6 are freed — and both values now sit in the register file, so both arrows are blue and start there. For R1 the number the station receives is exactly what it received before, from ROB3's Value field. Commit changes where a value lives, never whether a consumer can have it.

  4. Now force them "not ready". Why is this worse for a store than for the FMUL of task 1?
    What you should see

    The tag lands on the base register: without R1 the store cannot even compute its address, so it cannot start at all. A missing data operand, by contrast, lets the store start and capture the data later.

"Mapped means wait." No: a mapping says who produces the newest value, not that the value is missing. Check the entry's Ready bit before you write a tag into the station — in the paper's scenario half the entries behind the stuck head are ready, and their consumers take the value straight from the ROB.

Issue needs a free station and a free entry; it takes the entry at the tail, renames the destination to it, and gives each source a value (register file or ready entry) or a tag (entry not ready).

Execute and write result: the value goes to the entry, not to the register

Execution starts when every operand is in the station: "RAW hazards must already be solved to start execution", page 239. A station holding a tag listens to the CDB and captures the value the moment its tag is broadcast; nothing else is blocked meanwhile. Three special cases. A store needs only its base register to start: the effective address is computed, and the data "can still arrive later, closer to the commit phase" (page 240). A load computes its address and then waits until no earlier store in the buffer has an address still unknown or equal to its own — the memory disambiguation of lesson 17 — because a store writes memory only at commit, so the value a load needs may still be sitting in a store's entry. A branch computes its condition and compares it with the prediction: this is the moment the guess is checked, not commit.

When the unit finishes, write result puts ⟨ROB tag, value⟩ on the CDB. Two kinds of listener catch it: every reservation station waiting for that tag, and the entry itself — Value filled, Ready = Yes. The station that held the instruction is freed in the same cycle; the entry is not. And the register file is not touched: in the notes' Figure 27 (Fig. 3 of lesson 17) the CDB no longer reaches the registers at all. A store's data, if it was waiting as a tag, is captured here into the entry's Value, so that by commit the entry holds both address and data.

reservation station Mult1 BusyOpVjVkQjQk YesFMUL—F2 ROB0 — CDB: quiet entry ROB0 (head) BusyDestValueReady YesF0—No register file F0: old value rename table F0 ROB0

Before: the load is still missing in the cache

reservation station Mult1 BusyOpVjVkQjQk YesFMULM[R1]F2—— ⟨ROB0, M[R1]⟩ entry ROB0 (head) BusyDestValueReady YesF0M[R1]Yes register file F0: old value rename table F0 ROB0

After: ⟨ROB0, M[R1]⟩ on the CDB — the station and the entry change, the register does not

Fig. 2. Write result of the loop's first load. The station Mult1 swaps its tag for the value and may start; entry ROB0 fills its Value and turns Ready. F0 in the register file keeps its old value until commit, and the rename table keeps saying ROB0 — a later reader of F0 would now take the value from the entry (the middle case of the operand rule).

The consequence the exam likes: a dependent instruction never waits for the producer's commit. The FMUL of the loop gets F0 the cycle after the load's write result, whether or not the load has reached the head. The notes say otherwise at one point:

"The reservation station is released at commit." No — at write result, in the cycle of the broadcast; it is the ROB entry that lives until commit. That is the whole division of labour: stations are short-lived waiting rooms, entries are the record of program order.

Write result publishes ⟨tag, value⟩ to the stations and to the entry, frees the station, and leaves the register file alone.

Commit: the head, and only the head

Commit is the only stage that touches the architectural state, and it looks at one entry: the head. Three conditions: the instruction is at the head; its Ready bit is set; and, in the course's table model, its Spec flag is No (Hennessy and Patterson need no flag: an instruction reaches the head only after every older branch has committed, so the two views agree — lesson 17 said so). Then one of three things happens, page 241:

Real cores recover as soon as the branch resolves rather than when it reaches the head — Hennessy and Patterson say so, and the notes' Example 13 silently does the same — but the state after recovery is identical, and the exam's tables describe that state. An exception is handled like a misprediction: it is recorded in the entry and taken when the instruction reaches the head, so an exception raised on a mispredicted path is simply flushed with its entry and never happens.

head = tail ROB0 ROB1 ROB2 ROB3 ROB4 ROB5 ROB6 ROB7 all 8 stay

BNEZ taken, as predicted: Spec of ROB5–7 → No; F0 → ROB5 and F4 → ROB6 stay

head tail ROB0 ROB1 ROB2 ROB3 ROB4 ROB5 ROB6 ROB7 3 torn off

BNEZ not taken — mispredicted: ROB5–7 freed, tail ← ROB5; F0 → ROB0 and F4 → ROB1 again

Fig. 3. The same moment of Example 14 — the buffer full, the second iteration issued behind the branch — resolved both ways. Right guess: nothing moves, three flags clear. Wrong guess: the three younger entries are torn off, the tail retreats to the first of them, their stations are released, and the rename table falls back to the older producers (ROB0 and ROB1 are still in flight, so they take the names back; had they committed, the entries would read "—"). Nothing in the register file or in memory has to be undone, because nothing speculative was ever written there.

The price of in-order commit is the one the puzzle is about. A head that waits — a load missing in the cache — stops every commit behind it, however finished; the entries stay busy; the tail catches up with the head; issue stops. Execution continues until the stations run dry, then the machine idles with a buffer full of results. This is why the size of the buffer matters so much: Tomasulo's 1967 paper had the hardware "looking ahead" about eight instructions; Intel's Golden Cove core of 2021 keeps 512 in its reorder buffer, so that a miss of a few hundred cycles at the head does not freeze the whole core. The exam's 8- and 10-entry buffers fill in eight and ten cycles of issue — which is exactly the moment the papers ask you to draw.

Only the head commits, only when Ready and not speculative; a stuck head freezes the buffer, and "ROB full" stalls issue, not execution.

Example 14: eight entries, a stuck head and a second iteration on credit

Now the notes' snapshots, pages 242–244, with the column the exam adds (Res. Alloc.) and the exam's status words: No, issued · No, exec. · Yes; the asterisk marks the cache miss, as the paper writes it. First snapshot: the five instructions of the first iteration have issued, one per cycle, each taking the entry at the tail.

Snapshot 1 — first iteration issued (cycle 5). Head ROB0, tail ROB5. Rename table: F0 → ROB0, F4 → ROB1, R1 → ROB3.
ROB#BusyInstructionDest.ValueRes. Alloc.Ready/StatusSpec.
ROB0YesLD F0, 0(R1)F0—Load1No, exec.(*)No
ROB1YesFMUL F4, F0, F2F4—Mult1No, issuedNo
ROB2YesSD F4, 0(R1)Mem [0+R1]—–No, issuedNo
ROB3YesSUBI R1, R1, 8R1—Int1No, issuedNo
ROB4YesBNEZ R1, Loop–—Int2No, issuedNo
ROB5No
ROB6No
ROB7No

Three details worth a mark each. The store's destination is a memory address and it takes none of the listed stations. The branch has no destination but does take an integer station, because its condition is computed on the integer unit. And R1 → ROB3 belongs in the rename table even though the notes' printed table has only F columns (the text on page 242 says "R1 to ROB3", the table forgets it); the paper's rename table lists F registers only, so write R1 beside it if the exercise mentions integer registers at all.

Second snapshot, "after some cycles": the load has completed and sits at the head, Ready — it will commit in the next cycle; the notes freeze the picture just before. Meanwhile the fetch unit, trusting the prediction, has issued the first three instructions of the second iteration behind the branch. They take ROB5–7 and the flag Spec = Yes, because BNEZ in ROB4 is, in the notes' timeline, still unresolved. The tail has caught up with the head: eight busy entries, the ROB is full, issue stops.

Snapshot 2 — the ROB full (the notes' "after some cycles"; the status cells are the notes', see the red pen below). Head ROB0, tail = head and busy. Rename table: F0 → ROB5 (ROB0 struck), F4 → ROB6 (ROB1 struck), R1 → ROB3.
ROB#BusyInstructionDest.ValueRes. Alloc.Ready/StatusSpec.
ROB0YesLD F0, 0(R1)F0M[0+R1]Load1YesNo
ROB1YesFMUL F4, F0, F2F4—Mult1No, issuedNo
ROB2YesSD F4, 0(R1)Mem [0+R1]—–No, issuedNo
ROB3YesSUBI R1, R1, 8R1—Int1No, exec.No
ROB4YesBNEZ R1, Loop–—Int2No, issuedNo
ROB5YesLD F0, 0(R1)F0—Load2No, issuedYes
ROB6YesFMUL F4, F0, F2F4—Mult2No, issuedYes
ROB7YesSD F4, 0(R1)Mem [0+R1]—–No, issuedYes

Look at the rename table: F0 → ROB5 while ROB0, the older writer of F0, is still in the buffer. That is renaming doing the work of loop unrolling at run time — "enables dynamic loop unrolling without conflicts", page 238 — and it is why the second FMUL (ROB6) waits for ROB5 and not for ROB0. One thing in the snapshot is not what the hardware would do:

Third snapshot, the first commit. ROB0 is at the head, Ready and not speculative: its value goes into F0, the entry is freed, the head moves to ROB1 and the tail finds ROB0 free. The rename table does not change: F0 still says ROB5, because the mapping no longer pointed to ROB0. The multiply, which has had F0 since the broadcast, is now executing.

Snapshot 3 — first commit (the notes' "first commit and progress"). Head ROB1, tail ROB0. Rename table unchanged: F0 → ROB5, F4 → ROB6, R1 → ROB3. Register file: F0 = M[0+R1].
ROB#BusyInstructionDest.ValueRes. Alloc.Ready/StatusSpec.
ROB0No
ROB1YesFMUL F4, F0, F2F4*Mult1No, exec.No
ROB2YesSD F4, 0(R1)Mem [0+R1]—–No, issuedNo
ROB3YesSUBI R1, R1, 8R1—Int1No, exec.No
ROB4YesBNEZ R1, Loop–—Int2No, issuedNo
ROB5YesLD F0, 0(R1)F0—Load2No, issuedYes
ROB6YesFMUL F4, F0, F2F4—Mult2No, issuedYes
ROB7YesSD F4, 0(R1)Mem [0+R1]—–No, issuedYes

The notes' fourth snapshot follows at once: the tail issues the second iteration's SUBI into the freed ROB0, speculative, and the buffer is full again — the "critical situation" of page 244. The whole run now rests on the prediction. If BNEZ resolves taken, the flags of ROB5, ROB6, ROB7 and the new ROB0 clear and the entries commit in their turn. If it resolves not taken, all four are flushed, the tail returns to ROB5, F4 goes back to ROB1 and R1 to ROB3 — but F0 goes to "—", not to ROB0, because ROB0 has committed and F0's value is in the register file now. Fig. 3 drew the two outcomes one snapshot earlier, with ROB0 still in flight: compare the rename tables and you have the restore rule.

Renaming to entries lets the next iteration issue behind an unresolved branch with fresh names; a stuck head keeps all of it in the buffer until the guess is checked.

The February 2025 exercise: ten rows and a rename table

The paper of 11 February 2025, Exercise 4 "Reorder Buffer", as transcribed in the notes (pages 567–568). Registers R1 and R2 are initialised at 0 and 40; the branch is predicted taken. The paper writes the registers with dollar signs and the H&P spellings ADDD $F4, $F2, $F2 and ADDI $R1, $R1, 4; here they are in the 2026 paper's form.

L0: LD   F2, 0(R1)
L1: FADD F4, F2, F2
L2: SD   F4, 0(R1)
L3: ADDI R1, R1, 4
L4: BNE  R1, R2, L0      ; branch predicted as taken

Three counting questions come first, then the unrolling, then the table. The counts are a four-line derivation.

  1. Iterations: R1 goes 0, 4, 8, … and the loop exits when R1 equals R2 = 40. The index is updated before the compare, so the branch is taken 9 times and falls through once.

    \[ N = \frac{R2 - R1_0}{\Delta R1} = \frac{40 - 0}{4} = 10 . \]
  2. Instructions per iteration: 5. Control versus datapath: the notes use the course's loop-overhead criterion — the index update and the branch "control the flow", the load, the add and the store "do actual data processing" — so 2 control, 3 datapath. State the criterion in your answer: under a strict ISA taxonomy ADDI is an integer ALU instruction.

  3. Unrolling by 2 with register renaming: copy the body, give the second copy fresh registers (F6, F8) so that the two copies share no name, shift the second copy's offsets by one element (+4), keep one index update of twice the step and one branch.

    \[ N' = \frac{N}{2} = 5 \] \[ I_{\text{iter}} = 2 \cdot 3 + 2 = 8 \]
  4. So the unrolled loop runs 5 iterations of 8 instructions: 2 control versus 6 datapath, the overhead down from 2/5 to 2/8, the dynamic instruction count from 50 to 40.

L0: LD   F2, 0(R1)
L1: FADD F4, F2, F2
L2: SD   F4, 0(R1)
L3: LD   F6, 4(R1)
L4: FADD F8, F6, F6
L5: SD   F8, 4(R1)
L6: ADDI R1, R1, 8
L7: BNE  R1, R2, L0      ; predicted taken

Then the machine: "the Speculative Tomasulo architecture with a 10-entry ROB and 4 Load Buffers (Load1, Load2, Load3, Load4); 4 FP Reservation Stations (FP1, FP2, FP3, FP4); 2 Integer Reservation Stations (Int1, Int2)", to be filled "until the ROB becomes full while the first instruction is still in execution due to a cache miss (*)". The paper gives the first two rows and a rename table with F0 already "–". From those rows you read the paper's snapshot convention: the head is "No, exec.(*)" and every entry behind it is "No, issued" — even the ADDI, which in a cycle-accurate run would have finished long ago; the snapshot records allocation, not progress. The remaining conventions: Res. Alloc. is the lowest free buffer or station of the instruction's class, the branch takes an integer station, a store takes "–"; Dest. is the register, "Mem" with the address for a store, "–" for the branch; Spec is Yes for every entry issued behind the unresolved BNE, and the branch itself is never speculative.

Fill the table yourself. The widget checks each row against the recomputed answer and keeps the pools, the pointers and the rename table in step.

The February 2025 ROB table, row by row

Rows ROB0 and ROB1 are given by the paper. For each next row choose the instruction, its Dest., Res. Alloc., Ready/Status and Spec., then press "Check this row": when all five cells are right the row is entered; otherwise the right cells turn green, the wrong ones red, and the row stays open. Below the table: the rename table (an overwritten mapping is struck through), the resource pools and the two pointers. When all ten rows are in, the last control shows what happens next. The insight to take away: issue is allocation at the tail in program order; a stuck head freezes the whole buffer; "ROB full" stops issue, not execution.

  1. Before you enter ROB3 (L3: LD F6, 4(R1)): which load buffer does it take, and why not Load1?
    What you should see

    Load2. Load1 belongs to L0 until L0's write result, and L0 is still missing; a load buffer, like a station, is held from issue through write result. The lowest free one is Load2. F6 → ROB3 appears in the rename table.

  2. Before ROB7 (L7: BNE R1, R2, L0): what goes in Dest. and in Res. Alloc., and is the branch itself speculative?
    What you should see

    Dest. "–" (a branch writes nothing), Res. Alloc. Int2 (Int1 is held by the ADDI; the condition is computed on an integer station), Spec. No: the branch is the thing being guessed about, not the guess. From the next row on, Spec. is Yes.

  3. Before ROB8: which instruction comes next, and what happens to the rename entry of F2 when it issues?
    What you should see

    L0 of the second iteration: the branch was predicted taken, so fetch continues at L0, not at the fall-through. F2 is re-mapped from ROB0 to ROB8 — the newest producer wins — although ROB0 is still in the buffer, still missing. It takes Load3 and Spec. Yes.

  4. With all ten rows in, set "and then …" to the misprediction. Predict first: which entries are torn off, where does the tail go, what does F2 map to afterwards?
    What you should see

    ROB8 and ROB9 are freed, Load3 and FP3 with them, the tail goes back to ROB8, and F2 → ROB0, F4 → ROB1 again, because those producers are still in flight. The BNE stays and will commit normally; ROB0 is still waiting for memory; nothing in the registers or in memory needs undoing. (In this loop the first BNE is actually taken — R1 = 8 ≠ 40 — so this is what happens at the fifth iteration, when a sixth is fetched that does not exist.)

Back to the puzzle. The complete table is in the exam section below; its shape is the answer. Ten busy entries with head = tail = ROB0: the buffer is full and issue has stopped, while inside it the second load, the adds and the stores are still free to execute. ROB8 and ROB9 are the two tickets of an iteration that exists only on the strength of a prediction, and they hold the names F2 and F4 — the rename table reads F2 → ROB8, F4 → ROB9 with ROB0 and ROB1 struck through but still in flight. If the guess is wrong, what is torn up is exactly that: two entries, a load buffer and a station, and two mappings that fall back to ROB0 and ROB1. Registers and memory never knew.

head = tail ROB0 ROB1 ROB2 ROB3 ROB4 ROB5 ROB6 ROB7 ROB8 ROB9 issue stalls rename table F0 — F2 ROB0 ROB8 F4 ROB1 ROB9 F6 ROB3 F8 ROB4 pools: dashed = free Load1 Load2 Load3 Load4 FP1 FP2 FP3 FP4 Int1 Int2
Fig. 4. The paper's snapshot as a ring. ROB0 (dashed orange) is the head stuck on the miss; ROB8 and ROB9 (violet wash) are the speculative tickets of the second iteration; head = tail on a busy entry means full. The rename table names the newest producers, with the older ones struck through, and two of the four load buffers and FP stations are still free — the shortage is entries, not stations.

In the paper's snapshot: head "No, exec.(*)", everything else "No, issued", the lowest free station of the class, "–" for stores, Spec = Yes behind the branch, and the rename table pointing at the newest writer.

Exam-style questions

Explain the main concepts and benefits of each one of the following techniques used by modern superscalar processors (Tomasulo-style): (a) dynamic branch prediction and speculative execution; (b) dynamic register renaming; (c) out-of-order execution; (d) reorder buffer. (Five lines each.)

Show model answer

(a) The outcome and target of a branch are predicted at fetch from run-time history (BHT, BTB); the instructions after the branch are fetched, issued and executed before the branch resolves, their results forwarded among themselves but kept out of the architectural state until the branch is validated; on a misprediction the younger instructions are flushed and fetch restarts. Benefit: branches no longer drain the pipeline, the window of exploitable ILP spans several basic blocks. Cost: wasted work and recovery time on a misprediction, so accuracy matters.

(b) Each issued instruction that writes a register gets a fresh name for its result — a reservation station in classic Tomasulo, a ROB entry in the speculative version; the rename (register status) table maps the architectural register to its newest producer, and consumers receive the value or its tag. Benefit: WAR and WAW (name) hazards disappear, instructions and loop iterations can be overlapped freely; only true RAW dependences remain.

(c) Instructions wait in reservation stations and start as soon as their own operands are ready, regardless of program order (data-flow execution), results being broadcast on the CDB. Benefit: a stalled instruction (cache miss, long FP latency) does not block independent later ones; functional units stay busy; CPI falls. It requires in-order commit to keep exceptions precise.

(d) A circular FIFO allocated at issue in program order, written out of order at write result (value, ready bit) and drained in order at commit from the head; it supplies operands between completion and commit, provides the names for renaming and replaces the store buffer (address and data kept until commit). Benefits: precise exceptions (nothing after the faulting instruction has touched the state), cheap misprediction recovery (discard the entries after the branch), safe speculation. Cost: a finite window — a blocked head stalls issue when the buffer fills.

These are sub-points 2, 5, 6 and 7 of the June 2026 question, which gives five blank lines per sub-point. Each line must pair a mechanism with its benefit: "renaming removes WAR/WAW", "the ROB commits in order, so exceptions are precise". Name the hardware (BHT/BTB, reservation station, CDB, head/tail) rather than describing the idea in prose: short labelled statements fit the lines, an essay does not.

Let's consider the following assembly loop where registers R1 and R2 are initialized at 0 and 40 respectively (the branch is predicted as taken). How many loop iterations? How many instructions per iteration? How many control vs datapath instructions? Write the unrolled version of the loop with unrolling factor 2 by using register renaming, and answer the same three questions. Then execute the unrolled version on the Speculative Tomasulo architecture with a 10-entry ROB and 4 load buffers (Load1–Load4), 4 FP reservation stations (FP1–FP4), 2 integer reservation stations (Int1, Int2): complete the ROB and the Rename Table until the ROB becomes full while the first instruction is still in execution due to a cache miss (*).

L0: LD   F2, 0(R1)
L1: FADD F4, F2, F2
L2: SD   F4, 0(R1)
L3: ADDI R1, R1, 4
L4: BNE  R1, R2, L0
Show model answer

Iterations: 40/4 = 10. Instructions per iteration: 5. Control vs datapath: 2 (ADDI, BNE) vs 3 (LD, FADD, SD). Unrolled by 2 with renaming (second copy on F6, F8 with offset +4; one ADDI of 8; one BNE): L0 LD F2, 0(R1); L1 FADD F4, F2, F2; L2 SD F4, 0(R1); L3 LD F6, 4(R1); L4 FADD F8, F6, F6; L5 SD F8, 4(R1); L6 ADDI R1, R1, 8; L7 BNE R1, R2, L0. Iterations 40/8 = 5; 8 instructions per iteration; 2 control vs 6 datapath.

ROB#InstructionDest.Res. Alloc.Ready/StatusSpec.
ROB0L0: LD F2, 0(R1)F2Load1No, exec.(*)No
ROB1L1: FADD F4, F2, F2F4FP1No, issuedNo
ROB2L2: SD F4, 0(R1)Mem [0+R1]–No, issuedNo
ROB3L3: LD F6, 4(R1)F6Load2No, issuedNo
ROB4L4: FADD F8, F6, F6F8FP2No, issuedNo
ROB5L5: SD F8, 4(R1)Mem [4+R1]–No, issuedNo
ROB6L6: ADDI R1, R1, 8R1Int1No, issuedNo
ROB7L7: BNE R1, R2, L0–Int2No, issuedNo
ROB8L0: LD F2, 0(R1) (iter. 2)F2Load3No, issuedYes
ROB9L1: FADD F4, F2, F2 (iter. 2)F4FP3No, issuedYes

Head = ROB0, tail = ROB0 (busy): full. Rename Table: F0 –; F2 ROB0 ROB8; F4 ROB1 ROB9; F6 ROB3; F8 ROB4 (and R1 → ROB6 if integer registers are listed). Load4 and FP4 remain free.

The marks hide in five places: the corrected offset of L5 (4, not 0); "–" in Res. Alloc. for the stores and Int2 for the branch; "No, issued" everywhere behind the head; Spec = Yes only for ROB8–9; and the rename table pointing to the newest producers with the old ones struck through. The notes' solution (pages 568–573) agrees with this table cell for cell except the store offset.

The loop LD F0, 0(R1); FMUL F4, F0, F2; SD F4, 0(R1); SUBI R1, R1, 8; BNEZ R1, Loop runs on a speculative Tomasulo machine with an 8-entry ROB, 2 load buffers (Load1, Load2), 2 multiplier reservation stations (Mult1, Mult2) and 2 integer reservation stations (Int1, Int2). The first LD misses in the cache; BNEZ is predicted taken. (a) Show the ROB (with the allocated resources) and the Rename Table at the moment the ROB becomes full. (b) BNEZ then resolves as not taken. Show the ROB, head, tail and Rename Table after the recovery.

Show model answer

(a) ROB0 LD F0, 0(R1) · F0 · Load1 · No, exec.(*) · No; ROB1 FMUL F4, F0, F2 · F4 · Mult1 · No, issued · No; ROB2 SD F4, 0(R1) · Mem [0+R1] · – · No, issued · No; ROB3 SUBI R1, R1, 8 · R1 · Int1 · No, issued · No; ROB4 BNEZ R1, Loop · – · Int2 · No, issued · No; ROB5 LD F0, 0(R1) · F0 · Load2 · No, issued · Yes; ROB6 FMUL F4, F0, F2 · F4 · Mult2 · No, issued · Yes; ROB7 SD F4, 0(R1) · Mem [0+R1] · – · No, issued · Yes. Head = ROB0, tail = ROB0 (busy): full. Rename table: F0 ROB0 ROB5, F4 ROB1 ROB6, R1 ROB3.

(b) ROB5, ROB6, ROB7 are flushed (Busy = No); Load2 and Mult2 are released; head still ROB0 (the load has not committed), tail = ROB5; Rename table: F0 → ROB0, F4 → ROB1 (the older producers are still in flight), R1 → ROB3; fetch restarts at the instruction after BNEZ, which will take ROB5 with Spec = No; the BNEZ in ROB4 commits normally when it reaches the head.

Part (b) is where answers diverge: the tail goes back to the entry after the branch, not to 0; the rename table is restored, not emptied — had ROB0 already committed, F0 would read "–" instead. Say explicitly that nothing is undone in the registers or in memory.

The same five-instruction loop runs with R1 initialized at 0, R2 at 96 and the same step of 4. It is unrolled with unrolling factor 4 (register renaming, one index update, one branch). How many instructions are executed in total by the unrolled loop (dynamic instruction count)?

Show model answer

Original: 96/4 = 24 iterations of 5 instructions. Unrolled by 4: 24/4 = 6 iterations of 4 · 3 + 2 = 14 instructions. Dynamic count: 6 × 14 = 84 (down from 120).

The two usual slips: counting 4 · 5 = 20 instructions per unrolled iteration (the index update and the branch are not copied), and forgetting to divide the iteration count by the unrolling factor.

In the speculative Tomasulo algorithm, when is the architectural register file updated? (single answer)

  • At write result, when the CDB broadcasts the value The broadcast reaches the waiting stations and the ROB entry; the registers are not on the CDB any more.
  • At issue, when the destination is renamed Issue writes the rename table and the entry, not a value.
  • At commit, when the instruction is at the head of the ROB with its result ready Only the head writes the architectural state, in program order, and only when Ready and not speculative.
  • As soon as the functional unit finishes That is the moment of write result: the value goes to the entry's Value field.
Show model answer

Answer 3: at commit. The register file must reflect only instructions that are certainly part of the program's path; at write result the value lives in the ROB entry and in the stations that captured it.

Answer 1 is the classic Tomasulo rule (lesson 12) — the quiz checks that you know what the ROB changed.

In the Speculative Tomasulo architecture, what type of hardware block is used to undo speculative instructions in case of a mispredicted branch? (single answer; motivate your answer by explaining what happens in the case of a mispredicted branch)

  • Reorder Buffer The entries younger than the branch are discarded before they commit; the architectural state was never touched.
  • Instruction Dispatcher Issue logic allocates stations and entries; it keeps no record of what to undo.
  • Store Buffer The speculative design has no store buffer: the ROB holds the stores' address and data until commit.
  • Reservation Stations They hold operands and tags, not the commit state; they are squashed as a consequence of the flush, not as its mechanism.
  • Load Buffers Same as the stations: released by the flush, not the thing that performs it.
Show model answer

Answer 1, the Reorder Buffer. Every speculative result is held only in its ROB entry (and in station copies), marked speculative, never in the register file or memory. When the branch resolves wrong: the entries younger than the branch are invalidated and the tail goes back to the entry after the branch; their reservation stations and load buffers are squashed; the rename table is restored to the mapping valid at the branch; fetch is redirected to the correct path; the branch itself commits normally. Nothing has to be undone in the architectural state.

This is the February 2025 quiz. The extra point is the motivation: five concrete actions, and the sentence "nothing speculative ever reached the registers or memory".

Which statements about the reorder buffer are true? (multiple answers)

  • The tag broadcast on the CDB is the ROB entry number The entry is the unique name of the in-flight result from issue to commit.
  • The reservation station is released at commit The station is released at write result; the ROB entry is released at commit.
  • A store writes memory at commit Address and data wait in the entry; memory is written in program order at the head.
  • The tail pointer points to the newest issued instruction The tail points to the next free entry — the one the next issue will take.
  • When head = tail the ROB may be either full or empty The busy bit of that entry tells the two apart.
  • A load may read memory only when no earlier store in the ROB has an unresolved or equal address Memory disambiguation: a RAW hazard through memory, because stores write only at commit.
Show model answer

True: 1, 3, 5, 6. False: 2 (station freed at write result, entry at commit) and 4 (the tail is the next free entry).

Two of the six are about when something is freed; the 2026 paper's multiple-answer quizzes are built from exactly such timing pairs.

Check yourself

The 10-entry ROB is full; the head is a load waiting on a long cache miss; the ADDI in ROB6 finished nine cycles ago. What happens during the miss?

  • The ADDI commits out of order to free an entry Never: commit is in order from the head, whatever has finished behind it.
  • Nothing commits and issue stalls; the instructions already inside keep executing as their operands arrive A stuck head freezes commit and, through "full", issue — not execution.
  • The machine is deadlocked until the ROB is emptied by a flush No flush is needed: when the miss is served the head commits and the cascade resumes.
  • The load is cancelled and re-issued later to let the others commit Cancelling the oldest instruction would break program order; the machine simply waits.

An FMUL's result is broadcast on the CDB at cycle 12 and the FMUL commits at cycle 20. Which statements are true?

  • A station waiting for its tag can start at cycle 13 Dependents wait for the broadcast, never for the commit.
  • Its reservation station is free from cycle 13 Released at write result.
  • Its ROB entry is free from cycle 13 The entry lives until commit at cycle 20.
  • An instruction issued at cycle 15 that reads F4 takes the value from the ROB entry Mapped and Ready: the middle case of the operand rule.
  • The register F4 holds the new value from cycle 12 The register file is written at commit, cycle 20.

Write result changes the entry and the stations; commit changes the register. Everything else follows.

The rename table says F2 → ROB8 while ROB0, an older writer of F2, reaches the head and commits. What happens to the mapping?

  • It is cleared: F2 now lives in the register file Clearing it would send the next reader of F2 to a stale value; the newer producer is still in flight.
  • Nothing: it still points to ROB8, the newest producer A commit clears the mapping only if it still points to the committing entry.
  • It goes back to ROB0 ROB0 is gone after its commit; mappings go back to an older producer only on a flush, and only if that producer is still in flight.

SD F4, 0(R1) sits in its station with the tag ROB1 for F4 and the value of R1. Can it start executing?

  • No: both operands must be present, as for any instruction A store is the exception: it starts with the base register alone.
  • Yes: it needs only the base register to compute its address; the data may arrive later on the CDB The data is captured into the entry's Value when ROB1 broadcasts; memory is written at commit.
  • Yes, and it writes memory as soon as F4 arrives Memory is written only when the store reaches the head and commits.

The February 2025 machine had an 8-entry ROB instead of 10. When it fills, how many speculative entries does the table show?

  • Two, as before Count the body: eight instructions fill eight entries before anything behind the branch issues.
  • None: the eight instructions of the unrolled body fill it exactly The branch is the last entry; the buffer is full before the next iteration can issue.
  • Three That would need 11 entries.

Which entries carry Spec = Yes in the paper's snapshot, and why?

  • ROB7, the BNE itself The branch is the thing being guessed about; it resolves and commits normally.
  • ROB8 and ROB9, issued while the BNE was unresolved Everything issued behind an unresolved branch is tentative until that branch resolves.
  • The flags are cleared when the BNE resolves taken, and the entries are flushed if it resolves not taken Right guess: two flags clear; wrong guess: the two entries, their load buffer and station, and their two mappings go.
  • ROB6, because the ADDI changes the loop index the branch tests Feeding the branch is a RAW dependence, not speculation; ROB6 is older than the branch.

Key take-aways

What to watch and read