Intermediate Code Generation Notes (1)
Intermediate Code Generation Notes (1)
Process:
Parse the declaration (e.g., int x;).
Update the symbol table with the variable’s name, type, and memory offset.
No explicit three-address code is generated for declarations, but the compiler ensures space allocation.
Example:
Source: int x, y;
Action: Symbol table entries: x: type=int, offset=0, y: type=int, offset=4 (assuming 4
bytes per int).
Note: For arrays (e.g., int arr[10];), the compiler calculates total memory (e.g., 10 * 4 = 40 bytes) and
stores the base address in the symbol table.
2. Assignment Statements
Assignment statements are translated into three-address code, where each instruction has at most three operands (e.g.,
t1 = a + b).
Process:
Parse the assignment (e.g., x = a + b * c).
Break it into simpler operations using temporary variables.
Generate three-address code.
Example:
Source: x = a + b * c
Intermediate Code:
t1 = b * c
t2 = a + t1
x = t2
3. Boolean Expressions
Boolean expressions (e.g., a < b && c > d) are used in control structures (if, while). They’re translated into
intermediate code with conditional jumps to handle true/false outcomes.
Process:
Evaluate the expression using short-circuit evaluation (e.g., stop if the first condition fails in &&).
Use labels to jump to true/false blocks.
Example:
Source: if (a < b && c > d) x = 1; else x = 0;
Intermediate Code:
if a < b goto L1
goto L2
L1: if c > d goto L3
goto L2
L3: x = 1
goto L4
L2: x = 0
L4:
Explanation:
L1, L2, L3, L4 are labels for jumps.
First checks a < b, jumps to L1 if true, else to L2 (false case).
At L1, checks c > d, jumps to L3 (true case) or L2 (false case).
4. Case Statements
Case statements (switch statements) are translated into intermediate code using a jump table or a series of conditional
jumps.
Process:
Evaluate the switch expression.
Generate jumps to the corresponding case labels.
Example:
Source:
switch (x) {
case 1: y = 10; break;
case 2: y = 20; break;
default: y = 0;
}
Intermediate Code:
if x == 1 goto L1
if x == 2 goto L2
goto L3
L1: y = 10
goto L4
L2: y = 20
goto L4
L3: y = 0
L4:
Alternative: Use a jump table for efficiency (array of labels indexed by x).
5. Backpatching
Backpatching is a technique to handle incomplete jumps in intermediate code (e.g., in if-else or while loops) by filling in
jump addresses later.
Process:
Generate code with placeholder jumps (e.g., goto _).
Maintain a list of instructions with unresolved jumps.
Fill in the labels once the target addresses are known.
Example:
Source: if (a > b) x = 1; else x = 0;
Initial Code (with placeholders):
Backpatching:
At 100, goto _ is filled as goto 103 (true case).
At 102, goto _ is filled as goto 104 (next instruction after false case).
Final Code:
6. Procedure Calls
Procedure calls involve generating code for function calls, passing parameters, and handling return values.
Process:
Push parameters onto the stack or pass via registers.
Call the procedure (jump to its address).
Handle return values.
Example:
Source: y = foo(a, b);
Intermediate Code:
param a
param b
call foo, 2
y = return
Explanation:
param a, param b: Push parameters a and b.
call foo, 2: Call function foo with 2 arguments.
y = return: Store the return value in y.
Flowchart: Intermediate Code Generation
Process
[Syntax Tree] → [Generate Declarations (Symbol Table)] → [Translate Assignments] →
[Handle Boolean Expr (Jumps)] → [Process Case Stmts] → [Backpatch Jumps] →
[Generate Procedure Calls] → [Intermediate Code]
Summary
Intermediate code generation translates source code into a machine-independent form like three-address code, handling
declarations (symbol table updates), assignments (e.g., x = a + b * c → t1 = b * c), boolean expressions (using
jumps), case statements (jump tables), backpatching (filling jump addresses), and procedure calls (parameter passing).
This IR simplifies optimization and final code generation.