Architectures · Chapter 2 of 16 · CPUs

Branch prediction and out-of-order execution

A CPU guesses which way a program will go and starts work early. It also runs steps in whatever order is ready first, then puts the results back in order.

Branch predictors guess the outcome and target of each branch before it is known, so the pipeline keeps filling. Out-of-order cores look ahead through a window of instructions, run whichever have their inputs ready, and retire them in program order so the result looks sequential.

Branch predictors from two-bit counters through gshare to TAGE, target buffers and return stacks, the misprediction penalty, Tomasulo’s reservation stations, register renaming, the reorder buffer and precise exceptions, load/store queues, ILP limits and window sizing, and what Spectre and Meltdown showed about speculation’s side effects.

A processor runs a program one small step at a time. A fast one works like an assembly line, with many steps in progress at once.

Two things keep jamming the line. The first is an “if”. The processor can’t know which way the program goes until the “if” is worked out, and by then the line should already be full. The second is memory. Some steps wait a long time for a number to arrive.

Fast processors fix both with two tricks. They which way each “if” will go and keep working down that path. And they don’t wait in line. Any step whose numbers are ready can go, even if it comes later in the program. This is called .

The catch: the processor must make it look as if every step ran in order. And guessing wrong throws work away.

The previous chapter, Instructions and pipelines, built a processor that overlaps instructions like an assembly line and stalls on . This chapter is about the two ideas that let a high-performance core keep far more work in flight than a simple pipeline can.

  • Branch prediction. A branch (an if-statement or loop test) decides which instruction comes next, but its outcome is known only many stages after it is fetched. A predictor guesses, so fetch never waits. Modern predictors are right more than 95% of the time.
  • Out-of-order execution. Instead of stalling the whole line behind one slow instruction, the core looks ahead through a window of upcoming instructions and runs any whose inputs are ready, then puts the results back in program order.

Together they make a core : it does work before it knows the work is needed, and it can undo that work if a guess was wrong. High-performance cores for x86, Arm and RISC-V alike are built this way. The cost is area, energy and complexity, and, as the Spectre attacks showed in 2018, a new kind of security risk.

You know the classic five-stage pipeline, its hazards and ; if not, start with Instructions and pipelines. Its two remaining limits are control flow and latency. A modern core may have more than ten stages between choosing the next fetch address and resolving a branch, so without prediction each branch costs roughly that depth times the fetch width in lost slots. And a load that misses stalls every younger instruction in an in-order machine, even the independent ones.

The out-of-order superscalar answers both with five mechanisms, which this chapter takes in turn:

  1. Prediction of branch direction and target, so fetch runs ahead of execution.
  2. Register renaming, so only true dataflow dependences remain.
  3. Dynamic scheduling from reservation stations or issue queues in dataflow order.
  4. In-order commit from a , which provides precise exceptions and mispredict recovery.
  5. Memory disambiguation with load and store queues, so loads can pass stores speculatively.

We use open material throughout: MIT’s graduate architecture lectures, the open-source RISC-V BOOM core and its documentation, and the Hot Chips disclosures of x86, Arm and RISC-V cores as case studies. Caches appear here only as a latency to hide; how they work is the next chapter, Caches and coherence.

in order (front end)out of orderin order (commit)PredictorFetchDecodeRenameWindowUnitsReorder bufferRetirepredictions steer fetch

An out-of-order core: in order at the front, out of order in the middle, in order at retirement. Tap a part.

The parts of an out-of-order core, grouped by the order they keep. Schematic: real cores have more stages and units.Share freely with credit: ‘Figure from chipfieldguide.com’

Programs are full of “if”s and loops. Every one is a fork in the road. The processor fetches the next steps long before it knows which way the fork goes. So it has to guess.

A wrong guess is costly. Every step started down the wrong road gets thrown away. On a fast chip that can be a dozen or more ticks of the clock, each one wasted.

The simplest guesser always says “yes”. Better ones remember what happened before. One kind just repeats last time’s answer. A smarter kind only changes its mind after being wrong twice in a row. That way, one odd answer at the end of a loop doesn’t fool it.

Try both guessers in the picture below with a loop: yes, yes, yes, no.

Why prediction matters more as pipelines deepen

A branch’s outcome is known only when it executes, many stages after fetch. On a modern core more than ten stages separate choosing the next address from resolving the branch, and the work lost on a wrong guess is about that distance times the number of instructions fetched per cycle. That loss is the . Measured penalties on recent x86 cores are around 15 to 25 cycles. Arm describes keeping its pipelines short specifically for quick mispredict recovery. The deeper the pipeline, the higher the penalty, which is why the architecture stage’s pipeline model finds that better prediction makes deeper pipelines pay off.

Static prediction

The cheapest guess uses only the instruction itself. Branches are taken roughly 60–70% of the time overall, and backward branches (loop tests) are taken about 90% of the time, so “backward taken, forward not taken” is a reasonable rule. Static schemes are typically reported around 80% accurate.

One bit and two bits of memory

A dynamic predictor learns from history. The simplest keeps one bit per branch: the last outcome. On a loop it mispredicts twice per loop: at the exit, and again on the first iteration of the next loop, because it remembered the exit.

A 2-bit saturating counter, proposed by James Smith in 1981, counts up on taken and down on not taken, stopping at 0 and 3; the top bit is the prediction. It changes its prediction only after two wrong guesses in a row, so a loop exit costs one misprediction, not two. McFarling measured that a table of these counters, one per branch, levels off at 93.5% correct on the SPEC’89 benchmarks once the table is big enough that branches don’t share counters.

Prediction has three parts: is this fetch block holding a branch, which way does it go (direction), and where does it go (target). Direction is the hard part for conditional branches; target is the hard part for indirect jumps and returns.

The cost model is simple. A mispredicted branch wastes the issue slots from its fetch to its resolution plus the refill: at least the front-end depth, and more if the branch itself waited on a , since everything fetched after it is squashed. Measured penalties: 15–20 cycles on Intel’s Haswell-through-Lake cores; about 18 on AMD Zen 1–3, 15–18 on Zen 4 and 15–25 on Zen 5. The open-source SonicBOOM pegs a mispredict at about a 10-cycle bubble, the same as an instruction-cache miss.

  • Static (BTFN or ISA hint bits): about 80%.
  • 1-bit last-outcome: two mispredictions per loop execution.
  • Bimodal 2-bit counters indexed by PC (Smith 1981; used in the Alpha 21164): one miss per loop; McFarling’s SPEC’89 data saturates at 93.5%.

Two refinements matter in hardware. The predictor is updated at commit so wrong-path branches don’t pollute it, but any speculative history it uses must be updated at fetch, because dozens of branches are in flight by the time the first one commits; the history is snapshotted per fetch block and repaired on a mispredict. And counters reset after a context switch or a flush cost a warm-up period, which is why the sim’s accuracies include the first few branches.

00strong N01weak N10weak T11strong Ttaken → rightnot taken → leftleft half predicts not taken · right half predicts takenactualguessno outcomes yet
Predictor

A 2-bit counter: taken outcomes count up, not-taken count down, and the top bit is the prediction. Feed it outcomes.

A 1-bit predictor and a 2-bit saturating counter. Feed outcomes; try a loop pattern (taken ×3, not taken) and compare.Share freely with credit: ‘Figure from chipfieldguide.com’

Some “if”s follow a pattern. One might go yes, no, yes, no. Another might always copy the “if” just before it. A guesser that only looks at one “if” can’t see these patterns.

So better guessers also remember the last several answers of every “if”, as a short string of yes and no. They keep a separate counter for each string. Now “yes, no, yes” can lead to a different guess than “no, no, yes”. Modern chips stack several of these, each looking back a different distance.

Guessing where a jump lands matters too. A list called the remembers where each jump went last time. For the end of a helper part of a program, a stack of notes works better. Each time the program steps into a helper, the processor writes down where to come back to.

Global history

Branches are often correlated: one branch’s outcome depends on what earlier branches did. In the early 1990s Yeh and Patt’s two-level predictors added a history register recording the last few outcomes, used to choose among counters. A global history records every branch; a local history records one branch’s own past.

McFarling’s gshare combines global history with the branch address using a bitwise exclusive-or to index one table of 2-bit counters, which separates cases that a simple concatenation can’t fit in the same table size. He also showed that a chooser that learns, per branch, which of two predictors is better reaches 98.1% on SPEC’89 versus 97.1% for the best single scheme. The Alpha 21264 shipped such a “tournament” of local and global predictors.

TAGE: many history lengths at once

Short histories learn fast; long ones capture patterns that span many branches. TAGE, introduced by Seznec and Michaud in 2006, keeps several tables indexed by histories whose lengths form a geometric series (for example 4, 8, 16, 32 … outcomes), each entry tagged so it only answers for the branch and history that created it. The prediction comes from the matching table with the longest history. TAGE variants are now the published direction predictors of the open-source SonicBOOM and of current x86, Arm and RISC-V cores. A different line of work replaced counters with a tiny neural network, the perceptron, whose cost grows only linearly with history length.

Predicting the target

Knowing a branch is taken isn’t enough; fetch needs the address. The (BTB) maps the address of each recently taken branch to its destination and is read in the first fetch stage, before the instruction is even decoded. Function returns are a special case: the same return instruction goes back to whichever place called it. A pushes the return address on each call and pops it on each return, and is much more accurate than a BTB for returns. Wall found that even a small return stack improved some programs a lot.

From two-level to TAGE

In Emer’s predictor algebra, a global-history predictor is MSB(Counter(History(0;T);T))\mathrm{MSB}(\mathrm{Counter}(\mathrm{History}(0;T);T)), gselect concatenates history with PC bits, and gshare indexes with History⊕PC\mathrm{History} \oplus \mathrm{PC}. McFarling’s point was index efficiency: with 8 index bits, gshare 8/8 distinguishes cases that gselect 4/4 aliases; below about 256 bytes, though, adding history to an already-contended table hurts. The perceptron predictor (one weight vector per branch, dot product with the history as ±1) cut mispredictions 10.1% versus gshare at 4 KB on SPEC 2000, because its storage grows linearly rather than exponentially with history length.

TAGE’s structure: a PC-indexed bimodal base table T0T_0 and tagged tables T1…TMT_1 \ldots T_M indexed by hashes of PC and history lengths L(i)=⌊αi−1L(1)+0.5⌋L(i) = \lfloor \alpha^{i-1} L(1) + 0.5 \rfloor. An entry holds a 3-bit signed counter, a partial tag and a useful counter. The longest-history hit provides the prediction; on a misprediction a new entry is allocated in a longer-history table whose useful bits are clear. TAGE-SC-L adds a statistical corrector that can overturn low-confidence predictions and a loop predictor; the championship versions scored 3.986 mispredictions per thousand instructions at 64 KB and 4.991 at 8 KB on the CBP-5 traces. Under the hood sketches the algorithm.

Targets and front-end organization

Modern front ends are decoupled: the predictor runs ahead of fetch, writing predicted fetch blocks into a queue, and acts as an instruction prefetcher. Target structures are layered: a small zero- or one-cycle BTB (a next-line predictor), a large main BTB, an indirect-target predictor and a return stack. Published sizes include a 16K-entry first-level BTB and a 52-entry return stack (AMD Zen 5), and a 1K-entry next-line predictor, 2.5K-entry indirect predictor and 64-entry return stack (SiFive P870); Arm’s Neoverse V2 split its main BTB into two levels and enlarged its nano-BTB tenfold. Zen 5 and Neoverse V2 both predict two taken branches per cycle, because a 6- to 8-wide back end runs dry if fetch stops at every taken branch. The BTB is indexed before decode, so it can be trained by one context and consulted by another, which is what Spectre’s variant 2 abused.

codemain: A: call f after_A: … B: call f after_B: …f: … retreturn stackemptyBTB (for ret)last target—predictions appear at each ret
1 / 5

Function f is called from two call sites. Its return is an indirect jump: the target depends on who called.

A function called from two places. A branch target buffer remembers the last target; a return address stack matches each return to its call.Share freely with credit: ‘Figure from chipfieldguide.com’

A simple processor does its steps strictly in order. If one step waits, every step behind it waits too, even the ones that don’t need it.

An out-of-order processor keeps a waiting room of upcoming steps. Each step waits in a seat until the numbers it needs are ready. Then it goes, whatever its place in line. When a step finishes, it calls out its answer. Every step waiting for that answer grabs it.

Fast chips also have several workers side by side, so they can start several steps in each tick. A chip like that is called . Today’s biggest cores can start about eight steps per tick.

A core fetches, decodes and issues several instructions per cycle to several execution units. Width alone doesn’t help an in-order machine much: if the oldest instruction can’t issue, nothing behind it may either. Dynamic scheduling removes that rule.

Tomasulo’s algorithm

The idea dates to 1967, when Robert Tomasulo designed the floating-point unit of the IBM System/360 Model 91. Instructions are issued, in order, into in front of the execution units. Each source operand in a station is either a value, if it was ready, or a tag naming the station whose result it is waiting for. When a unit finishes, it broadcasts the tag and the value on a common data bus; every station waiting for that tag grabs the value, and an instruction whose operands are all present can execute. The result is dataflow order: an instruction runs when its inputs exist, not when its turn comes.

An earlier machine, the CDC 6600, used a scoreboard that tracked which registers were busy, but it still issued in order and had to stall on reused register names. Tomasulo’s tags were in effect a form of the next section’s idea, renaming, and the 360/91 had only four floating-point registers to work with.

Out-of-order execution then all but disappeared for twenty-five years. The 360/91’s version did nothing for memory latency, which turned out to matter more, and it made exceptions imprecise: when an instruction faulted, later ones might already have changed registers. The reorder buffer, two sections down, fixed the second problem, and out-of-order cores returned in the mid-1990s.

Modern cores split Tomasulo’s station into stages: rename (operand naming), dispatch into an issue queue and the ROB, and issue (wakeup and select). Wakeup broadcasts each completing destination tag to every waiting entry, which compares it with its source tags; select picks among the ready entries, usually oldest first, up to the number of free ports. For a dependent instruction to issue in the cycle right after its producer, wakeup and select must complete in one cycle; Palacharla, Jouppi and Smith called them an atomic operation and found them, with the bypass network, likely to be the critical timing paths of wide machines.

Values move either through the queue (data-capture, like the 360/91) or are read from a physical register file after issue (non-data-capture). Most current designs read operands after issue: Arm describes Neoverse V2’s physical register files as “read after issue”, and BOOM uses a unified physical register file. Issue queues are typically distributed by unit type; Zen 5, for example, shows separate schedulers feeding its six integer ALUs.

Issue width and commit width are separate design choices. Intel’s Lion Cove allocates and renames 8 per cycle but retires 12 and has 18 execution ports; AMD’s Zen 5 dispatches and retires 8. Ports exceed rename width because instruction mixes are uneven: the scheduler needs a free unit of the right type far more often than it needs many at once.

program orderstations: operands1ld f2, [a]runL1address ready: accessing memory2mul f4, f2, f6waitM1tag L1f6 = value3add f8, f6, f6runA1f6 = valuef6 = value4sub f10, f8, f4waitA2tag A1tag M1L = load buffer, M/A = mul/add stationscommon data busidle
1 / 5

All four instructions are issued, in order, into reservation stations. Each operand is a value or the tag of the station that will produce it. The load and the add start.

Reservation stations and the common data bus. Instructions wait for tags, not for their turn. Step through the broadcasts.Share freely with credit: ‘Figure from chipfieldguide.com’

A program keeps its numbers in a few named boxes called registers, like r1 and r2. There aren’t many of them, so programs reuse the same names again and again.

That causes fake traffic jams. Say step 3 wants to put a new number in box r1. It has to wait until step 2 has read the old number from r1, even though step 3 doesn’t need step 2’s answer at all.

The fix is . Inside, the chip has hundreds of hidden boxes. Every new answer gets a fresh one, and the chip keeps a list of which hidden box “r1” means right now. The fake jams vanish. Only real ones are left, where a step truly needs another step’s answer.

An gives programs a small, fixed number of register names; the IBM 360 that Tomasulo worked on had only four floating-point registers. Compilers reuse the names constantly, and every reuse creates an ordering rule that has nothing to do with data flowing between instructions:

  • Read after write (RAW), a true dependence: instruction 2 needs the value instruction 1 computes. This one is real.
  • Write after read (WAR): instruction 3 overwrites a register that instruction 2 still has to read.
  • Write after write (WAW): two instructions write the same register, and the later value must be the one that survives.

WAR and WAW are false dependences, also called name dependences. removes them by giving every instruction that writes a register a fresh physical register. A map table records which physical register currently holds each architectural one; later readers look it up; a free list supplies new physical registers and gets old ones back when they can no longer be needed. After renaming, only true RAW dependences are left.

The hidden register file is large. SiFive’s P870, a RISC-V core, lists 228 integer and 240 floating-point rename registers, several times the number the instruction set names. In Wall’s limit study, taking renaming away from an otherwise perfect machine cut median parallelism from 30.6 to 4.8 instructions per cycle.

Two organizations exist. In data-in-ROB (implicit) renaming, as in Intel’s P6, speculative results live in ROB entries and are copied to an architectural register file at commit. In explicitrenaming, as in the MIPS R10000, Alpha 21264 and BOOM, one unified physical register file holds both committed and speculative values, and only pointers move. Explicit renaming avoids the commit copy and decouples ROB size from register count, at the cost of a free list and freeing rules: the physical register a new writer replaces is released when that new writer commits, since no older instruction can need it after that.

Rename sits on the critical path of the front end. Each cycle a W-wide renamer reads 2W sources and W old destination mappings and writes W new ones, and must also check dependences within the group being renamed that cycle. Recovery after a mispredict needs the map table as it was at the branch: BOOM keeps a copy per in-flight branch for single-cycle recovery, and Arm’s Neoverse V2 lists six rename checkpoints; the alternative is to walk the ROB backward, undoing mappings.

Renaming also enables cheap tricks at the map table: a register-to-register move or a zeroing idiom can be “executed” by pointing two names at one physical register. The Meltdown paper notes that Intel’s cores handle move elimination and zeroing idioms this way.

as written1mul r1, r2, r32add r4, r1, 13ld r1, [x]4sub r5, r1, r4true (RAW)false (WAR, WAW)map tabler1—r4—r5—free listunused
Renaming

Without renaming: the load (3) writes r1, so it must wait until 2 has read the old r1 (WAR) and 1 has written it (WAW). Red dashed lines are false dependences.

Register renaming removes false dependences. Solid lines: one instruction needs another’s result. Dashed red: they only share a register name.Share freely with credit: ‘Figure from chipfieldguide.com’

Steps finish in a jumble. But the program must see them happen in order. Otherwise a mistake halfway through would leave a mess.

So the processor keeps a line-up list called the . Every step joins the list in program order. When a step finishes, it’s marked done, but it stays put. Results only become final from the front of the list, one after another.

This makes undoing easy. If a guess was wrong, or a step hits a problem, the processor wipes out everything behind that point in the list. Everything in front of it is already final. It looks as if the program stopped at exactly the right place.

The (ROB) holds every in-flight instruction in program order, from the moment it is dispatched until it retires (also called commits). Instructions execute and complete in any order, but they leave the ROB only from its head, in order. Retirement is the moment a result becomes part of the official, architectural state.

That one rule handles three problems:

  • Precise exceptions. An exception (a page fault, a divide by zero) must look as if it happened between two instructions: everything before it complete, nothing after it with any effect. The ROB records the fault and acts on it only when the faulting instruction reaches the head; then it flushes everything younger and jumps to the handler. That is a .
  • Mispredictions. Instructions after a wrong guess are younger than the branch, so the same flush discards them.
  • Stores. A store must not change memory until it is certain. Stores wait in a store queue and are written to memory only after they commit, in program order.

Loads, stores and memory disambiguation

Registers are renamed, but memory addresses aren’t known until instructions compute them. May a load run before an older store whose address isn’t known yet? Waiting is safe but slow. Most cores let the load go and check afterwards: a load queue remembers executed loads, and when an older store’s address arrives, any younger load to the same address that already ran is caught and the pipeline is flushed from there. If the store’s data is already known, it can be forwarded straight to the load. Some designs predict which loads tend to conflict with which stores and make only those wait, a technique called store sets.

The ROB is a circular buffer of W banks for a W-wide machine, allocated at dispatch and freed at commit. Per entry it tracks completion, exception status, branch mask and rename state (the stale physical register to free), and BOOM stores one PC per row of W instructions to save area. Commit retires up to W completed instructions from the head per cycle and stops at the first that isn’t complete or that faulted.

Mispredict recovery doesn’t have to wait for the branch to reach the head. With per-branch map-table snapshots and branch masks on every younger instruction, a branch that resolves wrong can kill exactly its dependents in the queues and restore rename state in a cycle, while older instructions keep executing. Exceptions, by contrast, are usually handled only at commit. That deferral is what Meltdown exploited: on affected cores the faulting load’s data reached dependent transient instructions before the permission fault was raised at retirement.

Memory ordering

BOOM’s load/store unit illustrates the common scheme. Stores enter the store queue at decode, wait for commit, and are then sent to memory in order. Loads issue speculatively as soon as their address is ready; each searches older uncommitted stores for a match, killing the memory request and store data when one hits. When a store’s address is resolved, it searches younger loads; one that already got its data from memory or from an even older store is a memory-ordering failure, which flushes and resets the rename maps. Store-set predictors start with naive speculation and record each violation, so that a load later waits only for the stores in its set. The ISA’s memory model sets what the queues must preserve between cores; RISC-V’s RVWMO, which BOOM implements, still requires ordering between same-address loads. Consistency across cores is part of Caches and coherence.

oldestyoungest →1busyc52busyc23busyc34busyc65busyc26busyc47busyc78busyc3c = finish cycle · ✓ retired · ✗ flushedhead: next to retire
Exception
1 / 9

Eight instructions in the reorder buffer, oldest on the left. They finish out of order; they retire from the head, two per cycle, in order.

The reorder buffer: out-of-order completion, in-order retirement. Turn on a fault in instruction 4 to see a precise exception.Share freely with credit: ‘Figure from chipfieldguide.com’

This is a small processor running a 24-step program. Each row is one step. The first step needs a number from far-away memory, which takes a long time. Some later steps need that number; others don’t.

Switch between “In order” and “Out of order” and compare the number of ticks. Then shrink the look-ahead room and watch the core get stuck. The “Guessing” tab lets you test guessing rules on different kinds of “if”.

The core mode runs a 24-instruction trace on an illustrative core. Each row shows an instruction waiting in the window (thin line), executing (bar) and retiring (green tick). The top strip shows how many instructions issued each cycle, red where none did. Things to try:

  • Compare in order and out of order at the defaults. Which instructions run during the miss?
  • Shrink the window to 4 and then grow it to 32. Why does a bigger window help only up to a point?
  • Set the branch guess to “wrong”. The branch depends on the miss, so how much work gets thrown away?
  • In the predictor tab, find a pattern where the 1-bit predictor is worse than always guessing taken.

The Expert view adds a register-renaming toggle (off adds WAR and WAW interlocks), a front-end refill delay after a mispredict, and in the predictor bench a history-length slider and a penalty for a cycles-lost readout. Try:

  • Width 4, window 32: then turn renaming off. How many fewer instructions finish under the miss, and which register reuse is responsible?
  • Raise the miss latency to 100 cycles. The out-of-order advantage stays about 23 cycles. Why doesn’t it grow with the miss?
  • On the loop pattern, lower the history to 2 bits and then 3. What is the shortest history that sees the loop exit coming?

The model is deliberately small: fixed latencies, no structural hazards beyond width, no cache or memory bandwidth, and one branch. It isolates window size, width, renaming and speculation.

Loading simulation…

How much can a processor find to do at once? Less than you might hope. Most programs are chains of steps where each needs the one before. In a typical stretch of code, only three or four steps can run together.

Looking further ahead finds more. But to keep busy during a long memory trip, a core needs to hold a lot of steps. A core that starts four steps per tick and waits 50 ticks needs room for about 200 steps.

That’s why today’s biggest cores can hold several hundred steps at once. Even that isn’t enough for the slowest trips to main memory.

Instruction-level parallelism

Instruction-level parallelism (ILP) is how many instructions could run at once if hardware were the only limit. David Wall’s 1993 study at DEC simulated 18 programs under hundreds of machine models. Inside a basic block (a straight run of code between branches, about 10 instructions) parallelism rarely exceeds 3 or 4. Across many branches, with good prediction and renaming, it reached 4 to 10 for most programs, and far more only for regular numeric code. Branch prediction was the biggest single factor: removing it from his perfect model dropped median parallelism from 30.6 to 2.2.

The instruction window

Finding that parallelism needs a large , the set of instructions fetched but not yet retired. When the oldest instruction stalls on a , a W-wide core keeps dispatching until the window is full, so a window of N entries buys about N ÷ W cycles. The SonicBOOM authors put it plainly: an L3 hit takes about 50 cycles, and to hide it a 4-wide core must run 200 instructions ahead, exhausting its reorder buffer. A miss all the way to DRAM takes hundreds of cycles, more than any window covers, so prefetching and caches carry the rest.

The window is not one structure but all of them: ROB entries, physical registers, issue-queue entries, and load- and store-queue entries. Whichever fills first stops dispatch. Speculation can run several hundred instructions ahead on modern cores, bounded by the ROB; the Spectre authors cite 192 micro-operations for Intel’s 2013 Haswell. Recent cores roughly doubled or tripled that (see the table under By the numbers).

By Little’s law (in-flight = throughput × latency), sustaining W instructions per cycle across a stall of L cycles needs

N≳W⋅LN \gtrsim W \cdot L

window entries, and proportionally many physical registers, load-queue and store-queue entries. With W=4W = 4 and an L3 latency of about 50 cycles that is 200, the SonicBOOM figure. In practice the window fills with instructions dependent on the miss, so the useful fraction is lower, and a branch that depends on the missing load is the worst case: if it mispredicts, the whole run-ahead is squashed. The sim’s “branch guess: wrong” setting shows that interaction.

Wall’s numbers bound what a window can find. With perfect everything, median parallelism was 30.6; without branch prediction 2.2, without alias analysis 3.4, without renaming 4.8. With realistic but ambitious assumptions it stayed at 4–10, and speculating down both sides of branches added only modestly (to 7–13). The study assumed unlimited functional units and no cache misses, which makes those optimistic ceilings for ILP, not predictions of .

Growing the window has diminishing returns because the instructions that matter are often dependent on the stall, because mispredictions cap useful run-ahead (at one mispredict per few hundred instructions, a 600-entry window is often partly wrong-path), and because the structures get slower. Latency beyond the window is the job of prefetchers and of the cache hierarchy, and of other threads on the same core: see Multicore and vector units.

1101001000cycles (log scale)window lasts ≈ 48 cyclesL2 hit (illus.)L3 hit ≈ 50DRAM (illus.)

A 192-entry window at 4 instructions per cycle fills about 48 cycles after the oldest instruction stalls. Enough to hide L2 hit, not a DRAM miss.

A window lasts about (entries ÷ width) cycles once the oldest instruction stalls. Compare with how long memory takes. Log scale; L2 and DRAM marks are illustrative.Share freely with credit: ‘Figure from chipfieldguide.com’

For decades, people thought wrong guesses were only a waste of time. The work was thrown away, so it seemed harmless.

In 2018, researchers showed it wasn’t. Work done on a wrong guess is wiped from the processor’s notes. But it can leave footprints in its fast memory, the cache. A clever program can time how fast different parts of the cache answer and learn what the wrong-guess work touched. That can give away secrets, like passwords from another program.

These attacks are called and Meltdown. They affected chips from almost every major maker. Fixing them fully is still hard, because the guessing is what makes chips fast.

was designed so that wrong-path work has no architectural effect: registers and memory are restored exactly. But the core also has microarchitectural state that programs can’t read directly and that nobody rolls back: what is in the caches, what the predictors have learned. Instructions that run and are then discarded, the papers call them transient instructions, can change that state.

Two papers published in January 2018 turned this into attacks. The authors’ own summaries, at a conceptual level:

  • Spectre. An attacker trains the branch predictor so that the victim’s code speculatively runs past a check it should have stopped at, for example a bounds check, and touches memory in a way that depends on a secret. When the branch resolves, the CPU reverts its registers, “however, changes made to the cache state are not reverted”, and the attacker recovers the secret by timing cache accesses. A second variant mistrains the branch target buffer instead. They demonstrated it on Intel, AMD and Arm processors.
  • Meltdown. Out-of-order execution let a user program’s load of kernel memory run, and its value feed later instructions, before the permission fault was raised at retirement. The cache traces survived the fault. It affected many Intel cores and some Arm-based ones; AMD stated its cores were not affected.

These are side-channel attacks: they read secrets through timing rather than through any architectural bug. The cache side of the story, why a line that was touched answers faster, is in Caches and coherence.

The general model is a covert channel from transient to architectural execution. Something steers the machine onto a path it would not architecturally take: a mistrained conditional predictor (Spectre variant 1), a poisoned BTB entry (variant 2), or a fault that is only acted on at commit (Meltdown). Transient instructions on that path read a secret and encode it in microarchitectural state, typically by making the address of a dependent load depend on it. After the squash, a receiver measures that state, typically with a cache timing probe.

Each ingredient is a performance feature described earlier in this chapter: predictor state shared across security domains, a BTB indexed before decode, exceptions deferred to commit, a window several hundred instructions deep, and caches that are never rolled back. Mcilroy et al. argue the leaks “lie at the foundation of optimization”: for most languages with a timer, today’s CPUs let a program build a procedure that reads arbitrary memory in its own process, so isolation must come from hardware and OS process boundaries.

Mitigations sit at every layer, each with a cost. OS: separate kernel page tables (KAISER/KPTI) for Meltdown. Software: speculation barriers and index masking at sensitive bounds checks, and process isolation for untrusted code. Hardware: flushing or partitioning predictor state across privilege and context changes, blocking forwarding of faulting loads, and research designs that hide or undo transient cache fills. Each gives back some of the performance speculation bought, which is why this remains, in Mcilroy et al.’s words, an open design problem.

architectural statecheck: allowed?registers:—secret (protected)cache: not rolled back0123456789101112131415nothing speculative yet
1 / 5

A secret sits in memory the running code must not read. A bounds or permission check guards it.

The idea behind Spectre-style attacks: speculative work is undone in the registers but not in the cache. A concept picture, not a recipe.Share freely with credit: ‘Figure from chipfieldguide.com’
Modern predictor accuracy
> 95%
Misprediction penalty, recent x86 (measured)
≈ 15–25 cycles
ILP in real programs (Wall, 1993)
4–10
In-flight window, recent big cores
320–576+

What these numbers mean:

  • More than 95%: good guessers get almost every “if” right.
  • 15 to 25 ticks: the time lost each time a fast chip guesses wrong.
  • 4 to 10: how many steps of a normal program could run at once, even with a perfect-sized chip.
  • Several hundred: how many steps the biggest cores hold at once while they look for work.

Two readings of the table. First, every family has converged on the same shape: 6 to 8 instructions per cycle through rename, a window of several hundred, and a TAGE-style predictor, whatever the instruction set. Second, widths of 8 sit far above what Wall measured as typical parallelism of 4 to 10 with idealized hardware. Real cores sustain well under their width on most code; the width is there for the bursts, and to recover quickly after a stall or a mispredict.

The measured penalties put the predictors in context. At 15–25 cycles per miss, going from 95% to 97% accuracy removes two in five mispredictions, which can be worth more than an extra execution unit.

A rough budget for a modern core: at 20% branches, 97% accuracy and a 17-cycle penalty (illustrative midpoints), mispredicts add about 0.2×0.03×17≈0.10.2 \times 0.03 \times 17 \approx 0.1 CPI, which at a base CPI of 0.25 (4 IPC) is a 40% slowdown. TAGE-SC-L’s championship results, about 4 mispredictions per thousand instructions at 64 KB, show how far the state of the art has pushed that term. Zen 5’s 448-entry ROB at 8-wide dispatch covers about 56 cycles of a stalled head, and Lion Cove’s 576 at 8-wide about 72, which matches the SonicBOOM rule of thumb that windows are sized for last-level-cache hits, not DRAM.

Guessing and running out of order make one program run much faster. But they cost a lot. The planning parts take up a big share of the chip, and they use power on every tick, even when the guesses are wrong.

Making a core wider and deeper gets harder fast. Every waiting step has to listen for every answer, so the wiring grows much faster than the core.

That’s why some chips go the other way. A GPU uses many simple cores that don’t guess or reorder, and keeps busy by switching between thousands of workers instead.

You getYou give up
Latency hidden by independent work in the windowLarge ROB, register files and queues, all read and written every cycle
A full pipeline past branchesEnergy spent on wrong-path work, and a penalty on every misprediction
Several instructions per cycle (superscalar)Wakeup, select and bypass logic that grows faster than the width
Precise exceptions with out-of-order completionIn-order commit bandwidth, and the ROB storage that implies
Fast single-thread performanceArea and power per core, and speculative side channels to defend against

Ways designs go wrong

  • A window bigger than the predictor supports. If one branch in a few hundred instructions is mispredicted, the far end of a huge window is mostly wrong-path work.
  • Slow wakeup. If wakeup and select can’t finish in one cycle, dependent instructions can’t issue back to back, and chains of simple operations run at half speed.
  • An unbalanced window. Running out of physical registers or load-queue entries stalls dispatch while the ROB still has room.
  • Leaky speculation. Predictor or cache state shared between security domains turns performance features into side channels.

The opposite design point is the GPU, which spends no area on prediction or reordering and hides latency with thousands of threads instead; see SIMT and GPUs, where a branch that splits a warp costs rather than a misprediction.

  • Wakeup and select scale badly. Palacharla, Jouppi and Smith’s SPICE models showed wakeup delay rising about 34% from 2- to 4-wide and 46% from 4- to 8-wide at a 64-entry window, with tag-drive delay quadratic in window size and a growing share of wire delay at smaller feature sizes. They proposed dependence-based clustering (FIFOs of dependent chains) to keep issue logic simple. Modern cores answer with distributed schedulers and clustered execution.
  • Bypass networks. Every result must reach every consumer in the next cycle. A fully bypassed design needs 2⋅W2⋅S2 \cdot W^2 \cdot S paths for SS result-producing stages, and because the result wires get longer too, bypass delay grows quadratically with issue width.
  • Register-file ports. A W-wide machine reads 2W and writes W registers per cycle. More ports make every cell larger and slower; Palacharla et al.’s clustered design kept a copy of the register file per cluster partly to cut the ports on each copy.
  • Prediction versus pipeline depth. A deeper front end raises clock rate and the penalty together; the architecture stage works out the optimum. Arm’s V2 keeps its pipelines short to recover quickly from mispredicts.
  • Security against speed. Partitioning or flushing predictor state at domain switches, fencing sensitive branches and isolating processes all cost performance.
  • Single thread against throughput. The same area could hold several smaller cores, or wider vector units; a core can also share its window among two threads, as Zen 5 does by splitting its ROB. Those choices are the subject of Multicore and vector units.
1×10×100×vs 2-wide/32Wakeup tag compares×12N entries × 2 sources × W results, every cycleBypass paths×4.0W results × 2 inputs × W unitsRegister-file ports×2.02W read + W write portsPhysical registers×3.5≈ 32 architectural + 1 per instruction in flight

4-wide, 192-entry window versus 2-wide, 32-entry: wakeup tag compares ×12, bypass paths ×4.0, register-file ports ×2.0, physical registers ×3.5. The fastest-growing is wakeup tag compares.

What grows with issue width and window size, relative to a 2-wide, 32-entry core. Counts for a textbook design; log scale.Share freely with credit: ‘Figure from chipfieldguide.com’

This part goes deeper, into the math, models and algorithms behind the chapter. It’s written for the Expert level.

1. Bimodal and gshare

Both predictors are a table of 2-bit saturating counters; they differ only in the index. Predict with the counter’s top bit at fetch; update at commit; keep the global history register speculative and repair it on a mispredict.

gshare.txt (illustrative)text
table ctr[2^n] = 2          # 2-bit counters, start weakly taken
ghr = 0                     # global history, speculative

predict(pc):
  i = (pc >> 2) ^ (ghr & (2^h - 1))   # gshare; bimodal: i = pc >> 2
  taken = ctr[i % 2^n] >= 2
  ghr = (ghr << 1) | taken            # speculative update at fetch
  return taken, snapshot(ghr), i

resolve(branch):                      # at execute
  if branch.taken != branch.predicted:
    ghr = (branch.snapshot without its guess << 1) | branch.taken

commit(branch):
  c = ctr[branch.i]
  ctr[branch.i] = min(3, c + 1) if branch.taken else max(0, c - 1)
  1. 1L5XOR spreads (address, history) pairs over the whole table instead of concatenating them.
  2. 2L7History must move at fetch: dozens of branches are in flight before the first commits.
  3. 3L12Repair: restore the history the branch saw and shift in its real outcome.
  4. 4L14Counters train at commit so wrong-path branches never touch them.

2. TAGE

The base table T0T_0 is bimodal. Tagged tables T1…TMT_1 \ldots T_M use history lengths in a geometric series; each entry holds a 3-bit signed counter, a partial tag and a useful counter.

tage.txt (illustrative, after Seznec)text
L(i) = round(alpha^(i-1) * L(1))      # e.g. 4, 8, 16, 32, 64 ...

predict(pc, h):
  hits = [i for i in 1..M if T[i][idx(pc, h, L(i))].tag == tag(pc, h, L(i))]
  provider = max(hits) or 0             # longest matching history
  alt      = next-longest hit or T0
  p = T[provider].ctr
  if provider is newly allocated and |2*ctr+1| == 1:  use alt   # weak, untrained
  return sign(p)

update(pc, h, taken):
  train T[provider].ctr toward taken
  if prediction != alt: adjust T[provider].u toward (prediction == taken)
  if mispredicted and provider < M:
    allocate in some j > provider with u == 0 (else age the u counters)
  periodically decay all u                # so stale entries can be replaced
  1. 1L1Geometric lengths cover short and very long correlations with few tables.
  2. 2L5Prediction comes from the longest history that matches; tags stop aliasing.
  3. 3L8A fresh entry knows nothing yet; the alternate prediction is safer.
  4. 4L14Allocation on a miss moves a branch to longer history only when it needs it.

TAGE-SC-L wraps this with a statistical corrector, which reverts predictions that were statistically wrong in similar circumstances, and a loop predictor for long regular loops.

3. Rename, issue and commit: the sim’s loop

The simulation above runs this loop each cycle over a 24-instruction trace with ALU latency 1, multiply 3, load hit 4 and one load that misses.

ooo_core.txt (the sim’s model)text
each cycle t:
  # 1. commit, in order
  while ROB.head.done and retired < W: retire(ROB.head)
  # 2. branch resolution
  if branch.done and branch.mispredicted:
    squash every entry younger than branch; fetch resumes at t + refill
  # 3. issue (wakeup + select), oldest first
  for e in ROB from head, while issued < W:
    if e.dispatched_before(t) and all producers(e) done by t:
      if renaming or (older readers of e.dst issued and older writer done):
        e.done = t + latency(e); issued += 1
    elif in_order: break          # nothing may pass a stalled instruction
  # 4. dispatch (rename + allocate)
  while fetched < W and ROB.size < N: ROB.push(next instruction)
  1. 1L3In-order commit: precise state at every cycle boundary.
  2. 2L6Everything after the branch is thrown away, issued or not.
  3. 3L9RAW dependences: the only ones left after renaming.
  4. 4L10Without renaming, WAR and WAW interlocks also apply.
  5. 5L15N is the window: dispatch stops when it is full.

4. Two first-order models

Mispredictions add CPI in proportion to their rate and penalty:

ΔCPIbranch=fbr⋅(1−a)⋅P,\Delta\mathrm{CPI}_{\text{branch}} = f_{\text{br}} \cdot (1 - a) \cdot P,

where fbrf_{\text{br}} is branches per instruction, aa the accuracy and PP the penalty in cycles. Because the penalty is roughly the front-end depth plus the branch’s own wait for its operands, a branch that depends on a cache miss can cost far more than PP. The window needed to keep issuing at width WW through a stall of LL cycles follows Little’s law:

N≈W⋅L,e.g. 4×50=200.N \approx W \cdot L, \qquad \text{e.g. } 4 \times 50 = 200.

That is the SonicBOOM sizing argument. Combining the two: useful run-ahead ends at the first mispredicted branch, so the expected useful window is about min⁡ ⁣(N, 1/(fbr(1−a)))\min\!\left(N,\ 1 / \bigl(f_{\text{br}}(1 - a)\bigr)\right) instructions; at fbr=0.2f_{\text{br}} = 0.2 and a=0.97a = 0.97 that is about 167, which is why window size and predictor accuracy have grown together.

This chapter kept saying “a trip to memory takes a long time”. The next chapter explains why. It’s about the small, fast memories called caches that sit close to the processor, and how they decide whether a trip takes a few ticks or hundreds.

This chapter treated a load as either a hit or a slow miss and asked how much work a core can do while it waits. Caches and coherence explains where those latencies come from, why most loads hit, and what changes when several cores share data. After that, Multicore and vector units covers what chips did when making one core faster stopped paying off.

The window-sizing and side-channel arguments both lead into the memory system. Next, Caches and coherence covers locality, cache organization, miss types and average memory access time, prefetching (the answer to latency beyond the window), and the coherence and consistency rules the load/store queues must respect. Then Multicore and vector units covers sharing a window between threads and the turn from single-thread speed to many cores.

PipelinesOut of orderyou are hereCachesnextMulticore
3 / 4

Next: why most loads hit, what a miss costs, and how cores keep their copies in agreement.

The CPU chapters of this guide. The next one, Caches, explains the miss latency this chapter took as given.Share freely with credit: ‘Figure from chipfieldguide.com’
Novice · 0 of 4 correct
  1. Q1A loop branch is taken 3 times, then not taken, over and over. Once warmed up, how often is a 2-bit counter right?

  2. Q2What does register renaming remove?

  3. Q3Why do out-of-order cores retire instructions in program order?

  4. Q4A core holds 200 instructions in flight and issues 4 per cycle. Roughly how long can it keep issuing after the oldest one stalls on a miss?

Sources

Show Hide 22 sources
  1. Branch Prediction (MIT 6.5900 Computer System Architecture, lecture L08)Joel Emer · MIT CSAIL · 2022More than 10 stages between next-PC and branch resolution; lost work ≈ loop length × width; predictors over 95% accurate; static prediction about 80%; branches taken 60–70% of the time; 1-bit mispredicts twice per loop; Smith’s 2-bit counter (1981); Yeh and Patt history predictors; gshare; Pentium Pro and Alpha 21264 tournament; BTB; return stack of 8–16 entries, much more accurate than a BTB for returns.
  2. Complex Pipelining: Out-of-Order Execution, Register Renaming, and Exceptions (MIT 6.5900, lecture L07)Joel Emer · MIT CSAIL · 2022CDC 6600 scoreboard; IBM 360 had 4 FP registers; Tomasulo’s 1967 on-the-fly renaming; renaming removes WAR and WAW hazards; the reorder buffer; first built in the 360/91 (1969) but rare until the mid-1990s because of memory latency and imprecise exceptions; definition of precise exceptions; in-order commit.
  3. Speculative Execution (MIT 6.5900, lecture L09)Mengjia Yan · MIT CSAIL · 2022In-order commit for mis-speculation recovery; recovering the ROB and rename table; snapshots of the map table at each branch; recovering branch-predictor history; data-in-ROB versus unified physical register file designs.
  4. Advanced Memory Operations (MIT 6.5900, lecture L10)Joel Emer · MIT CSAIL · 2022Speculative stores held in a store buffer until commit; store-to-load bypass; speculative loads; memory dependence prediction with store sets (Alpha 21464).
  5. Combining Branch Predictors (WRL Technical Note TN-36)Scott McFarling · Digital Equipment Corporation Western Research Laboratory (archived at bitsavers.org) · 1993Bimodal 2-bit counters saturate at 93.5% on SPEC’89; gshare (branch address XOR global history); a chooser that combines two predictors reaches 98.1% versus 97.1% for the best earlier scheme, at about half the size.
  6. TAGE-SC-L Branch Predictors Again (5th Championship Branch Prediction)André Seznec · Journal of Instruction-Level Parallelism, CBP-5 workshop · 2016TAGE: a bimodal base predictor plus partially tagged tables indexed with geometric history lengths (first described by Seznec and Michaud, JILP 2006); entries with a 3-bit counter, partial tag and useful bit; statistical corrector and loop predictor; 3.986 MPKI (64 KB) and 4.991 MPKI (8 KB) on the CBP-5 traces.
  7. Dynamic Branch Prediction with PerceptronsDaniel A. Jiménez, Calvin Lin · HPCA 2001 (author copy, University of Texas at Austin) · 2001A perceptron in place of 2-bit counters; hardware grows linearly with history length; 10.1% fewer mispredictions than gshare at a 4 KB budget on SPEC 2000.
  8. Limits of Instruction-Level Parallelism (WRL Research Report 93/6)David W. Wall · Digital Equipment Corporation Western Research Laboratory (archived at bitsavers.org) · 1993Basic blocks average about 10 instructions with parallelism of 3–4 inside one; ambitious but known techniques give parallelism of 4–10; removing branch prediction from the perfect model drops median parallelism from 30.6 to 2.2, removing renaming to 4.8; a small return-prediction ring helps a lot.
  9. Complexity-Effective Superscalar ProcessorsSubbarao Palacharla, Norman P. Jouppi, J. E. Smith · ISCA 1997 (MINDS@UW institutional repository) · 1997Rename, wakeup, select and bypass delay modeled versus issue width and window size; wakeup tag drive grows quadratically with window size; wakeup delay up 34% from 2- to 4-wide and 46% from 4- to 8-wide at 64 entries (0.18 µm); wakeup and select form an atomic operation; window and bypass logic become critical.
  10. Rename Stage (BOOM documentation)The Berkeley Out-of-Order Machine project · BOOM open-source RISC-V core documentationExplicit renaming into a unified physical register file (versus data-in-ROB designs like the P6); map table, busy table and free list; a copy of the map table per branch for single-cycle recovery; renaming breaks WAW and WAR, leaving only RAW.
  11. The Reorder Buffer (ROB) and the Dispatch Stage (BOOM documentation)The Berkeley Out-of-Order Machine project · BOOM open-source RISC-V core documentationThe ROB tracks every in-flight instruction and gives the illusion of in-order execution; commit from the head; an exception at the head flushes the pipeline and redirects to the handler; rename-state rollback on misprediction.
  12. The Backing Predictor (BOOM documentation)The Berkeley Out-of-Order Machine project · BOOM open-source RISC-V core documentationGlobal history updated speculatively at fetch (waiting for commit would be too late, with dozens of branches in flight) and snapshotted for repair; predictor tables updated at commit to avoid wrong-path pollution; gshare and TAGE implementations.
  13. The Load/Store Unit (BOOM documentation)The Berkeley Out-of-Order Machine project · BOOM open-source RISC-V core documentationLoad and store queues; stores go to memory only after commit, in order; loads issue speculatively before older store addresses are known; store-to-load forwarding; a detected ordering failure flushes the pipeline.
  14. SonicBOOM: The 3rd Generation Berkeley Out-of-Order MachineJerry Zhao, Ben Korpan, Abraham Gonzalez, Krste Asanović · Fourth Workshop on Computer Architecture Research with RISC-V (CARRV 2020) · 2020Open-source RV64GC superscalar out-of-order core with a TAGE predictor, decode width 1–5, 6.2 CoreMark/MHz; security now a design concern; an I-cache miss costs at least a 10-cycle bubble, ‘equivalent to a branch misprediction’; L3 hits take about 50 cycles, and a 4-wide core 50 cycles ahead needs 200 instructions, exhausting the ROB.
  15. The microarchitecture of Intel, AMD and VIA CPUs: An optimization guide for assembly programmers and compiler makersAgner Fog · agner.org (Technical University of Denmark) · 2026Measured branch misprediction penalties: 15–20 cycles on Intel Haswell through recent Lake cores; about 18 on AMD Zen 1–3, 15–18 on Zen 4 and 15–25 on Zen 5; return stack buffer of 32 entries on Zen 1–4 and 52 on Zen 5.
  16. “Zen 5”: AMD’s Next-Generation Core (Hot Chips 2024)Brad Cohen, Mahesh Subramony · Hot Chips 36 · 20248-wide dispatch, rename and retire; 6 integer ALUs; ROB/retire queue of 448 entries with one thread (224 per thread with two); a 16K-entry L1 BTB, larger TAGE, 52-entry return address stack and two taken predictions per cycle.
  17. Next Gen P-core: The Lion Cove Microarchitecture (Intel Tech Tour 2024)Intel · Intel · 2024Allocation and rename widened from 6 to 8, retirement from 8 to 12, execution ports from 12 to 18, and the instruction window deepened from 512 to 576.
  18. Arm Neoverse V2 Platform (Hot Chips 2023)Magnus Bruce · Hot Chips 35 · 2023Run-ahead (decoupled) branch prediction with large BTBs; an 8-table TAGE direction predictor; ‘maintain short pipelines for quick branch mispredict recovery’; 320+ entry out-of-order window, 6-wide decode, 8-wide dispatch and retire; 6 rename checkpoints; store-to-load forwarding at L1 hit latency.
  19. SiFive P870 High-Performance RISC-V Processor (Hot Chips 2023)SiFive · Hot Chips 35 · 20236-wide decode and dispatch; ‘ROB – up to 1120 instructions’ (counted in instructions; no entry count given); 228 integer, 240 floating-point and 128 vector rename registers; 1K-entry next-line predictor, 64-entry return address stack, 16K-entry TAGE and 2.5K-entry indirect predictor.
  20. Spectre Attacks: Exploiting Speculative ExecutionPaul Kocher, Jann Horn, Anders Fogh, Daniel Genkin, Daniel Gruss, Werner Haas, Mike Hamburg, Moritz Lipp, Stefan Mangard, Thomas Prescher, Michael Schwarz, Yuval Yarom · IEEE Symposium on Security and Privacy 2019 (authors’ copy, spectreattack.com) · 2019Mispredicted work is reverted in registers but ‘changes made to the cache state are not reverted’; a mistrained bounds check (variant 1) and a mistrained BTB (variant 2); speculation runs several hundred instructions ahead, bounded by the ROB (192 µops on Haswell); BTB and return stack buffer; demonstrated on Intel, AMD and Arm processors.
  21. Meltdown: Reading Kernel Memory from User SpaceMoritz Lipp, Michael Schwarz, Daniel Gruss, Thomas Prescher, Werner Haas, Anders Fogh, Jann Horn, Stefan Mangard, Paul Kocher, Daniel Genkin, Yuval Yarom, Mike Hamburg · 27th USENIX Security Symposium, 2018 (authors’ copy, meltdownattack.com) · 2018Exploits out-of-order execution: transient instructions run before a permission fault is raised at retirement, and their cache state is not reverted; background on Tomasulo’s reservation stations and common data bus, the reorder buffer and 1-bit, 2-bit and two-level predictors; affected Intel and one Arm core, not AMD per AMD’s statement; the KAISER countermeasure.
  22. Spectre is here to stay: An analysis of side-channels and speculative executionRoss Mcilroy, Jaroslav Sevcik, Tobias Tebbi, Ben L. Titzer, Toon Verwaest · arXiv:1902.05178 · 2019From the V8 JavaScript team: speculative side channels ‘lie at the foundation of optimization’; language-level isolation within one address space cannot be guaranteed on today’s CPUs, so Chrome relies on process isolation; hardware mitigation for future designs is an open problem.