Skip to content

Theory test — Chapter 11

51 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 11                                   # interactive
./course quiz template 11 -o answers/ch11.yaml  # or fill in a file ...
./course quiz grade 11                             # ... and grade it
Question 1 sdt-temporaries · mapping · 1 pt · 01-ssa-generation-strategies

The reference lowering (spec §15) compiles let x = a * (b + 1); (all int) in a function
with parameters a, b. The final result is assigned straight to x (destination-driven).
How many unnamed temporaries does it create, and how many assert statements?

Keys: temporaries, asserts
Answer format: one value per key
Question 2 mem2reg-phi-blocks · set · 1 pt · 01-ssa-generation-strategies

A front end lowered a loop by syntax-directed translation with one alloca for x:

B0: store 0 -> x;           br H
H:  br (i < n), Body, X
Body: br c, T, E
T:  x = x + 1  (load, add, store);  br J
E:  br J
J:  br H
X:  use x

x is stored in B0 and T. In which blocks does mem2reg place a phi for x (pruned SSA:
iterated dominance frontier, restricted to blocks where x is live on entry)?

Answer format: items separated by commas or spaces, e.g. {a, b}
Question 3 llvm-where-mem2reg-livein · text · 1 pt · 01-ssa-generation-strategies

In llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp (LLVM 23.1.2), which function computes
the blocks where an alloca is live on entry, so that PromoteMem2Reg::run places only pruned
phis? Give the function name.

Answer format: a short answer
Question 4 braun-trivial-phi · single · 1 pt · 01-ssa-generation-strategies

During on-the-fly SSA construction a loop header gets %p = phi [%a, %entry], [%p, %latch]
when it is sealed. What happens to it (Algorithm 11.1.9)?

  1. It stays: a phi in a loop header is never trivial
  2. It is replaced by %a, because its only operand other than itself is %a
  3. It is replaced by poison, because one operand is the phi itself
  4. It becomes an incomplete phi until the latch is sealed
Answer format: one letter
Question 5 braun-phi-count · mapping · 1 pt · 01-ssa-generation-strategies

Braun et al.'s construction (Algorithm 11.1.9) generates var i = 0; while i < n { i = i + 1; }
where n is a parameter. The header is still unsealed when its condition reads i and n.
How many phis are created in the header, and how many remain after sealing and
trivial-phi removal?

Keys: created, remaining
Answer format: one value per key
Question 6 jc-targets · mapping · 1 pt · 02-short-circuit-lowering

if (p || q) && !(r && s) { T } else { F } is compiled with jumping code (Algorithm 11.2.2;
each atom is one comparison and one conditional branch; a subexpression's block is named
after its first atom). Give each atom's true target and false target (an atom or T/F).

Keys: p-true, p-false, q-true, q-false, r-true, r-false, s-true, s-false
Answer format: one value per key
Question 7 jc-blocks-edges · mapping · 1 pt · 02-short-circuit-lowering

For the jumping code of (p || q) && !(r && s), how many basic blocks (entry and the T and F
blocks included, no join) and how many CFG edges does it have?

Keys: blocks, edges
Answer format: one value per key
Question 9 bool-values-ops · mapping · 1 pt · 02-short-circuit-lowering

(p && (q || !r)) || s is compiled with the boolean-value strategy (Algorithm 11.2.6), one
instruction per operator node. How many icmp, and, or and not (xor) instructions?

Keys: icmp, and, or, not
Answer format: one value per key
Question 10 llvm-where-fold-branch · text · 1 pt · 02-short-circuit-lowering

In llvm/lib/Transforms/Utils/SimplifyCFG.cpp, which function folds a conditional branch into a
predecessor that branches to a common destination, turning jumping code into an or/and of
the conditions (the %or.cond of Lesson 11.2)?

Answer format: a short answer
Question 11 jt-density · number · 1 pt · 03-switch-lowering

A switch on x has eight cases 1, 2, 3, 5, 8, 13, 21, 34, each to a different destination.
LLVM (-O2, density threshold 10 %) lowers it as one jump table. How many entries does the
table have?

Answer format: a number
Question 12 jt-range-check · single · 1 pt · 03-switch-lowering

A jump table covers values min … max. Which single check sends every other value of x (signed
i64) to the default?

  1. icmp sgt x, max
  2. icmp ugt (sub x, min), (max − min)
  3. icmp slt x, min and icmp sgt x, max, both needed
  4. icmp ugt x, max
Answer format: one letter
Question 13 bst-depth · number · 1 pt · 03-switch-lowering

A sparse switch with 13 cases is lowered as a balanced binary search over the sorted values
(Algorithm 11.3.4). By Theorem 11.3.5, at most how many comparisons execute on any path?

Answer format: a number
Question 14 bst-signed · single · 1 pt · 03-switch-lowering

A switch on a signed i64 has cases −5, −1, 3 and 7. The tree first compares with the median
pivot 3 (x < 3 goes left). Which comparison is correct?

  1. icmp ult x, 3
  2. icmp slt x, 3
  3. either: the case values are sorted
  4. icmp ule x, 3
Answer format: one letter
Question 15 bits-mask · mapping · 1 pt · 03-switch-lowering

LLVM lowers switch x { 1, 3, 5 → A; 2, 6 → B; default → D } with bit tests. Because 0 < min
and max < 64, it skips the subtraction (LowBound = 0, Lesson 11.3 §6) and tests 1 << x.
Give the two masks in decimal.

Keys: A, B
Answer format: one value per key
Question 16 bits-suitable · multi · 1 pt · 03-switch-lowering

Which of these switches does LLVM at -O2 lower (entirely) as one bit-test cluster?

  1. 1, 2, 3 → A
  2. 0, 10, 20 → A; 30, 40 → B
  3. 0, 100 → A; 200 → B
  4. 0 → A; 1 → B; 2 → C; 3 → D; 5 → A; 7 → B
Answer format: letters, e.g. a, c
Question 17 llvm-switch-clusters · mapping · 1 pt · 03-switch-lowering

LLVM lowers the switch with cases 0, 1 → A; 2, 3 → B; 5 → C; 100 → A; 101 → B; 102 → C;
103 → A; 1000 → B (Algorithm 11.3.7: rangeify, jump-table dynamic program with at least 4
clusters per table, bit tests). How many clusters does the final partition have, and how many
of them are jump tables and bit-test clusters?

Keys: clusters, tables, bits
Answer format: one value per key
Question 18 llvm-where-jt-vs-bits · text · 1 pt · 03-switch-lowering

In llvm/lib/CodeGen/SwitchLoweringUtils.cpp, which SwitchLowering member function refuses to
build a jump table for a range that bit tests could handle? (It asks
TargetLowering::isSuitableForBitTests.)

Answer format: a short answer
Question 19 lvalue-order · mapping · 1 pt · 04-places-values-and-loops
fn bump(k: &mut int) -> int { k = k + 1; return k; }
...
var xs = [0, 0, 0, 0];
var k = 2;
xs[k] = bump(&mut k) * 10;

After the assignment, what are xs[2], xs[3] and k?

Keys: xs2, xs3, k
Answer format: one value per key
Question 20 lvalue-copies · mapping · 1 pt · 04-places-values-and-loops

With bump as above and var x = 5;, the reference lowering compiles print(x + bump(&mut x) + x);.
How many times does it copy x into a temporary before the call (materialization,
Definition 11.4.2), and what does it print?

Keys: copies, prints
Answer format: one value per key
Question 21 rotate-branch-count · mapping · 1 pt · 04-places-values-and-loops

A loop runs n = 5 iterations. Count the conditional branches and unconditional jumps executed by
the loop's control flow in the top-tested form and in the rotated form (Theorem 11.4.8).

Keys: top-condbr, top-jumps, rotated-condbr, rotated-jumps
Answer format: one value per key
Question 22 rotate-preheader · single · 1 pt · 04-places-values-and-loops

Why does loop rotation copy the test in front of the loop (the guard) instead of just moving it
to the bottom?

  1. To give the loop a preheader for LICM, the copy itself is optional
  2. Because a do-while runs the body at least once, and the original loop may run zero times
  3. Because LLVM requires every loop to have two exits
  4. To keep the loop in LCSSA form
Answer format: one letter
Question 23 llvm-where-rotate-limit · mapping · 1 pt · 04-places-values-and-loops

In llvm/lib/Transforms/Scalar/LoopRotation.cpp (LLVM 23.1.2), what is the command-line option
that bounds the size of a header loop-rotate will duplicate, and its default?

Keys: option, default
Answer format: one value per key
Question 24 abi-direct-rust · single · 1 pt · 05-aggregates-and-the-abi

rustc compiles fn mixed(m: Mixed) -> Mixed for struct Mixed { a: i32, b: f64 } with the Rust
ABI (Lesson 11.5 box). How is m passed?

  1. As a pointer to a caller copy
  2. byval, on the stack
  3. As its two fields directly, double %0, i32 %1 (reordered, double first)
  4. Coerced to { i32, double } like Clang
Answer format: one letter
Question 25 abi-direct-regs · mapping · 1 pt · 05-aggregates-and-the-abi

Continuing abi-direct-rust on x86-64: which registers carry the two scalar arguments
double %0, i32 %1, and which registers carry the returned { double, i32 }?

Keys: arg-double, arg-int, ret-double, ret-int
Answer format: one value per key
Question 26 sret-noalias · single · 1 pt · 05-aggregates-and-the-abi

Pebble's code generator marks the hidden result pointer ptr noalias sret(%Pair). Why is
noalias correct?

  1. Because Pebble has no pointers
  2. Because the caller always passes fresh storage (%call.result) and copies it to the destination after the call, so nothing else the callee can reach points to it
  3. Because LLVM requires noalias on every sret parameter
  4. Because the callee never reads its parameters
Answer format: one letter
Question 27 sret-steps · sequence · 1 pt · 05-aggregates-and-the-abi

Order the caller's and callee's steps for q = swap(p) with p, q: Pair under Pebble's
convention (Algorithm 11.5.2). Steps: copy-arg (memcpy p into a temporary),
call (call swap with %call.result and the temporary), callee-writes (swap writes its result
through the sret pointer), copy-result (memcpy %call.result into q).

Answer format: items in order, e.g. A B C
Question 28 byval-vs-copy · single · 1 pt · 05-aggregates-and-the-abi

What is the difference between passing ptr byval(%T) and passing a plain ptr to a caller-made
copy?

  1. None: both are lowered identically
  2. With byval the call sequence makes the copy in the argument area (stack), invisible to IR optimizations; with a caller copy it is an ordinary alloca + memcpy the optimizer can remove
  3. byval passes the value in registers, the caller copy on the stack
  4. byval is only for results, caller copies for arguments
Answer format: one letter
Question 29 byval-aarch64 · mapping · 1 pt · 05-aggregates-and-the-abi

Clang compiles void take(struct Big b) with struct Big { long a, b, c; } (24 bytes). How is
the parameter passed on x86-64 and on AArch64 (AAPCS64)? Answer byval or pointer-to-copy.

Keys: x86-64, aarch64
Answer format: one value per key
Question 30 abi-classify-mixed · mapping · 1 pt · 05-aggregates-and-the-abi

Classify struct S { float a; int b; double c; }; (16 bytes) under the System V x86-64 ABI
(Algorithm 11.5.4) and give Clang's coerced type for passing it (write the type without spaces
inside braces, e.g. {i32,double}).

Keys: eightbyte0, eightbyte1, coerced
Answer format: one value per key
Question 31 clang-where-integer-coerce · text · 1 pt · 05-aggregates-and-the-abi

In clang/lib/CodeGen/Targets/X86.cpp, which X86_64ABIInfo function chooses i32 rather than
i64 for an INTEGER eightbyte that holds only an int?

Answer format: a short answer
Question 32 trap-assume · single · 1 pt · 06-safety-checks

PIR o = saddo a, b; assert !o, overflow; r = add a, b is compiled with a trap block calling
pebble_trap (noreturn nounwind). What may LLVM assume about r in code after the check?

  1. Nothing: the add wraps
  2. That a + b did not overflow, because every path to that code took the Ok edge (Lemma 11.6.4)
  3. Only that r is not poison
  4. That r ≥ a
Answer format: one letter
Question 33 swift-where-cond-fail · text · 1 pt · 06-safety-checks

In Swift's lib/IRGen/IRGenSIL.cpp (swift-6.1-RELEASE), which IRGenSILFunction method lowers the
SIL instruction behind every Swift overflow and bounds trap?

Answer format: a short answer
Question 34 unwind-invoke · single · 1 pt · 06-safety-checks

Under Rust's default panic=unwind, when does a call to a function that may panic become an
invoke in the caller's LLVM IR?

  1. Always
  2. When the caller has a cleanup (e.g. a value with a destructor) live across the call, or a catch
  3. Only when the callee is in another crate
  4. Never: panics use setjmp/longjmp
Answer format: one letter
Question 35 unwind-abort · single · 1 pt · 06-safety-checks

What changes when the same Rust program is compiled with -C panic=abort?

  1. Overflow checks are removed
  2. Panics abort the process: no landing pads or cleanup code are emitted, and calls need not be invokes
  3. Panics become error returns
  4. Nothing observable
Answer format: one letter
Question 36 error-return-cost · number · 1 pt · 06-safety-checks

Rust's a.checked_add(b) compiles to llvm.sadd.with.overflow and returns Option<i64> as
{ i64, i64 } (tag = zext (not f), payload = the sum). How many conditional branches does the
callee (checked_add itself, inlined or not) execute?

Answer format: a number
Question 37 error-return-branch · single · 1 pt · 06-safety-checks

Under the error-return policy, a function three calls deep fails, and every level propagates the
error with ?. What does the program execute on the way back?

  1. An unwinder walks three frames using tables
  2. A longjmp to the outermost handler
  3. An ordinary return and one tag test + branch at each of the three levels
  4. Nothing: the failure is ignored
Answer format: one letter
Question 38 eh-callsite-lookup · mapping · 1 pt · 07-exception-handling

A function's call-site table (offsets from the function start; ranges are half-open):

range landing pad
[0x10, 0x18) L1
[0x18, 0x30) none
[0x30, 0x38) L2

An exception passes through this frame. For each return address, where does the unwinder go
next: L1, L2, or caller (keep unwinding into the caller)?

Keys: 0x14, 0x22, 0x34
Answer format: one value per key
Question 39 llvm-where-callsite-table · text · 1 pt · 07-exception-handling

In llvm/lib/CodeGen/AsmPrinter/EHStreamer.cpp, which function builds the list of call-site
entries, including the "no landing pad" gaps, from the EH_LABELs around each invoke?

Answer format: a short answer
Question 40 sjlj-site-index · mapping · 1 pt · 07-exception-handling

Lesson 11.7's safe (an invoke @parse whose handler calls log through a second invoke) is
compiled with llc -exception-model=sjlj. Which call-site index is stored into the function
context before the call to parse, and before the call to log?

Keys: parse, log
Answer format: one value per key
Question 41 sjlj-cost · single · 1 pt · 07-exception-handling

Why is SjLj exception handling not "zero-cost"?

  1. The unwinder must decode DWARF CFI
  2. Every call of a function with handlers registers and unregisters a context (two runtime calls) and stores a call-site index before each invoke, even if nothing throws
  3. Throwing is slower than with tables
  4. It needs a garbage collector
Answer format: one letter
Question 42 swifterror-register · mapping · 1 pt · 07-exception-handling

Swift returns errors in a register reserved by its calling convention (swifterror in LLVM IR).
Which register on x86-64 and which on AArch64?

Keys: x86-64, aarch64
Answer format: one value per key
Question 43 errors-cost-depth · mapping · 1 pt · 07-exception-handling

A recursion of depth d = 6 (six frames, each with a handler or error check) returns normally,
with nothing thrown. How many extra runtime calls does SjLj EH execute, how many extra error
tests do explicit error returns execute, and how many extra instructions do zero-cost tables
execute?

Keys: sjlj-calls, error-tests, tables
Answer format: one value per key
Question 44 cc-free-vars · set · 1 pt · 08-closure-conversion
fn outer(a, b, c) {
    let d = a + 1
    let g = fun x -> x + d + b      // a function value
    return apply(g, c)
}

What is FV(fun x -> x + d + b) (Definition 11.8.1)?

Answer format: items separated by commas or spaces, e.g. {a, b}
Question 45 cc-env-field · mapping · 1 pt · 08-closure-conversion

Closure conversion (Algorithm 11.8.2) turns the lambda of cc-free-vars into the record
MakeRecord(code, [captures in sorted order]), with the code pointer in field 0. In which field
is each captured variable?

Keys: b, d
Answer format: one value per key
Question 46 ll-fixpoint · mapping · 1 pt · 08-closure-conversion

Local non-escaping functions of outer(a, b, c):

fn f(x) = x + a + h(x - 1)
fn g(y) = f(y) * b
fn h(z) = z * c

Give each function's extra parameters E after Algorithm 11.8.4's fixed point.

Keys: f, g, h
Answer format: one value per key (a set: {x, y})
Question 47 go-where-direct-closure · text · 1 pt · 08-closure-conversion

In Go's src/cmd/compile/internal/walk/closure.go (go1.24.7), which function rewrites a directly
called function literal into a plain call with the captured variables as extra leading
arguments?

Answer format: a short answer
Question 48 rt-libcall-choice · mapping · 1 pt · 09-runtime-and-linking

clang 23 at -O2 on x86-64 Linux: which operations become inline instructions (inline) and
which become calls to a runtime or library function (call)?

Keys: long * long, long / long, __int128 * __int128, __int128 / __int128, fmod(double, double)
Answer format: one value per key
Question 49 llvm-where-libcall · text · 1 pt · 09-runtime-and-linking

In llvm/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp, which DAGTypeLegalizer function
expands a too-wide signed division into the __divti3 libcall?

Answer format: a short answer
Question 51 llvm-where-emit-file · text · 1 pt · 09-runtime-and-linking

In llvm/lib/CodeGen/CodeGenTargetMachineImpl.cpp, after adding the code-generation passes,
which function does addPassesToEmitFile call to attach the AsmPrinter with the object writer?

Answer format: a short answer