IAE Academy

GATE CS

Intermediate code generation

Intermediate code is the machine-independent form that sits between the front end and the back end of a compiler. It covers syntax trees and DAGs for expressions, postfix notation, three-address code in quadruple, triple or indirect-triple form, basic blocks, and SSA. GATE questions ask you to count DAG nodes, write or count three-address instructions, fill in the jump targets that backpatching produces, count basic blocks, and count the names or assignments in SSA form. All of these are mechanical once the construction rules are fixed.

2 previous-year questions · 2 solved · 5 practice questions

Why intermediate code; syntax trees and DAGs

A compiler splits into a front end (scanning, parsing, semantic analysis) and a back end (optimisation and target code). The link between them is an intermediate representation (IR). With one shared IR, source languages and target machines need front ends and back ends, which is pieces instead of separate compilers. The IR is also where machine-independent optimisation is done.

Common IRs: the syntax tree and the DAG (graphical forms); postfix notation, a linear form in which each operator follows its operands, so becomes a b c * +; three-address code, a linear form; SSA form, three-address code with one assignment per name; and the control-flow graph, whose nodes are basic blocks.

A syntax tree has one node per operator and one leaf per operand, exactly as the expression is written. A DAG (directed acyclic graph) is the same tree with identical subexpressions shared, so a repeated subexpression appears once and has several parents.

Building the DAG for an expression (value-numbering rule)

  1. Create a leaf for each distinct variable or constant. Reuse the existing leaf when the same name or constant appears again.

  2. For an operator node with operator and children , , first search for an existing node with the same operator and the same children (in the same order).

  3. If such a node exists, reuse it. Otherwise create a new node.

  4. Convention for counting: a node is either a leaf or an operator node, so the node count is the leaves plus the operators of the DAG. Children are matched in the same order unless the question says the operator is commutative.

WORKED EXAMPLE

Count the nodes in the syntax tree and in the DAG of . The expression groups as .

Solution
  1. Syntax tree: the leaves are , which is 7. The operators are , which is 6. Total 13.

  2. DAG: the leaves are each shared, so there are 4 leaves.

  3. The subexpression occurs twice and becomes one node.

  4. Operator nodes: , , the first , , the second . That is 5.

  5. The DAG total is .

Answer: Tree: 13 nodes. DAG: 9 nodes.

EXAM TIP

A subexpression is shared only when its operands have the same values. Inside a basic block, if one of the operands is assigned between two occurrences of the subexpression, the later occurrence uses the node that holds the operand's new value. Its children are then different, so the subexpression is not shared.

Three-address code and its instruction forms

Three-address code (TAC) is a sequence of instructions. Each has at most one operator on the right and at most three addresses (two operands and one result). A complex expression is broken into steps, and each intermediate result gets a compiler-generated name called a temporary, written t1, t2, and so on. Statements are numbered in the printed order unless a question gives a starting number.

Standard TAC instruction forms

Form

Meaning

x = y op z

binary operation

x = op y

unary operation (for example uminus, not)

x = y

copy

goto L

unconditional jump

if x relop y goto L

conditional jump on a comparison

if x goto L, ifFalse x goto L

conditional jump on a Boolean value

param x, call p, n, return y

procedure call with n parameters and return

x = y[i], x[i] = y

indexed copy; the index is an offset (in bytes unless stated)

x = &y, x = *y, *x = y

address and pointer assignments

INSTRUCTION AND TEMPORARY COUNT FOR AN ASSIGNMENT

For where has operator occurrences and nothing is shared. The scheme gives each operator a new temporary (temporaries are never reused) and then copies the last one to . With a DAG, replace by the number of operator nodes in the DAG.

WORKED EXAMPLE

Write TAC for using the scheme above, and again with the DAG. Count the instructions and temporaries each way.

Solution
  1. Without sharing, the operators are , so . The code is:

  2. t1 = a + b
    t2 = a + b
    t3 = t1 * t2
    t4 = t3 - c
    x = t4
  3. That is 5 instructions and 4 temporaries.

  4. With the DAG, the two copies of are one node, so there are 3 operator nodes. The code is:

  5. t1 = a + b
    t2 = t1 * t1
    t3 = t2 - c
    x = t3
  6. That is 4 instructions and 3 temporaries.

Answer: Without sharing: 5 instructions, 4 temporaries. With the DAG: 4 instructions, 3 temporaries.

EXAM TIP

Check which scheme the question assumes. If the last operation stores straight into the target (x = t2 - c), no final copy is generated. For operators that gives instructions and temporaries.

Quadruples, triples and indirect triples

There are three ways to store TAC in a table. Quadruples have four fields: op, arg1, arg2, result. Triples have three fields (op, arg1, arg2) and no result field: a later instruction refers to the result of an earlier one by that instruction's row number, written . Indirect triples keep the triples table and add a separate list of pointers that gives the execution order.

Take . Its three-address code is:

t1 = uminus b
t2 = a * t1
t3 = t2 + c
x = t3

For a copy, the quadruple has the source in arg1 and the target in result. In a triple, the copy is written with the target in arg1 and the reference to the value in arg2.

Quadruples for the statement

#

op

arg1

arg2

result

0

uminus

b

t1

1

*

a

t1

t2

2

+

t2

c

t3

3

=

t3

x

Triples for the statement

#

op

arg1

arg2

0

uminus

b

1

*

a

(0)

2

+

(1)

c

3

=

x

(2)

Indirect triples: the instruction list points into the triples above

Instruction list (execution order)

Points to triple

100

(0)

101

(1)

102

(2)

103

(3)

Comparison

Property

Quadruples

Triples

Indirect triples

Fields per entry

4

3

3, plus one pointer per instruction

Temporary names in the table

Yes, in result

No, row numbers are used

No

Moving an instruction (optimisation)

Easy, since results are named

Hard: every reference to a moved row must be renumbered

Easy: only the pointer list is reordered

Space

Most per entry

Least

Triples plus the pointer list

For an indexed store x[i] = y or load x = y[i], quadruples use one entry, with the operators written []= and =[]. Triples need two entries, because there is no result field. The store x[i] = y is the triple (k) []=, x, i, followed by the triple (k+1) =, (k), y. The load x = y[i] is the triple (k) =[], y, i, followed by the triple (k+1) =, x, (k).

TABLE SIZE IN FIELDS

For a table of entries, counting every field including blank ones and each pointer in the instruction list as one field. The same code does not always give the same number of entries in each form: an indexed assignment takes one quadruple but two triples, and a quadruple can store a result straight into a named variable where a triple needs a separate = entry.

WORKED EXAMPLE

For , give the three representations and count the fields, taking the TAC below as the starting point.

Solution
  1. Three-address code, :

  2. t1 = a + b
    t2 = c - d
    t3 = t1 * t2
    x = t3
  3. Quadruples (op, arg1, arg2, result; a blank field is left empty):

  4. 0: +   a    b    t1
    1: -   c    d    t2
    2: *   t1   t2   t3
    3: =   t3        x
  5. Triples (op, arg1, arg2):

  6. 0: +   a    b
    1: -   c    d
    2: *   (0)  (1)
    3: =   x    (2)
  7. Indirect triples: the same four triples, plus an instruction list of four pointers, for example 100 to (0), 101 to (1), 102 to (2) and 103 to (3).

  8. Fields: quadruples , triples , indirect triples when each pointer counts as one field.

Answer: Quadruples 16 fields, triples 12 fields, indirect triples 16 fields (counting each pointer as one field).

Translating expressions and array references

An expression is translated bottom-up. Each operator node gets a new temporary that holds its value, and the instruction for the operator is emitted after the instructions for its operands. A name or constant is its own address and generates no instruction.

ONE-DIMENSIONAL ARRAY ADDRESS

is the width of one element in bytes and is the lower bound of the index. For a 0-based array the term is just .

TWO-DIMENSIONAL ARRAY ADDRESS, ROW-MAJOR

is the number of columns, and are the lower bounds. Row-major is the default for C-style arrays unless stated otherwise.

TWO-DIMENSIONAL ARRAY ADDRESS, COLUMN-MAJOR

is the number of rows. Use this only when the question says the layout is column-major.

Convention for the TAC below: in x = y[i] and x[i] = y, the index i is already an offset in bytes, so the multiplication by the element width is written out as a separate instruction.

WORKED EXAMPLE

Array A is 0-based, its int elements are 4 bytes wide, and it is declared with 10 rows and 20 columns in row-major order. Write TAC for the statement below.

x = A[i][j] + 1;
Solution
  1. Each row holds 20 elements, so the byte offset of is .

  2. t1 = i * 20
    t2 = t1 + j
    t3 = t2 * 4
    t4 = A[t3]
    t5 = t4 + 1
    x = t5
  3. Count: 6 instructions and 5 temporaries. The first 3 instructions compute the offset. Computing it as also takes 3 instructions.

Answer: 6 instructions, using 5 temporaries; the address computation alone takes 3 instructions.

EXAM TIP

With 0-based arrays and an element width greater than 1, loading A[i] from a one-dimensional array takes 2 instructions (t1 = i * w, then t2 = A[t1]). Loading a two-dimensional element takes 4: multiply by the number of columns, add the column index, multiply by the width, then load. A store replaces the final load with A[t] = value.

Control flow, short-circuit jumps and backpatching

For if, while and Boolean expressions, TAC uses labels or instruction numbers as jump targets. Under short-circuit evaluation, in the second operand is not evaluated when is true, and in it is not evaluated when is false. The Boolean expression is never stored as a value. It is compiled into jumps to a true target and a false target.

The target of a forward jump is not known when the jump is emitted. Backpatching emits the jump with an empty target and records its instruction number in a list. When the target becomes known, backpatch(list, target) fills it in. makelist(i) creates a list holding only , merge(p1, p2) joins two lists, nextinstr is the number of the next instruction to be emitted, and the marker saves nextinstr as .

Backpatching rules

Production

Action

, ; emit if E1 rel E2 goto _ then goto _

; emit goto _; falselist empty

; emit goto _; truelist empty

backpatch(, ); truelist = merge(.truelist, .truelist); falselist = .falselist

backpatch(, ); truelist = .truelist; falselist = merge(.falselist, .falselist)

truelist = .falselist; falselist = .truelist

both lists copied from

backpatch(.truelist, .instr); .nextlist = merge(.falselist, .nextlist)

backpatch(.truelist, .instr); backpatch(.falselist, .instr); .nextlist = merge(.nextlist, .nextlist, .nextlist); here emits goto _ and .nextlist is makelist of that instruction

backpatch(.nextlist, .instr); backpatch(.truelist, .instr); .nextlist = .falselist; emit goto .instr

backpatch(.nextlist, .instr); .nextlist = .nextlist

Procedure for working a backpatching question by hand

  1. Number the instructions from the given starting number, in the order they are emitted.

  2. Each relational expression emits two instructions: a conditional jump (the true exit), then an unconditional jump (the false exit).

  3. Write the truelist and falselist of each Boolean subexpression as sets of instruction numbers.

  4. For ||, the false exit of the left operand goes to the first instruction of the right operand. For &&, the true exit of the left operand goes there.

  5. Send the remaining true exits to the start of the controlled statement. Send the remaining false exits to the first instruction after the statement (for a while, after its closing goto; for an if-else, to the start of the else part).

WORKED EXAMPLE

Instructions start at 100. Generate code for the statement below and give the final jump targets. The first instruction after the statement is 107.

if (a < b || c < d && e < f) x = 1;
Solution
  1. Since && binds tighter than ||, the condition is .

  2. a < b: 100 if a < b goto _, 101 goto _.

  3. The right operand starts at 102 (this is ), so 101 is backpatched to 102.

  4. c < d: 102 if c < d goto _, 103 goto _. The && marker is at 104, so 102 goes to 104.

  5. e < f: 104 if e < f goto _, 105 goto _.

  6. The truelist of the whole condition is {100, 104} and the falselist is {103, 105}.

  7. The statement x = 1 is emitted at 106, so the truelist goes to 106 and the falselist goes to 107.

  8. 100: if a < b goto 106
    101: goto 102
    102: if c < d goto 104
    103: goto 107
    104: if e < f goto 106
    105: goto 107
    106: x = 1

Answer: Seven instructions, 100 to 106. Jumps: 100 to 106, 101 to 102, 102 to 104, 103 to 107, 104 to 106, 105 to 107.

EXAM TIP

In the straightforward scheme above, every relational test emits two instructions, even when the second goto ends up next to its target. Count instructions before any clean-up that removes redundant jumps, unless the question asks for the optimised form.

Basic blocks and flow graphs

Partitioning three-address code into basic blocks

  1. A basic block is a maximal run of consecutive instructions that control enters only at the first instruction and leaves only after the last, with no jump into or out of the middle.

  2. Leader rule 1: the first instruction of the code is a leader.

  3. Leader rule 2: any instruction that is the target of a conditional or unconditional jump is a leader.

  4. Leader rule 3: any instruction that immediately follows a conditional or unconditional jump is a leader.

  5. Each basic block runs from a leader up to, but not including, the next leader (or the end of the code).

  6. In the flow graph there is an edge from block to block if the last instruction of jumps to the first instruction of , or if immediately follows and does not end in an unconditional jump.

WORKED EXAMPLE

The code below is the translation of a while loop; instruction 206 is the first instruction after the loop. How many basic blocks does the loop's code (200 to 205) form?

Solution
  1. 200: if a < b goto 202
    201: goto 206
    202: if c < d goto 204
    203: goto 206
    204: x = 1
    205: goto 200
  2. Rule 1: 200. Rule 2 (jump targets): 202, 204, 200 and 206. Rule 3 (instructions after a jump): 201, 202, 203, 204 and 206.

  3. Leaders within 200 to 205: 200, 201, 202, 203, 204. Instruction 205 is not a leader, because it is not a jump target and 204 is not a jump.

  4. Blocks: {200}, {201}, {202}, {203}, {204, 205}, which is 5.

  5. Edges: 200 to 202 and 200 to 201; 201 to the exit; 202 to 204 and 202 to 203; 203 to the exit; {204, 205} to 200.

Answer: 5 basic blocks.

Static single assignment (SSA) form

In SSA form every variable is assigned in exactly one place in the program text. Each assignment to a variable in the original gets a new name , and each use is changed to the name of the definition that reaches it. Where two control-flow paths meet and different names of the same variable arrive, a -function is inserted at the start of the joining block: . It takes the value of the argument that belongs to the path actually taken, and its arguments are listed in the order of the block's predecessors.

Converting to SSA

  1. Within straight-line code, rename each new assignment to the next number and each use to the latest name.

  2. At a join block (after an if-else, or at a loop header), add a for each variable whose definitions differ along the paths that reach the block. These notes place a wherever different definitions meet (minimal SSA).

  3. At a loop header the two paths are the entry and the back edge, so a variable that is set before the loop and changed inside it needs a there.

  4. Uses after the join refer to the result.

ASSIGNMENTS IN SSA FORM

Each original assignment gets exactly one new name, and each is one more assignment. Since each SSA name is assigned once, this is also the number of names created by assignments and -functions. Variables that are only read and never assigned (inputs) keep their names and are not counted.

WORKED EXAMPLE

Write the SSA form of the code below and count the -functions and the total assignments.

i = 0;
s = 0;
while (i < 10) {
    s = s + i;
    i = i + 1;
}
t = s;
Solution
  1. Before the loop: i1 = 0 and s1 = 0.

  2. Loop header (predecessors: the entry, then the back edge): i2 = phi(i1, i3) and s2 = phi(s1, s3); the test reads i2 < 10.

  3. Body: s3 = s2 + i2, then i3 = i2 + 1.

  4. After the loop: t1 = s2.

  5. The original code has 5 assignments; adding 2 -functions gives names: i1, s1, i2, s2, s3, i3, t1.

Answer: 2 phi-functions, 7 assignments in SSA form.

EXAM TIP

A block having two predecessors does not by itself call for a . One is needed only when the two paths bring different definitions of that variable. A variable assigned on neither branch keeps one name and gets no . Pruned SSA also leaves out a whose result is never used afterwards. If a count depends on this, follow the question's wording.

PREVIOUS-YEAR QUESTIONS

2 questions from the CS papers

Try each question first, then open the answer. We placed each question under a topic ourselves; the exam paper does not label topics.

See these papers in full: GATE CSE 2025 Set 2 · GATE CSE 2024 Set 2

GATE 2025 CS2 Q.21

GATE 2025 · CS2Q.21 · 1 mark · MCQ

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 expression 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?

  1. (A)

    Only (I) is correct.

  2. (B)

    Only (II) is correct.

  3. (C)

    Both (I) and (II) are correct.

  4. (D)

    Neither (I) nor (II) is correct.

Answer and solution
See the question in the paper
GATE 2025 CS2 Q.21: answer and solution

GATE 2024 CS2 Q.43

GATE 2024 · CS2Q.43 · 2 marks · MCQ

Consider the following expression: . The following sequence shows the list of triples representing the given expression, with entries missing for triples (1), (3), and (6).

Which one of the following options fills in the missing entries CORRECTLY?

GATE 2024 CS2 question 43: A table of eight triples (0) to (7), each with an operator and two operands.Tap to open full size
Figure from the GATE 2024 CS2 paper. It is part of the question.
Figure description

A table of eight triples (0) to (7), each with an operator and two operands. (0): +, p, r. (1): blank, blank, blank. (2): uminus, (1), blank. (3): blank, blank, blank. (4): /, u, w. (5): +, (3), (4). (6): blank, blank, blank. (7): =, (6), (5). Gray watermark text reading GATE2024 lies across the image.

  1. (A)

    (1) =[] s i; (3) * (0) (2); (6) []= x i

  2. (B)

    (1) []= s i; (3) - (0) (2); (6) =[] x (5)

  3. (C)

    (1) =[] s i; (3) * (0) (2); (6) []= x (5)

  4. (D)

    (1) []= s i; (3) - (0) (2); (6) =[] x i

Answer and solution
GATE 2024 CS2 Q.43: answer and solution

PRACTICE

5 questions to test yourself

PRACTICE 11 mark · NAT

Build the DAG for the expression . Count every node, leaves and operators, with identical subexpressions shared. What is the number of nodes in the DAG?

Answer and solution

Answer:

  1. Leaves: , which is 4.

  2. The subexpression occurs three times and is one node.

  3. Operator nodes: ; ; ; the product of the two sums; the final subtraction. That is 5.

  4. Total nodes .

PRACTICE 21 mark · MCQ

Which statement about quadruples, triples and indirect triples is correct?

  1. (A)

    Triples allow instructions to be moved without changing any other entry.

  2. (B)

    Indirect triples allow instructions to be reordered by changing only the list of pointers.

  3. (C)

    Quadruples refer to the result of an earlier instruction by its row number.

  4. (D)

    Quadruples never need temporary names.

Answer and solution

Answer: (B)

  1. A is false: a triple refers to earlier results by row number, so moving a row means renumbering the references to it.

  2. B is true: the triples stay where they are and only the execution-order list changes.

  3. C is false: it is triples that use row numbers; quadruples store the result in a named field.

  4. D is false: quadruples keep results in explicit temporaries.

PRACTICE 32 marks · NAT

Consider the code:

x = 1;
y = 2;
if (c) { x = x + y; } else { y = y + x; }
z = x + y;

It is converted to SSA form with a -function at the join after the if-else for each variable that needs one. What is the total number of assignments, including -functions, in the SSA form?

Answer and solution

Answer:

  1. Straight-line part: x1 = 1, y1 = 2.

  2. Then branch: x2 = x1 + y1. Else branch: y2 = y1 + x1.

  3. At the join, x arrives as x2 (then) and x1 (else), and y as y1 (then) and y2 (else), so both need a : x3 = phi(x2, x1) and y3 = phi(y1, y2).

  4. After the join: z1 = x3 + y3. The variable c is only read, so it is not renamed or counted.

  5. Count: x1, y1, x2, y2, x3, y3, z1, which is 7 (5 original assignments plus 2 phi-functions).

PRACTICE 41 mark · MSQ

Which of the following statements about SSA form are true?

  1. (A)

    Each name in SSA form is assigned at exactly one place in the program text.

  2. (B)

    A variable set before a while loop, changed in its body and read in its condition needs a -function at the loop header.

  3. (C)

    A -function is needed for a variable at every block that has two predecessors.

  4. (D)

    After conversion, the same variable name may appear on the left side of two different assignments.

Answer and solution

Answer: (A), (B)

  1. A is the definition of SSA form, so it is true.

  2. B is true: the header is reached from the entry with one name and from the back edge with another, and the variable is used in the condition, so a is needed.

  3. C is false: a is needed only when different definitions of the variable reach the block.

  4. D is false: it contradicts the single-assignment property.

PRACTICE 52 marks · MCQ

Instructions are numbered from 200. Backpatching is used to translate the loop below. Each relational test emits a conditional goto followed by an unconditional goto, and the loop ends with a goto back to the start of the condition. The instruction after the loop is the first one not generated for the loop.

while (a < b && c < d) x = 1;

What is the target of the unconditional jump at instruction 201?

  1. (A)

    204

  2. (B)

    205

  3. (C)

    206

  4. (D)

    202

Answer and solution

Answer: (C)

  1. 200: if a < b goto _, 201: goto _. The marker for the right operand of && is 202.

  2. 202: if c < d goto _, 203: goto _. The loop body starts at 204.

  3. 204: x = 1, 205: goto 200. The first instruction after the loop is 206.

  4. The true exit of 200 goes to 202. The truelist of the condition is {202}, which goes to 204.

  5. The falselist is {201, 203}, which goes to the exit, 206. So instruction 201 jumps to 206.

OFFICIAL GATE 2027 SYLLABUS WORDING

Copied from the GATE 2027 syllabus; see all 2027 syllabi.

GATE CS

Intermediate code generation

Preparing in Kolkata? IAE Academy's course for GATE CSE.