Three ways instructions collide, and pipelines where EX takes more than a cycle

After this lesson you will fill the exam's dependence table for any loop, say which of its entries a renaming machine deletes, and explain why a pipeline that finishes instructions out of order needs one place where it puts them back in order.

In the paper of 22 June 2026 the first exercise opens with a seven-instruction loop whose I3 is FADD F2, F2, F4. In the exam's dependence table that one line gets an entry in all three columns through the same register, F2: a true data dependence with I1, an anti-dependence with I1 and an output dependence with I0 and I1 (plus a true one with I2 for F4).

The second half of the exercise runs the loop on a Tomasulo machine, which deletes two of the three F2 entries before I3 even starts. Which two, and why can it never delete the third? The answer is one sentence — and the same sentence explains why the IBM System/360 Model 91 of 1967 could not tell the operating system where a floating-point fault had happened.

Dependence, hazard, stall: three words for three different things

This chapter is about instruction-level parallelism (ILP): overlapping independent instructions of one program inside one core. Pipelining is its simplest form (lesson 1); issuing several instructions per cycle and executing them out of order are the aggressive forms (lessons 8 to 18). How much can overlap is bounded by dependences, so start with the loop of the puzzle, written as the paper writes it (older papers write $F2; the 2026 paper and this notebook drop the dollar sign):

I0  FOR: LD    F2, AA(R1)
I1       FADD  F2, F2, F2
I2       LD    F4, BB(R1)
I3       FADD  F2, F2, F4
I4       SD    F2, A(R1)
I5       ADDUI R1, R1, 8
I6       BNE   R1, R2, FOR

I1 adds F2 to itself, and F2 is what I0 has just loaded: I1 needs a value that I0 produces. That is a data dependence — a fact about the program, true on every machine. Whether it costs anything is a question for the pipeline.

Take I1 → I3, two FADDs through F2. On the five-stage pipeline of lesson 2, with forwarding, the second FADD loses nothing; give the adder three cycles and the pair costs two stalls; let the hardware run other instructions meanwhile and it costs nothing again. Same dependence, three prices (Fig. 1).

(a) 5-stage pipeline, forwarding: 0 stalls 123456 I1 IF ID EX ME WB I3 IF ID EX ME WB (b) 3-cycle FP adder, forwarding: 2 stalls 123456789 I1 IF ID X1 X2 X3 WB I3 IF ID X1 X2 X3 WB 2 bubbles
Fig. 1. One program dependence, two pipeline prices. The magenta arrow is forwarding: the value is usable the cycle after it is computed. In (b) the sum is ready only at the end of X3, so I3 waits two cycles; in the real loop the compiler put the independent LD F4 between them, filling one bubble for free.

Dependence — a property of the program: one instruction needs, or re-uses, what another touches. Hazard — a property of the pipeline: the hardware, left alone, would read or write in the wrong order. Stall — the consequence: cycles given up so that the hazard does not happen. Neither step is automatic: a dependence need not become a hazard, and a hazard need not cost a stall.

The notes (p. 74): "dependencies are intrinsic to the program semantics, hazards are an architectural artifact of the pipeline implementation". The exam uses the first two words — EX1's first table asks for dependences, its Tomasulo table has a "Hazards Type" column — and the third hides in its "Calculate the CPI": every stall is a cycle in that count.

A dependence tells the designer three things (Hennessy & Patterson): a hazard is possible; the order in which the results must be computed; and an upper bound on the parallelism — the longest chain of dependent instructions, weighted by the latencies of their units, is the critical path that no machine can beat (lesson 20 computes it for the VLIW exercise).

Two properties may never be broken by reordering (p. 74): data flow — each consumer receives the value program order designates — and exception behaviour — the same exceptions are raised. Everything else is negotiable.

True dependences: a value flows

Data dependence (true dependence) — \(I_j\) reads a value that an earlier \(I_i\) produces, directly or through a chain. It may become a RAW hazard (read after write): the read must not happen before the write.

"True" because a value travels along it. Nothing removes it; you can only shorten the wait (forwarding) or fill it with other work — the compiler by moving instructions (lessons 19–21), the hardware by executing out of order. The chain of true dependences is the floor under every schedule; the first widget shows the longest one of each exam loop.

"The second instruction cannot proceed correctly until the first one completes its write operation" (the notes, p. 75). With forwarding the consumer proceeds as soon as the value is computed — end of EX for an ALU result, end of MEM for a load. The dependence is on the value, not on the write.

Values flow through registers, where the decoder sees the dependence by comparing register numbers, or through memory, where A(R1) and B(R1) may or may not be the same address. The exam's table asks for registers only.

Loop-carried dependences (not in the notes, asked in a quiz)

Loop-carried dependence — from an instruction in one iteration to an instruction in a later one; within one iteration it is intra-iteration (the exam's "intra-loop").

In X[i] = X[i] + X[i-1] the read of X[i-1] depends on the previous iteration: loop-carried. A read of X[i] in the next statement of the same iteration is not. In the exam loops the ADDUI R1 feeds the next iteration's LD: loop-carried, hence absent from the "intra-loop" table. The quiz of 15 July 2024 on exactly this is in the exam section.

Name dependences, and the renaming that deletes them

Now I1 and I3 again. Three relations link them on F2, and only one carries a value: I3 reads the sum I1 produces (true). I3 also overwrites the register I1 reads, and the register I1 writes — nothing flows there; the two merely share the name F2. Write I3's result into F8 instead, with I4 reading F8, and both vanish.

Anti-dependence — \(I_i\) reads a location, a later \(I_j\) writes it; it may become a WAR hazard (write after read) if \(I_j\) writes first. Output dependence — \(I_i\) and a later \(I_j\) write the same location; it may become a WAW hazard (write after write) if \(I_i\) finishes last. Both are name dependences — false or pseudo-dependences in the notes (p. 76).

When can each become a hazard? WAR needs a younger instruction to write before an older one has read: impossible in the five-stage pipeline, where reads happen in ID and writes in WB, both in order; possible once an older instruction waits for its operands while younger ones proceed (the scoreboard of lesson 9, which therefore checks WAR at write-back). WAW needs two writes to complete out of order: possible as soon as units have different latencies, even with in-order issue (section 4).

"This avoids WAR and WAW hazards by ensuring in-order commit" (the notes, p. 81). In-order commit removes WAW; WAR is prevented by in-order operand reads at decode — reads, not commit.

Register renaming

Two instructions that share only a name can be given different names: register renaming. The second writer gets a fresh destination and — the line the notes' Example 4 leaves out — every later reader of the old name is redirected to the fresh one. On the loop:

I0  FOR: LD    F2, AA(R1)
I1       FADD  F6, F2, F2      # was F2: fresh name F6
I2       LD    F4, BB(R1)
I3       FADD  F8, F6, F4      # was F2: fresh name F8, reads I1's F6
I4       SD    F8, A(R1)       # reads I3's F8
I5       ADDUI R3, R1, 8       # was R1: fresh name R3
I6       BNE   R3, R2, FOR     # reads I5's R3

Every name dependence is gone; the chain of true dependences, I0 → I1 → I3 → I4 and I5 → I6, remains (Fig. 2). The next iteration must use R3 for R1: a compiler carries the new names forward, hardware keeps a map, so that "R1" means the physical register most recently assigned to it.

F2 F2 F4 F2 R1 I0 LD F2, AA(R1) I1 FADD F2, F2, F2 I2 LD F4, BB(R1) I3 FADD F2, F2, F4 I4 SD F2, A(R1) I5 ADDUI R1, R1, 8 I6 BNE R1, R2, FOR

Before: twelve dependences, three kinds

F2 F6 F4 F8 R3 I0 LD F2, AA(R1) I1 FADD F6, F2, F2 I2 LD F4, BB(R1) I3 FADD F8, F6, F4 I4 SD F8, A(R1) I5 ADDUI R3, R1, 8 I6 BNE R3, R2, FOR

After renaming: only the five true ones

Fig. 2. The June 2026 loop before and after renaming: orange solid arrows are true dependences, dashed arrows anti-dependences, dotted ones output dependences. Renaming crosses out every arrow on the left and none on the right.

Hardware renaming happens at run time: a register alias table maps each architectural register to one of a larger set of physical registers, or the renaming is implicit, as in Tomasulo's algorithm (lesson 11), where the name of a pending result is the reservation station that will produce it — flexible, at a hardware cost. Software renaming happens at compile time with spare architectural registers, for VLIW machines (lesson 15): simpler hardware, more pressure on the compiler (p. 78). The notes' condition "more physical registers than … logical registers" (p. 77) really means more names than the program uses at once — physical registers, tags, or spare architectural registers.

The exam's table, drilled

EX1 of every paper since 2024 asks for "all types of true-data-dependences, anti-dependences and output dependences for each instruction". For each instruction: true — for each source register, the nearest earlier writer (a register read twice counts once); anti — for the destination, every earlier reader; output — for the destination, every earlier writer. SD and BNE have no register destination: true dependences only. Intra-loop only.

The dependence table of the 22 June 2026 paper, filled in (the exam gives "None" for I0 and I2).
I#InstructionTrue data dependence with I# for FxAnti-dependence with I# for FyOutput dependence with I# for Fz
I0LD F2, AA(R1)NoneNoneNone
I1FADD F2, F2, F2with I0 for F2Nonewith I0 for F2
I2LD F4, BB(R1)NoneNoneNone
I3FADD F2, F2, F4with I1 for F2; with I2 for F4with I1 for F2with I0 and I1 for F2
I4SD F2, A(R1)with I3 for F2NoneNone
I5ADDUI R1, R1, 8Nonewith I0, I2 and I4 for R1None
I6BNE R1, R2, FORwith I5 for R1NoneNone

Two patterns repeat in every paper: the ADDUI R1 collects an anti-dependence with every load and store that used R1, and the BNE depends on it; a register re-used as an accumulator produces a true, an anti- and an output dependence on one instruction — the highlighted row, the puzzle of the warm-up.

Dependence detective: the exam table on the exam loops

Pick one of the four exam loops (three official papers and the notes' February 2025 paper). In the table, click the earlier instructions each row depends on, column by column, then check. The drawing shows the same dependences as arrows: true ones on the right, name dependences on the left. The toggle renames every re-used destination — the insight to take away: renaming crosses out only the left side, and the longest orange chain that survives is what every machine must wait for.

  1. Before touching anything: in the July 2024 loop, which rows get an entry in the anti-dependence column, and how many instructions does each cite? Fill the table and check.
    What you should see

    Two rows. I2 (FADD F2, F2, F4) cites I1, which reads F2 before I2 overwrites it. I4 (ADDUI R1) cites I0 and I3, the load and the store that read R1 — every earlier reader, not only the nearest.

  2. Switch to June 2025. Predict: does any instruction have an anti-dependence on a floating-point register? Then check your table.
    What you should see

    No. The two FADDs write F4 and F6, which no earlier instruction reads (the loads only write them), so their only name dependences are output dependences with I1 and I2. All four anti-dependences of this loop are on R1, in the row of I6.

  3. Back to June 2026: switch the rename toggle on. Which arrows disappear, and what does the "longest true chain" readout say? Does it change when you rename?
    What you should see

    All seven arrows on the left are crossed out; the five orange arrows stay, and the readout keeps saying I0 → I1 → I3 → I4, four instructions. Renaming removes anti- and output dependences and nothing else: the critical path is made of true dependences, and no renaming shortens it.

  4. February 2025: before checking, count how many output dependences the table will contain.
    What you should see

    Zero. Each destination (F0, F2, F4, F6, R0) is written exactly once in the body, so the output column is "None" in every row — and the anti-dependences are on an integer register, R0, although the column heading says "Fy". Write "R0".

Control dependences: the two rules, and the licence to bend them

The loop ends with BNE R1, R2, FOR. Whether the next LD executes at all depends on its outcome, known in ID at the earliest (lesson 4): the fetch unit must wait or guess. That is the third kind of collision.

Control dependence — \(I_j\) is control dependent on a branch \(I_b\) if whether \(I_j\) executes depends on the outcome of \(I_b\), and that outcome is not known when \(I_j\) would enter the pipeline (the notes, p. 79).

Two constraints follow (Hennessy & Patterson, not the notes): an instruction control dependent on a branch cannot be moved before it, so that the branch no longer controls it; an instruction not control dependent on a branch cannot be moved after it, so that the branch controls it.

Yet control dependence is not itself sacred — data flow and exception behaviour are. If moving an instruction across a branch changes neither, the move is legal: the delay slot, whose instruction runs on both paths (lesson 4), predication (lesson 21) and speculation — executing the dependent instructions before the branch resolves and keeping their results undoable (lesson 17) — all live on that licence, "breaking or relaxing control dependencies, without violating program semantics" (p. 79).

I1: BGTZ R1, LABEL I2: ... I3 LABEL: A not taken taken A runs on both paths

The notes' Example 5: A is not controlled by the branch

BLEZ R1, SKIP A SKIP: ... not taken taken A only if not taken

Corrected: branch around A

Fig. 3. Left: the notes' code — both paths arrive at LABEL, A always executes, I2 is the control-dependent instruction. Right: the usual translation jumps past A, which runs only when the branch is not taken.

Prediction, static and dynamic, is lessons 4 to 6; this chapter adds speculation — executing the guessed path and holding its results until the branch is known.

When EX takes more than one cycle

Lesson 1's pipeline assumes every stage lasts one cycle. In the MIPS pipeline of Hennessy & Patterson a dependent instruction waits 3 cycles after an FP add, 6 after a multiply and 24 after a divide — and the divider is not pipelined, since replicating an iterative divider for a rare operation is not worth the area. One cycle for everything would mean a clock as slow as the slowest operation, so EX becomes a set of functional units with their own latencies, pipelined or not (pp. 80, 82).

Latency — how long a unit takes to deliver its result. The exam papers count the execution cycles ("latency 4" for a load); Hennessy & Patterson (app. C.5) count the stall cycles a back-to-back consumer suffers, which is one less. Initiation interval — the cycles between two issues to the same unit: 1 if fully pipelined, the execution time if not (Hennessy & Patterson; not named in the notes).

A unit of \(n\) cycles that forwards at the end costs a back-to-back consumer \(n - 1\) stalls: 0 for a one-cycle ALU, 1 after a load, 2 after the three-cycle adder of Fig. 1. That \(n - 1\) is what Hennessy & Patterson tabulate as the latency; the exam papers tabulate \(n\) — "latency 4" for a load: issued in cycle 1, executing in 2 to 5, writing in 6 (lesson 12).

123456789101112 FADD X1 X2 X3 FADD X1 X2 X3 pipelined 3-stage adder: a new FADD every cycle FDIV divide, 8 cycles FDIV divide unpipelined divider: the next FDIV waits 8 cycles
Fig. 4. The adder: latency 3, initiation interval 1. The divider: latency 8, interval 8 — a second divide waits in orange until the first is out. The numbers are this lesson's.

In order all the way: one commit point (Figure 20)

The notes' model (p. 80) is single-issue and in-order, with write-back "delayed or synchronized". Figure 20 (p. 82) shows what that means: an integer lane, an FP-add lane, an FP-multiply lane and an unpipelined divider all pass through the same sequence of pipeline registers, X1, X2, X3, W — short operations are padded so that every result reaches W after the same number of cycles — and a vertical line through W is labelled "Commit Point". Results reach W in issue order, one per cycle.

That line buys three things: no WAW hazard, since completion order equals issue order; one write per cycle, so one write port suffices; and an exception taken at the commit point comes after every older instruction and before any younger one — a precise exception (section 5). The price: an unpipelined divide or a cache miss stalls everything behind it — "all instructions are serialized through the same issue logic, even if no true dependence exists" (p. 81). Table 8: everything in order, stalls propagate, moderate complexity, exceptions always precise.

Out of order at the end: no commit point (Figure 21)

Figure 21 (p. 86) splits ID into ID (decode) and Issue (read the registers, check the operands); after Issue each instruction goes to its own unit — the ALU followed by Mem for integer and memory instructions, Fadd, Fmul, Fdiv — of its own length, and each unit delivers to WB when it is done: "there is no single commit point shown" (p. 86).

commit point IF D X1 ALU X2 Mem X3 W X1 X2 Fadd X3 W X1 X2 Fmul X3 W X1 Fdiv X3 W same depth for everyone

In order: the notes' Figure 20

IF ID Issue ALU Mem Fadd Fmul Fdiv WB no commit point

Out of order: the notes' Figure 21

Fig. 5. Before: lanes padded to the same depth, results reach W one per cycle in issue order through a single commit point. After: each unit reaches WB on its own — results arrive out of order, several may arrive together, nothing stands between a result and the register file.

Hennessy & Patterson list what this design must cope with even with in-order issue (app. C.5): a structural hazard on the divider; several instructions reaching WB in the same cycle — a write-port conflict; WAW hazards; imprecise exceptions; longer RAW stalls. WAR is not on the list: operands are read in order at Issue.

Watch it on five instructions: divider 8 cycles, not pipelined; multiplier 3; adder 3; integer unit 1; a result written in cycle \(t\) is usable from \(t + 1\); one write port, oldest first.

I0  FDIV  F0, F2, F4        # divider, 8 cycles, not pipelined
I1  FADD  F6, F0, F8        # needs F0 from I0
I2  FMUL  F10, F8, F14      # independent
I3  FADD  F6, F10, F12      # needs F10 from I2; writes F6 like I1
I4  ADDUI R1, R1, 8         # independent, 1 cycle
The two machines on the same five instructions (the last column adds a reorder buffer to the out-of-order machine, section 5).
InstructionIn-order, one commit pointOut-of-order completionCommit with a ROB
IssueStart execWrite resultIssueStart execWrite result
I0 FDIV F011911910
I1 FADD F61010132101314
I2 FMUL F1011111433615
I3 FADD F6151518471016
I4 ADDUI R1161619557 (port busy at 6)17

In order: 19 cycles — the divide freezes issue until cycle 9 and every RAW wait holds everything behind it. Out of order: 13 cycles, with completion order I2, I4, I0, I3, I1; a write-port fight in cycle 6 (the older I2 writes, the ADDUI slips to 7); and F6 written by I3 in cycle 10, then overwritten by the older I1 in 13 — a WAW hazard a real design must catch by stalling the younger writer at issue (scoreboard, lesson 9) or renaming (Tomasulo, lesson 11). The fourth thing, the divide faulting, is the next section and the widget's toggle.

Pipeline race: one commit point, or none, or a reorder buffer

The five instructions above, cycle by cycle, on the machine you choose. Blue bars are execution; orange dashed cells are waiting — for an operand, for the busy divider or, in order, for its turn; faint outlines are the cycles still to come; the blue tick is the write of the result, the dashed ochre tick the commit (reorder-buffer machine only). The register panel shows which instruction's value each register holds after the current cycle. The insight to take away: finishing out of order is what makes the fast machine fast, and it is also exactly what makes an exception imprecise — in-order commit gives the precision back without giving up the speed.

  1. Before stepping: on the in-order machine, in which cycle does the ADDUI write R1, and why so late? Then play the run.
    What you should see

    Cycle 19. It is the last instruction of a strictly in-order pipeline: it cannot issue while the divide holds the lane (until cycle 9), then it queues behind I3, which waits for F10 until cycle 14. An instruction that needs nothing from anyone still pays for everyone ahead of it.

  2. Switch to "out-of-order completion" and step to cycle 6, then 7. What happens to the ADDUI's write?
    What you should see

    In cycle 6 both I2 and I4 are ready to write; there is one write port and the older instruction, the multiply, takes it. The orange cross marks the cycle I4 wanted; its tick appears in cycle 7. Two results in one cycle is the structural hazard of this design.

  3. Still out of order: step to cycle 13 and read the register panel. Which register is wrong, and which instruction's value should be there?
    What you should see

    F6 holds I1's sum, written in cycle 13, but I3 is the younger writer of F6 and the program wants I3's value — I3 wrote it in cycle 10 and I1 overwrote it. That is a WAW hazard, and the panel flags it.

  4. Switch the exception toggle on. Predict for each of the three machines whether the state at the fault equals the sequential state at the FDIV; then step to the fault in each.
    What you should see

    In order: the fault is reported at the commit point in cycle 9 and nothing younger has even issued — precise. Out of order: the same cycle 9, but F10 already holds I2's product and R1 has been incremented by I4 — imprecise, and nothing can take those writes back. With the reorder buffer: I2 and I4 have finished too, but their results sit in the buffer; the fault is taken when I0 reaches the head of the buffer in cycle 10, the younger entries are discarded and the registers never changed — precise again, with the same execution speed.

Precise exceptions, and the fix that won

Precise exception — when it is reported, every older instruction has completed and no younger one has changed the state, so the program can restart at the faulting instruction. The notes define the opposite (p. 85): an exception is imprecise "if the processor state when an exception is thrown does not look exactly as if the instructions were executed in order".

Why insist? Many exceptions are requests, not errors: a page fault (lesson 27) means "this data is on disk", and the operating system brings it in and re-executes the faulting load — only that load, with everything before it done and nothing after. An IEEE floating-point trap must name the exact operation. Imprecise exceptions are a correctness problem: the program cannot be restarted.

How the five-stage pipeline handles exceptions (textbook, not in the notes)

The professor's topic list names "Multicycle Pipeline; Exceptions Handling"; the notes give it one sentence, so the mechanism is Hennessy & Patterson's (app. C.4). Each stage can raise its own exceptions (Fig. 6): IF a page fault or misaligned fetch, ID an illegal opcode, EX overflow or divide by zero, MEM a page fault or misaligned data access, WB none. A stage only sets a flag in a status vector that travels with the instruction; the vector is checked at WB. If it is not empty, the instruction's writes are turned off, the younger instructions are flushed, the PC is saved and the handler is fetched. Checking at WB takes exceptions in program order even when they arise in another: if a load page-faults in MEM (its fourth cycle) and the instruction right behind it page-faults in IF (its first cycle, two cycles earlier in time), the load reaches WB first and its fault is handled first. The pipeline is precise because every instruction reaches WB in order — what Figure 20's commit point preserves and Figure 21 gives up.

exception flags travel with the instruction to WB IF ID EX MEM WB page faultmisaligned illegalopcode overflow÷ by zero page faultmisaligned none here:taken here oldest instruction first
Fig. 6. Where exceptions arise in the five-stage pipeline and where they are taken: a stage sets a flag, WB checks it, flushes the faulting instruction and everything behind it, and starts the handler — precise and oldest-first.

Four remedies for multi-cycle units, and the one that won

Once units complete out of order the trick breaks: when the divide reports its fault, a younger multiply may have written its register. Hennessy & Patterson list four ways out (app. C.5): accept imprecise exceptions, where no restart is ever needed; buffer the results until all earlier instructions have completed — a future file that updates the registers in order, or a history file that keeps the old values to restore them; keep enough information for a software handler to finish the older instructions and restart; issue an instruction only when all earlier ones are certain not to fault. The notes name the second, "Reorder Buffers (ROB) to store results until it's safe to commit them in program order", and "checkpointing and rollback" (pp. 84–85). The second won: every modern core completes out of order and commits in order through a reorder buffer — the widget's third machine, built in lesson 17. Table 9: issue in order, execution and completion out of order, ILP high, complexity high, exceptions: "Risk of imprecision without commit logic".

IBM's answer to the CDC 6600 was the System/360 Model 91: about fifteen were built, the first went to NASA's Goddard Space Flight Center in October 1967, and at 16.6 MIPS it was the most powerful computer in user operation. Its floating-point unit ran Tomasulo's algorithm and finished instructions out of order — and it had imprecise exceptions, one of the two problems the reorder buffer later fixed. "Precise" got its standard definition in Smith and Pleszkun's 1988 paper, which compared five solutions; the reorder buffer survived.

Dynamic scheduling, defined properly

The notes open section 3.3 with three instructions (p. 87), here in the mnemonics of the papers:

I1  FDIV  F0, F2, F4        # long-latency divide
I2  FADD  F10, F0, F8       # depends on F0
I3  FSUB  F12, F8, F14      # independent of I1 and I2

In order, I2 waits for F0 and I3 waits behind I2 although it needs nothing from either. Dynamic scheduling parks I2 until F0 arrives and lets I3 execute and complete at once — the race widget's 13 cycles instead of 19.

Dynamic scheduling — the hardware rearranges the order in which instructions execute, at run time, to reduce stalls, preserving data flow and exception behaviour. Decode is split in two: issue — decode, check for structural hazards — stays in program order; read operands and execute waits until there is no RAW hazard and happens out of order. In-order issue, out-of-order execution and completion; with a reorder buffer, in-order commit.

Why must issue stay in order, if everything else may move?

At issue, every older instruction has already been seen, so each source can be bound to its producer — the value, or the name of the station that will produce it — and the reorder-buffer entry taken at issue is in program order for free.

The structures (p. 87): reservation stations or issue queues, where instructions wait for operands; a reorder buffer, for in-order commit; register renaming tables; a common data bus that broadcasts each result with its tag to every waiting consumer. The stages: Fetch, Decode, Issue (into a reservation station), Execute (when all operands are available), Write Result (to a buffer or broadcast), Commit (architectural registers updated in order). The CDC 6600's scoreboard (lesson 9) has none of these structures and still executes out of order; Tomasulo (lesson 11) adds stations, bus and implicit renaming; the reorder buffer (lesson 17) adds commit.

"Issue: place instruction into reservation station if operands aren't ready" (the notes, p. 87). In Tomasulo's algorithm every instruction is issued to a reservation station; ready operands are copied in, missing ones recorded as the tags of their producers.

What it buys (p. 88): higher ILP, units kept busy, latency hiding, independent operations that no longer block one another; and, Hennessy & Patterson add, dependences unknown at compile time (memory, control flow, cache misses) are handled, the same binary runs well on different pipelines, and hardware speculation becomes possible. What it costs (p. 88): WAR and WAW hazards, cured by renaming; imprecise exceptions, cured by the reorder buffer; and hardware — dependency tracking, the wakeup-and-select logic that picks the ready instructions each cycle, the broadcast bus, the instruction window. How wide that window can be is lesson 8.

Issue in order, execute out of order, commit in order: everything the next eleven lessons build keeps these three rules.

Back to the puzzle

A Tomasulo machine renames: I3's result gets a fresh name, so it no longer overwrites a register that I1 still has to read or write — anti- and output dependence gone. The true dependence stays: I3 needs the sum I1 computes, and no name can replace a value that does not exist yet. One sentence: renaming deletes name dependences because nothing flows along them, and keeps true dependences because something does. The out-of-order freedom that renaming unlocks is what left the Model 91 unable to say where a fault had happened, until completion and commit were separated — by the reorder buffer.

Exam-style questions

(10 June 2025, EX1, first part.) Let's consider the following assembly code containing multiple types of intra-loop dependences. Complete the following table by inserting all types of true-data-dependences, anti-dependences and output dependences for each instruction (I0, I1 and I2: None).

I0  FOR: LD    F2, A(R1)
I1       LD    F4, B(R1)
I2       LD    F6, C(R1)
I3       FADD  F4, F2, F4
I4       FADD  F6, F4, F6
I5       SD    F6, A(R1)
I6       ADDUI R1, R1, 8
I7       BNE   R1, R2, FOR
Show model answer
I#True data dependence with I# for FxAnti-dependence with I# for FyOutput dependence with I# for Fz
I0, I1, I2NoneNoneNone
I3with I0 for F2; with I1 for F4Nonewith I1 for F4
I4with I3 for F4; with I2 for F6Nonewith I2 for F6
I5with I4 for F6NoneNone
I6Nonewith I0, I1, I2 and I5 for R1None
I7with I6 for R1NoneNone

Three rules fill the table: nearest earlier writer of each source; every earlier reader of the destination; every earlier writer of the destination. The marks usually lost: forgetting that the ADDUI collects all readers of R1, including the store; writing an anti-dependence for I3 on F4 (nobody reads F4 before I3 — I1 writes it, which is an output dependence); listing I0 as a producer for I4 (I4 reads F4 from I3, the nearest writer). The loop-carried dependence through R1 is not asked.

(In the form of Question 1 of 22 June 2026.) Explain the main concepts and benefits of each one of the following techniques used by modern superscalar processors (Tomasulo-style): 1. Dynamic instruction scheduling; 2. Dynamic register renaming; 3. Out-of-order execution.

Show model answer

Dynamic instruction scheduling. The hardware, not the compiler, decides at run time when each instruction executes. Instructions are issued in program order to reservation stations and start executing as soon as their operands and a functional unit are available, so an instruction stalled on a long-latency producer (a divide, a cache miss) does not block the independent instructions behind it. Benefits: higher ILP and unit utilisation, latency hiding, handling of dependences unknown at compile time (memory, control flow), and binary compatibility — the same code runs efficiently on different pipelines; it is also the base for hardware speculation.

Dynamic register renaming. At issue, each destination register is mapped to a new name — a physical register, a reservation-station tag or a reorder-buffer entry — and later readers are redirected to it, so two instructions that re-use the same architectural register no longer conflict. Benefits: it eliminates WAR and WAW hazards (name dependences), leaving only true RAW dependences, which lets instructions execute out of order and loop iterations overlap without running out of registers.

Out-of-order execution. Instructions execute in the order in which their operands become ready rather than in program order, while issue and commit remain in program order; results are broadcast on the common data bus to the waiting consumers. Benefits: independent instructions fill the cycles that dependent ones would waste, long latencies are overlapped, more ILP is extracted from the instruction window. Cost: renaming, reservation stations and a reorder buffer to keep exceptions precise.

The question gives five lines per technique: write concept, mechanism, benefit and cost as short labelled statements, with "in-order issue, out-of-order execution, in-order commit" somewhere in the answer — that triad is what the examiner looks for. The other five items of the real question — 2 dynamic branch prediction and speculative execution, 3 multiple-issue pipeline, 4 reservation stations, 7 reorder buffer, 8 simultaneous multithreading — are answered in lessons 5–6, 8, 11, 17 and 29.

A 5-stage pipeline is extended with multi-cycle floating-point units of different latencies (a pipelined adder and multiplier, an unpipelined divider), keeping in-order issue. Which new hazards and problems appear, and why? How does a single in-order commit point remove them, and at what cost?

Show model answer

New problems: (1) a structural hazard on the unpipelined divider; (2) several instructions may reach write-back in the same cycle, so the register-file write port must be arbitrated or reserved at issue; (3) WAW hazards, because a short operation issued after a long one to the same register completes first — WAR hazards cannot occur, since operands are still read in order in ID; (4) imprecise exceptions, because a younger instruction may have written its result before an older one faults; (5) more and longer RAW stalls, since forwarding cannot hide a multi-cycle computation.

A single commit point delays every result so that all operations reach the W stage after the same number of cycles: write-back is then in issue order, one per cycle (no WAW, no write-port conflict), and an exception is taken at commit, after all earlier instructions and before any later one (precise). Cost: independent fast instructions wait behind slow ones, and an unpipelined divide or a cache miss stalls the whole pipeline — low ILP, which is what motivates out-of-order execution with a reorder buffer that keeps commit in order while execution reorders.

The examiner checks that you separate the five effects and that you know which hazard cannot occur (WAR) and why. "Precise" must be defined, not just named: older ones done, younger ones without effect, restart at the faulting instruction.

(15 July 2024, quiz 1, single answer.) Let's consider the following loop: for (i=1; i<=100; i++) { X[i] = X[i] + X[i-1]; /*S1*/ Z[i] = X[i] + Y[i-1] /*S2*/ }. How many loop-carried dependencies are in the code?

  • Answer 1: Only one in S2 because Z[i] depends on Y[i-1] Y is never written inside the loop, so Y[i-1] is an input, not a dependence between iterations.
  • Answer 2: Only one in S1 because X[i] depends on X[i-1] S1 reads X[i-1], which S1 wrote in the previous iteration: that is the one loop-carried dependence.
  • Answer 3: Both of them S2 depends on X[i] from the same iteration (intra-iteration) and on Y, which the loop does not write.
Show model answer

Answer 2. S1 reads X[i-1], produced by S1 in iteration i-1: loop-carried. S2's dependence on X[i] is within the iteration, and Y[i-1] is read only.

The exam's own distractor is the array index: "[i-1]" does not make a dependence unless the loop writes that array.

(Multiple answers.) Which hazards can occur in the classic 5-stage in-order pipeline with a single-cycle EX stage?

  • RAW A consumer may need a value before the producer has computed it (a load followed by its user still stalls one cycle even with forwarding).
  • WAR Impossible: every read happens in ID and every write in WB, both in program order, so no younger instruction can write before an older one reads.
  • WAW Impossible: all writes happen in WB, in program order.
  • Structural With one memory for instructions and data, or one register-file port too few, two instructions want the same resource in the same cycle.
  • Control The branch outcome is known in ID or EX, after the next instructions have been fetched.
Show model answer

RAW, structural and control. WAR and WAW cannot occur: reads precede writes in order (no WAR) and all writes happen in WB in order (no WAW). Register renaming therefore buys nothing on this pipeline; it matters only once multi-cycle units (WAW) and out-of-order execution (WAR) appear.

A quiz in the 2026 "multiple answer" form. The trap is to tick WAR and WAW because they exist as dependences in the program; the question asks which can become hazards on this pipeline.

(Single answer.) In a pipeline with in-order issue, functional units of different latencies and no reorder buffer, an FDIV reports a divide-by-zero at the end of its execution. Why is the exception imprecise?

  • Because the FDIV was issued out of order Issue is in order in this pipeline; the order that breaks is completion.
  • Because younger instructions have already written their registers when the fault is reported, so the state is not the sequential state at the FDIV The program cannot be restarted at the FDIV: part of what follows it has already happened.
  • Because the write port was busy when the FDIV wanted to write A write-port conflict delays a write; it does not change which instructions have taken effect.
  • Because the instructions that depend on the FDIV have been squashed Squashing the dependent instructions is harmless: they had not changed the state. The problem is the independent ones that did.
Show model answer

The second option. An exception is precise when all older instructions have completed and no younger one has changed the state; here younger, independent instructions completed and wrote their registers before the older FDIV faulted, so the state corresponds to no point of the sequential program. In-order commit (a reorder buffer) fixes it by holding their results until the FDIV has committed.

Watch for the word "issued": the examiner uses it to check that you know which stages stay in order.

Check yourself

In the notes' Example 5 code — I1: bgtz r1, LABEL, I2: ..., I3: LABEL: A — which instruction is control dependent on the branch?

  • I3, the instruction A A is reached on both paths: by the jump when the branch is taken, by falling through I2 when it is not.
  • I2 I2 executes only when the branch is not taken: its execution depends on the outcome, so it is the control-dependent one.
  • Both I2 and I3 I3 runs whatever the branch does — nothing about it depends on the outcome.
  • Neither, because the branch resolves in ID Where the branch resolves changes the cost of the dependence, not its existence.

Register renaming removes which of the following?

  • True data dependences (RAW) A value flows along a true dependence; a new name cannot replace a value that does not exist yet.
  • Anti-dependences (WAR) The younger writer gets a fresh name, so it can no longer overwrite what the older reader needs.
  • Output dependences (WAW) Two writers with different names cannot finish in the wrong order into the same register.
  • Control dependences Renaming concerns registers; which instructions execute is decided by branches, and only speculation bends that.

Renaming deletes every arrow on the left of Fig. 2 and none on the right.

The June 2026 loop runs on the 5-stage pipeline of lesson 2, with full forwarding and a one-cycle EX. Which dependence of I3 (FADD F2, F2, F4) costs a stall cycle?

  • The true dependence on I1 for F2 ALU to ALU with forwarding costs nothing, and I2 sits between them anyway.
  • The true dependence on I2 for F4 I2 is a load: its value is ready only at the end of MEM, one cycle too late for I3's EX. One stall, the load-use hazard of lesson 2.
  • The anti-dependence on I1 for F2 Name dependences never stall the 5-stage pipeline: reads in ID precede writes in WB, in order.
  • None of them The load-use pair I2 → I3 stalls once; forwarding cannot help because the value does not exist before MEM.

A pipeline issues strictly in order, but its functional units have different latencies and each writes the register file as soon as it finishes. Which hazard appears that the 5-stage pipeline never had?

  • WAR Operands are still read in order at issue, before any younger instruction can write: no WAR.
  • WAW A short operation issued after a long one to the same register finishes first, and the older write lands last.
  • RAW RAW hazards existed already on the 5-stage pipeline; they only get longer here.
  • A structural hazard on the register read ports Reads are still one instruction per cycle; the conflict that appears is on the write port.

In a dynamically scheduled processor with a reorder buffer, which steps happen in program order?

  • Issue Always in order: that is how every source can be bound to the right producer.
  • Execute Out of order: whoever has its operands and a unit goes first.
  • Write result Out of order: different units take different times.
  • Commit In order, through the reorder buffer: that is what keeps exceptions precise.

Issue in order, execute and write out of order, commit in order — the rule of every step in the notes' Table 10.

An exception is called precise when…

  • it is reported in the same cycle in which it occurs The five-stage pipeline deliberately reports exceptions later, at WB, and they are precise all the same.
  • every older instruction has completed and no younger one has changed the state, so the program can restart at the faulting instruction That is the definition; restartability is the whole point.
  • the faulting instruction has finished writing its result The faulting instruction must not write; what matters is the others.
  • the pipeline is empty when the handler starts The pipeline is flushed in any case; precision is about which instructions have taken effect, not about emptiness.

How many loop-carried dependences are in for (i = 2; i < n; i++) { A[i] = A[i-2] * 2; /*S1*/ B[i] = A[i] + 1; /*S2*/ }?

  • None S1 reads A[i-2], which S1 wrote two iterations earlier.
  • One, in S1 A[i-2] was written by S1 two iterations before: loop-carried. S2 reads A[i] from the same iteration.
  • Two, one in each statement S2's dependence on A[i] is intra-iteration: S1 wrote it in the same pass.

Key take-aways

What to watch and read