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 worksIf statements, loops, and short-circuit logic require the code generator to emit labels and conditional jumps. GCC's expand_expr and LLVM's CreateCondBr split control flow into basic blocks before machine lowering.
Evaluate condition to a temp. Emit conditional jump to else label. Generate then-branch. Unconditional jump to end. Else label. Generate else-branch. End label.
cLoading…
&&: jump past right operand if left is falseProduction 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 control flow code generation for if, else, and loops. This exercise requires emitting labels, conditional branches, and merge points so execution follows the AST's control structure.
Generate code for control flow using jumping code: a condition is never turned into a 0/1 value. Instead it compiles directly into conditional jumps, with &&, || and ! handled purely by where the jumps go. This is how real compilers emit if, loops, break and continue.
cLoading…
&& binds tighter than ||. A bare expr used as a condition means expr != 0. break/continue only appear inside loops.
Expressions translate as in Translate an AST into Your IR: operands first, then a new temporary tN per operator (t = a OP b). Labels L1, L2, … and temporaries are numbered in allocation order.
Conditions. jump(c, want, L) emits code that jumps to L if c evaluates to want, and falls through otherwise:
| Condition | want = true | want = false |
|---|---|---|
a REL b | code(a); code(b); if a REL b goto L | code(a); code(b); if a INV b goto L (INV is the opposite: <↔>=, >↔<=, ==↔!=) |
! c | jump(c, false, L) | jump(c, true, L) |
c1 && c2 | allocate Ls; jump(c1, false, Ls); jump(c2, true, L); Ls: | jump(c1, false, L); jump(c2, false, L) |
c1 || c2 | jump(c1, true, L); jump(c2, true, L) | allocate Ls; jump(c1, true, Ls); jump(c2, false, L); Ls: |
Statements (labels are allocated when the statement starts):
| Statement | Code |
|---|---|
if (c) S | allocate Lend; jump(c, false, Lend); S; Lend: |
if (c) S1 else S2 | allocate Lelse, Lend; jump(c, false, Lelse); S1; goto Lend; Lelse:; S2; Lend: |
while (c) S | allocate Ltop, Lend; Ltop:; jump(c, false, Lend); S; goto Ltop; Lend: |
for (I; c; U) S | allocate Ltop, Lstep, Lend; I; Ltop:; jump(c, false, Lend); S; Lstep:; U; goto Ltop; Lend: |
break; / continue; | goto Lend / goto Ltop (while) or goto Lstep (for) of the innermost loop |
x = e; / return e; | code(e); x = a / return a |
The TAC, then temps=T labels=L.
Input:
cLoading…
Output:
cLoading…
No temporaries: the condition exists only as control flow.
if rule needs to know whether an else exists before allocating labels).jump(cond, want, label) function, and a stack of loop labels for break/continue.Hidden tests cover for loops with continue and break, || combined with !, nested loops (break leaves only the inner one), arithmetic inside relations, and bare-expression conditions.