Theory test — Chapter 13¶
55 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 13 # interactive
./course quiz template 13 -o answers/ch13.yaml # or fill in a file ...
./course quiz grade 13 # ... and grade it
scope-ebb · set · 1 pt · 01-scopes-and-constant-foldingCFG with entry A and edges A→B, A→C, B→D, C→D, C→E, D→F, E→F. Which blocks form the
extended basic block rooted at A (Definition 13.1.1)?
scope-classify · mapping · 1 pt · 01-scopes-and-constant-foldingCFG: A→B, A→C, B→D, C→D. The expression a + b (operands defined in the entry block A) is
recomputed at four places. For each, give the smallest scope (local, superlocal, regional,
global) in which it is already available and can be replaced:
- r1: computed twice inside block C;
- r2: computed in A and again in B;
- r3: computed in A and again in D;
- r4: computed in B and in C (not in A) and again in D.
r1, r2, r3, r4fold-values · mapping · 1 pt · 01-scopes-and-constant-foldingWhat does pebble-constfold (exercise E1) turn each instruction into? Answer with the folded
constant (signed decimal, true/false for i1), poison, or unchanged.
- a:
add nsw i8 100, 100 - b:
shl i8 1, 8 - c:
sdiv exact i8 -7, 2 - d:
ashr i8 -7, 1 - e:
udiv i8 7, 0 - f:
icmp samesign ult i8 -1, 1 - g:
sdiv i8 -7, 2
a, b, c, d, e, f, gfold-fp · single · 1 pt · 01-scopes-and-constant-foldingA front end folds (1.0 + 1e16) - 1e16 in IEEE double with round-to-nearest-even.
What is the correctly folded result, and why must a folder not reassociate it?
- 1.0, because addition is associative
- 0.0, because 1 + 1e16 rounds to 1e16 (a tie between neighbours 2 apart, broken to the even significand); reassociating would give 1.0
- 2.0, because the spacing of doubles near 1e16 is 2
- It cannot be folded: the result depends on the host's rounding mode
peephole-trace · sequence · 1 pt · 02-peephole-enginesRun pebble-peephole (rules R1–R17 of exercise E2, FIFO worklist, Algorithm 13.2.4) on
define i8 @f(i8 %x) {
%a = sub nsw i8 %x, 3
%b = add nsw i8 %a, 5
%c = mul nsw i8 4, %b
%d = or i8 %c, 0
ret i8 %d
}
Give the names of the rules that fire, in order (e.g. sub-const add-add-const …).
instsimplify-contract · single · 1 pt · 02-peephole-enginesWhich transformation may InstSimplify perform, but not only InstCombine (Definition 13.2.3)?
mul i32 %x, 8→shl i32 %x, 3sub i32 %x, %x→0add i32 %x, %x→shl i32 %x, 1udiv i32 %x, 7→ a multiply-high and a shift
pattern-match-bind · mapping · 1 pt · 02-peephole-enginesWith Value *X, *Y; const APInt *C;, the pattern
match(I, m_Sub(m_Value(X), m_Shl(m_Value(Y), m_APInt(C)))) is applied to %r in
%s = shl i32 %q, 4
%r = sub i32 %p, %s
What are X, Y and C after a successful match? (Give value names without %, and C as a number.)
X, Y, Cfind-patternmatch · text · 1 pt · 02-peephole-enginesIn LLVM 23's llvm/include/llvm/IR/PatternMatch.h, which matcher do you use for "the same value
that an earlier m_Value(X) in this pattern bound", as in X + X? (Give the function name.)
dsl-decision-tree · sequence · 1 pt · 02-peephole-enginesUse the decision tree built in Lesson 13.2 §3 for the four add rules R1, R2, R9, R11
(nodes n0–n6; n0 tests the root, n1 position 1, n2/n3/n4 position 2, n5/n6 position 1.2).
Which nodes does the input add (add y, 5), 3 visit before reaching a leaf? (e.g. n0 n1 …)
matchpd-fp · single · 1 pt · 02-peephole-enginesGCC 15's match.pd rule for (minus @0 @0) folds x - x to 0 for int at -O2, but for
double only with options such as -ffinite-math-only. Why?
- Floating-point subtraction is not commutative
- For x = NaN or x = ±∞, x − x is NaN, not 0 (and under directed rounding the zero can be −0.0)
- genmatch cannot generate code for floating-point types
- x − x overflows for large doubles
superopt-shortest · number · 1 pt · 03-superoptimizationEnumerative superoptimization (Algorithm 13.3.3) over i4 with instruction set
\(I = \{\mathsf{add}, \mathsf{shl}\}\) and constant set \(K = \{1\}\) (operands: x, 1, earlier
results). What is the length of a shortest program computing \(5x \bmod 16\)?
gso-signum · single · 1 pt · 03-superoptimizationThe GNU superoptimizer finds Massalin's four-instruction branch-free signum
(addl d0,d0; subxl d1,d1; negxl d0; addxl d1,d1 on the 68020). What does it rely on to
accept a candidate, according to its README and Lesson 13.3?
- An SMT proof of equivalence for all 32-bit inputs
- Simulation on a set of test values only, so a result might (rarely) be wrong
- Exhaustive enumeration of all 2^32 inputs
- A Coq proof generated for each sequence
stoke-accept · number · 1 pt · 03-superoptimizationSTOKE (Algorithm 13.3.5) with \(\beta = 0.5\) proposes a rewrite whose cost is 3 higher than the
current one (\(\Delta = +3\)). With what probability is it accepted? (Two decimals.)
stoke-cost · number · 1 pt · 03-superoptimizationSTOKE's correctness term \(\mathrm{eq}(R; T)\) is the total number of differing output bits over
the tests (Algorithm 13.3.5). Target: \(s(x) = 3x \bmod 16\) on i4, tests \(T = \{1, 3, 5\}\).
Candidate \(R\): shl x, 1. What is \(\mathrm{eq}(R; T)\)?
cegis-iterations · number · 1 pt · 03-superoptimizationRun CEGIS (Algorithm 13.3.6) as in Lesson 13.3 §3, but for udiv i8 %x, 5: template
\(t_{C,s}(x) = \mathrm{trunc}((\mathrm{zext}_{16}(x) \cdot C) \gg s)\) with \(C < 2^9\),
\(8 \le s \le 16\); Synth returns the lexicographically smallest \((s, C)\) consistent with \(E\),
Verify the smallest counterexample; \(E\) starts as \(\{0\}\). How many times is Synth called?
souper-magic · mapping · 1 pt · 03-superoptimizationSouper's test infers udiv i16 %x, 515 as trunc((zext32(x) * 65155) >> 25). What does the same
method (equivalently, the smallest-shift magic number of Algorithm 13.7.7) give for
udiv i16 %x, 1000? Give the multiplier C and the total shift s.
C, sgeneralize-rule · single · 1 pt · 03-superoptimizationA superoptimizer found the concrete i8 rule (x >>u 2) << 2 → x & -4. Which generalization,
with \(C\) a symbolic shift amount \(0 \le C < N\), is valid for every \(C\) and width \(N\)
(Algorithm 13.3.8)?
(x >>u C) << C→x & ((1 << C) - 1)(x >>u C) << C→x & (-1 << C)(x >>u C) << C→x & -C(x >>u C) << C→x & ~C
infer-precondition · single · 1 pt · 03-superoptimizationFor the generalized rule icmp eq (and x, C1), C2 → false, which precondition is the
weakest precondition WP (Definition 13.3.7): sound, and true for every \((C1, C2)\) where the
rule is valid?
C2 != 0C1 != C2(C2 & ~C1) != 0C2 >u C1
canon-form · text · 1 pt · 04-canonicalization-and-rewritingWhat does pebble-peephole (R1–R17) leave of %r = icmp uge i8 7, %x? Write the resulting
instruction's right-hand side as icmp <pred> i8 %x, <constant>.
find-getcomplexity · mapping · 1 pt · 04-canonicalization-and-rewritingIn LLVM 23, InstCombiner::getComplexity (llvm/include/llvm/Transforms/InstCombine/InstCombiner.h)
ranks operands so that commutative instructions put the more complex one first. What does it
return for each value?
- a:
poison - b:
i32 5 - c: the argument
%x - d:
%n = sub i32 0, %x
a, b, c, dnewman · single · 1 pt · 04-canonicalization-and-rewritingA rule set is terminating, and every critical pair is joinable. What can you conclude?
- Nothing about confluence: local confluence never implies confluence
- It is confluent, so every input has a unique normal form (critical-pair lemma + Newman's lemma)
- It is confluent only if it also has no overlapping rules
- It terminates in polynomial time
measure-step · mapping · 1 pt · 04-canonicalization-and-rewritingThe termination measure of Theorem 13.4.9 is \(\mu = (I, S, A, M)\). For
%a = sub i8 %x, 3
%b = add i8 %a, 5
%c = mul i8 4, %b
ret i8 %c
\(\mu = (4, 2, 0, 0)\). Give \(I, S, A, M\) after R8 (sub-const) rewrites %a to add i8 %x, -3.
I, S, A, Mcritical-pair · single · 1 pt · 04-canonicalization-and-rewritingR2 (x op 0 → x) and R9 (add (add x, C1), C2 → add x, C1 + C2) overlap on
%r = add i32 %t, 0 with %t = add nsw i32 %y, 5 (the outer add has no flags). What are the two
results, and is the critical pair joinable?
- Both give
add nsw i32 %y, 5: joinable - R2 gives
%t(add nsw %y, 5), R9 givesadd i32 %y, 5without nsw: not joinable, although both refine the source - R2 gives
%y, R9 givesadd %y, 5: not joinable, and R2's result is wrong - They do not overlap, because R9 needs C2 ≠ 0
lvn-trace · mapping · 1 pt · 05-value-numbering-and-dagsRun local value numbering with the extensions of Algorithm 13.5.3 (commutativity, identities,
folding; i8) on this block (inputs a, b, c):
t1 = mul a, b
t2 = add c, 0
t3 = mul b, a
t4 = sub t1, t3
t5 = add t2, t4
t6 = add 3, 4
t7 = mul t6, a
t8 = add c, t4
For t2–t8, give what each is replaced by (a name or a constant), or kept.
t2, t3, t4, t5, t6, t7, t8lvn-flags · single · 1 pt · 05-value-numbering-and-dagsA block contains %a = add nsw i32 %x, %y and later %b = add i32 %x, %y. LVN replaces %b by
%a. What else must it do?
- Nothing: flags are not part of the key
- Drop
nswfrom%a(keep only the flags both instructions have) - Add
nswto every user of%b - Refuse the replacement: instructions with different flags are never equal
lvn-memory · mapping · 1 pt · 05-value-numbering-and-dagsLVN with memory versions (Algorithm 13.5.5, no alias analysis), block:
l1 = load p
t1 = add l1, a
store t1, q
l2 = load p
l3 = load q
l4 = load p
x = call g(a) ; memory(none)
y = call g(a) ; memory(none)
call f ; may write memory
l5 = load q
For l2, l3, l4, y, l5 give the value each is replaced by, or kept.
l2, l3, l4, y, l5find-earlycse-generation · text · 1 pt · 05-value-numbering-and-dagsIn LLVM 23's llvm/lib/Transforms/Scalar/EarlyCSE.cpp, what is the name of the unsigned member
of the EarlyCSE class that plays the role of Algorithm 13.5.5's memory version?
dag-nodes · number · 1 pt · 05-value-numbering-and-dagsBuild the DAG (Algorithm 13.5.7) of this non-SSA block:
x = a * b
y = b * a
a = x + c
z = a * b
w = y + c
How many nodes does it have in total (leaves and interior nodes)?
find-csemap · text · 1 pt · 05-value-numbering-and-dagsIn LLVM 23's llvm/include/llvm/CodeGen/SelectionDAG.h, what is the name of the
FoldingSet<SDNode> member through which SelectionDAG::getNode finds an existing identical
node instead of creating a new one?
dce-order · sequence · 1 pt · 05-value-numbering-and-dagsRun Algorithm 13.5.10 with \(W\) a stack, initially the trivially dead instructions pushed in
program order, and operands pushed in operand order:
%a = add i32 %x, 1
%b = mul i32 %a, 2
%c = sub i32 %b, %a
%d = xor i32 %x, 5
%e = add i32 %x, 7
ret i32 %e
In which order are instructions deleted?
dce-cycle · single · 1 pt · 05-value-numbering-and-dagsIn a loop, %s = phi i32 [0, %entry], [%s.next, %loop] and %s.next = add i32 %s, 1 have no other
users. What does worklist DCE (pebble-dce, LLVM dce) do with them?
- Deletes both: they compute nothing observable
- Keeps both: each has a use (the other), so neither is trivially dead; removing dead cycles needs aggressive (mark-and-sweep) DCE
- Deletes the add and keeps the phi
- Reports an error: SSA values must not form cycles
ranks-map · mapping · 1 pt · 06-reassociationRanks of Definition 13.6.2 (blocks numbered in reverse postorder from 1; successors in listed
order) for
define i32 @f(i32 %a, i32 %b, i32 %n, ptr %p) {
entry:
br label %loop
loop:
%i = phi i32 [ 0, %entry ], [ %i1, %body ]
%s = phi i32 [ 0, %entry ], [ %s1, %body ]
%c = icmp slt i32 %i, %n
br i1 %c, label %body, label %exit
body:
%v = load i32, ptr %p
%t1 = add i32 %v, 5
%t2 = add i32 %t1, %i
%t3 = add i32 %t2, %a
%t4 = add i32 %t3, 3
%s1 = add i32 %t4, %b
%i1 = add i32 %i, 1
br label %loop
exit:
ret i32 %s
}
Give the ranks of a, i, v, t2, i1.
a, i, v, t2, i1reassoc-order · sequence · 1 pt · 06-reassociationFor the same function (ranks-map), pebble-reassociate rebuilds the tree rooted at %s1
(Algorithm 13.6.3). List the operands of the new left-deep chain from the innermost add
outwards, with the folded constant last (e.g. x y 3).
tree-height · number · 1 pt · 06-reassociationWhat is the minimal height of a binary tree of adds over 11 leaves that are all available at
time 0 (Corollary 13.6.11)?
thr-greedy · number · 1 pt · 06-reassociationAlgorithm 13.6.5 (repeatedly combine the two earliest-ready values) on six leaves with arrival
times (0, 0, 0, 0, 0, 3). What is the completion time of the resulting tree?
naf-trace · sequence · 1 pt · 07-strength-reduction-and-divisionCompute NAF(119) with Algorithm 13.7.2. Give the digits least significant first
(e.g. 1 0 -1 …).
mul-lea · single · 1 pt · 07-strength-reduction-and-divisionOn x86-64, clang-23 -O2 compiles x * 45 into two lea instructions. Which decomposition is
that?
- 45 = 2⁶ − 2⁴ − 2² + 1 (the NAF): three
leas would be needed - 45 = 9 × 5:
lea (x, x, 8)computes 9x, thenlea (y, y, 4)computes 5y - 45 = 32 + 13: a shift plus one
lea - 45 = 48 − 3: one
leaand onesub
magic-7 · mapping · 1 pt · 07-strength-reduction-and-divisionUnsigned udiv i8 %x, 7 by Algorithm 13.7.7 (smallest ℓ by the exact criterion). Give ℓ, the
magic m, and whether the add fix-up is needed (yes/no).
l, m, addmagic-criterion · mapping · 1 pt · 07-strength-reduction-and-divisionFor udiv i8 %x, 98, give the smallest post-shift ℓ allowed by Granlund–Montgomery's
sufficient condition \(e \le 2^\ell\) (Theorem 13.7.4) and by the exact criterion
\(e \cdot n_c < 2^{N+\ell}\) (Theorem 13.7.6).
gm, exactfind-divconst · text · 1 pt · 07-strength-reduction-and-divisionIn LLVM 23's llvm/include/llvm/Support/DivisionByConstantInfo.h, which bool field of
UnsignedDivisionByConstantInfo says that the magic number needs N + 1 bits, so the code uses
the add fix-up?
refine-closure · set · 1 pt · 08-refinement-ub-and-fast-mathSource: %f = freeze i2 %x; %r = and i2 %f, 1; ret i2 %r. On the input %x = poison, which
outcomes may a refining target produce? Choose among 0, 1, 2, 3, poison, UB.
refine-direction · single · 1 pt · 08-refinement-ub-and-fast-mathWhich rewrite is a refinement (source ⇒ target) on i8?
add i8 %x, 1⇒add nsw i8 %x, 1add nsw i8 %x, 1⇒add i8 %x, 1udiv i8 %x, %y⇒udiv i8 %x, 1when you do not know y%r = add i8 %x, %y⇒%r = poison
freeze-dup · single · 1 pt · 08-refinement-ub-and-fast-mathSource A: %f = freeze i4 %x; %g = freeze i4 %x; %r = sub i4 %f, %g.
Source B: %f = freeze i4 %x; %r = sub i4 %f, %f. Which of these rewrites are valid?
- A ⇒
ret 0is valid, and B ⇒ A is valid - A ⇒
ret 0is valid, but B ⇒ A is invalid - A ⇒
ret 0is invalid, but B ⇒ A is valid - Both are invalid
select-branch · single · 1 pt · 08-refinement-ub-and-fast-mathA pass turns %r = select i1 %c, i32 %x, i32 %y into br i1 %c, … with a phi. What happens for
%c = poison, and what repairs it?
- Nothing changes: both give poison
- The select gives poison but the branch on poison is immediate UB; branching on
freeze %crepairs it - Both are UB, so the rewrite is valid
- The branch picks the true side deterministically; no repair is needed
flags-r9 · mapping · 1 pt · 08-refinement-ub-and-fast-mathR9 (add-add-const) on i8, where both adds carry nsw nuw. Which flags does the result keep?
(Give a set per case; [] for none.)
- A:
(x + 100) + 27 - B:
(x + 100) + 28 - C:
(x + -1) + -1
A, B, Cflags-mul-min · single · 1 pt · 08-refinement-ub-and-fast-mathR10 (mul-pow2) on %r = mul nuw nsw i8 %x, -128. What does pebble-peephole produce?
shl nuw nsw i8 %x, 7shl nuw i8 %x, 7shl nsw i8 %x, 7- Nothing: -128 is not a power of two
find-flags-dropped · text · 1 pt · 08-refinement-ub-and-fast-mathWhich llvm::Instruction member function (declared in llvm/include/llvm/IR/Instruction.h)
clears all poison-generating flags of an instruction, the fallback the InstCombine guide
recommends when keeping a flag would need a proof?
fp-nsz · single · 1 pt · 08-refinement-ub-and-fast-mathWhich floating-point rewrite is valid without any fast-math flag in LLVM IR?
fadd double %x, 0.0⇒%xfadd double %x, -0.0⇒%xfmul double %x, 0.0⇒0.0fsub double %x, %x⇒0.0
fp-contract · single · 1 pt · 08-refinement-ub-and-fast-mathclang-23 -O2 compiles double f(double a, double b, double c) { return a * b + c; } to a call of
llvm.fmuladd.f64, but not with -ffp-contract=off. What does fmuladd permit?
- Nothing: it is always computed as a fused multiply-add with one rounding
- The back end may fuse the multiply and add into one rounding (an FMA) or keep two roundings; clang's C default
-ffp-contract=onallows this contraction within an expression - Arbitrary reassociation of a * b + c
- Ignoring NaNs and infinities
smt-query · single · 1 pt · 09-verifying-transformationsAlive2's refinement check for a source with nondeterminism (e.g. freeze) is an SMT query of the
form of Definition 13.9.2. Which quantifier structure does it have?
- ∀ inputs ∃ source choices: the values are equal
- ∃ inputs, poison masks and target choices, ∀ source choices: the source is defined and the target's outcome is not allowed; unsat means valid
- ∃ inputs: source ≠ target; sat means valid
- ∀ inputs ∀ choices: source = target
alive-checks · single · 1 pt · 09-verifying-transformationsalive-tv checks %r = add nsw i8 %x, %x ⇒ %r = shl nuw i8 %x, 1. Which part of the
refinement check (Algorithm 13.9.3: UB, then poison, then value) fails?
- UB: the target has undefined behavior
- Poison: the target is poison where the source is not (e.g. x = −64: source −128, target poison)
- Value: the two compute different numbers
- None: the rewrite is valid
bounded-width · number · 1 pt · 09-verifying-transformationsThe width-generic rule and iN %x, 1023 ⇒ %x is checked by bounded enumeration. What is the
smallest width N at which it is invalid?
bounded-inputs · number · 1 pt · 09-verifying-transformationsHow many inputs does ch13-rewrite-check (lab Part B) enumerate for functions with arguments
(i4 %x, i3 noundef %y, i2 %z)?
tv-vs-verified · multi · 1 pt · 09-verifying-transformationsWhich statements about translation validation (TV, e.g. Alive2 run after each opt pass) and a
verified compiler (CompCert) are true?
- TV checks one run of the compiler on one input program; a verified compiler's proof covers every run
- TV may answer 'unknown' (timeouts, unsupported features); CompCert either compiles with a guarantee or fails
- TV requires the optimizer itself to be proved correct
- Csmith found no wrong-code bugs in the verified parts of CompCert
compcert-theorem · single · 1 pt · 09-verifying-transformationsWhat does CompCert's main correctness theorem (transf_c_program_correct) state?
- Every C program compiles successfully
- If compilation succeeds, there is a backward simulation: every behavior of the generated assembly is a behavior of the source C program (for programs without undefined behavior)
- The generated code is as fast as GCC -O2
- The assembly and the C program have identical sets of behaviors, including undefined ones