Branches that talk to each other: correlating and two-level predictors

After this lesson you will fill the prediction table of a (1,1) or (2,2) correlating predictor cycle by cycle, say which predictor can learn a loop exit and why, and explain what gshare's XOR buys.

Two branches, one after the other, test the same variable d, which alternates 2, 0, 2, 0. Give each branch its own one-bit history entry, as lesson 5 did, and watch: 0 of 8 predictions right. A coin would do better.

Now add one bit of information — not about this branch, but the outcome of the branch executed just before. After two warm-up misses it never misses again: 8 of 8 on every later pass. Where did that information come from, if the branch's own history was worthless?

Two branches, one variable: where the per-branch table goes blind

Recall from lesson 5: a branch history table (BHT) is indexed by the low-order PC bits, has no tags, and holds a tiny state machine per entry — one bit or two (Strongly/Weakly Taken, Weakly/Strongly Not Taken) — whose state is the prediction; the branch target buffer (BTB) is the tagged cache that supplies where to fetch when the prediction says Taken.

Every BHT entry looks at one thing, that branch's own past. The notes open §2.6.4 (p. 65) with the simplest case where that is not enough:

if (x > 0)   // Branch A
   ...
if (x > 0)   // Branch B
   ...

Branch B behaves like Branch A because both test the same condition, and "a normal predictor doesn't know this. It treats A and B independently" (p. 65). Hennessy and Patterson sharpen the example with a variable that changes between the tests — in RISC-V in the book, here in the papers' mnemonics, with d in R1 and R0 as the zero register:

      BNE   R1, R0, L1      ; b1: taken if d != 0
      ADDUI R1, R0, 1       ;     d = 1
L1:   SUBI  R3, R1, 1       ;     R3 = d - 1
      BNE   R3, R0, L2      ; b2: taken if d != 1
      ...
L2:

Three cases. d = 0: b1 not taken, d becomes 1, b2 not taken. d = 1: b1 taken, b2 not taken. d = 2: both taken. So if b1 is not taken, b2 is certainly not taken — a correlation no per-branch counter can see. Let d alternate 2, 0, 2, 0 and give each branch a 1-bit entry initialised Not Taken; the prediction is the bit before the branch, the bit after is the outcome.

Table 1. A 1-bit entry per branch on d = 2, 0, 2, 0: every prediction is one step behind.
db1 predictionb1 outcomeb1 bit afterb2 predictionb2 outcomeb2 bit after
2NTTTNTTT
0TNTNTTNTNT
2NTTTNTTT
0TNTNTTNTNT

Eight predictions, eight misses: the bit always holds the previous outcome and the pattern flips every time. What decides b2 is not what b2 did last time but what b1 did a moment ago.

PC k low-order bits BHT: last outcome b1 NT b2 NT one bit, no context

Before: one entry per branch

last outcome (GHR) 1 PC GHR = 0 b1T b2NT GHR = 1 b1NT b2T one entry per context

After: one entry per (branch, last outcome)

Fig. 1. Same two branches, same PC bits. On the right the outcome of the previous branch chooses between two tables, so b2 after a taken b1 and b2 after a not-taken b1 are two different entries that can hold two different answers. The entries shown are the trained ones of Table 2.

The right panel is the notes' (1,1) correlating predictor (p. 67): a 1-bit global history stores the last branch outcome and selects one of two 1-bit tables (T1 if the last branch was taken, T2 if not); the low-order PC bits index the selected table. Each branch now has two entries, written below as "if last NT / if last T", all initialised NT; the history starts at NT.

Table 2. The (1,1) predictor on the same stream: the two cold entries miss, then nothing does.
dGH before b1b1 entriesb1 pred.b1 outcomeGH before b2b2 entriesb2 pred.b2 outcome
2NTNT / NTNT (miss)TTNT / NTNT (miss)T
0TT / NTNTNTNTNT / TNTNT
2NTT / NTTTTNT / TTT
0TT / NTNTNTNTNT / TNTNT

Two misses, both on cold entries, then a perfect record. The column "GH before b1" is b2's previous outcome, which encodes the previous value of d: b1 is predicted from the last b2, b2 from the current b1 — the two branches predict each other.

Correlating predictor (also two-level predictor) — a dynamic predictor that selects the counter by the branch address and the recent history of branch outcomes, one counter per (branch, context). The notes' key idea (p. 65): "use global branch history (outcomes of previous branches) to improve prediction for the current branch".

A per-branch counter answers "what did this branch do last time?"; a correlating predictor answers "what did this branch do last time it was in this situation?"

The (m, n) predictor: a history register that picks the table

Generalise the right panel of Fig. 1 and you get the notes' general case (p. 65): in an (m, n) correlating predictor the outcomes of the last m branches select one of \(2^m\) prediction tables of n-bit entries. The four steps, in the order the hardware takes them:

  1. Track the last m branches: their outcomes, in program order, whichever branches they were, are shifted into an m-bit Global History Register (GHR), Taken = 1, Not Taken = 0.
  2. Select a table: the GHR value picks one of the \(2^m\) tables.
  3. Index it with k low-order bits of the branch PC.
  4. Predict with the n-bit entry — the last outcome (n = 1) or the four-state machine of lesson 5 (n = 2); after the branch resolves, update the entry and shift the outcome into the GHR.
Why a real core shifts the predicted outcome in, not the resolved one

Background, not in the notes: the next branch is fetched long before this one resolves, so a GHR updated only at resolution would lag by the pipeline depth and the next prediction would read a stale history. Real cores shift the predicted outcome in at fetch and repair the register on a misprediction. The notes (p. 71) and the exam tables update at resolution: keep that convention on paper.

2ᵐ = 4 tables, one per history value 00 01 10 11 2ᵏ rows PC of the branch upper k bits 00 k low-order bits GHR, m = 2 bits 1 0 older · newest mux n-bit prediction
Fig. 2. The (2, n) predictor of the notes' Figure 19 (p. 70), redrawn: the PC's k low-order bits pick the same row in all four tables at once, the GHR picks which table's entry is used. The entry is an n-bit counter. The notes' Figure 18 (p. 68) is the same drawing with two tables and a multiplexer driven by the "Last Branch Result".

In memory (p. 66): \(2^m\) tables of \(2^k\) entries of n bits. The plain 2-bit BHT of lesson 5 is the (0, 2) predictor of this family — no history bits, one table. And the tables, selected by the GHR and indexed by the PC, are one table of \(2^{m+k}\) entries indexed by the concatenation of the GHR and the PC bits.

\[ \text{tables} = 2^m \] \[ \text{entries per table} = 2^k \] \[ \text{storage} = 2^m \cdot n \cdot 2^k \text{ bits} \] \[ \text{index} = \text{GHR} \,\Vert\, \text{PC}[k] \]
\(m\)
global history bits (outcomes of the last m branches)
\(n\)
bits per entry: 1 (last outcome) or 2 (the four states)
\(k\)
low-order PC bits used to index a table
\(\Vert\)
concatenation: GHR as the upper m bits, the k PC bits as the lower ones — an index of m + k bits

The shift convention, and the (2,2) walkthrough done right

An exercise is only as right as its shift convention. The notes' (p. 69), for m = 2: 00 both Not Taken; 01 last Taken, penultimate Not Taken; 10 last Not Taken, penultimate Taken; 11 both Taken — shift left, newest outcome on the right. The opposite convention exists elsewhere, so state yours at the top of an exercise.

\[ \text{GHR} \leftarrow \big( (\text{GHR} \ll 1) \,|\, o \big) \bmod 2^m \]
\(o\)
the outcome just resolved: 1 for Taken, 0 for Not Taken
\(\ll 1\)
shift left by one bit; the oldest bit falls out on the left

Now the notes' (2,2) example (pp. 69–70): branches A, B and C in program order, outcomes Taken, Not Taken, Taken; four tables of 2-bit counters, one per GHR value; GHR starting at 00. Each branch is predicted from the table the GHR selects at that moment, the entry is updated, the outcome shifted in — Table 3. For C the notes assume Weakly Taken in table 10: Taken is predicted and is right; the entry moves to Strongly Taken, the GHR becomes 01 again.

Table 3. The (2,2) walkthrough of the notes' p. 70, recomputed with "newest outcome on the right".
CycleBranchGHR beforeTable used (predict and update)OutcomeGHR after
1A00BHT[00]T01
2B01BHT[01]NT10
3C10BHT[10]T01

Each (branch, context) pair has its own counter, which converges where a single counter would flip-flop; and because the history also holds the branch's own previous outcomes, b1 was fixed in Table 2 although it comes first. Pan, So and Rahmeh, who published the scheme in 1992, priced it at "up to 11% additional accuracy at the extra hardware cost of one shift register".

Same budget, two designs

History bits are not free: tables multiply as \(2^m\), each context must be trained, and distant outcomes are mostly noise. The honest comparison is at equal storage, as Hennessy and Patterson make it on SPEC89.

  1. A (2,2) predictor with 1024 entries per table: \(2^m \cdot n \cdot 2^k = 4 \cdot 2 \cdot 1024\).

    \[ \text{storage} = 8192 \text{ bits} = 1 \text{ KB} \]
  2. A plain 2-bit BHT — the (0,2) predictor — with the same 8192 bits holds 8192 / 2 entries.

    \[ \text{entries} = 4096 \]
  3. Their SPEC89 measurement (background from the textbook): the (2,2) with 1024 entries per table mispredicts less than the 4096-entry 2-bit BHT, and even than one with unlimited entries (eqntott: about 6 % against 18 %). Unlimited entries remove aliasing, not the lack of context.

  4. The address of an entry in the single-table view: GHR = 10, PC bits = 1011, k = 4.

    \[ \text{index} = 10\,\Vert\,1011 = 101011_2 = 43 \]

Two levels: a history register and a pattern table — global or local

Yeh and Patt gave the family its general shape in 1991; the notes present it as two-level adaptive branch prediction (p. 71): a history-tracking component and a pattern-based prediction component, with the exam vocabulary of both:

The process (p. 71): index the PHT with the BHR, read the counter, predict Taken if it is in a Taken state; afterwards update the counter and shift the outcome in. The (m, n) predictor is one member — global history, table by history, row by PC; the variants differ only in whose history (global or local) and how the PHT is indexed (history alone, history ‖ PC, history ⊕ PC).

b1 b2 b3 one BHR for all 1 0 1 1 PHT WT shared history

Global: one register, all branches

PC BHR per branch b11101 b20111 b31111 PHT ST own history

Local: one register per branch

Fig. 3. Two first levels for the same second level. Global history sees what the other branches just did; local history sees only this branch's own last h outcomes, so it is never diluted by the rest of the loop body.

What local history buys: the loop exit

The loop of the June 2025 paper, whose 2-bit table lesson 5 filled in, runs five iterations: the BNE goes T T T T NT, and again on the next execution.

LOOP: LD    F0, 0(R1)
      FADD  F2, F0, F0
      SD    F2, 0(R1)
      ADDUI R1, R1, 4
      BNE   R1, R2, LOOP      ; R1 = 0, R2 = 20: T T T T NT

A 2-bit counter mispredicts the exit every time: it holds a bias, and no counter can express "after four taken comes a not-taken". Yet the exit comes at the same place in every pass. A local history of 4 bits makes it visible: before the fifth iteration the branch's own history is 1111, before the first 1110, and each context gets its own counter. The smallest example that fits in a table: a 3-iteration loop, pattern T T NT, 2 bits of local history (older, newer), counters initialised Weakly Taken:

Table 4. A two-level predictor with 2 bits of local history on the pattern T T NT, four times over (recomputed). History before step 1: T,T.
step123456789101112
historyT,TT,TT,TT,NTNT,TT,TT,NTNT,TT,TT,NTNT,TT,T
its counterWTSTSTWTWTWTSTSTWNTSTSTSNT
predictionTTTTTTTTNTTTNT
outcomeTTNTTTNTTTNTTTNT
resulthithitmisshithitmisshithithithithithit

Two warm-up misses while the counter of context "T,T" is dragged from Weakly Taken to Weakly Not Taken, then nothing: three contexts, three converged counters, the pattern memorised. A plain 2-bit counter on the same stream, started Strongly Taken, misses every third execution for ever — 4 of 12. The rule:

\[ h \ge N - 1 \]
\(h\)
bits of local history
\(N\)
iterations of the loop (the branch is taken N − 1 times, then not taken)

With h ≥ N − 1 every position in the pattern has its own history, so after warm-up the exit is predicted like the others. A global register can do the same only if the loop body holds no other branch; otherwise their outcomes dilute the pattern — the reason tournament designs such as the Alpha 21264 of 1998 keep both kinds. Yeh and Patt's 1991 figure: "97 percent accuracy on nine of the ten SPEC benchmarks, compared to less than 93 percent for other schemes".

Time to race them. The arena feeds one stream to eight predictors, from the two static rules and the BHTs of lesson 5 to the (1,1), the (2,2), gshare and a local two-level predictor with h history bits. Counters start Strongly Not Taken (the June 2025 initialisation), 1-bit entries Not Taken, histories at 0; a toggle switches the 2-bit entries from the saturating counter (pp. 69, 71) to the four-state figure of p. 59.

Predictor arena: one stream, eight predictors

Top: the outcome of each step (a filled square is Taken) and, for every predictor, a green square for a correct prediction and an orange one for a misprediction, with the running count on the right. Bottom: the four tables of the (2,2) predictor (rows b1, b2), the entry it uses at this step, and its history register. "One step" advances the stream; the stream, its length and the history length are set with the controls.

  1. Keep the H&P stream and, before pressing anything, predict the score of the 1-bit row and of the (1,1) row over 12 steps. Then run to the end.
    What you should see

    1-bit: 12 misses of 12 — every square orange, as in Table 1. (1,1): 2 misses, the first two steps, then green for ever. Always Taken and always Not Taken sit at 6 of 12: on this stream the per-branch learner is worse than not learning at all, and one bit of context fixes it completely. This is the insight the widget is built for.

  2. Same stream. Why does the (2,2) row take two passes (4 misses) to become perfect where the (1,1) took one? Predict, then step through the first six steps and watch the counters in the bottom picture.
    What you should see

    The (2,2) entries are 2-bit counters starting Strongly Not Taken: the first Taken moves the entry only to Weakly Not Taken, which still predicts Not Taken; the second pass moves it to Weakly Taken. Hysteresis costs one extra miss per context. Switch "counters start Weakly Taken" on and the (2,2) matches the (1,1).

  3. Switch to the loop stream (N = 5, 3 passes). Which of always Taken, 1-bit and 2-bit has the fewest misses, and how many does each have? Predict, then run.
    What you should see

    Always Taken: 3 misses (one exit per pass). 2-bit: 5 (two warm-up misses from Strongly Not Taken, then one per exit). 1-bit: 6 (the exit and the re-entry of every pass). The simplest learning predictor is the worst of the three on a loop, and no counter removes the exit miss.

  4. Still on the loop: set h = 4 and passes = 6. Which rows become perfect, and from which pass? Then try N = 8 with h = 4.
    What you should see

    The local row (and gshare, since the loop body has no other branch) reaches zero misses from the fourth pass on: each of the five contexts needs two Taken pushes from Strongly Not Taken. With N = 8 the exit needs 7 history bits and h = 4 is not enough: the context 1111 occurs both before a Taken and before the exit, so the exit keeps being mispredicted.

gshare and aliasing: let every branch reach every entry

Two named members of the family (p. 72). The Global Adaptive predictor (GA) indexes the PHT with the global history alone: one BHR shared by all branches captures their correlation. gshare (the name is McFarling's, 1993) is, in the notes' words, "designed to improve the indexing of the PHT and reduce aliasing (i.e., different branches mapping to the same PHT entry)": the index is the XOR of the BHR with the low-order PC bits of the branch. The notes' Table 7:

Table 5. Global Adaptive and gshare (the notes' Table 7, p. 72).
PredictorHistory usedIndexing to PHTBenefit
Global Adaptive (GA)global BHRBHR value directly indexes the PHTsimple, effective for globally correlated branches
gshareglobal BHR + PCBHR XOR PC bits index the PHTreduces aliasing, captures global + local context
\[ \text{index} = \text{BHR} \oplus \text{PC}[h \text{ bits}] \]
\(\oplus\)
bitwise exclusive OR
\(h\)
width of the history, of the PC slice and of the index: the PHT has \(2^h\) entries

By hand, h = 4: PC bits 1011 under history 0110 give 1011 ⊕ 0110 = 1101, entry 13 of 16; a second branch with PC bits 0101 under the same history lands on 0011, entry 3 — separated.

Why XOR rather than concatenation, when both fit h index bits? Concatenation must split them, m for the history and h − m for the PC: each branch reaches only \(2^m\) entries, its own partition, and branches whose low h − m PC bits agree collide in every context. With XOR all h PC bits and all h history bits take part: a branch reaches all \(2^h\) entries as the history varies, and under the same history two branches never share one. XOR is still many-to-one (\(2^{2h}\) pairs onto \(2^h\) entries), so aliasing is reduced, not removed.

"The PHT is local in the sense that it provides per-entry adaptation via 2-bit counters" (p. 72). In two-level vocabulary local means per-branch history (p. 71); a PHT indexed by global history is not local in any sense, and gshare's "global and local view" (p. 72) means only that the branch's identity is mixed in.

Index mixer: concatenation against XOR on the same 16 entries

A loop body holds up to six branches, A to F, with PC bits 0100, 0110, 1000, 1010, 1100, 1110; the program passes through a few distinct global histories, and every branch is seen under each of them. The left column maps each (branch, history) pair onto a 16-entry PHT by concatenating m history bits with 4 − m PC bits; the right column by XOR of 4 history bits with the 4 PC bits. A violet entry serves one branch, an orange one is shared by different branches — interference. The yellow frame follows one traced branch under the first history.

  1. With 4 branches, m = 2 and 2 histories: how many entries on each side are shared by different branches? Predict, then look.
    What you should see

    Concatenation: 4 shared entries — all 8 predictions sit in an entry that another branch also uses, because A and C have the same low two PC bits (00), and so do B and D (10). gshare: none. This is the insight: under one and the same history, XOR puts different branches on different entries; concatenation cannot, once it has given bits to the history.

  2. Keep everything and move m from 0 to 4. What does concatenation gain and lose at each end?
    What you should see

    At m = 0 nothing is shared — but no history is used at all, so no correlation can be learnt. At m = 4 all four history bits are used — but every branch maps to the same entry under the same history (2 entries, 4 branches each). Concatenation trades context for identity; gshare keeps both: 4 history bits and distinct entries.

  3. Set 6 branches and 4 histories. Does gshare still have zero shared entries? Why not?
    What you should see

    No: 8 shared entries (12 colliding pairs) against 6 on the left (18 pairs). Two branches collide in gshare when the XOR of their PCs equals the XOR of two histories, which happens across different contexts once there are enough of them. XOR reduces aliasing; it does not eliminate it.

Aliasing done right: harmless with tags, harmful without

The notes end with a deepening on aliasing (pp. 72–73): "two or more distinct entities become indistinguishable to the hardware or software". The idea is right, two of its three examples are wrong, and the difference is the point: an index collision happens in every structure indexed by a few address bits, and what it costs depends on whether there is a tag.

tag index 00 stored tag target / data = tags differ: a miss, never the wrong data

Cache or BTB: index + tag

unused index 00 no tag counter: WT another branch's counter: wrong guess, right program no tag, no check

BHT or PHT: index only

Fig. 4. The same index collision in two structures. With a tag the hardware notices and misses (a conflict miss, a BTB miss); without one it uses the other branch's state and mispredicts — and the misprediction is caught when the branch resolves, so the program stays correct and only accuracy is lost.

Branch-predictor aliasing is the untagged case: "different branches map to the same entry in a branch predictor table (e.g., BHT, PHT)" and pollute each other's prediction (p. 73) — harmless for correctness, since a prediction is a guess the pipeline checks, costly for accuracy. Caches and BTBs carry a tag because their payload must never be wrong. Two red pens on the notes' other examples:

The warm-up puzzle, resolved

Where did the information come from, if b2's own history was worthless? From b1, a few instructions earlier — and, for b1, from the previous b2: the global history register carries the last outcome, and in that program the last outcome is the value of d. A per-branch counter cannot see other branches; a correlating predictor sees them through one shift register — its whole advantage, and with the tables each context must train, its whole cost.

What the papers ask: counting accesses, initialisation and size

Three exam habits, all in the May 2025 midterm (notes pp. 538–541; quiz texts are the paper's, solutions the note author's, recomputed here).

Habit 1: count the executions of the branch, not the iterations

Quiz 5 gives a top-tested loop and a 1-entry 1-bit BHT initialised Taken, and asks how many BHT accesses and mispredictions, "assuming only the branch instruction accesses the BHT":

      ADDI R1, R0, 0
      ADDI R2, R0, 100
LOOP: BEQ  R1, R2, DONE
      ADD  R5, R5, R4
      ADDI R1, R1, 1
      J    LOOP
DONE:

The BEQ executes for R1 = 0, 1, …, 100 — the exit test is the 101st execution; the unconditional J does not touch the BHT. Outcomes: Not Taken 100 times, then Taken.

Table 6. The 1-bit entry on the midterm's top-tested loop: 101 accesses, 2 mispredictions.
accessR1predictionoutcomeresultbit after
10T (initial)NTmissNT
2 to 1001 to 99NTNT99 hitsNT
101100NTTmissT

Two mispredictions — one artificial (initialisation), one real (exit) — in 101 accesses; the bottom-tested loop of June 2025 executes its BNE five times for five iterations. The rule: branch at the bottom, N executions; test at the top, N + 1.

Habit 2: initialisation and size both change the count

Quiz 4 — "the number of mispredictions depends on the initialization and the size of the Branch History Table", true or false, motivate: true, for two reasons the motivation must name. Initialisation sets the start-up misses: a 2-bit entry started Strongly Not Taken misses twice before predicting a loop branch Taken (the 60 % of June 2025), started Strongly Taken it misses none. Size sets aliasing: no tags, so a small table lets unrelated branches overwrite each other's state (Fig. 4, right).

Any predictor table: state before each execution, prediction from it, outcome, hit or miss, update, shift — and the misprediction rate divides by the executions, warm-up included; the "state left in the BHT" is the state after the last update, one transition past the last row.

Habit 3: from accuracy to CPI

Quiz 6, third statement: "The introduction of dynamic branch predictors improves the CPI metric." True, by the formula the notes give on p. 541 — copy it onto your A4 sheet, since the course's example sheet has no branch formula at all.

\[ \text{CPI} = \text{CPI}_{\text{ideal}} + f_b \,(1 - a)\, P \]
\(f_b\)
branch frequency: fraction of executed instructions that are branches
\(a\)
prediction accuracy; \(1 - a\) is the misprediction rate
\(P\)
misprediction penalty in cycles: 1 in the course pipeline, where the branch is resolved in ID; 7 at least and about 11 on average on the Alpha 21264, around 20 on the deepest pipelines

A better predictor changes only \(a\); the penalty belongs to the pipeline — and that is why the percentage points matter: at \(f_b = 0.2\) the step from 90 % to 97 % accuracy is worth 0.014 CPI with \(P = 1\) and 0.28 with \(P = 20\).

The bridge to speculation

The June 2026 open question pairs "dynamic branch prediction" with "speculative execution". A prediction is only useful if the processor acts on it before the branch resolves — fetching, issuing, even executing the instructions after it. They must not touch registers or memory until the branch is known: their results wait in the reorder buffer; on a misprediction every entry younger than the branch is flushed, nothing architectural is undone, the PC is set right and fetch resumes. Lessons 5 and 6 decide how often that happens; lesson 17 builds the buffer.

Exam-style questions

Explain the main concepts of the correlating branch prediction technique and its benefits with respect to a standard Branch History Table.

Show model answer

A standard BHT predicts each branch from its own past only. A correlating (two-level) predictor also uses the outcomes of the branches executed just before it: an m-bit Global History Register, a shift register holding the last m outcomes (1 = Taken), selects one of 2^m tables of n-bit entries (1-bit, or the 2-bit states Strongly/Weakly Taken, Weakly/Strongly Not Taken), and the k low-order bits of the branch PC select the entry inside the selected table. Equivalently the GHR concatenated with the PC bits forms one index of m + k bits into a single table; gshare XORs the history with the PC bits instead, to spread the branches over the whole table and reduce aliasing. After the branch resolves, the entry is updated and the outcome is shifted into the GHR. The benefit: each (branch, recent context) pair has its own counter, so a branch whose outcome depends on previous branches — or on its own recent pattern, such as a loop exit — is predicted correctly after warm-up, where a per-branch counter flip-flops; H&P show a (2,2) predictor of 8 Kbit mispredicting less than a 2-bit BHT with unlimited entries. The cost is one shift register, 2^m × n × 2^k bits of tables, a longer warm-up (every context must be trained) and aliasing in the pattern table.

The wording is the fourth sub-point of Q2 of the January 2026 paper in the notes (p. 498); in the June 2026 format the same material sits inside the sub-point "dynamic branch prediction and speculative execution" of the 8-point question — about five lines either way. The examiner looks for the three mechanisms (history register, table selection, PC indexing), the update rule, the "separate counter per context" argument and one concrete benefit. Naming gshare's XOR earns the aliasing point; the January 2026 solution in the notes mentions "concatenating or XORing", so both words belong in the answer.

Consider the code BNE R1, R0, L1; ADDUI R1, R0, 1; L1: SUBI R3, R1, 1; BNE R3, R0, L2, where the first branch (b1) is taken if d = R1 is not 0 and the second (b2) is taken if d is not 1. The code is executed with d = 2, 0, 2, 0. (a) Fill the prediction table for a 1-bit predictor per branch, both entries initialised Not Taken. (b) Fill it for a (1,1) correlating predictor whose entries (one per branch and per value of the 1-bit global history) are initialised Not Taken, with the global history initialised Not Taken. (c) Count the mispredictions of each and explain the difference.

Show model answer

(a) 1-bit per branch (prediction = bit before, bit after = outcome):

db1 pred.b1 outcomeb2 pred.b2 outcome
2NT (miss)TNT (miss)T
0T (miss)NTT (miss)NT
2NT (miss)TNT (miss)T
0T (miss)NTT (miss)NT

(b) (1,1) predictor, entries written "if last NT / if last T":

dGH before b1b1 entriesb1 pred.b1 outcomeGH before b2b2 entriesb2 pred.b2 outcome
2NTNT/NTNT (miss)TTNT/NTNT (miss)T
0TT/NTNTNTNTNT/TNTNT
2NTT/NTTTTNT/TTT
0TT/NTNTNTNTNT/TNTNT

(c) 8 of 8 mispredicted against 2 of 8. The 1-bit entry always holds the previous outcome, and the outcomes alternate, so it is always one step behind. In the (1,1) predictor the global history bit selects a different entry for "after a taken branch" and "after a not-taken branch": in this code b1 not taken implies b2 not taken and b1 taken implies b2 taken, so each context converges after one miss; b1 is predicted from the previous b2, whose outcome encodes the previous value of d. Only the two cold entries miss.

Marks go to the correct shift of the history (it holds the last outcome, whichever branch produced it), to the two cold-entry misses being the only ones, and to the explanation in (c). A frequent slip: predicting b2 from b2's own previous outcome instead of from the entry the history selects.

A (2,2) correlating predictor has 1024 entries per table. How many bits of prediction storage does it use?

Show model answer
\[ 2^m \cdot n \cdot 2^k = 4 \cdot 2 \cdot 1024 = 8192 \text{ bits} \]

that is 1 KB; a plain 2-bit BHT with the same budget would have 4096 entries.

The m history bits multiply the tables, they do not add bits to an entry: 2^m tables, each of 2^k entries of n bits.

Consider the following assembly code executed by a processor with a 1-entry 1-bit Branch History Table initialised as Taken: ADDI R1, R0, 0; ADDI R2, R0, 100; LOOP: BEQ R1, R2, DONE; ADD R5, R5, R4; ADDI R1, R1, 1; J LOOP; DONE:. Assuming only the branch instruction accesses the BHT, how many accesses to the Branch History Table and how many mispredictions? (Single answer)

  • Answer 1: 101 accesses, 2 mispredictions The BEQ runs for R1 = 0 to 100: 101 executions; the initial Taken misses on the first (Not Taken), the exit misses on the last (Taken).
  • Answer 2: 100 accesses, 1 misprediction 100 counts the iterations, not the executions of the branch: the exit test is the 101st, and the initialisation costs a miss of its own.
  • Answer 3: 99 accesses, 1 misprediction The branch is executed for R1 = 0 as well as for R1 = 100; neither can be dropped.
  • Answer 4: 201 accesses, 2 mispredictions The unconditional J does not access the BHT by assumption; only the BEQ does.
Show model answer

Answer 1. Executions of BEQ: R1 = 0, 1, …, 100 → 101 accesses. Predict Taken at R1 = 0: outcome Not Taken, miss, bit → NT. R1 = 1 … 99: predict NT, outcome NT, 99 hits. R1 = 100: predict NT, outcome Taken, miss. Two mispredictions.

The midterm's quiz, recomputed. The two misses are the two changes of direction the 1-bit entry sees — one artificial (initialisation), one real (exit).

During the execution of a program, the number of mispredictions depends on the initialization and the size of the Branch History Table (BHT). True or False? Motivate your answer.

  • True Initialisation sets the start-up misses; size sets how many branches share an untagged entry (aliasing).
  • False Both matter: a loop branch started Strongly Not Taken misses twice before predicting Taken, and a small untagged table lets unrelated branches overwrite each other's state.
Show model answer

True. (1) Initialisation: the initial state of the entries decides whether the first executions of each branch are predicted right — a 2-bit entry initialised Strongly Not Taken mispredicts the first two taken executions of a loop branch, initialised Strongly Taken it mispredicts none; these start-up misses count in the misprediction rate. (2) Size: the BHT is indexed by the low-order PC bits without tags, so with a small table different branches map to the same entry (aliasing) and interfere, each spoiling the other's state; a larger table reduces the collisions.

Two separate reasons, each with its mechanism, is what the extra "motivate" point pays for. Adding that the counter type (1 bit or 2) and the program's patterns also matter does not hurt.

A loop of 10 iterations, whose body contains no other branch, is executed many times. Which predictor can reach 100 % accuracy on its loop branch after warm-up, exit included? (Single answer)

  • A 2-bit BHT with 4096 entries A counter holds a bias, not a pattern: it mispredicts the exit every time, 90 % at best.
  • A 2-bit BHT with unlimited entries Unlimited entries remove aliasing only; the exit is not an aliasing problem.
  • A two-level predictor with at least 9 bits of local history With h ≥ N − 1 = 9 every position in the pattern T×9 NT has its own history and its own counter; after warm-up all ten executions are predicted.
  • Always Taken Right nine times in ten, wrong at every exit: 90 %.
Show model answer

Answer 3. The exit is predictable because it comes at a fixed position; only a history long enough to distinguish "nine taken in a row" from the earlier positions can represent it, which is what a local (or, with no other branch in the body, global) history of at least 9 bits does. Counters of any size and static rules cannot.

The examiner is checking the difference between bias (what a counter stores) and pattern (what a history register stores).

Check yourself

In Table 2 the (1,1) predictor also fixes b1, although b1 comes first and nothing precedes it in the code. Why?

  • Because b1's own last outcome is in its entry That is the 1-bit scheme of Table 1, which missed all eight; the entry alone is always one step behind.
  • Because the global history holds the previous b2, whose outcome encodes the previous value of d The GHR records the last branch executed, whichever it was: before b1 that is b2, and b2's outcome tells which way d was last time, which is the opposite of this time.
  • Because the (1,1) predictor has twice the entries, so there is no aliasing There are only two branches; aliasing plays no role. The extra bit is context, not capacity.

With the notes' convention, after the outcomes Not Taken then Taken (in this order) a 2-bit GHR holds…

  • 10 That would be "last Not Taken, penultimate Taken": the reverse order.
  • 01 Shift left and insert the newest outcome on the right: 0 after Not Taken, then 1 after Taken gives 01 — "last Taken, penultimate Not Taken" (p. 69).
  • 11 Both bits 1 would mean both outcomes Taken.

A loop of 6 iterations is executed many times. Which predictors can learn its exit after warm-up? Select all that apply.

  • A 2-bit BHT, however large A counter cannot represent "after five taken comes a not-taken"; it mispredicts the exit every pass.
  • A two-level predictor with 5 bits of local history h = 5 ≥ N − 1: each position of T T T T T NT gets its own counter.
  • A (2,2) correlating predictor, the loop body containing no other branch Only 2 bits of history: the contexts before iterations 3, 4, 5 and 6 are all "11" and share one counter, which cannot be right for both T and NT.
  • A gshare predictor with 8 bits of history, the loop body containing no other branch With no other branch the global history is the loop branch's own, and 8 bits are more than the 5 needed.
  • Always Taken Static rules do not learn; the exit is missed every pass.

The question behind the question: bias (a counter) against pattern (a history). Only enough history bits capture a pattern, and global history only counts as "this branch's" when nothing else in the body dilutes it.

gshare XORs the history with the PC bits instead of concatenating them. What does that buy, with the same index width?

  • It removes aliasing completely XOR is still many-to-one: 2^2h (PC, history) pairs land on 2^h entries. The mixer's third task shows collisions returning with more contexts.
  • Every branch can reach every entry, and under the same history two different branches never share one Concatenation confines a branch to the 2^m entries of its partition and makes branches with equal low PC bits collide in every context; XOR uses all h bits of both.
  • It uses local history instead of global history gshare's history is global; mixing in the PC gives the branch's identity, not its own history.

Two branches share an entry of an untagged BHT; two addresses share a set of a tagged cache. In which case can the program compute a wrong result?

  • In the BHT case The polluted entry gives a wrong prediction, which the pipeline detects when the branch resolves; only cycles are lost.
  • In the cache case The tag comparison fails and the access is a miss: the right data is fetched from memory. That is a conflict miss, not corruption.
  • In neither A guess is checked at resolution, and a tag is checked on every access. Aliasing costs accuracy in the first structure and a miss in the second — never correctness.

In the arena the (2,2) predictor needs two passes of the H&P stream to become perfect, where the (1,1) needs one. What is the reason?

  • The (2,2) has four tables to fill instead of two More tables do mean more contexts, but each context of this stream is met once per pass; the number of tables does not add passes.
  • Its entries are 2-bit counters starting Strongly Not Taken, which need two Taken outcomes before they predict Taken Hysteresis: the first Taken moves an entry to Weakly Not Taken, which still predicts Not Taken; the second pass moves it to Weakly Taken. With counters started Weakly Taken the (2,2) matches the (1,1).
  • Its history register is updated one cycle late The widget updates every history register at resolution, in the same way for all predictors.

Inside a loop body, a branch is taken exactly when the branch three instructions earlier was taken. Which kind of history can see that?

  • Global history The GHR holds the outcomes of the last branches executed, whichever they were — the earlier branch is in it.
  • Local history A local register holds only this branch's own outcomes; the other branch never appears in it.
  • Neither: it needs a bigger 2-bit table Table size removes aliasing; it does not add context.

Key take-aways

What to watch and read