Gates → GPUs · Notes
Unit 3 · Lessons 8–15

Make one CPU core faster

Suppose the program and its instructions cannot change, but one core must finish it sooner. Where can time be saved? We will overlap instructions, keep data close, guess what to fetch next, and run ready work while other work waits. Each change is hidden inside the core: the program must still receive the results its instructions promise.

You'll be able to
Trace overlapping instructions through a small core, explain what makes each one wait (a value, a resource, or a decision), and tell apart a result that has been computed from one that may safely become official.
Study plan
Three passes, each its own session: overlap and hazards (8–9), memory and prediction (10–12), then width, scheduling and precise completion (13–15).
A note on models
Every explorer states its own model. The in-order five-stage pipeline of Lessons 8–9 is not the out-of-order core of 14–15. Cache sizes, latencies and widths are invented teaching numbers, not any real product's.
One distinction runs through the whole unit. Architectural state is what the instruction contract makes visible: registers, memory, PC. Microarchitectural state is everything the hardware keeps to go faster: pipeline registers, cache contents, predictor tables, rename maps, queues. It can change timing, but it must never change the program's architecturally defined results for the same inputs. (Timing can still matter in other ways: it can change how threads interleave and when a program interacts with the outside world.)
Lesson 8

Overlap instructions with a pipeline

LimitationOne instruction at a time leaves most of the hardware idle in any given cycle.
New mechanismSplit the datapath into stages separated by registers, and start a new instruction every cycle.
New statePipeline registers (with valid bits) holding several partly finished instructions at once.
CostRegister overhead per stage, a clock set by the slowest stage, fill and drain time, and hazards (Lesson 9).

Idle hardware

In Unit 2's multicycle machine, only one instruction is active. While it uses the ALU, the fetch hardware sits idle; while it reads memory, the ALU sits idle. Could the fetch hardware start the next instruction instead? In the picture, follow cycle 2: the first instruction is in decode while the second is already in fetch. By cycle 5, five independent instructions occupy five different stages. We assume each uses all five stages so the scheduling idea is easy to see.

Same hardware, very different utilization. Simplified so that every instruction uses all five phases (like a load). Left: one instruction at a time, so each unit is busy one cycle in five. Right: a new instruction enters every cycle, so once the pipe is full every unit is busy every cycle, each on a different instruction.

Five stages, and the registers between them

StageWorkWhat it passes forward
F · fetchRead the instruction at PCinstruction bits, its PC
D · decodeWork out the operation, read registersoperand values, destination, control bits
E · executeALU result or memory addressresult/address, control
M · memoryLoad or store, if neededloaded data or the carried result
W · writebackWrite the destination register, if any—

That overlap needs a way to keep the instructions apart. Between stages sit pipeline registers. At every clock edge, each stage hands its instruction's intermediate values and control bits to the next. The pipeline registers also carry each instruction's PC and destination register number. For an instruction that writes a register, the destination number identifies which architectural register receives its result at writeback. A valid bit marks whether a slot holds a real instruction or a bubble, an empty slot used when the pipeline must wait. There is no longer one "current instruction": up to five instructions can be in flight, each at a different stage.

An add still passes through M even though it doesn't touch memory, and a store passes through W without writing a register. A stage is a position in the hardware, not a job every instruction must do.

Try it: fill, flow, drain

The utilization picture compares the pipeline with Unit 2's multicycle machine. This explorer compares it with Unit 2's idealized single-cycle machine: five 1 ns pieces of logic in one 5 ns cycle, with no registers between the pieces. The pipeline puts registers between them, so each cycle can be shorter but pays register overhead. Adjust the instruction count and timing, then move the cycle slider to see what each stage holds.

Model: each stage's logic takes 1 ns (except E, if you change it), and a pipeline register adds the overhead. Instructions are independent, with no misses, branches or stalls. The timing baseline is a single-cycle unpipelined machine with no interstage registers; its full logic path takes 5 ns (or more) per instruction.

Latency, throughput, and the empty ends

The timing diagram can make the pipeline look five times faster. Faster in which sense? Separate the time for one instruction from the rate at which a stream finishes:

  • Latency: how long one instruction takes from start to finish. Here that's 5 × 1.1 = 5.5 ns, longer than the single-cycle baseline's 5 ns because of the interstage registers.
  • Throughput: how much work completes per unit time. Once the pipe is full, one instruction per 1.1 ns.

For N independent instructions in an ideal K-stage pipeline, the total is N + K − 1 cycles. The first instruction finishes at the end of cycle K; each of the remaining N − 1 finishes one cycle later, so the last finishes in cycle K + N − 1. For example, four instructions in five stages take 4 + 5 − 1 = 8 cycles. This count includes the empty start and the final drain; do not add either again. A short program cannot get close to the ideal K× speedup:

Longer instruction streams approach the limiting speedup. With five 1 ns stages and 0.1 ns register overhead, speedup approaches 5 / 1.1 ≈ 4.55× as the instruction count grows. Shorter streams achieve less because filling the pipeline takes a larger share of their execution time. The slowest stage sets the clock. If E takes 2 ns while the other stages remain at 1 ns, the pipeline's clock period becomes 2.1 ns and the single-cycle baseline's logic path becomes 6 ns. The limiting speedup then falls to 6 / 2.1 ≈ 2.86×.
In steady state, pipelining raises the machine's throughput without making any instruction faster. Each instruction actually takes a little longer here, and a short fragment can finish later because of fill time and register overhead. The diagrams assumed independent instructions. What happens when the next instruction needs a result that is still travelling through the pipe? That is Lesson 9's first hazard.
PredictDraw the diagram on paper first.
  1. With 4 independent instructions, which stage holds each instruction in cycle 4? How many have completed?
    Show answer

    I1 is in M, I2 in E, I3 in D, I4 in F. None have completed: I1 finishes W in cycle 5.

  2. Using 1 ns stages and 0.1 ns overhead, how long do 100 independent instructions take pipelined and unpipelined? What's the speedup?
    Show answer

    Pipelined: (100 + 4) × 1.1 = 114.4 ns. Unpipelined: 100 × 5 = 500 ns. Speedup ≈ 4.37×, close to the 4.55× ceiling because the fill cost is spread over many instructions.

  3. E's logic grows to 2 ns while the others stay at 1 ns. What is the minimum clock period? Does adding more pipeline registers somewhere else help?
    Show answer

    2 + 0.1 = 2.1 ns. Registers elsewhere don't help, because the clock is set by the slowest stage. Splitting E itself into two stages can shorten the clock period. It adds another register boundary and makes each instruction pass through six stages. An instruction that needs E's result may have to wait an extra cycle for it.

  4. Four instructions are in flight at once. Are they four software threads?
    Show answer

    No. They're consecutive instructions of one thread, each partly done. The extra state is the pipeline registers holding each one's intermediate values, not extra PCs or register files for other threads.

Draw a five-instruction timeline by hand, including the empty start and end. Explain why better throughput didn't improve the latency of any single instruction.

Lesson 9

Hazards, stalls & forwarding

LimitationOverlap lets an instruction read a register before the instruction ahead of it has produced the value.
New mechanismHazard detection that stalls when needed, plus forwarding paths that deliver fresh results early.
New stateRegister identifiers, valid bits and other metadata in the pipeline registers that control forwarding (much of it already there), plus stall/bubble control bits. The register-number comparators and bypass muxes are new hardware: combinational logic, not state.
CostBubbles waste cycles, and bypass wiring adds area and timing pressure. Nothing can deliver a value before it exists.

A dependency is in the program; a hazard is in the schedule

Lesson 8 let a new instruction enter every cycle because the instructions were independent. Now put add x6, x5, x9 immediately after a load into x5. The add needs the value produced by this load rather than the value previously stored in x5. That need is a dependency, a fact about the program. The attempted schedule creates a hazard when the needed value has not arrived. Other hazards arise when hardware or a branch decision is missing. Ask what the machine is waiting for:

HazardWaiting for…ExampleUsual fix
Dataa valueadd x6, x5, x9 right after the load of x5forward it, stall if it doesn't exist yet
Structurala resourcefetch and a load both need the only memory portmore hardware (separate instruction/data caches)
Controla decisionwhich instruction comes after a branch?stall, or predict (Lesson 12)

When is a value ready, and when is it needed?

For a data hazard, compare when the producer has a value with when the consumer needs it. In our model, an ALU result exists at the end of E, but loaded data exists only at the end of M. A consumer needs its operands at the start of its own E. In the figure, compare the arrows for an add and a load: which one reaches the next instruction in time? Forwarding gives a result a direct route from the producer's pipeline register to the consumer's ALU input, instead of making it wait to be written to and reread from the register file. Without forwarding, the value must reach W first. We assume W writes early enough that D can read it later in the same cycle.

Why a load costs a bubble even with forwarding. An add's result can be passed straight from the end of cycle 3 to the next instruction's E in cycle 4. A load's data only exists at the end of cycle 4, which is already too late for a cycle-4 E. Forwarding changes where a value comes from, not when it exists.

Take a load followed immediately by an add that uses it. The load goes through F in cycle 1, D in 2, E in 3 and M in 4. The add would enter E in cycle 4, before the load's data exists. The hazard unit therefore holds the add in D for cycle 4, freezes the younger fetch behind it, and sends an invalid bubble into E. The older load keeps moving. In cycle 5, the add enters E and receives the value forwarded from the load's M/W pipeline register. The bubble costs one cycle, but no instruction is skipped or executed twice.

Try it: stalls and forwarding paths

Hatched cells are stalls. The instruction is held in D, not re-executed. Copper arrows are forwarding paths, from where a value becomes available to the E stage that uses it. With forwarding off, teal outlines pair each producer's W with the consumer's D in the same cycle: W writes the register file early, and D reads it later. "Reordered load" places independent work between the load and the instruction that uses its result. With forwarding on, the consumer reaches E after the loaded value becomes available, eliminating the load-use stall in this example.

Structural hazards

Suppose instruction fetch and data access shared one single-ported memory. A load reaching M in cycle 4 would collide with a later instruction trying to fetch in cycle 4. The memory can serve only one request in cycle 4, so instruction fetch waits until cycle 5. Forwarding cannot resolve this conflict because both accesses need the memory port. Separate L1 instruction and data caches let these accesses use different ports.

One memory port, two requests in cycle 4. I4's fetch is pushed to cycle 5.

So the first diagnostic question is what is missing: a value, a resource, or a decision? Forwarding helps the first only after a value exists; another port helps the second. Prediction will address the third. Our timing model assumes the load's data is ready at the end of a single M cycle. A longer memory access delays that result and can extend the dependent instruction's stall. The next lesson examines how caches reduce memory-access time.

PredictMark "value ready" and "value needed" first.
  1. In the load → add → add program, why can't the loaded value reach the first add's E in cycle 4, even with forwarding? When can that add execute?
    Show answer

    The data exists only at the end of the load's M stage (cycle 4), but the add would need it at the start of cycle 4. The add stalls one cycle and executes in cycle 5, with the value forwarded from the M/W register.

  2. How many cycles does that three-instruction program take with and without forwarding (first F to last W)?
    Show answer

    With forwarding: 8 cycles (the ideal 7 plus one load-use bubble). Without: 11 cycles, since each dependent instruction waits two extra cycles to read the register file after W.

  3. Replace the load with an add that produces x5 at the end of E. How many stalls remain with forwarding?
    Show answer

    Zero. Each result is forwarded from the end of E straight into the next instruction's E. That's the "ALU chain" case.

  4. A load in M and a younger instruction's fetch both need the only memory port. What kind of hazard is it, and why doesn't forwarding fix it?
    Show answer

    A structural hazard: two requests, one port. Forwarding moves existing values around, but it can't create a second port. Only more hardware (or waiting) helps.

Before inserting a stall, mark when the producer creates the value and when the consumer needs it. Decide whether forwarding can deliver it by then; if it cannot, hold the consumer until it can.

Lesson 10

Keep reused data close: caches

LimitationOur pipeline assumed memory answers in one stage. Real main memory takes around a hundred times longer than a cycle.
New mechanismSmall, fast caches that keep copies of recently used memory in fixed-size lines.
New stateCached lines, plus tags, valid and dirty bits, and replacement (LRU) information.
CostCached data and tags use chip area and energy. Write policies affect memory traffic. A load that misses waits for its data, although the core may continue other work. Larger caches and extra tag comparisons for additional ways can increase lookup time.

Why a hierarchy

Forwarding cannot help an add until the load has received its data. If every load had to go all the way to main memory, many instructions would spend far longer waiting than executing. Fast memory is small and expensive per bit, while large memory is slower, so machines stack several kinds: registers, then one or more cache levels, then DRAM, then storage. Read the figure from top to bottom: the farther the data is, the more there is of it, but the longer the trip. L1, L2 and L3 simply name cache levels; their sizes and policies vary by design.

Orders of magnitude, not specifications. The gap between an L1 hit and a DRAM access is roughly 100×. A cache pays off when programs reuse what it holds: the same addresses again soon (temporal locality), or neighbouring bytes of a line it has just fetched (spatial locality). Even a single sequential pass hits on the later bytes of each line.

Temporal and spatial locality

A small cache helps only if it tends to hold the next bytes the program wants. Programs often reuse a value soon after reading it: temporal locality. They also often read nearby addresses, as in an array scan: spatial locality. That is why a cache fetches a whole line (often 64 bytes) on a miss. If an array of 4-byte integers begins at a 64-byte line boundary, one line holds its first 16 elements; the first access can miss and the next 15 can hit, provided the line remains in cache. In the plot, look for repeated heights (temporal reuse) and steadily rising addresses (spatial reuse).

Plots of address against time. A scan climbs steadily, showing spatial locality. Repeated accesses to a loop's few variables form flat bands, showing temporal locality. This pointer chase reads scattered addresses without revisiting them, providing little temporal or spatial locality. A chase through a small, compact set of nodes can still reuse cached lines.

How a cache finds a line

Once a cache has fetched a line, how does it find that line again? We'll use a tiny, fully traceable cache: 8-bit addresses, 16-byte lines, 64 bytes of data (4 lines). A hit means the requested line is present and valid; a miss means the cache must fetch it from the next level, possibly replacing another line. To locate and identify a line, split each address into three fields:

  • offset: which byte within the 16-byte line (4 bits).
  • index: which set the line must live in.
  • tag: the rest of the address, stored alongside the line to identify which block is there.

How many index bits there are depends on the organization. Direct-mapped has 4 sets of 1 line, so 2 index bits: each block has exactly one possible home. 2-way set-associative has 2 sets of 2 lines, so 1 index bit: two possible homes. Fully associative has 1 set of 4 lines and no index bits: a block can go anywhere. The capacity is the same in every case. What changes is the placement flexibility, which costs extra tag comparisons.

For a direct-mapped lookup of byte address 0x4C, first identify its 16-byte line, 0x40–0x4F. The low hex digit C is the offset within that line. Line number 4 maps to set 4 mod 4 = 0; the remaining upper bits give tag 1. Look only in set 0: a valid line tagged 1 is a hit, and offset C selects the requested byte. An invalid line or a different tag is a miss. On a miss the cache fetches the whole 16-byte line, so a later read of 0x4D can hit if the line has not been evicted.

Try it: a cache you can trace by hand

Trace:

Current access

Cache contents

Model: read-only byte accesses, one at a time. A miss fetches the whole 16-byte line and evicts the least recently used line in that set. Misses are labelled with the "3 Cs": compulsory (first touch of that block ever), conflict (a fully associative cache of the same size would have hit), or capacity (even fully associative would miss, because the data simply doesn't fit).

Try "3 blocks, 1 set" in all three organizations. Direct-mapped and 2-way both miss every time. Two ways cannot hold three blocks that all compete for one set, and LRU evicts the block needed next. Fully associative has room for all three. Now try "scan ×2 (too big)": the scan touches eight 16-byte lines, but the cache holds only four. On the second pass, the first access to each line misses again even in the fully associative cache: those are capacity misses. The following access within that newly fetched line can still hit through spatial locality. "Too big" does not mean every byte access misses.

Writes

Stores add two separate policy choices:

QuestionOptionWhat happens
When does the next level see the write?Write-throughUpdate the cache and send the write onward. Simple, but lots of traffic.
Write-backUpdate only the cache and set the line's dirty bit. Write it back when evicted.
A store misses. Bring the line in?Write-allocateFetch the line, then modify it in the cache.
No-write-allocateSend the write onward without caching it.

Hardware can also prefetch, fetching lines it predicts you'll need. Good prefetches hide waiting. Wrong ones waste bandwidth and evict useful data.

A first cache performance model

For a simple blocking, one-level cache, the average memory access time is AMAT = hit time + miss rate × miss penalty. Every access pays the hit lookup time; only the fraction that misses pays the additional miss penalty. With the default numbers below, that is 2 ns + 0.10 × 40 ns = 6 ns per access on average. This is an average across hits and misses, not a promise that any individual access takes 6 ns.

Miss rate changes average access time. With a 100 ns miss penalty and a 2 ns hit time, increasing the miss rate from 2% to 4% raises AMAT from 4 ns to 6 ns. The average increases by 2 ns, equal to the hit time in this example. Time per instruction also depends on how often instructions access memory and how much of the miss delay overlaps with other work (Lesson 14).

Overlapping misses: memory-level parallelism

The AMAT calculation treated each miss as an added cost. Can a core make progress while a miss is outstanding? A nonblocking cache keeps a small table of outstanding misses (often called miss-status holding registers, MSHRs). It can serve hits and send further misses while an earlier one is still in flight. Overlapping misses is memory-level parallelism (MLP). The number of miss-tracking entries and memory bandwidth limit it, but so does the program: a second miss cannot start until its address is known. In the figure, compare two known addresses with a pointer chase whose second address comes from the first load.

independent loadsa[0] and a[64] miss 1: 100 cycles miss 2 starts 2 cycles later both done ≈ 102 cycles pointer chasep = p->next; p = p->next miss 1: 100 cycles miss 2 needs miss 1's value done ≈ 200 cycles 0100200
Independence is what lets misses overlap. Two loads whose addresses are known up front can both be outstanding at once, so two misses cost about one miss latency. In a pointer chase, the next address is the previous load's result, so the misses serialize no matter how capable the cache is. Out-of-order execution (Lesson 14) lets a core find independent loads to start early.
PredictSplit the address into fields before deciding.
  1. In the direct-mapped cache, what are the block number, set, tag and offset of address 0x4C?
    Show answer

    0x4C = 0100 1100. Offset = low 4 bits = 0xC. Block = 0x4C / 16 = 4. Set = 4 mod 4 = 0. Tag = 4 / 4 = 1 (binary 01). So bits 01 | 00 | 1100.

  2. For the "ping-pong" trace (08, 48, 08, 48, 0C, 4C), how many hits does direct-mapped get? 2-way?
    Show answer

    Blocks 0 and 4 both map to set 0. Direct-mapped: they evict each other every time, so 0 hits (2 compulsory, 4 conflict misses). 2-way: both fit in set 0's two ways, so 2 compulsory misses, then 4 hits. Same capacity, different placement.

  3. A cache with a 1 ns hit time, 5% miss rate and 60 ns miss penalty: what's the AMAT? Is that the CPU's time per instruction?
    Show answer

    1 + 0.05 × 60 = 4 ns. No: it's an average per memory access under a serial model. Instruction time depends on how many instructions access memory and how much of the miss time overlaps with other work.

  4. Why might bigger lines or aggressive prefetching slow a program down? What extra state does a write-back cache need?
    Show answer

    They move bytes that might never be used, which consumes bandwidth and evicts useful lines. A problem if the program is bandwidth-limited or has poor spatial locality. A write-back cache needs a dirty bit per line, to know which lines must be written back when evicted.

Never call an access a hit until you've found its set and compared the valid tags there. Explain the difference between capacity and placement flexibility.

We have treated an address such as 0x4C as if it directly named a memory byte. A real process normally uses a virtual address. Logically, the machine must also establish which physical bytes the process means and whether it may access them; the next lesson follows that decision.

Lesson 11

Translate and protect addresses

LimitationOur machine so far has no memory-protection mechanism, so software can access any implemented memory address, and programs can't be placed flexibly. Virtual memory adds flexible per-address-space mappings and permissions.
New mechanismVirtual addresses translated page by page through per-process page tables, with a TLB caching recent translations.
New statePage tables in memory (owned by the OS), TLB entries (tagged by address space), and a page-table base register.
CostTranslation work on every access, TLB misses (page walks), page faults, and memory used by the tables.

Every program gets its own view of memory

The cache trace treated an address as the same physical location for everyone. But what if two programs both use pointer 0x1234? They should not accidentally read each other's bytes. With virtual memory, both instruction fetches and load/store accesses begin with virtual addresses. Hardware translates them to physical addresses using mappings controlled by the operating system. Virtual memory is divided into fixed-size pages; physical memory into equally sized frames. A mapping places a process's virtual page in a physical frame, without requiring the two numbers to match. Follow 0x1234 through both processes in the figure: the same virtual address reaches different physical bytes. An unmapped or disallowed access raises a fault. This private, protected view is useful even when nothing is swapped to disk.

Same pointer, different bytes. Both processes use virtual address 0x1234. Their page tables send it to different frames. Process B has no mapping at all for A's frame, so it has no way to express an access to it.

Splitting an address: page number + offset

Our teaching system uses 16-bit virtual addresses and 256-byte pages. The top 8 bits are the virtual page number (VPN) and the bottom 8 bits are the offset within the page. Translation replaces only the page number. The offset passes through unchanged.

Tiny 256-byte pages keep the hex arithmetic easy. Real systems typically use 4 KB pages (12 offset bits), plus larger "huge" pages.

The mapping also says what the process may do. A page-table entry holds permission bits (readable, writable, executable, user-accessible) and a present bit. Finding a physical frame does not make every kind of access legal. In the explorer below, every mapped page is readable, and only write permission is checked.

The page table itself lives in ordinary memory. Reading it for every instruction fetch and data access would add memory work just to find the address. A TLB (translation lookaside buffer) therefore caches recent translations and their permissions. It holds mappings, never the program's data: a TLB hit answers "which frame and is this allowed?", while a data-cache hit answers "are those bytes already nearby?"

For example, virtual address 0x1234 splits into page number 0x12 and offset 0x34. If page 0x12 maps to physical frame 0xA0 and the access has permission, the resulting physical address is 0xA034. A TLB hit supplies that frame number quickly. On a TLB miss, the processor consults the page table and can then cache the translation. The data cache checks whether it holds the line containing physical address 0xA034. The diagram places this check after translation to show the logical decisions; real hardware can overlap parts of the cache lookup with translation.

Try it: follow one access through translation

Before choosing a scenario, predict which question should fail: translation lookup, permission, or data lookup. The highlighted path then shows the decisions made. The TLB fills as you go, so an address that missed in the TLB can hit there on the next try; that alone does not decide the data-cache result.

Scenarios:

TLB (4 entries)

Page table (in memory, OS-managed)

Model: translation, then the data cache, drawn in sequence to show the logical decisions (real hardware overlaps them). A page walk is summarized as one step, though it's really several memory reads. Data-cache lines (16 B) present at the start: 0xA030, 0xA040, 0xA140, 0xB410.

Three different events, three different questions

EventThe question that failedWho handles itIllustrative cost
TLB miss"Do I have this translation cached?"Hardware walks the page table (some ISAs trap to software)tens of cycles
Cache miss"Do I have a copy of these data bytes nearby?"Hardware fetches the linetens to hundreds of cycles
Page fault"Is this access allowed and backed by memory at all?"An exception into the OSfrom microseconds (a minor fault, no disk I/O) to milliseconds (reading from storage)

The TLB and data cache perform different lookups. An access can hit one and miss the other, hit both, or miss both. A page fault doesn't always mean disk I/O. Depending on the mapping, the OS can allocate a new page filled with zeros, create a private copy of a shared page when the process first writes to it (copy-on-write), or load a page from storage. If the access is invalid, it can terminate the process with a segmentation fault. On a TLB hit, the processor still checks the entry's cached permission bits.

Page tables belong to an address space (a process), not a thread. Threads of one process share them. When the OS switches processes, it points the MMU at another page table, and the TLB must not serve the old process's translations. Tagging entries with an address-space ID avoids flushing the whole TLB.
PredictAnswer "which bytes?", "allowed?", and "cached where?" separately.
  1. Translate virtual 0x12E8 given VPN 0x12 → frame 0xA0. Which part is unchanged?
    Show answer

    VPN 0x12, offset 0xE8, so the physical address is 0xA0E8. The offset 0xE8 passes through unchanged.

  2. Can an access miss in the TLB and still hit in the data cache? Trace a read of 0x1344 right after a TLB reset.
    Show answer

    Yes. VPN 0x13 isn't in the TLB (miss), so the page walk finds frame 0xA1 and fills the TLB. The physical address is 0xA144, whose line 0xA140 is present: a cache hit. The TLB caches translations and the data cache caches bytes, so they hit and miss independently.

  3. A write hits in the TLB, but the entry says read-only. May it proceed? Is a missing cache line the problem?
    Show answer

    No, it raises a permission fault. The translation was found, but the check failed. In this model, the permission failure stops the access before the data-cache step. Real hardware can overlap the cache lookup with the permission check, but the disallowed write must never take architectural effect.

  4. Does every page fault mean reading from disk? Give two different OS responses.
    Show answer

    No. For the "page on disk" scenario the OS loads the page and installs a mapping, then the instruction retries. For the "unmapped page" scenario there's nothing valid there, so the OS terminates the process (segmentation fault). The OS can also allocate a page filled with zeros or, for a copy-on-write mapping, give the writing process its own copy of a shared page.

For any access, answer three questions separately: which physical bytes? (translation), is it allowed? (permissions), and where is a copy? (caches).

Address translation tells fetch where an instruction lives. It still cannot tell fetch which instruction comes after a branch whose result is not known yet.

Lesson 12

Predict the next instruction address

LimitationFetch needs the next PC every cycle, but a branch doesn't know its direction until E.
New mechanismPredict the direction (and target), fetch down the guessed path, and squash the wrong-path work if the guess was wrong.
New statePrediction counters and history, target buffers, and recovery information.
CostWasted wrong-path work, predictor area and energy, and a misprediction penalty that grows with pipeline depth.

The problem: fetch can't wait

Translation can tell fetch where an instruction lives, but a conditional branch must first choose which instruction comes next: the sequential one or one at a target address. The choice may depend on a value an earlier instruction has not produced yet. In our five-stage pipeline, the branch decides at the end of E. If fetch waits for every decision, it leaves two slots empty per branch. In an otherwise ideal run of 100 instructions with 20 branches, that is 40 extra cycles: about 140 cycles instead of 100, or CPI ≈ 1.4.

So the processor guesses both which way the branch will go and, if taken, where it will go. A direction predictor estimates taken or not taken; target information supplies the destination address. Fetch continues speculatively, before the guess is confirmed. For example, a branch at 0x100 might be predicted not taken, so fetch reads 0x104 and 0x108. If E later finds the branch taken to 0x140, those younger instructions are squashed (marked invalid), and fetch restarts at 0x140. The guess affects wasted work and timing; it must not change the program's accepted results.

A misprediction in the five-stage model. The branch was predicted not taken, but at the end of cycle 3 it turns out taken. The two wrong-path instructions (in D and F) are turned into bubbles, and the target is fetched in cycle 4. That's two lost fetch slots. Neither squashed instruction reached M or W, so neither wrote to data memory or an architectural register in this model. Deeper and wider machines have far more wrong-path work in flight, and need explicit machinery to discard it (Lesson 15).

Try it: a 2-bit saturating counter

A loop branch often goes back many times, then falls through once on exit. A predictor should learn the usual direction without reversing its guess after that single exit. This teaching model keeps a 2-bit counter for each branch. States 0–1 predict not taken and 2–3 predict taken. The predictor reads the state before the branch runs; after learning the true outcome, it increments toward 3 for taken or decrements toward 0 for not taken, stopping at either end. From strong-taken state 3, one unexpected not-taken result moves to weak-taken state 2, which still predicts taken next time. A second not-taken result moves to state 1 and changes the guess.

Pattern:

Top row: what actually happened. Bottom row: what the counter predicted, green if right and red if wrong. On "loop exits", each exit costs one miss but the counter doesn't flip. For the displayed T, N, T, N… sequence starting in state 1, the counter predicts every outcome incorrectly; other initial states give a different accuracy (try state 3). Real predictors add history: they index counters by the recent pattern of branch outcomes, which lets them learn alternation and much more.

How much does accuracy matter?

A simple long-run estimate is CPI ≈ base CPI + (branches per instruction) × (fraction of branches mispredicted) × (penalty per miss in cycles). With the default values below, one in five instructions is a branch, one in ten of those is mispredicted, and each miss wastes two cycles: the added CPI is 0.2 × 0.1 × 2 = 0.04, for a total of 1.04. The estimate treats penalties as separate and ignores fill and drain.

A longer penalty has a larger effect when base CPI is low. Keep branches at 20% of instructions. Set base CPI to 0.25, the fraction of branches mispredicted to 5%, and the penalty to 15 cycles. Mispredictions add 0.2 × 0.05 × 15 = 0.15 cycles per instruction, raising estimated CPI to 0.40. Under this model, the same instruction count takes 60% longer.
Squashing prevents wrong-path instructions from changing the architectural state. It can leave microarchitectural effects: a wrong-path load may already have brought a line into the cache. Such changes can reveal information through access timing, even though the instruction's result was discarded. Spectre attacks exploit effects of this kind.
PredictKeep "predicted", "actual" and "accepted" separate.
  1. Starting at state 1, trace T, T, T, N. What are the predictions, how many are wrong, and what is the final state?
    Show answer

    States 1 → 2 → 3 → 3 → 2. Predictions: N, T, T, T. Wrong: the first T and the final N, so 2 misses. Final state 2 (weakly taken), so the next prediction is still T.

  2. In the five-stage model, a branch predicted not taken is actually taken, resolved at the end of cycle 3. Which instructions are squashed? When does target fetch begin?
    Show answer

    The two fall-through instructions fetched in cycles 2 and 3 (in D and F during cycle 3). Target fetch begins in cycle 4.

  3. Base CPI 1, 25% branches, 8% mispredicted, 5-cycle penalty. What's the estimated CPI? What does the estimate ignore?
    Show answer

    1 + 0.25 × 0.08 × 5 = 1.10. It ignores overlap between penalties and other stalls, the fill and drain of short programs, and the fact that the penalty depends on where each branch resolves.

  4. A wrong-path load was squashed. Did it necessarily leave no trace in the cache? May its value ever reach an architectural register?
    Show answer

    It may have changed the cache, which is a microarchitectural side effect. Its value must never become architectural state. Squashing guarantees the second, not the first.

  5. Run the "alternating" pattern (T, N, T, N…) from start state 1, then from start state 3. How many of the 12 predictions are wrong in each case, and why?
    Show answer

    From state 1: 12 of 12 wrong. The counter predicts N, sees T and moves to 2; predicts T, sees N and moves back to 1; and so on, always one step behind. From state 3: 6 of 12 wrong. It predicts T every time, sliding 3 → 2 on each N and back to 3 on each T, so it gets every T right and every N wrong. For this alternating pattern, the two starting states lead to different repeating state cycles: state 1 gives 0% accuracy and state 3 gives 50%, even if the pattern continues.

Keep four things distinct: the predicted direction, the actual direction, speculative execution, and architectural acceptance.

Lesson 13

More than one instruction per cycle

LimitationA scalar pipeline starts at most one instruction per cycle, even when neighbouring instructions are independent.
New mechanismA superscalar core fetches, decodes and issues several instructions per cycle to several execution ports.
New stateWider pipeline registers and per-instruction metadata for multiple instructions moving through a stage together.
CostMore register-file and execution ports, bypass wiring, and dependency-check logic whose comparisons grow roughly with width². Width only helps when work is ready.

Prediction keeps fetch supplied with instructions, but a scalar pipeline can still start at most one per cycle. A superscalar core can start several independent instructions from the same thread in one cycle. Fetch and decode in the front end supply them; execution ports in the back end do the work. The core has different widths at these points, so a narrow stage or an incompatible port can limit the rate. Several instructions in one core are still not several cores or several software threads. (Unit 4 adds simultaneous multithreading, which may fill slots from more than one thread.)

Width is not one number. Fetch, decode, dispatch, issue and retire each have their own width, and every execution port only accepts certain operations. Four ALUs don't make four loads per cycle possible if there's one load port.

Try it: width meets dependencies

All four instructions are already decoded and waiting. The model issues in program order, up to W per cycle. An add takes 1 cycle and its result is usable the next cycle; two instructions issued together cannot use each other's new results. There are W ALU ports but only one load port, and loads take 3 cycles. Thus W = 4 can start four independent adds together, but a chain of four adds still starts one per cycle, and four independent loads still start on four successive cycles. Width, dependencies and port type are three separate limits.

Instructions and dependencies

Issue schedule (cycle × slot)

Independent instructions can fill extra issue slots. In this model, a chain of one-cycle adds issues one instruction per cycle, leaving the other slots empty. Independent adds can use several ALU slots in the same cycle. This available parallelism is called instruction-level parallelism (ILP).

The amount of available ILP depends partly on how the program expresses its work. In a sum with one accumulator, sum += a[i] must wait for the preceding update to sum. With four partial sums, one add can update s0 while other adds update s1, s2 and s3; only at the end must those four results be combined. That creates independent adds for a wide core to use. For floating-point numbers, regrouping additions can change rounding, so a compiler must respect the program's numerical rules.

Measuring it

IPC = retired instructions ÷ cycles. Its reciprocal is CPI, when both are measured over the same instructions and the same interval. The explorer above only shows the execution window. It leaves out fetch, decode and retirement, so it isn't a whole-program IPC.

Each load has a latency of 3 cycles: the time from starting the load to obtaining its result. The load unit's initiation interval is 1 cycle: because it is pipelined internally, it can accept another load every cycle. So four loads start in cycles 1, 2, 3 and 4 and finish in cycles 3, 4, 5 and 6, with up to three in flight at once. A single port limits the start rate, not the number outstanding.

PredictFind the ready instructions each cycle.
  1. With W = 2, how many cycles do the independent, two-chain and one-chain workloads need?
    Show answer

    2, 2 and 4. Independent: A B, then C D. Two chains: A B, then C D (each depends on the previous cycle). One chain: A, B, C, D, one per cycle, with the second slot idle.

  2. In the one-chain workload, why can't B issue with A? Do more ALUs fix it?
    Show answer

    B needs A's result, which becomes usable the next cycle. Adding ALUs cannot let B issue alongside A.

  3. Four independent loads, one load port, W = 4. What's the minimum number of cycles just to start them?
    Show answer

    4 cycles, one load per cycle through the single port. The idle ALUs can't start loads.

  4. Suppose instead the load unit couldn't accept a new load until the previous one finished. When would the four loads finish?
    Show answer

    Its initiation interval becomes 3 cycles, equal to its latency. Loads would start in cycles 1, 4, 7 and 10 and finish in cycles 3, 6, 9 and 12: twice as long as the pipelined unit's 6 cycles. "One load port" says how many loads can start per cycle, not how many can be in flight.

  5. A program retires 300 instructions in 200 cycles. What are IPC and CPI?
    Show answer

    IPC = 300 / 200 = 1.5, CPI = 0.67. A CPI below 1 is only possible with a superscalar core.

Before predicting IPC, name three things: which width you mean, which ports are compatible with the instructions, and which operands are ready.

Lesson 14

Rename values, schedule ready work

LimitationIn-order issue stops at the first blocked instruction, even when younger ones are ready. Reusing a register name can force instructions to wait even when they do not need one another's results.
New mechanismRename each destination to a fresh physical register, then issue whichever instructions have their inputs ready.
New stateA rename map, a free list, physical registers with ready bits, and an issue queue.
CostLarge, power-hungry structures and complex recovery. The core can track only a limited number of instructions at once.

Data dependencies and name dependencies

The Lesson 13 explorer issued in order. If I1 is a slow load and I2 needs its result, I2 blocks the issue slot even when a younger I3 is ready. In the figure, I1 writes x1, I2 reads it, and I3 writes x1 again. Allowing I3 to execute first requires keeping the two versions of x1 separate: I2 must read the value produced by I1, even if I3 has already produced a newer x1 value.

Read after write (RAW) is the real flow of a value from I1 to I2; I2 must wait for it. Write after read (WAR) is the apparent conflict between I2's read of x1 and I3's later write. Write after write (WAW) is the conflict between I1 and I3 writing the same name. WAR and WAW are name dependencies: if the two x1 values can live in different storage locations, I3 can make its new value without disturbing the old one I2 needs.

RAW (read after write) is a true flow of data: I2 really needs the value I1 produces. WAR and WAW exist only because the program reuses the name x1 for a new, unrelated value. Give that new value a different storage location and those constraints disappear.

Renaming: names become versions

The core has many more physical registers than the ISA's 32 architectural names. A rename map records the physical register assigned to the latest version of each architectural register. The producing instruction may still be waiting to execute; a ready bit indicates whether its result is available. For each instruction, in program order:

  1. Look up each source in the map and record its physical-register tag. This identifies the version the instruction must read, whether or not that value is ready yet.
  2. Take a free physical register for the destination, and update the map so later readers find it.

The order of those two steps matters: source tags are recorded before the destination is remapped. Step through the example (M[x10] = 7, x11 = 20, x12 = 5):

Program → renamed

Rename map (architectural → physical)

Free list

After renaming, I2 still points at p20 (assigned to the load's x1 result) even though the map now says x1 → p22. I3 writes its new x1 into p22 without disturbing the value I2 needs. The remaining register dependencies are RAW links: I1 → I2, I3 → I4, and I4, I2 → I5.

I1 remaps x1 to p20, so I2 records p20 as the source for both of its operands. Later I3 remaps x1 to p22. That map change affects instructions renamed after I3, including I4; it does not change the sources already recorded for I2. I2 can therefore wait for the load while I3 produces a different x1 value. In program order, the final x1 is I3's 24, while I2 still computes 7 + 7 = 14.

Out-of-order issue: go around the blocked load

Renaming removes the name dependencies by assigning separate storage to different versions of a register. It does not itself decide when work runs. The issue queue holds each renamed instruction and the physical-register tags of its inputs. When a producer finishes, its result becomes ready and waiting consumers of that tag can wake up. The scheduler can then choose a ready instruction even if an older instruction is still waiting for an operand. A consumer still cannot issue before its actual producer has made the value available.

In the comparison below, all five instructions have been renamed before cycle 1. One instruction may issue per cycle; the out-of-order scheduler picks the oldest ready instruction rather than stopping at the first blocked one. The load issues in cycle 1 and supplies its value at the end of cycle 4. Adds take one cycle, and a result is usable the next cycle. Before reading the lower timeline, find a place for I3 and I4 while I2 waits for the load.

Same instructions, same answers, two fewer cycles. Out of order, I3 and I4 run in cycles 2–3 while the load is outstanding, and everything finishes at the end of cycle 6 instead of 8. Both schedules end with x1 = 24, x2 = 14, x3 = 19, x4 = 33 once results are accepted in program order. Lesson 15 introduces retirement, the step that commits completed instructions in program order.

The issue slot is idle in cycle 4 of this model: I2 still needs the load's p20, and I5 needs I2's p21 as well as I4's p23. The load is still in progress and becomes ready only at the end of cycle 4, so I2 can issue in cycle 5 and I5 in cycle 6. Out-of-order issue filled two cycles of the load wait with useful work; it did not shorten the load itself or remove its true dependencies.

What new state this takes

StructureJob
Speculative rename mapThe physical register assigned to the newest version of each architectural register
Free listPhysical registers available to be allocated
Physical register file + ready bitsStores register values; ready bits indicate which values are available to consumers
Issue queue (reservation stations)Waiting instructions, each tagged with the physical registers it needs. They "wake up" when those become ready.

Extra physical registers don't give software more register names. They let more versions be in flight at once. Instructions still wait for their actual inputs, and memory bandwidth still limits how quickly data can arrive. The core can track only a finite number of instructions at once; this set is its instruction window. If an instruction queue is full or no physical register is free, the core must stop admitting instructions that need those resources. Independent work beyond that limit cannot yet be scheduled.

The old physical register for x1 (p1) cannot be freed as soon as x1 is remapped. Older instructions may still need to read it, and recovery may need to restore the old mapping.

PredictRename on paper before stepping.
  1. After renaming all five instructions, which physical register does I2 read for x1, and which does I4 read? What is the final mapping of x1?
    Show answer

    I2 reads p20 (the load's result). I4 reads p22 (I3's result). The final speculative mapping is x1 → p22.

  2. Which dependencies remain after renaming, and which name conflicts were removed?
    Show answer

    Remaining RAW: I1→I2, I3→I4, I4→I5, I2→I5. Removed: WAW between I1 and I3 (both wrote x1), and WAR between I2 (reads x1) and I3 (writes x1).

  3. In the out-of-order schedule, in which cycle does each instruction issue? When is everything done, compared with in-order?
    Show answer

    I1 → 1, I3 → 2, I4 → 3, (nothing ready in 4), I2 → 5, I5 → 6. Done at the end of cycle 6. In-order: I1 → 1, I2 → 5, I3 → 6, I4 → 7, I5 → 8, done at the end of cycle 8.

  4. Can p1, x1's old physical register, be freed as soon as I1 remaps x1 to p20?
    Show answer

    No. An older instruction that reads x1 might still need p1, and if something goes wrong (a fault or misprediction) the core may need to roll the map back to x1 → p1. It's freed only when the instruction that replaced it retires.

Rename a sequence that reuses a destination register, and prove that every consumer still reads the right version of its input.

Lesson 15

Complete precisely, respect memory order

LimitationYounger instructions can finish before an older instruction faults or a branch reveals a wrong prediction. The core must prevent incorrect speculative effects from becoming committed state.
New mechanismA reorder buffer that retires results in program order, and load/store queues that check memory addresses.
New stateROB entries (status, exception), a committed rename map, and store and load queues.
CostROB and queue area and energy. The oldest blocked instruction holds up retirement, and wrong speculation means replays.

Computed is not the same as accepted

In Lesson 14, I3 and I4 finished before the older load I1. Now suppose I1 faults. If I3's x1 = 24 had already changed the committed register state, the fault handler would see an effect from an instruction after the fault. The core therefore separates completion—producing a result or detecting an exception—from retirement (also called commit)—committing an instruction's effects to architectural state in program order.

The reorder buffer (ROB) makes that separation possible. Instructions enter this queue in program order as they are renamed, and leave from its head in the same order. I3 may finish and leave 24 in a speculative physical register while I1 still waits; its ROB entry records "done", but I3 cannot retire yet. Only a completed, fault-free instruction at the head can retire. Once I1 completes, the head may advance through the waiting results. If I1 faults or a branch reveals a wrong path, the younger results can instead be discarded before software sees them.

This gives precise exceptions. If an instruction faults, the handler sees a machine where every older instruction has taken effect and neither the faulting instruction nor anything younger has, even if younger instructions already computed their results.

Try it: watch the ROB

This uses the out-of-order schedule from Lesson 14. Retirement is one per cycle, at the start of a cycle, using results completed by the end of the previous cycle. Committed registers x1–x4 start at 0. Select Load faults and advance to cycle 5. Check that I3 and I4's completed results are discarded while the committed registers retain their values from before I1.

Committed (architectural) registers

Speculative map (for newly renamed work)

I3 and I4 finish in cycles 2–3 but can't retire until I1, I2 ahead of them have. In the fault case, cycle 5 finds the faulting load at the ROB head: all five instructions are squashed, including the finished I3 and I4, and the handler sees x1–x4 exactly as they were before I1. (The handler's own work isn't shown.)

In the successful run, I1 finishes at the end of cycle 4 and retires at the start of cycle 5. I2 cannot retire until cycle 6, even though I3 and I4 already finished earlier. Then I3 retires in cycle 7 and I4 in cycle 8. This delay is intentional: if the load had faulted instead, the younger values would have been discarded, leaving the handler the exact state before I1.

Notice two views of "x1". The committed x1 is 7 after I1 retires, then 24 after I3 retires. Meanwhile the speculative map already said x1 → p22 from the moment I3 was renamed, because any newly renamed instruction must read the newest version. Recovery from this fault copies the committed map back over the speculative one: every in-flight instruction is squashed, so none of their mappings are needed.

Recovery from a mispredicted branch is different. Instructions older than the branch are still valid even if they haven't retired, so the core must keep their mappings. A common approach is to checkpoint the speculative map when the branch is renamed and restore that checkpoint on a misprediction; copying the committed map instead would throw away the older instructions' mappings. The capstone shows this with real register names.

A retirement map records committed register mappings; a branch checkpoint saves speculative mappings for branch recovery. A core may use both. Cores also differ in where they store committed values. Some copy retiring results into a separate architectural register file. Others keep committed and speculative values in one physical register file and track which physical register holds each committed value. BOOM's documentation describes both storage arrangements. Both must preserve precise, in-order architectural effects: when an instruction faults, all older instructions have taken effect, and the faulting instruction and younger instructions have not.

Memory dependencies: the one renaming can't see

Register dependencies are visible in the instruction bits. Memory dependencies aren't. Whether a store and a later load touch the same bytes depends on addresses computed at run time. Take a single thread with M[A] = 3. The listing is schematic, not assemblable RISC-V: in real RV32I the store would be sw x6, 0(x10) (store x6 = 9 at the address in x10 = p) and the load lw x7, 0(x11).

S1sw 9 → [p]older store; p is computed by a slow instruction
L2lw r ← [q]younger load; q = A is already known
I3addi s ← r + 1uses the loaded value

If p turns out to equal A, L2 must see the store's 9. The core could make L2 wait for every older store address to become known, or it could guess that the addresses do not overlap and check later. Step through p = B and p = A. In which run can L2 keep its early read of 3?

In-flight instructions

Memory & store queue

Follow L2 and I3 when p resolves. The highlighted rows show whether their early results survive or must be replayed. The last two steps distinguish a store retiring from its data draining out of the store buffer.

If p = B, L2's early read of A was correct and the dependent add can keep its result 4. If p = A, L2 must obtain 9 from the older store, so its early result of 3 is incorrect. The store queue holds the older store's address and buffered data; the load queue records where the early load read. When the store's address becomes known, the core detects the overlap and squashes the load, the dependent add and anything younger. On replay, store-to-load forwarding can supply the buffered 9 directly from the older store queue entry; L2 and I3 then produce 9 and 10. This does not require the store to have updated the cache. Neither wrong value retires.

Retiring a store doesn't mean other cores can see it yet. It may still wait in a store buffer before reaching the cache. "Retired" (architecturally committed for this thread) and "globally visible" are different events. How multiple cores observe each other's memory operations is the memory-consistency topic of the next unit, and a ROB alone doesn't settle it.
PredictTrack completion and retirement separately.
  1. In the successful run, which instructions have completed by the end of cycle 3, and how many have retired? Why?
    Show answer

    Completed: I3 and I4. Retired: none. I1 is at the head and hasn't completed, and nothing may retire past it.

  2. If the load faults at the end of cycle 4, what happens in cycle 5? Can x1 = 24 or x3 = 19 appear in the committed registers?
    Show answer

    The fault is at the ROB head, so all in-flight instructions are squashed and the handler starts. No: I3's 24 and I4's 19 were computed but never retire. The handler sees x1–x4 = 0, the state before I1.

  3. A younger load speculatively read 3 from A, and a dependent add produced 4. Then the older store's address resolves to A. What must happen, and why can't renaming prevent it?
    Show answer

    The core detects a violation, squashes the load, the add and anything younger, and replays. This time the load gets 9 (forwarded from the store queue) and the add produces 10. Renaming works only on register names written in the instruction. A memory overlap depends on addresses computed at run time.

  4. In the successful run, in which cycle does each instruction retire? Does retiring a store mean other cores can see it?
    Show answer

    I1 → 5, I2 → 6, I3 → 7, I4 → 8, I5 → 9. And no: a retired store may still sit in a store buffer before becoming visible to other cores.

For the fault example, identify which completed values are discarded and why the committed register state survives. For a branch misprediction, explain why recovery must preserve mappings from older instructions that have not yet retired. Then explain how an incorrectly executed load and its dependent add are replayed before either can retire.

Unit 3 · Capstone

Put the eight lessons together

Now several mechanisms meet in one short run: a load misses, an independent instruction can proceed, and a branch waits on the missing value while fetch follows a guess. Draw cycles 1–7 before opening the answer.

Simplified out-of-order core. All four instructions are renamed and queued before cycle 1. Issue is one per cycle, oldest ready first, and execution may overlap. Retirement is one per cycle at the start of a cycle. Adds and branches take one cycle. The load's translation hits, but its data-cache miss makes it complete at the end of cycle 4. That latency already includes everything, so don't add TLB or cache penalties. The branch is predicted not taken. x8 points to a word holding 7, and x5, x6, x7 start at 0.
I1lw x5, 0(x8)misses in the data cache
I2addi x6, x0, 3independent
I3bne x5, x0, skipneeds x5; actually taken, since 7 ≠ 0
I4addi x6, x6, 1the predicted fall-through path, i.e. the wrong path

Mark: waiting for the load, useful independent work, speculative work, branch execution, the squash, and retirement. State the committed x5, x6, x7 after I3 retires. Explain why I2 can execute during the cache miss, which instructions must wait for I1, and when retirement can begin.

Show the worked answer
CycleExecutionRetirement (start of cycle)
1I1 issues; the load miss begins—
2I2 issues → x6′ = 3 (independent, useful)—
3I4 issues speculatively: it's ready (it needs I2's x6), so x6″ = 4. I3 waits for x5.—
4nothing ready; the load completes at the end: x5′ = 7—
5I3 executes: 7 ≠ 0 → taken, mispredicted. At the end of the cycle, squash I4 (its 4 is discarded).I1 retires (x5 = 7)
6target fetch restartsI2 retires (x6 = 3)
7—I3 retires

After I3 retires: x5 = 7, x6 = 3, x7 = 0. I4's x6 = 4 was computed but never retired.

Out-of-order issue lets I2 and, speculatively, I4 execute while I1 waits for memory because their input values are ready. I3 needs I1's result, so it cannot execute until cycle 5. The load also prevents retirement during cycles 1–4. Once I3 reveals the misprediction, the core discards I4's result and restarts correct-path fetch in cycle 6. I4 used an otherwise idle issue slot in this example; in a busier core, wrong-path instructions can also occupy execution resources needed by useful work.

How the misprediction is repaired. Say x5 and x6 start mapped to p5 and p6, and renaming gives I1's x5 → p10, I2's x6 → p11 and I4's x6 → p12. When I3 resolves in cycle 5, I1 has just retired but I2 hasn't, and I2's result is correct and still needed:

Committed map (cycle 5)Checkpoint taken at I3Speculative map before repair
x5p10p10p10
x6p6 (I2 not retired yet)p11 (I2's result)p12 (wrong-path I4)

The core restores the checkpoint, so x6 → p11 and p12 returns to the free list. Restoring the committed map instead would point x6 back at p6 and lose I2's 3 even though I2 is on the correct path. This is one representative design; cores differ in how they checkpoint.

Combine the mechanismsNew cases; each needs two lessons at once.
  1. A branch's direction is predicted correctly, but its predicted target address is wrong. Is recovery still needed?
    Show answer

    Yes. Fetch needs the whole next PC, so a correct taken/not-taken guess with the wrong target still fetched the wrong instructions. They must be squashed and fetch redirected, just as for a wrong direction.

  2. A younger load on the wrong path faults before the older branch resolves. Should the OS receive that fault?
    Show answer

    No. The fault is only recorded in the load's ROB entry. When the branch resolves as mispredicted, the load is squashed, and a squashed instruction never delivers an architectural exception. Only a fault that reaches the ROB head is taken.

  3. Two virtual addresses map to the same physical bytes. For dependency checks and the memory system, are they necessarily different memory objects?
    Show answer

    No. They're two names (aliases) for the same bytes. A store through one and a load through the other conflict, so memory-ordering checks and caches must recognize the shared physical identity, not just compare virtual addresses.

  4. An independent load misses in the cache while the ROB is nearly full. Can out-of-order execution hide an arbitrarily long delay?
    Show answer

    No. Younger instructions can run while the miss is outstanding, but they can't retire past it. Once the ROB, issue queue, load/store queues or free physical registers fill, renaming stops and the core stalls until the load returns. The window is finite, so it hides only as much latency as it has room for.

A map of the state this unit added

MechanismState it adds or organizesLimitation that remains
Pipelinepipeline registers, valid bits, controldependencies, bubbles
Forwardingregister identifiers and valid bits used to control bypassing (the comparators and bypass muxes are logic, not state)values that don't exist yet
Cachelines, tags, valid/dirty, replacement statecapacity, conflicts, bandwidth
Virtual memorypage tables, TLB, permissionstranslation and fault costs
Predictioncounters, history, target buffers, checkpointswrong guesses
Superscalar widthwider pipeline registers and control for several instructions per cyclesupply of ready, compatible work and available execution ports
Renaming + OoOmaps, free list, physical registers, issue queuetrue dependencies, finite window
ROB + load/store queuesin-order retirement, committed map, address checksoldest blocked instruction, replay cost

The table summarizes the delays each mechanism addresses. Pipelining overlaps stages from different instructions. Forwarding allows an instruction to use a completed result before it is written back to the register file. A cache hit can supply a memory operand sooner than fetching it from the next memory level. Translation identifies and protects the physical bytes being requested. If fetch lacks the next PC, prediction lets it proceed speculatively. Width provides more instruction slots, and renaming with out-of-order issue finds ready work when an older instruction is waiting.

Those mechanisms leave a final obligation: speculative, out-of-order work must produce the program's promised architectural results. The ROB retires instructions in order, while the load and store queues catch memory conflicts that register renaming cannot identify. That is why I4's computed 4 in the capstone could be thrown away without changing the committed x6 = 3.

You're ready for the next unit when you can explain this table with examples, without mistaking overlapping instructions for extra threads, or a cached translation for cached data. Unit 4 will add multiple cores, cache coherence, memory ordering, hardware multithreading and vectors.

Sources and model assumptions. The explorers are teaching models. Latencies, cache sizes and costs are illustrative round numbers, not measurements of a particular chip. Lessons 8–9 use a simple five-stage in-order pipeline; Lessons 14–15 use a separate out-of-order model with its own timing rules. The diagrams leave out many real timing interactions (overlapped translation and cache lookup, multiple outstanding misses, port conflicts, and more). References: cache organization and the hit/miss taxonomy follow Cornell CS 3410's cache notes; page tables, permissions and page faults follow the RISC-V privileged specification and OSTEP's virtual-memory chapters; the reorder buffer and load/store queues are modelled on the open-source BOOM core's reorder buffer and load/store unit documentation (including committed stores waiting in a queue before they reach memory); simultaneous multithreading is described in IBM's SMT overview. Links checked September 2026.