Introduction
Intermediate-code generation turns the syntax tree into three-address code with syntax-directed rules. Arithmetic expressions become sequences of temporaries; array references become address arithmetic; Boolean conditions become jumping code, in which the value of a condition is represented by where control goes rather than by a stored 0 or 1. Statements such as if, if-else and while are built from labels and jumps around that code.
Jumps forward to code not yet generated create a problem for a one-pass translator: the target is not known when the jump is emitted. Backpatching solves it by emitting jumps with empty targets, keeping lists of them, and filling them when the target becomes known. Examination questions ask for array addresses, the number of instructions in short-circuit code, and the true and false lists produced by backpatching.
Prerequisites
- Intermediate Representations: Syntax Trees, DAGs and Three-Address Code: three-address instruction forms, temporaries, conditional and unconditional jumps.
- Synthesized and inherited attributes and their placement in translation schemes (see the notes Syntax-Directed Definitions and Syntax-Directed Translation Schemes).
Core concepts
E.addrandE.code. For an expression,addris the name, constant or temporary holding its value;codeis the instruction sequence computing it.gen(...)emits one instruction;new Temp()creates a temporary.- Row-major layout. Elements of a multi-dimensional array are stored row by row: the last subscript varies fastest. C uses row-major order.
- Jumping code. A Boolean expression has two inherited labels, and ; its code jumps to one of them.
- Short-circuit evaluation. In , if is true, is not evaluated; in , if is false, is not evaluated.
- Backpatching. Instructions are numbered; incomplete jumps are collected on lists and ;
backpatch(p, i)writes target into every jump on list ;merge(p1, p2)concatenates lists;makelist(i)creates the list .
How it works
Expressions and assignments
The SDD for assignments is
This is a postfix SDT: every instruction is emitted when its production is reduced, so instructions appear in postorder, one per operator.
Array addressing
For a one-dimensional array with element width and lower bound 0, A[i] is at . For a two-dimensional array A[n1][n2] in row-major order with lower bounds 0,
With lower bounds and , replace by and by . In general the offset is built by Horner's rule: , times . Column-major order (Fortran) reverses the roles: for two dimensions.
Example 1. int A[10][20] with at base 1000. Row-major: is at . Column-major: .
Example 2. B[1..6][-2..2] with at base 2000 (so ). Then is at .
Example 3: three dimensions. int A[4][5][6] with at base 1000. By Horner's rule the row-major offset of in elements is , so the address is . The first dimension's size, 4, never enters the formula. If the same array is declared with lower bounds 1, as A[1..4][1..5][1..6], the subscripts become : , and the address of is .
The three-address code for x = A[i][j] with int A[10][20] (row width ) is
t1 = i * 80
t2 = j * 4
t3 = t1 + t2
t4 = A[t3]
x = t4Here A[t3] uses the byte offset t3 from the base of A. Lower bounds other than 0 can be folded into a compile-time constant subtracted once, since does not depend on the subscripts.
Boolean expressions as jumping code
The control-flow SDD gives each the inherited labels and :
| Production | Rules |
|---|---|
| ; ; ; ; code = .code, label(), .code | |
| ; ; ; ; code = .code, label(), .code | |
| ; ; code = .code | |
code = .code, .code, if goto , goto | |
| / | goto / goto |
Negation generates no code: it swaps the labels.
For statements, with the label after :
if (B) S1: , ; code = .code, label(), .code.if (B) S1 else S2: and are new labels; after .code comesgoto S.next.while (B) S1: , , , ; code = label(), .code, label(), .code,goto begin.
Example 4. if (x < 100 || x > 200 && x != y) x = 0; with (&& binds tighter than ||):
if x < 100 goto L2
goto L3
L3: if x > 200 goto L4
goto L1
L4: if x != y goto L2
goto L1
L2: x = 0
L1:Short-circuiting is visible in the trace. With , needs one comparison, two, and three. The goto L3 jumps to the next instruction; a peephole pass, or the fall-through variant of the rules, removes such redundant jumps.
Backpatching
In one pass, a jump's target may lie ahead. The backpatching scheme gives synthesized lists and of instruction numbers whose targets are still empty, and uses a marker to record where code for the next part starts:
| Production | Actions |
|---|---|
| backpatch(); = merge(); | |
| backpatch(); ; = merge() | |
| ; | |
= makelist(nextinstr); = makelist(nextinstr + 1); emit if E1.addr rel E2.addr goto _ and goto _ |
Example 5. The same condition, x < 100 || x > 200 && x != y, with instructions numbered from 100. The three comparisons emit six instructions, 100 to 105. Reducing backpatches its first truelist with . Reducing the || backpatches with . The result is
100: if x < 100 goto _
101: goto 102
102: if x > 200 goto 104
103: goto _
104: if x != y goto _
105: goto _with and . The enclosing statement fills these later: for if (B) M S1, backpatch() points them at the start of , and = merge() is passed upward.

Example 6: while with backpatching. For while (a < b) a = a + 1 starting at 100, with : the condition emits 100 and 101; the body emits 102 and 103; then goto is emitted. Backpatching with gives
100: if a < b goto 102
101: goto _
102: t1 = a + 1
103: a = t1
104: goto 100and , to be filled with whatever instruction follows the loop.
Example 7: if-else with backpatching. The scheme uses two markers and a marker that emits the jump over the else part:
with backpatch(), backpatch() and = merge(); sets = makelist(nextinstr) and emits goto _. For if (a < b) x = 1; else x = 2; starting at 100:
100: if a < b goto 102
101: goto 104
102: x = 1
103: goto _
104: x = 2Five instructions. is patched with and with . The assignments leave no jumps, so : only the jump at the end of the then-part waits for the instruction after the statement. The else-part simply falls through to it.
Important properties and observations
- Each relational test costs two instructions in the basic scheme (a conditional and an unconditional jump);
&&,||and!emit none of their own. - Labels are inherited; lists are synthesized. The label scheme needs inherited attributes (L-attributed), which suits top-down translation; backpatching uses only synthesized lists plus markers, which suits bottom-up one-pass translation.
- The marker's
instris the target for the second operand, used by||for 's false exits and by&&for 's true exits. - Row-major offset of is ; the number of columns matters, the number of rows does not.
- Lists hold instruction numbers, not labels, and a jump is patched exactly once.
Common misconceptions.
- "
!Bgenerates a negation instruction." It only swaps the true and false exits (or lists). - "Short-circuit code evaluates all comparisons and combines them." It stops as soon as the result is known.
- "Column-major uses the number of columns." It uses the number of rows as the multiplier.
GATE examination perspective
- Array address calculation. Combine this with row-major or column-major order, non-zero lower bounds and a given element width.
- Three or more dimensions. Apply Horner's rule over the sizes of all dimensions except the first, after subtracting each lower bound (Example 3).
- if-else layout. The then-part ends with a jump over the else-part; that jump is the one left on (Example 7).
- Three-address code for array references and assignments. Count instructions and temporaries.
- Short-circuit code. Count instructions or comparisons executed for given inputs; know that
&&binds tighter than||. - Backpatching. Compute truelist and falselist, find which instruction gets which target, or fill in the targets of a numbered code fragment.
- Statement translation. If, if-else and while layouts; where
goto begingoes.
Summary
Sources
- A. V. Aho, M. S. Lam, R. Sethi, J. D. Ullman, Compilers, Principles, Techniques, and Tools, 2nd ed., Pearson Addison-Wesley, 2007: Section 6.4 (operations within expressions, incremental translation, addressing array elements, translation of array references), Section 6.6 (Boolean expressions, short-circuit code, flow-of-control statements, control-flow translation of Boolean expressions, avoiding redundant gotos), Section 6.7 (one-pass code generation using backpatching, backpatching for Boolean expressions, flow-of-control statements).