Phases of a Compiler for GATE: One Statement Traced Through Every Phase

Follow position = initial + rate * 60 through all six compiler phases. Each step shows the exact artifact, error class and hand-off that GATE can test.

Prashant Jain

KnowledgeGate AI educator

Updated 15 Jul 20265 min read

GATE often asks which compiler phase performs a task or which artifact a phase produces. Reciting six names in order will not settle those questions. The reliable method is to watch one statement change form from source text to target instructions and notice exactly what each phase hands over.

The two halves of a compiler

A compiler is usually explained as an analysis half and a synthesis half.

The analysis phase, or front end, breaks the source program into meaningful parts and checks them. Its three main stages are lexical analysis, syntax analysis and semantic analysis. By the end of analysis, the compiler has an annotated internal representation that says both what the program means and whether its use of names and types is valid.

The synthesis phase, or back end, turns that checked representation into a target program. Its stages are intermediate code generation, code optimization and target code generation. The target may be assembly or machine code for a particular architecture.

Two services run across this pipeline:

  • The symbol table stores information about identifiers, such as their names, types and storage details. Lexical analysis starts entries, and later phases add or use information.

  • The error handler receives different error classes from different phases. There is not one final error check after all compilation work is complete.

This front-end and back-end split is useful, but GATE usually tests the finer phase-to-artifact mapping.

The running assignment statement

We will trace this source statement:

position = initial + rate * 60

Assume position, initial and rate are floating-point variables. The literal 60 is an integer. That difference matters because the semantic phase must reconcile the types before floating-point multiplication can be generated.

For compact internal names, let the symbol table assign position -> 1, initial -> 2 and rate -> 3. Every later form below preserves the multiplication before addition required by operator precedence.

Analysis phases traced step by step

Lexical analysis reads characters and groups them into tokens. It also removes layout details such as spaces when they carry no meaning. For this statement, the token stream is:

<id,1> <=> <id,2> <+> <id,3> <*> <60>

The three identifier tokens point to symbol-table entries rather than repeating full names. An illegal character or an invalid token pattern is a lexical error. The deeper token and lexeme distinction is covered in Lexical Analysis in Compiler Design: Tokens, Patterns and Lexemes Explained.

Syntax analysis takes the tokens and checks whether their order follows the grammar. Its tree has assignment = at the root. The left child is id1. The right child is +, with id2 on the left and a multiplication node on the right. That multiplication has children id3 and 60.

In compact form, the tree is =(id1, +(id2, *(id3, 60))). A misplaced operator, an unbalanced parenthesis or another grammar violation belongs here. If you need to connect this tree to parser mechanics, read Parsing in Compiler Design: Top-Down and Bottom-Up Explained.

Semantic analysis checks meaning that grammar alone cannot establish. The compiler confirms that the identifiers are declared and that the operand types are compatible. Here rate is a float but 60 is an integer, so the phase inserts a coercion:

=(id1, +(id2, *(id3, inttofloat(60))))

The result is an annotated syntax tree. An undeclared identifier or an invalid type combination is a semantic error, not a syntax error.

Synthesis phases traced step by step

Intermediate code generation converts the annotated tree into a machine-neutral form. One possible three-address sequence has exactly four instructions:

t1 = inttofloat(60)
t2 = id3 * t1
t3 = id2 + t2
id1 = t3

There is one operation on the right of each instruction. The order also makes the data dependency visible: t2 needs t1, and t3 needs t2.

Code optimization improves the intermediate code without changing its result. A machine-independent optimizer can write the known converted constant as 60.0, eliminate the conversion instruction and store the addition directly into id1:

t1 = id3 * 60.0
id1 = id2 + t1

The count has fallen from four instructions to two. This is still intermediate code, not target assembly.

Code generation selects machine instructions and registers. For an illustrative floating-point target, the two optimized operations could become five instructions:

LDF  R2, id3
MULF R2, R2, #60.0
LDF  R1, id2
ADDF R1, R1, R2
STF  id1, R1

This exact assembly is an example target sequence, not a universal instruction set. The phase itself is machine-dependent because register choices and instruction names depend on the target.

A vertical pipeline shows position = initial + rate * 60 passing through six labelled phases with the exact outputs token stream (<id,1><=><id,2><+><id,3><><60>), syntax tree (= over id1 and +(id2, (id3, 60))), semantic tree with inttofloat(60), four-line TAC, two-line optimized code and five-line target assembly, while a side symbol table spanning all phases lists position->1, initial->2 and rate->3.

Artifact-per-phase table for match-the-phase questions

Input

Phase

Output

Source character stream

Lexical analysis

Tokens and initial symbol-table entries

Token stream

Syntax analysis

Parse tree or abstract syntax tree

Syntax tree

Semantic analysis

Annotated tree with types and coercions

Annotated tree

Intermediate code generation

Three-address code

Intermediate code

Code optimization

Improved intermediate code

Improved intermediate code

Code generation

Target assembly or machine code

Read the table in both directions. The examiner may name an output and ask for its producer, or name a phase and ask what it emits.

Traps GATE plants in compiler-phase questions

The most common error is assigning a fault to the wrong phase. An illegal character is lexical. A missing operand or unbalanced parenthesis is syntactic. An undeclared variable or incompatible type is semantic. An array index going out of bounds while the program runs is a runtime error, unless a specific compile-time check can prove it.

Do not treat the symbol table as the private output of one phase. It is created and maintained across phases. Also keep machine-independent optimization separate from machine-dependent target generation. Optimization may transform three-address code without knowing the final register set.

Finally, do not confuse a parse tree with a token stream. Tokens have already discarded irrelevant character-level detail; a tree adds grammatical structure and precedence.

How GATE tests the six phases

Typical stems ask where inttofloat is inserted, which phase catches an undeclared identifier, what syntax analysis produces or how many stages belong to analysis. More than 700 published Compiler Design questions in the KnowledgeGate bank support this phase-and-artifact drill.

For wording tied to a particular year or paper pattern, verify the paper and answer key on the official GATE portal of the organising IIT. The compiler concepts themselves remain stable.

Short version and next step

Lexical analysis produces tokens, syntax analysis produces a tree, and semantic analysis annotates that tree. Intermediate code generation then produces TAC, optimization improves it, and code generation emits target instructions. The symbol table and error handling support the complete route.

Trace one arithmetic statement and one conditional statement on paper. Then use GATE Guidance by Sanchit Sir for the full Compiler Design sequence, or move through the wider GATE preparation category when you are building a subject plan.

Keep learning

LL(1) Parsing Table Construction for GATE

Build an LL(1) table cell by cell, test it for conflicts, and see how left recursion and common prefixes break predictive parsing.

Updated 14 Jul 20265 min readCompiler Design

First and Follow in Compiler Design: Step-by-Step Computation with Solved GATE Examples

First and Follow sets computed as a fixed-point algorithm: the full rule set, two grammars solved pass by pass, the nullable-symbol trap, and how GATE tests them in LL(1) and SLR(1) questions.

Updated 14 Jul 20267 min readCompiler Design

SLR, CLR and LALR Parsers Compared for GATE: LR Item Sets, Conflicts and Parser Power

All three LR parsers built on one shared grammar: canonical item sets, why SLR(1) hits a shift/reduce conflict that CLR(1) and LALR(1) survive, the 10-vs-14 state count, the reduce/reduce drill grammar, and the exact question patterns GATE uses to test parser power.

Updated 14 Jul 20266 min readCompiler Design

Syntax-directed translation and code optimization in compilers explained

The back end of a compiler is where a parse tree stops being a grammar exercise and starts becoming a program that runs fast. Two ideas carry most of the weight there: syntax-directed translation, which attaches meaning and code to the grammar, and code optimization, which rewrites that code to do the same work with fewer instructions. Both are heavily tested and both reward understanding the mechanics rather than memorising names. Let us build them up in order.

Updated 14 Jul 20265 min readCompiler Design

Discussion