Theory test — Chapter 8¶
41 questions; you pass at 80 %. This page shows the questions only: take the test in the terminal, where every answer is graded and explained.
./course quiz 8 # interactive
./course quiz template 8 -o answers/ch08.yaml # or fill in a file ...
./course quiz grade 8 # ... and grade it
tac-count-quads · number · 1 pt · 01-linear-irsAlgorithm 8.1.4 translates the two Tiny statements
x = (a + b) * c - d;
y = a && b;
into three-address code (each statement computes straight into its variable). How many
TAC instructions does it emit for these two statements (labels are not instructions)?
triples-reorder · number · 1 pt · 01-linear-irsA triple table: (0) add a, b; (1) sub a, b; (2) mul (0), (1); (3) add (2), c.
You exchange the execution order of triples 0 and 1 by physically moving them in the
table (plain triples, no indirection). How many argument references must be rewritten?
stack-heights · sequence · 1 pt · 01-linear-irsGive the stack height at entry to each instruction (positions 0–8) as computed by
Algorithm 8.1.6:
0 push 2
1 load x
2 add
3 load y
4 jz L
5 push 1
6 store x
L:
7 load x
8 ret
verify-reject · single · 1 pt · 01-linear-irsWhich of these stack programs does height verification (Algorithm 8.1.6) reject?
push 1; jz L; push 5; L: push 7; add; retpush 1; push 2; add; retpush 0; jz L; push 3; ret; L: push 4; retload x; neg; ret
stack-to-register-count · number · 1 pt · 01-linear-irsThe stack code of (a + b) * (c - d) has 7 instructions. How many register
instructions remain after Algorithm 8.1.9 and copy propagation, if leaves may be
used directly as operands (as in Lua's RK operands)?
lua-rk · single · 1 pt · 01-linear-irsIn Lua 5.4's listing, what does MODK 3 2 0 (with constant 0 = 3) compute?
- R[3] := R[2] % K[0], i.e. R[3] := i % 3 when R[2] holds i
- R[3] := K[2] % R[0]
- push R[2] % 3 on the operand stack
- R[2] := R[3] % 0
leaders-listing · set · 1 pt · 02-cfgs-and-ordersGive the leaders (instruction numbers) of this listing:
0 i = 0
loop:
1 c = lt i, 10
2 ifz c goto done
3 r = rem i, 2
4 if r goto odd
5 s = add s, i
6 goto next
odd:
7 s = sub s, 1
next:
8 i = add i, 1
9 goto loop
done:
10 if s goto out
11 s = 1
out:
12 return s
leader-unreferenced-label · single · 1 pt · 02-cfgs-and-ordersA label U: precedes instruction 6, but no instruction jumps to U, and instruction 5 is x = add x, 1. Is instruction 6 a leader?
- No: only jump targets and instructions after jumps start blocks, so 5 and 6 stay in one block
- Yes: every labeled instruction starts a basic block
- Only if 6 is a jump
- It depends on the DFS order
critical-edges-set · set · 1 pt · 02-cfgs-and-ordersCFG (successors in order): A→B, C; B→D, E; C→E; D→F; E→F, B; F (exit).
Which edges are critical? Write them as X->Y.
split-count · number · 1 pt · 02-cfgs-and-ordersAfter splitting every critical edge of the CFG of the previous question (6 blocks), how many blocks does the CFG have?
llvm-where-critical · text · 1 pt · 02-cfgs-and-ordersFind where LLVM does it: which function in llvm/lib/Analysis/CFG.cpp (LLVM 23.1.2) decides whether the successor edge of a terminator is critical? (name only)
rpo-sequence · sequence · 1 pt · 02-cfgs-and-ordersCFG (successors visited in the listed order): A→B, C; B→D, E; C→E; D→F; E→F, B; F.
Give the reverse postorder of a DFS from A.
llvm-where-rpo · text · 1 pt · 02-cfgs-and-ordersFind where LLVM does it: which class template in llvm/include/llvm/ADT/PostOrderIterator.h iterates a function's blocks in reverse postorder?
hir-desugar-while · single · 1 pt · 03-trees-and-dagsWhat does rustc's AST-to-HIR lowering turn while c { b } into?
loop { if c { b } else { break } }if c { loop { b; if !c { break } } }- a MIR
gotoloop - nothing:
whileis kept in HIR
ast-vs-dag-size · mapping · 1 pt · 03-trees-and-dagsLet e0 = a and e(k+1) = e(k) + e(k). For e3, give the number of nodes of its syntax
tree and of its expression DAG.
tree, daglvn-redundant · set · 1 pt · 03-trees-and-dagsRun local value numbering (Algorithm 8.3.5; add and mul are commutative) on this block.
Which instructions (numbers) are redundant?
0 t1 = add a, b
1 t2 = mul t1, c
2 t3 = add b, a
3 a = sub a, 1
4 t4 = add a, b
5 t5 = mul t3, c
6 t6 = add t2, t5
lvn-nodes · number · 1 pt · 03-trees-and-dagsHow many nodes does the DAG of the block in the previous question have (leaves plus operator nodes)?
llvm-where-earlycse-commutative · text · 1 pt · 03-trees-and-dagsFind where LLVM does it: llvm/lib/Transforms/Scalar/EarlyCSE.cpp hashes instructions through a small wrapper struct whose hash swaps the operands of commutative binary operators. What is the struct called?
phi-parallel-swap · mapping · 1 pt · 04-ssa-and-block-argumentsBlock B1 starts with a1 = phi [1, B0], [b1, B2] and b1 = phi [2, B0], [a1, B2], and B2
branches back to B1 without changing anything. With the parallel-copy semantics of
Definition 8.4.2, what are a1 and b1 when control enters B1 for the third time
(B0→B1, then B2→B1 twice)?
a1, b1pruned-phi-count · number · 1 pt · 04-ssa-and-block-argumentsThe TAC of gcd.tiny is
a = 1071
b = 462
L.0:
t.0 = ne b, 0
ifz t.0 goto L.1
t = rem a, b
a = b
b = t
goto L.0
L.1:
return a
How many phi functions (block parameters) does pruned, minimal SSA of this listing have?
phi-to-args-mapping · mapping · 1 pt · 04-ssa-and-block-argumentsIn phi form, block J has x = phi [x1, T], [x0, E] and y = phi [5, T], [y0, E], and its
predecessors are T and E. In block-argument form, J becomes J(x, y). What argument lists
do the branches pass? Write each list as a b.
T, Emulti-edge-args · single · 1 pt · 04-ssa-and-block-argumentsBlock B0 ends with cbr c, B1(1), B1(2) and B1 is B1(x): ret x. What happens when you convert to phi form?
- It cannot be converted directly: a phi has one entry per predecessor block, so one of the two edges must be split first
- It becomes
x = phi [1, B0], [2, B0], which LLVM accepts - It becomes
x = phi [1, B0], since the second edge is redundant - Block arguments and phis are identical, so nothing special happens
mlir-where-phi · text · 1 pt · 04-ssa-and-block-argumentsFind where LLVM does it: in mlir/lib/Target/LLVMIR/ModuleTranslation.cpp (LLVM 23.1.2), which function fills in the LLVM phi nodes created for MLIR block arguments?
gcm-late-placement · single · 1 pt · 05-graph-irsIn a sea-of-nodes graph, the floating node x = mul n, 3 has the function parameter n as
its only input (early block: Start) and is used only inside the loop body, which is at
loop depth 1. Where does global code motion (Algorithm 8.5.3) place it?
- In Start (or the latest block of loop depth 0 that dominates the loop), outside the loop
- In the loop body, next to its use
- In the loop header
- Nowhere: floating nodes are never placed
son-why-v8-left · multi · 1 pt · 05-graph-irsWhich reasons does V8's 2025 blog give for replacing TurboFan's sea of nodes with the CFG-based Turboshaft?
- In JavaScript most nodes end up on the effect or control chain, so the graph mirrors a CFG anyway
- The graphs are hard to inspect and debug
- Poor cache locality and slow compilation; the CFG compiler halved compile time
- Sea of nodes cannot represent loops
pdg-control-dep · mapping · 1 pt · 05-graph-irsCFG: A→B, E; B→C, D; C→D; D→E; E (exit). For B, C and D, give the set of blocks it is
control dependent on (Definition 8.5.4).
B, C, Drvsdg-theta · single · 1 pt · 05-graph-irsHow does an RVSDG represent a while loop?
- As a θ node whose single acyclic region computes the next values and a repeat predicate
- As a back edge between two γ nodes
- As a cycle of value edges in the top-level region
- As a phi node with a Loop control input
egraph-classes · number · 1 pt · 05-graph-irsAn e-graph holds exactly the terms g(f(a)) and g(f(b)) (6 e-classes: a, b, f(a), f(b),
g(f(a)), g(f(b))). You merge the classes of a and b and then rebuild. How many e-classes
remain?
egraph-extraction · single · 1 pt · 05-graph-irsAfter saturation, the root e-class contains a, (* e0 e7) and (/ e2 e1). Extraction with the cost function 'number of nodes' returns which term?
- a
- (/ (* a 2) 2), the input, because extraction prefers the original term
- (* a 1)
- It is undefined, because the e-graph is cyclic
cps-admin-redexes · number · 1 pt · 06-functional-irsHow many administrative redexes does the naive (Plotkin-style) CPS transform of (x + y) * z create (Definition 8.6.2)?
cps-tail-calls · single · 1 pt · 06-functional-irsWhich statement about a program in continuation-passing style is true?
- Every call is a tail call; results are passed to continuations instead of being returned
- Continuations must be first-class closures, so CPS always allocates on the heap
- CPS cannot express loops
- CPS programs need phi functions at joins
anf-join-why · single · 1 pt · 06-functional-irsWhy does the lab's ANF use join points for if statements?
- Without them, the code after the if must be copied into both branches, which grows exponentially for a chain of ifs
- Because ANF forbids nested lets
- Because
ifcannot appear in tail position in ANF - To make variables mutable
anf-size-chain · number · 1 pt · 06-functional-irsA program is a chain of 5 statements if c_i {} else {} followed by return x. Translated
to ANF without join points (the rest is copied into both branches of each if), how many
copies of return x does the term contain?
kelsey-nesting · mapping · 1 pt · 06-functional-irsCFG in block-argument SSA: A→B; B→C, F; C→D, E; D→B; E→B; F returns. In Kelsey's
translation (Algorithm 8.6.7), inside which block's term is each block's join point
defined? Give the enclosing block for B, C, D, E and F.
B, C, D, E, Fscope-is-dominance · single · 1 pt · 06-functional-irsIn the SSA ≡ ANF correspondence (Theorem 8.6.12), which SSA rule becomes "every name is used inside its lexical scope"?
- Every definition dominates its uses
- Every name is defined exactly once
- Phi operands must be constants
- Every block ends in exactly one terminator
mlir-partial-conversion · single · 1 pt · 07-multi-level-and-pipelinesYou run mlir-opt euler1.mlir --convert-to-llvm without --convert-scf-to-cf first. What does the output contain?
- llvm operations, plus the scf.for / scf.if / scf.yield operations, which no pattern converted (a partial conversion)
- An error: scf operations are illegal
- Only llvm operations: --convert-to-llvm lowers every dialect
- Only the original arith and scf operations
mlir-where-conversion · text · 1 pt · 07-multi-level-and-pipelinesFind where LLVM does it: in mlir/lib/Transforms/Utils/DialectConversion.cpp (LLVM 23.1.2), which function runs a conversion that may leave legal-but-unconverted operations in place?
pipeline-match · mapping · 1 pt · 07-multi-level-and-pipelinesName the main mid-level IR (one word) that each compiler runs its mid-end analyses on: rustc (where the borrow checker runs), swiftc, gcc (tree optimizers).
rustc, swiftc, gccmir-asserts-count · number · 1 pt · 07-multi-level-and-pipelinesrustc at opt-level 0 turns the Rust running example (if i % 3 == 0 || i % 5 == 0 { s += i; } i += 1; inside the loop) into MIR. Using Algorithm 8.7.6 and Rust's rules for %
(a check for division by zero and one for MIN % -1), how many assert terminators does the
function's MIR contain?
pir-phis-gcd · number · 1 pt · 07-multi-level-and-pipelinesThe hand-written PIR gcd of Lesson 8.7 is lowered with one alloca per local and then sroa. How many phi instructions does the resulting LLVM function @gcd have?
pir-not-ssa · single · 1 pt · 07-multi-level-and-pipelinesWhich statement about PIR is true?
- PIR locals are mutable storage; SSA is built by the shared back end (alloca + mem2reg, and your Ch 16 pass)
- PIR is in SSA form with block arguments, like SIL
- PIR has phi functions, like LLVM IR
- PIR has no control-flow graph; it is a tree