Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Compiler Overview

A compiler is a program that translates source code written in a high-level programming language into a lower-level representation—machine code, bytecode, or another high-level language. Compilers are fundamental infrastructure: every program you run on a CPU was translated by one.

Why Compilers Matter

  • Performance: Compilers exploit hardware features (pipelines, SIMD, caches) that humans cannot manage manually at scale.
  • Portability: Write once in C, compile for x86, ARM, RISC-V, and WebAssembly.
  • Correctness: Static analysis at compile time catches bugs before runtime.
  • Abstraction: Programmers use high-level constructs; compilers lower them to machine instructions.

The Compilation Pipeline

flowchart LR
    A[Source Code] --> B[Lexical Analysis<br/>Tokens]
    B --> C[Syntax Analysis<br/>AST]
    C --> D[Semantic Analysis<br/>Typed AST]
    D --> E[IR Generation<br/>Three-Address Code]
    E --> F[Optimization<br/>SSA Transforms]
    F --> G[Code Generation<br/>Assembly]
    G --> H[Assembly / Linking<br/>Executable]
PhaseInputOutputKey Data Structure
Lexical AnalysisSource textToken streamFinite automaton / DFA
ParsingToken streamParse tree / ASTContext-free grammar
Semantic AnalysisASTDecorated (typed) ASTSymbol table
IR GenerationTyped ASTThree-address code / SSAControl flow graph
OptimizationIROptimized IRData-flow analysis frameworks
Code GenerationIRAssembly / machine codeRegister allocator, instruction selector
LinkingObject filesExecutable / librarySymbol resolution tables

Interpreted vs. Compiled vs. JIT

CriterionInterpretedCompiled (AOT)JIT
Translation timingAt runtime, line by lineBefore executionDuring execution, after profiling
Startup costLowLow (pre-compiled)Higher (warm-up phase)
Peak performanceSlowestHighCan exceed AOT via profile data
ExamplesPython (CPython), RubyC (GCC), Rust (rustc)Java HotSpot, V8, PyPy
PortabilityHigh (source ships)Requires per-target buildBytecode is portable; native code at runtime

In practice, most modern runtimes use a hybrid approach: Python compiles to .pyc bytecode, Java compiles to JVM bytecode then JIT-compiles hot paths to native code.

Bytecode and Virtual Machines

Bytecode is an intermediate instruction set designed for a virtual machine (VM) rather than physical hardware. This provides:

  • Platform independence: JVM bytecode runs on any OS with a JVM.
  • Security: VMs can sandbox untrusted code (e.g., Java security manager).
  • Optimization surface: The VM can profile and recompile bytecode at runtime.

Common examples: JVM bytecode, CPython .pyc, .NET CIL, WebAssembly (Wasm).

Cross-Compilation

Cross-compilation is building an executable for a target architecture different from the host running the compiler. This is essential in embedded systems (e.g., compiling ARM firmware from an x86 workstation).

# Example: Cross-compile C for ARM from x86
arm-linux-gnueabihf-gcc -o hello_arm hello.c -static

# Verify with file(1)
file hello_arm  # ELF 32-bit LSB executable, ARM, EABI5

Challenges include differing calling conventions, endianness, word size, and available system libraries. Toolchains like crosstool-NG and build systems like CMake simplify this.

Major Compiler Infrastructure

CompilerLanguage(s)BackendNotes
GCCC, C++, Fortran, Go, DTarget-specificGNU toolchain; gcc.gnu.org
Clang/LLVMC, C++, Rust (via rustc), SwiftLLVM IRModular, library-based; llvm.org
rustcRustLLVM IRBorrows LLVM for codegen
Go toolchainGoGo internalSelf-hosting; fast compilation
GraalVMJava, polyglotTruffle/GraalAOT compilation for JVM languages

References

Interview Questions

  1. What are the phases of a compiler? Name each phase and what it produces.
  2. How does a JIT compiler differ from an AOT compiler? Discuss warm-up time, peak performance, and profiling.
  3. Why does LLVM use an intermediate representation? Portability, shared optimization passes, retargetability.
  4. What is cross-compilation? When would you need it? Embedded systems, mobile, heterogeneous targets.
  5. Can a language be both compiled and interpreted? Give an example.** Java: compiled to bytecode, interpreted/JIT-compiled by the JVM. Python: compiled to .pyc, interpreted by CPython.