G27
Self-checkedTier 1 12 min readcd.control-flow-translation

Translating expressions and control flow, backpatching

Translating boolean expressions as jumping code with short-circuit evaluation, the fall-through label, if, if-else and while statements, and the single-pass method of backpatching with truelists, falselists, nextlists and marker nonterminals, with counting questions.

Why it exists

Boolean expressions are used in two ways: to compute a value (x = a < b), or, far more often, to decide where control goes (if (a < b) ...). In the second case there is no need to compute a boolean at all: the translator can emit jumps. This makes conditions cheap, and it allows short-circuit evaluation: in B1 || B2, once B1 is true the code never evaluates B2 (a semantic requirement in C). The two main techniques are jumping code with labels and backpatching for one-pass translation.

Prerequisites

You need the three-address instructions, in particular the jumps (goto L, if x relop y goto L, ifFalse) (three-address code).

Precise definitions

Short-circuit (jumping) code for conditions. The grammar B→B1∣∣B2∣B1 && B2∣!B1∣(B1)∣E1 rel E2∣true∣falseB \to B_1 \mid\mid B_2 \mid B_1 \,\&\&\, B_2 \mid ! B_1 \mid ( B_1 ) \mid E_1 \,\text{rel}\, E_2 \mid \text{true} \mid \text{false}. Each BB gets two labels: B.true, where control goes if BB is true, and B.false, where it goes if BB is false. The code of BB jumps to one of them. Rules:

ProductionCode
B→E1 rel E2B \to E_1 \,\text{rel}\, E_2code for E1E_1 and E2E_2, then if E1.addr rel E2.addr goto B.true and goto B.false
B→B1∣∣B2B \to B_1 \mid\mid B_2B1.true = B.true; B1.false = newlabel() (the start of B2B_2); code: B1.code, label B1.false, B2.code; B2.true = B.true, B2.false = B.false
B→B1 && B2B \to B_1 \,\&\&\, B_2B1.false = B.false; B1.true = newlabel() (the start of B2B_2); code: B1.code, label B1.true, B2.code
B→!B1B \to ! B_1exchange the labels: B1.true = B.false, B1.false = B.true
B→trueB \to \text{true}goto B.true
B→falseB \to \text{false}goto B.false

Fall-through (the label fall). If one of the targets is the instruction that comes right after the test, no jump is needed. The special label fall means "continue with the next instruction". For E1 rel E2E_1 \,\text{rel}\, E_2 the code is then: if both labels are real, if test goto B.true followed by goto B.false; if only B.true is real, just if test goto B.true; if only B.false is real, ifFalse test goto B.false; if both are fall, nothing. The ifFalse instruction is the three-address form of "jump if the test fails". This removes most of the redundant gotos.

Statements. Every statement SS has an inherited label S.next for the instruction that follows it.

StatementTranslation
S→if(B)S1S \to \text{if} ( B ) S_1B.true = fall; B.false = S1.next = S.next; code: B.code, S1.code
S→if(B)S1 else S2S \to \text{if} ( B ) S_1 \text{ else } S_2B.true = fall; B.false = newlabel(); S1.next = S2.next = S.next; code: B.code, S1.code, goto S.next, label B.false, S2.code
S→while(B)S1S \to \text{while} ( B ) S_1begin = newlabel(); B.true = fall; B.false = S.next; S1.next = begin; code: label begin, B.code, S1.code, goto begin
S→S1S2S \to S_1 S_2S1.next = newlabel(); S2.next = S.next; code: S1.code, label S1.next, S2.code

Boolean values. When a boolean value is needed, as in x = a < b, the translator produces code that sets a temporary to 1 or 0 using the jumping code: if a < b goto L1, t = 0, goto L2, L1: t = 1, L2: x = t.

Backpatching. In the label method a statement must know S.next before its code is generated (an inherited attribute), which forces either two passes or symbolic labels that a later pass resolves. Backpatching generates the jump instructions with empty targets and fills them in later, in one pass with an LR parser. The attributes are lists of instruction numbers:

  • B.truelist: the jumps in the code of BB that must go to the code that follows when BB is true; B.falselist likewise for false;
  • S.nextlist: the jumps that must go to the instruction after SS.

Three functions work on lists: makelist(i) creates a list holding the instruction number i; merge(p1, p2) concatenates two lists; backpatch(p, i) sets the target of every jump on list p to i. A global counter nextinstr is the number of the next instruction to be emitted. Marker nonterminals record nextinstr at the right moment: M→ε {M.instr=nextinstr}M \to \varepsilon\ \{M.instr = nextinstr\}, and N→ε {N.nextlist=makelist(nextinstr); gen(goto _)}N \to \varepsilon\ \{N.nextlist = makelist(nextinstr);\ gen(\text{goto \_})\} emits an unconditional jump with an empty target.

ProductionActions
B→E1 rel E2B \to E_1 \,\text{rel}\, E_2B.truelist = makelist(nextinstr); B.falselist = makelist(nextinstr + 1); gen('if' E1.addr rel E2.addr 'goto _'); gen('goto _')
B→B1∣∣MB2B \to B_1 \mid\mid M B_2backpatch(B1.falselist, M.instr); B.truelist = merge(B1.truelist, B2.truelist); B.falselist = B2.falselist
B→B1 && MB2B \to B_1 \,\&\&\, M B_2backpatch(B1.truelist, M.instr); B.truelist = B2.truelist; B.falselist = merge(B1.falselist, B2.falselist)
B→!B1B \to ! B_1B.truelist = B1.falselist; B.falselist = B1.truelist
S→if(B)MS1S \to \text{if} ( B ) M S_1backpatch(B.truelist, M.instr); S.nextlist = merge(B.falselist, S1.nextlist)
S→if(B)M1S1N else M2S2S \to \text{if} ( B ) M_1 S_1 N \text{ else } M_2 S_2backpatch(B.truelist, M1.instr); backpatch(B.falselist, M2.instr); S.nextlist = merge(S1.nextlist, N.nextlist, S2.nextlist)
S→whileM1(B)M2S1S \to \text{while} M_1 ( B ) M_2 S_1backpatch(S1.nextlist, M1.instr); backpatch(B.truelist, M2.instr); S.nextlist = B.falselist; gen('goto' M1.instr)
S→S1MS2S \to S_1 M S_2backpatch(S1.nextlist, M.instr); S.nextlist = S2.nextlist

At the end the nextlist of the whole program is backpatched to the first instruction after the code.

How it works

Jumping code for the condition x<100∣∣x>200  &&  x≠yx < 100 \mid\mid x > 200 \;\&\&\; x \ne y in an if. Because the then-part follows the test, B.true is fall, and the code is shown instruction by instruction with the rule that produced it. The first test jumps forward to the body (L2) when true; the other two tests jump to the end (L1) when false.

Jumping code for a condition with || and &&: start

Computed by a script, not typed
labelthree-address instruction
1
2
3
4
5

Step 0

Jumping code translates a condition into jumps: control falls through into the code when the condition holds, and jumps to the label after the statement when it does not. The special label fall means no jump instruction is needed.

0 / 6
computed by script: the algorithm is executed, one step at a time. python3 scripts/gen-cd-icg-traces.py

A while loop: the label L1 marks the test, the test jumps out when false, and the body ends with goto L1. (The textbook translation of s = s + i includes the copy from a temporary.)

Jumping code for a while loop: start

Computed by a script, not typed
labelthree-address instruction
1
2
3
4
5
6
7

Step 0

A while loop starts with a label for the test; the test jumps out of the loop when it fails, and the body ends with a jump back to the test.

0 / 8
computed by script: the algorithm is executed, one step at a time. python3 scripts/gen-cd-icg-traces.py

The same if statement by backpatching. Instructions are numbered from 100. Every comparison emits two jumps, if ... goto _ and goto _, whose indices go into the truelist and falselist. When the parser reaches || it patches the falselist of the first condition to the start of the second (the marker), and so on. Watch the _ become numbers.

Backpatching: if (x < 100 || x > 200 && x != y) x = 0;: start

Computed by a script, not typed
addressinstruction
100
101
102
103
104
105
106

Step 0

With backpatching the jumps are emitted first with empty targets (shown as _) and recorded in lists; when the target becomes known, the lists are patched. Addresses start at 100. The truelist and falselist of a condition hold the jumps still to be completed.

0 / 11
computed by script: the algorithm is executed, one step at a time. python3 scripts/gen-cd-icg-traces.py

And the loop by backpatching. The test is at 100; its false exit stays open until the whole loop is complete; the back jump at the end is patched to the start.

Backpatching: while (i < 10) s = s + i;: start

Computed by a script, not typed
addressinstruction
100
101
102
103
104
105
106

Step 0

The loop test begins at the first address; the false exit is patched at the end, the true exit to the start of the body, and the jump at the end of the body goes back to the test.

0 / 9
computed by script: the algorithm is executed, one step at a time. python3 scripts/gen-cd-icg-traces.py

Worked examples

EX 1Jumping code for a simple if

Easy

if (a < b) x = 1; With the fall-through convention: ifFalse a < b goto L1, x = 1, and L1: after it. 2 instructions and no goto. Without fall (both labels real) the same statement needs if a < b goto L2, goto L1, L2: x = 1, L1: — 3 instructions and one goto. By backpatching (instructions numbered from 100): 100: if a < b goto 102, 101: goto 103, 102: x = 1: 3 instructions, one goto.

EX 2if-else

Easy

if (a < b) x = 1; else x = 2; gives ifFalse a < b goto L2, x = 1, goto L1, L2: x = 2, L1:. 4 instructions, one goto (it jumps over the else part). By backpatching: 100: if a < b goto 102, 101: goto 104, 102: x = 1, 103: goto 105, 104: x = 2: 5 instructions, 2 gotos.

EX 3Short-circuit evaluation: the second test is skipped

Medium

if (a < b || c < d) x = 1; gives if a < b goto L2, ifFalse c < d goto L1, L2: x = 1, L1:. If a < b is true the first instruction jumps directly to x = 1: the comparison c < d is not evaluated, which matters if it has a side effect or can fail (p != 0 || p->v > 0). By backpatching the code has 5 instructions and 2 gotos (the falselist of the first test is patched to 102, the start of the second test).

EX 4Count in a loop with &&

Medium

while (a < b && c < d) { x = x + 1; }. Jumping code: L1: ifFalse a < b goto L2, ifFalse c < d goto L2, t1 = x + 1, x = t1, goto L1, L2:: 5 instructions, 1 goto. By backpatching: 100: if a < b goto 102, 101: goto 107, 102: if c < d goto 104, 103: goto 107, 104: t1 = x + 1, 105: x = t1, 106: goto 100: 7 instructions, 3 gotos, and the instruction after the loop is 107, which is the target of the two exits (the falselist of the whole condition, patched at the end of the statement).

EX 5What do the lists contain?

GATE level

For B=a<b∣∣c<dB = a < b \mid\mid c < d starting at instruction 100: the first comparison emits 100: if a < b goto _ and 101: goto _, with B1.truelist = {100}, B1.falselist = {101}. The marker MM records 102. The second emits 102: if c < d goto _, 103: goto _ with B2.truelist = {102}, B2.falselist = {103}. The rule B→B1∣∣MB2B \to B_1 \mid\mid M B_2 patches 101 to 102, and sets B.truelist = {100, 102}, B.falselist = {103}. Questions of this type ask for the truelist or falselist or for the state of the code after a given step; the method is always the rule table above.

Cheat box

Cheat box

  • Conditions become jumps: control reaches B.true or B.false. || and && short-circuit: the second operand is evaluated only if needed.
  • fall means "next instruction": it removes redundant gotos; ifFalse jumps when the test fails.
  • if: B.true = fall, B.false = S.next. if-else: extra goto S.next after the then part. while: label at the test, goto begin at the end.
  • Backpatching: emit jumps with empty targets, record them in truelist, falselist, nextlist; makelist, merge, backpatch; markers MM (record nextinstr) and NN (emit a jump).
  • ||: backpatch B1.falselist to the start of B2; &&: backpatch B1.truelist to the start of B2.
  • Every comparison emits two instructions in backpatching (a conditional jump and a goto).

Traps and GATE patterns

Connections

The instructions used here are those of three-address code; the translation rules are a translation scheme built on the expression translation of intermediate code generation. The jump instructions create the leaders from which basic blocks and the flow graph are built.

PYQs

1 previous year question from your book. I re-solved each one myself (0 by running or simulating, 1 by reasoning only); the book's key is shown next to my answer. All PYQs

GATE 2025 · MCQQ3 · page 279 of the book
Consider the following statements about the use of
backpatching in a compiler for intermediate code
generation:

(I) Backpatching can be used to generate code for
Boolean expressions in one pass.

(II) Backpatching can be used to generate code for
flow-of-control statements in one pass.

Which ONE of the following options is CORRECT?
  • (a)Only (I) is correct.
  • (b)Only (II) is correct.
  • (c)Both (I) and (II) are correct.
  • (d)Neither (I) nor (II) is correct. COMPILER DESIGN

Self-check

Question 1

How many instructions has the jumping code of if (a < b) x = 1; else x = 2; (fall-through convention)?

Show answer

4: ifFalse a < b goto L2, x = 1, goto L1, x = 2 (with the labels L2 and L1 not counted).

Question 2

In backpatching, what do makelist(i), merge(p1, p2) and backpatch(p, i) do?

Show answer

makelist(i): a new list with the instruction number i. merge: the union of the two lists. backpatch(p, i): fill i as the target of every jump on list p.

Question 3

For B→B1 && MB2B \to B_1 \,\&\&\, M B_2 which list of B1B_1 is backpatched, and to what?

Show answer

B1.truelist, to M.instr (the start of B2B_2); the falselists are merged.

Question 4

Why can a one-pass translator not simply emit the target labels in a while statement?

Show answer

The target after the loop (S.next) is not known until the whole loop has been generated; backpatching leaves the jump incomplete and fills it in later.

Revision card

One-screen revision

  • Conditions as jumps with B.true and B.false; || and && short-circuit; fall removes redundant gotos.
  • if: 2 instructions for if (a < b) x = 1;; if-else adds a goto; while adds a goto back.
  • Backpatching: lists + makelist, merge, backpatch, markers M and N; comparison = 2 jumps.
  • ||: patch falselist of B1 to start of B2; &&: patch truelist.

References

  • ·Aho, Lam, Sethi, Ullman, Compilers: Principles, Techniques, and Tools (2nd edition), Chapter 6 Intermediate-Code Generation (6.6 control flow: boolean expressions, short-circuit code, flow-of-control statements, avoiding redundant gotos; 6.7 backpatching)

Verification

Both translation methods are implemented in scripts/cd_icg.py (jumping code with labels and the fall convention, and backpatching with truelist, falselist and nextlist) and checked in scripts/enum-checks/cd_icg.py: for 1500 random programs with nested if, if-else, while, &&, || and ! the generated code is executed and gives the same variables, arrays and output as the source, and every jump is patched to a valid address. The instruction and goto counts quoted for the small statements were computed by the same code. Different books differ in the exact code produced (redundant gotos, label placement), so counts depend on the convention; the page states the conventions used. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (token counts, FIRST and FOLLOW sets, LL(1) tables and moves, LR item sets, state counts and parses, operator-precedence values, attribute evaluation and printing schemes, activation-record counts and access links, parameter-passing results, three-address code, jumping and backpatched code, leaders and blocks, value numbering and DAGs, gen/kill and data flow sets, Sethi-Ullman labels and live ranges); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.

last verified: never