A compiler team wants to support 5 source languages targeting 4 hardware architectures. Without IR, how many separate translators do they need? With a shared IR?
AWithout IR: 9 (5 + 4); with IR: 20 (5 × 4)
BWithout IR: 20 (5 × 4); with IR: 9 (5 + 4)
CWithout IR: 20 (5 × 4); with IR: 5 (one per source language)
DThe number is the same either way; IR only affects optimization quality, not translator count
Without IR, each source language needs a direct translator to each target — 5 × 4 = 20 translators. With IR, you need m frontends (source → IR) plus n backends (IR → machine code) = 5 + 4 = 9 components. This m+n vs. m×n tradeoff is the core strategic value of IR. It scales dramatically: at 10 languages and 10 targets, the difference is 20 vs. 100 components. This is why LLVM's IR has been so influential — any language that emits LLVM IR gets all LLVM backends for free.
Question 2 Multiple Choice
Why is three-address code (TAC) better than an AST as a target for optimization passes like dead code elimination or constant folding?
ATAC is closer to machine code, so optimization passes run faster
BTAC eliminates all temporary variables before optimization, reducing state to track
CTAC mirrors source language syntax, making it easier to preserve programmer intent
DTAC flattens expressions into sequential instructions with named temporaries, making data flow and control flow explicit and easy to analyze
An AST mirrors the tree structure of the source syntax, which is awkward for optimization — you need recursive tree traversals, and data flow between expressions is implicit in the nesting. TAC decomposes every expression into at most one operation using named temporaries (t1 = b*c; t2 = a+t1), making every data dependency an explicit named reference. Control flow becomes explicit labels and gotos. This flat, explicit form is ideal for constructing data flow graphs, identifying dead assignments, finding common subexpressions, and all standard optimizations.
Question 3 True / False
A single set of optimization passes (e.g., dead code elimination) written for an IR can be applied to programs from multiple source languages.
TTrue
FFalse
Answer: True
Yes — this is a core benefit of IR. Optimizations are written once against the IR, not against individual source languages. Once Python, Rust, and C all compile to LLVM IR, LLVM's dead code elimination, constant folding, and inlining passes apply to all three without modification. The frontend translates source to IR; the optimizer works on IR; the backend generates machine code from IR. Each phase is independent.
Question 4 True / False
Using an IR layer typically produces slower machine code than a direct source-to-machine translation, because the extra translation step introduces inefficiency.
TTrue
FFalse
Answer: False
The opposite is generally true. IR enables machine-independent optimizations (constant folding, dead code elimination, common subexpression elimination, inlining) that operate before any target-specific code generation. A direct source-to-machine translation typically applies only simple, local optimizations. The IR-based approach, by exposing program structure in an analyzable form, enables deeper analysis and better code. SSA form in particular enables powerful optimizations that would be impractical on an AST or direct machine code.
Question 5 Short Answer
Why is three-address code a better target for optimization than an abstract syntax tree, even though both represent the same program?
Think about your answer, then reveal below.
Model answer: An AST mirrors source syntax: expressions are nested, data flow is implicit in tree structure, and control flow is buried inside if/while/for nodes. Optimization requires traversing the tree recursively and reasoning about implicit relationships. TAC explodes every expression into a flat sequence of single-operation instructions using named temporaries, making every data dependency an explicit reference and every control transfer an explicit goto. This means a compiler can extract data flow and control flow graphs directly, identify which definitions reach which uses, and apply standard analysis algorithms (liveness, reaching definitions, dominance) without first transforming the structure.
The transformation from AST to TAC trades tree structure for sequential explicitness. Optimizations that would require complex tree transformations become simple instruction-level substitutions on TAC. This is why SSA form (a restricted TAC where each temporary is assigned exactly once) simplifies optimization further — it makes data flow not just visible but unique, trivializing many analyses.