Master the fundamental concepts of code generation through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksInstruction selection maps IR trees to machine instruction trees. BURG and LLVM's SelectionDAG use dynamic programming over tree patterns. A simple matcher associates IR opcodes with assembly templates and picks the cheapest covering.
Break the IR expression tree into tiles each coverable by one or two machine instructions. a + b where both are in registers tiles to a single add. a + constant may tile to lea on x86.
cLoading…
[base + index*scale + offset] on x86Production compilers embed this step inside a longer pipeline. GCC flows through cpp, cc1, assembly, and ld; Clang uses the driver, Sema, LLVM IR passes, and a target backend. LLVM bitcode, JVM bytecode, and WASM are other familiar IRs at the same layer. The exercise isolates one pass so you can test it alone before chaining it to the next stage.
You will implement instruction selection using tree pattern matching. This exercise asks you to match IR operation trees to assembly instruction sequences, choosing tiles that minimize instruction count.
Select machine instructions by tiling IR trees with the cheapest set of instruction patterns (optimal tiling by dynamic programming), for the Jouette machine from Appel's Modern Compiler Implementation.
One IR tree per line, as an S-expression:
cLoading…
A TEMP is already in a register named after it; r0 always holds 0. A MOVE is always to memory.
Every tile costs 1; e marks a subtree the tile leaves to other tiles, c a CONST.
| # | Instruction | Tree pattern |
|---|---|---|
| 1 | ADD ri <- rj + rk | (+ e e) |
| 2 | SUB ri <- rj - rk | (- e e) |
| 3 | MUL ri <- rj * rk | (* e e) |
| 4 | DIV ri <- rj / rk | (/ e e) |
| 5 | ADDI ri <- rj + c | (+ e c) |
| 6 | ADDI ri <- rj + c | (+ c e) |
| 7 | ADDI ri <- r0 + c | c |
| 8 | SUBI ri <- rj - c | (- e c) |
| 9-12 | LOAD ri <- M[rj + c] | (MEM (+ e c)), (MEM (+ c e)), (MEM c) with rj = r0, (MEM e) with c = 0 |
| 13-16 | STORE M[rj + c] <- ri | (MOVE (MEM (+ e c)) e), (MOVE (MEM (+ c e)) e), (MOVE (MEM c) e) with rj = r0, (MOVE (MEM e) e) with c = 0 |
| 17 | MOVEM M[rj] <- M[ri] | (MOVE (MEM e) (MEM e)) |
A TEMP leaf costs 0 (it is its own register).
1 + the costs of the tile's e-subtrees. On a tie, the tile earlier in the table wins.e-subtrees left to right, then the tile itself, writing its result to a new register r1, r2, … (numbered in emission order, restarting at r1 for each tree).For each tree: the tree echoed with single spaces, then its instructions indented two spaces, then cost=N. Finish with trees=T total-cost=C.
Input:
cLoading…
Output:
cLoading…
CONST operand must not also charge for that constant.Hidden tests include Appel's a[i] := x tree (two optimal tilings of cost 6: the table order decides), constant folding opportunities the tiler cannot see ((+ (CONST 3) (CONST 4))), and every LOAD/STORE form.