Three-Address Code
Walk the syntax tree bottom-up; every operator emits exactly one instruction.
Skip to the animationThree-address code is a flat list of instructions, each with at most one operator and at most three operands — the representation a compiler optimises before it commits to any particular machine.
Why flatten the tree at all
The syntax tree is the right shape for checking a program and the wrong shape for improving one. Questions an optimiser asks constantly — is this value used again? is this operand a constant? does this instruction depend on that one? — require walking a tree, but are simple inspection on a list.
So the tree is flattened into instructions with explicit names for every intermediate value. The name three-address comes from the general form x = y op z: two operand addresses and one result.
| Form | Example |
|---|---|
| Binary | t2 = rate * t1 |
| Unary | t1 = inttofloat(60) |
| Copy | position = t3 |
| Unconditional jump | goto L1 |
| Conditional jump | if x < y goto L1 |
| Indexed | t1 = a[i] |
The generation rule
One postorder walk, and one rule: every interior node emits exactly one instruction, after its children have been reduced to addresses. Leaves emit nothing, because a variable or constant is already an address.
- 1Visit the left child, then the right child.
- 2Both are now names — either original variables or temporaries.
- 3Emit one instruction combining them, into a fresh temporary.
- 4Return that temporary as this subtree's address.
Count the operators in the tree and you know exactly how many instructions you will get. Four operators here — a conversion, a multiply, an add and an assignment — give four instructions.
Instruction order comes from the tree, not the source
The source reads initial + rate * 60, but the multiplication is emitted before the addition. That is not an optimisation — it is forced. The addition cannot emit until its right operand has a name, and that name only exists once the multiply has run.
So the precedence that syntax analysis baked into the tree's shape reappears here as the order of the instructions. Every phase preserves that decision in whatever form it uses.
Quadruples, triples and SSA
- Quadruples
- (op, arg1, arg2, result) — each instruction names its own result, so instructions can be reordered or deleted freely. This is what optimising compilers use.
- Triples
- (op, arg1, arg2), with results referred to by instruction position. More compact, but moving one instruction renumbers every reference to it.
- Static single assignment
- The modern refinement: every name is assigned exactly once, so "where did this value come from?" has one answer. Nearly every serious optimiser works in SSA today.
Advantages and disadvantages
Advantages
- Simple and uniform, so optimisations are easy to state and implement.
- Machine independent — one front end can serve many targets.
- Every intermediate value has a name, which makes dataflow analysis straightforward.
Disadvantages
- Loses the structure the tree made explicit; loops must be rediscovered from jumps.
- Generates many temporaries, most of which register allocation must later eliminate.
- Longer than the source, so compilation uses more memory.
Watch it work
Check yourself
question 1 / 4
One question at a time. Pick an answer to see why it is right or wrong, then move on — there is no score to keep and nothing is saved.