Compiler DesignUnit 111 min read

Compiler Design: Phases, Roles & Real-World Impact

Unit 1 of Compiler Design introduces the fundamental concepts of compilers—what they are, why they exist, their architecture, and the phases they undergo to translate high-level code into machine code, with real-world applications in Nepalese software and systems.

TAKEAWAYS:

  • A compiler is a program that translates high-level code (e.g., C, Java) into machine code while preserving semantics, unlike interpreters that execute line-by-line.
  • Compilers follow multi-phase processing: lexical → syntax → semantic → intermediate → optimization → code generation, each with distinct tasks and data structures.
  • Lexical analysis converts source code into tokens (e.g., if, +, 5), while syntax analysis builds parse trees to validate grammar rules.
  • Syntax-directed translation attaches semantic actions (e.g., type checking, symbol table updates) to grammar rules, enabling intermediate code generation.
  • Real-world compilers (e.g., GCC for Linux, Java’s javac) optimize performance, reduce errors, and enable cross-platform execution (e.g., Python → .exe via PyInstaller).
  • Exam focus: Define phases, compare compilers vs. interpreters, and explain how a simple statement (e.g., x = a + b) flows through phases.

What Is a Compiler?

A compiler is a system software that converts high-level programming language (HLL) code (e.g., C, Python) into machine code (binary) or low-level assembly for execution. Unlike interpreters (e.g., Python’s CPython), compilers produce an executable file upfront, improving speed and portability.

Why Use Compilers?

Feature Compiler Interpreter
Execution Speed Faster (pre-compiled to machine code) Slower (executes line-by-line)
Portability Limited (target-specific binary) High (runs on any system with interpreter)
Error Detection Early (all errors reported at once) Late (errors appear during execution)
Memory Usage Lower (optimized binary) Higher (interpreter overhead)

Example in Nepal:

  • eSewa’s backend uses compiled languages (Java, C++) for high-speed transaction processing.
  • Khalti’s mobile app relies on compiled native code (Kotlin/Java) for performance-critical tasks like payment validation.

Phases of a Compiler

Compilers process source code in 9 phases, grouped into front-end (language-specific) and back-end (machine-specific) tasks. Below is the pipeline with key data structures:

flowchart TD
    A["Source Code"] --> B["Lexical Analyzer"]
    B --> C["Tokens"]
    C --> D["Syntax Analyzer"]
    D --> E["Parse Tree"]
    E --> F["Semantic Analyzer"]
    F --> G["Intermediate Code"]
    G --> H["Optimizer"]
    H --> I["Target Code Generator"]
    I --> J["Machine Code"]

1. Lexical Analysis (Scanner)

Goal: Convert source code into tokens (lexemes + categories). How it works:

  • Reads characters → groups into tokens (e.g., if, +, 5).
  • Uses finite automata (DFA/NFA) to recognize patterns (keywords, identifiers, operators).
  • Ignores whitespace/comments.

Example: Input: int x = 5 + 3; Output Tokens:

Token Type Lexeme
KEYWORD int
IDENTIFIER x
OPERATOR =
INTEGER 5
OPERATOR +
INTEGER 3
PUNCTUATION ;

Visual: Finite Automaton for Identifiers

Real-World Link:

  • GCC’s cpp preprocessor performs lexical analysis before compilation.
  • Pathao’s ride-matching system uses tokenization to parse user inputs (e.g., pickup: "Kathmandu" → TOKEN: LOCATION).

2. Syntax Analysis (Parser)

Goal: Validate grammar and build a parse tree or abstract syntax tree (AST). How it works:

  • Uses grammar rules (e.g., BNF) to check if tokens form valid sentences.
  • Two main methods:
    • Top-down parsing (LL parsers, e.g., recursive descent).
    • Bottom-up parsing (LR parsers, e.g., shift-reduce).

Example Grammar (Arithmetic Expressions):

E → T E'
E' → + T E' | ε
T → F T'
T' → * F T' | ε
F → ( E ) | id

Input: a + b * c Parse Tree:

Real-World Link:

  • Ncell’s billing system uses parsers to validate user commands (e.g., balance check).
  • Daraz’s order processing parses customer inputs (e.g., add item: "iPhone 15" → validates syntax before processing).

3. Semantic Analysis

Goal: Attach meaning to the parse tree (e.g., type checking, scope resolution). Key Tasks:

  • Symbol table management: Tracks variables/functions (name, type, scope).
  • Type checking: Ensures operations are valid (e.g., int + string → error).
  • Scope rules: Resolves variable declarations (global vs. local).

Example:

int x = 5;
x = x + "hello"; // Error: Type mismatch (int + string)

Symbol Table:

Variable Type Scope
x int Global
y float Local

Visual: Symbol Table Update

Real-World Link:

  • Nepal Rastra Bank’s loan calculator performs semantic checks to ensure inputs (e.g., principal, interest_rate) are numeric and valid.
  • YouTube’s comment system uses semantic analysis to flag invalid inputs (e.g., SQL injection attempts).

4. Intermediate Code Generation

Goal: Produce portable intermediate code (e.g., three-address code, quadruples) for optimization. Example (Three-Address Code): Source: z = a + b * c Intermediate Code:

t1 = b * c
z = a + t1

Quadruple Representation:

Op Arg1 Arg2 Result
* b c t1
+ a t1 z

Real-World Link:

  • Google’s V8 JavaScript engine generates intermediate bytecode before JIT compilation.
  • NTC’s network traffic analyzer uses intermediate representations to detect anomalies in packet headers.

5. Code Optimization

Goal: Improve performance/size of intermediate code. Techniques:

  • Constant folding: x = 2 + 3 → x = 5.
  • Dead code elimination: Remove unreachable code.
  • Loop optimization: Unroll loops or use strength reduction.

Example: Before:

t1 = 2 + 3
x = t1

After (Constant Folding):

x = 5

Real-World Link:

  • WhatsApp’s end-to-end encryption uses optimization to reduce battery drain on mobile devices.
  • NEPSE’s stock trading platform optimizes queries to handle high-frequency trades efficiently.

6. Code Generation

Goal: Convert optimized intermediate code to target machine code (assembly or binary). Steps:

  1. Instruction selection: Map intermediate ops to machine instructions.
  2. Register allocation: Assign variables to CPU registers.
  3. Assembly generation: Produce assembly code (e.g., x86, ARM).

Example (x86 Assembly): Source: int sum = a + b; Assembly:

MOV EAX, [a]   ; Load 'a' into EAX
ADD EAX, [b]   ; Add 'b' to EAX
MOV [sum], EAX ; Store result in 'sum'

Real-World Link:

  • Daraz’s checkout system generates optimized assembly for fast payment processing.
  • Ncell’s SMS gateway compiles SMS parsing logic into efficient machine code.

Compilers vs. Interpreters

Feature Compiler Interpreter
Execution Entire program → machine code Line-by-line execution
Speed Faster (pre-compiled) Slower (runtime overhead)
Portability Limited (target-specific binary) High (runs on any system)
Error Handling All errors at once Errors during execution
Examples GCC, javac, Rust Python, JavaScript (Node.js)

Hybrid Approach:

  • Java: Compiled to bytecode → interpreted by JVM (or JIT-compiled).
  • C#: Compiled to IL → executed by CLR.

In the Real World

  1. eSewa (Nepal):

    • Uses GCC (C/C++) to compile backend services for high-speed transaction processing.
    • Lexical/syntax analysis validates user inputs (e.g., payment: "Rs. 500").
    • Optimization reduces latency during peak hours (e.g., Dashain).
  2. Khalti’s Mobile App:

    • Kotlin/Java compiler generates efficient bytecode for Android.
    • Symbol tables track user accounts and transaction histories.
    • Intermediate code enables cross-platform support (iOS/Android).
  3. Daraz’s Order Fulfillment:

    • Parser validates order syntax (e.g., add item: "iPhone 15, quantity: 2").
    • Code generation optimizes warehouse robot paths (e.g., shortest route to pick items).
  4. NTC’s Network Monitoring:

    • Lexical analyzer scans packet headers for anomalies.
    • Semantic checks ensure protocol compliance (e.g., TCP handshake).
  5. Nepal Rastra Bank’s Loan System:

    • Compiler optimizations reduce processing time for loan approvals.
    • Symbol tables track borrower details (e.g., loan_id, interest_rate).

Worked Example: Compiling x = a + b

Let’s trace the compilation of this statement through all phases.

Phase 1: Lexical Analysis

Input: x = a + b; Tokens:

Type Lexeme
IDENTIFIER x
OPERATOR =
IDENTIFIER a
OPERATOR +
IDENTIFIER b
PUNCTUATION ;

Phase 2: Syntax Analysis

Grammar:

S → ID = E
E → E + T | T
T → ID

Parse Tree:

Phase 3: Semantic Analysis

Symbol Table:

Variable Type Scope
a int Global
b int Global
x int Global

Phase 4: Intermediate Code (Three-Address)

t1 = a
t2 = b
t3 = t1 + t2
x = t3

Phase 5: Optimization

After constant folding (if a and b are constants):

x = 5 + 3  →  x = 8

Phase 6: Code Generation (x86 Assembly)

MOV EAX, [a]   ; Load 'a'
ADD EAX, [b]   ; Add 'b'
MOV [x], EAX   ; Store in 'x'

Exam Tip

  1. Define Key Terms:

    • Compiler: "A program that translates HLL to machine code."
    • Lexeme: "A sequence of characters in source code (e.g., if)."
    • Parse Tree: "A tree representing the syntactic structure of code."
  2. Phase Order: Memorize the 9 phases in sequence: Lexical → Syntax → Semantic → Intermediate → Optimization → Code Generation.

  3. Diagrams Are Critical:

    • Draw parse trees for syntax questions.
    • Show symbol table updates for semantic analysis.
    • Illustrate finite automata for lexical analysis.
  4. Real-World Applications:

    • Link compilers to Nepali systems (eSewa, Khalti, NTC).
    • Explain how optimization reduces latency in Daraz/Ncell.
  5. Common Pitfalls:

    • Confusing interpreters (execute line-by-line) with compilers (full translation).
    • Forgetting semantic analysis (type checking, symbol tables).
    • Skipping optimization in intermediate code questions.
  6. Practice Questions:

    • Given a grammar, build a parse tree for a + b * c.
    • Trace lexical analysis for while (x > 0) { x--; }.
    • Optimize the intermediate code: t1 = 2 * 3; y = t1 + 1.

Visual Summary:

mindmap
  root((Compiler Phases))
    Lexical Analysis
      Tokens
      Finite Automata
    Syntax Analysis
      Parse Trees
      Grammar Rules
    Semantic Analysis
      Symbol Tables
      Type Checking
    Intermediate Code
      Three-Address Code
      Quadruples
    Optimization
      Constant Folding
      Dead Code Elimination
    Code Generation
      Assembly
      Machine Code

Based on the PU BE Computer (PU) syllabus for Compiler Design (CMP360), unit 1.

Discussion

Loading…