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.
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).
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.
Before: twelve dependences, three kinds
After renaming: only the five true ones
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.
| I# | Instruction | True data dependence with I# for Fx | Anti-dependence with I# for Fy | Output dependence with I# for Fz |
|---|---|---|---|---|
| I0 | LD F2, AA(R1) | None | None | None |
| I1 | FADD F2, F2, F2 | with I0 for F2 | None | with I0 for F2 |
| I2 | LD F4, BB(R1) | None | None | None |
| I3 | FADD F2, F2, F4 | with I1 for F2; with I2 for F4 | with I1 for F2 | with I0 and I1 for F2 |
| I4 | SD F2, A(R1) | with I3 for F2 | None | None |
| I5 | ADDUI R1, R1, 8 | None | with I0, I2 and I4 for R1 | None |
| I6 | BNE R1, R2, FOR | with I5 for R1 | None | None |
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.
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).
The notes' Example 5: A is not controlled by the branch
Corrected: branch around A
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).
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).
In order: the notes' Figure 20
Out of order: the notes' Figure 21
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
| Instruction | In-order, one commit point | Out-of-order completion | Commit with a ROB | ||||
|---|---|---|---|---|---|---|---|
| Issue | Start exec | Write result | Issue | Start exec | Write result | ||
I0 FDIV F0 | 1 | 1 | 9 | 1 | 1 | 9 | 10 |
I1 FADD F6 | 10 | 10 | 13 | 2 | 10 | 13 | 14 |
I2 FMUL F10 | 11 | 11 | 14 | 3 | 3 | 6 | 15 |
I3 FADD F6 | 15 | 15 | 18 | 4 | 7 | 10 | 16 |
I4 ADDUI R1 | 16 | 16 | 19 | 5 | 5 | 7 (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.
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.
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".
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 Fx | Anti-dependence with I# for Fy | Output dependence with I# for Fz |
|---|---|---|---|
| I0, I1, I2 | None | None | None |
| I3 | with I0 for F2; with I1 for F4 | None | with I1 for F4 |
| I4 | with I3 for F4; with I2 for F6 | None | with I2 for F6 |
| I5 | with I4 for F6 | None | None |
| I6 | None | with I0, I1, I2 and I5 for R1 | None |
| I7 | with I6 for R1 | None | None |
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?
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?
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?
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?
Register renaming removes which of the following?
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?
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?
In a dynamically scheduled processor with a reorder buffer, which steps happen in program order?
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…
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*/ }?
Key take-aways
- A dependence is a property of the program, a hazard a property of the pipeline, a stall a consequence; the same true dependence costs 0, 1 or \(n - 1\) cycles depending on the machine, and reordering may never change data flow or exception behaviour.
- True dependences carry a value and survive every machine; anti- (WAR) and output (WAW) dependences share only a name, and renaming — in hardware through a map or tags, in software with spare registers — deletes them.
- The exam's table: nearest earlier writer of each source; every earlier reader of the destination; every earlier writer of the destination; stores and branches have true dependences only; intra-loop only.
- WAR can never occur in the 5-stage pipeline; WAW appears as soon as units of different latency complete out of order, even with in-order issue.
- A multi-cycle in-order pipeline pads every lane to one commit point: no WAW, one write per cycle, precise exceptions — and every stall propagates. Letting results arrive when ready buys ILP and loses all three.
- An exception is precise when all older instructions have completed and no younger one has changed the state; the 5-stage pipeline takes exceptions at WB, oldest first; with out-of-order completion the reorder buffer restores precision by committing in order.
- Dynamic scheduling: issue in order, execute and complete out of order, commit in order. Renaming removes its WAR/WAW hazards, the reorder buffer its imprecise exceptions, and both cost hardware.
What to watch and read
-
How CPUs do Out Of Order Operations
Computerphile, Matt Godbolt
A programmer's-eye view of why a modern core is a dataflow machine behind a sequential façade, and why the three kinds of dependence are what limits it. No tables, no notation: watch it before the lesson to see where it is going.
-
Ripes — visual RISC-V pipeline simulator
ripes.dk — open source, runs in the browser
Type the June 2026 loop in RISC-V form and step the five-stage model: you will see the load-use stall of I2 → I3 and the forwarding paths of Fig. 1 on the real datapath. Ripes has no out-of-order model — it is the "before" picture of this lesson.
-
Complex Pipelining: Out-of-Order Execution and Register Renaming (MIT 6.823, lecture 12)
Arvind, Asanović, Emer28 slides
The lineage of the notes' Figure 20: the same multi-cycle in-order pipeline with its padded lanes and commit point, then the step to out-of-order issue and renaming. Read it after the lesson, with Fig. 5 next to it; the notation differs from the notes' (p1/src1, tags t1…tn).
-
Modern Microprocessors: A 90-Minute Guide
Jason R. C. Patterson (Lighterra)
The sections "Instruction Dependencies & Latencies" and "Instruction Scheduling, Register Renaming & OOO" retell this lesson with real processor names and widths — the right second reading once the vocabulary is in place.
-
Implementing Precise Interrupts in Pipelined Processors
J. E. Smith and A. R. Pleszkun, IEEE Transactions on Computers, 198812 pages
Where the definition of "precise" and the reorder buffer come from. Read the introduction and the comparison of the five solutions after the lesson; the rest is lesson 17.