Yasir Explains/Compiler Design/Structure of Compiler/Types of Compilers
Structure of Compiler

Types of Compilers

On this page

Why Are There Different Types?Single-Pass CompilerMulti-Pass CompilerCross CompilerJust-In-Time (JIT) CompilerSource-to-Source Compiler (Transpiler)Incremental Compiler
Structure of Compiler

Types of Compilers

Not all compilers work the same way. Learn the different types — single-pass, multi-pass, cross, JIT, and transpilers — and when each one is used.

Why Are There Different Types?

Different problems call for different compiler strategies. A compiler for an embedded microcontroller has very different needs from one that runs inside a web browser.

Compilers are classified by:

  • How many passes they make over the source code
  • What target they produce code for
  • When they translate the code (ahead of time vs at runtime)
  • What level of source code they accept

Single-Pass Compiler

A single-pass compiler reads the source code exactly once from top to bottom and produces the output in one sweep.

  • Fast and uses little memory
  • Limited: it cannot look ahead or back, so some optimizations are impossible
  • Requires the programmer to declare things before using them

Example: Early C compilers, Pascal compilers

Source Code → [Read once, top to bottom] → Machine Code

Think of it like reading a recipe and cooking at the same time — you cannot go back to re-read step 2 after you have already moved to step 5.

Multi-Pass Compiler

A multi-pass compiler reads the source code several times, each pass doing a different job.

  • More powerful — can perform deeper analysis and better optimization
  • Uses more memory (stores intermediate results between passes)
  • Most modern compilers are multi-pass
Pass 1 → Build symbol table Pass 2 → Type checking and semantic analysis Pass 3 → Generate intermediate code Pass 4 → Optimize Pass 5 → Generate machine code

Example: GCC, LLVM (Clang), Java compiler (javac)

Cross Compiler

A cross compiler runs on one machine but produces code for a different machine (different CPU or OS).

  • Used when developing software for devices that cannot compile their own code (e.g., tiny microcontrollers, game consoles, embedded systems)
  • The host machine (where you develop) is different from the target machine (where the code runs)
Developer's PC (x86) ↓ [Cross Compiler] ↓ Code for ARM microcontroller

Example: Arm-none-eabi-gcc (compiles C for ARM from a PC), Android NDK toolchain

Just-In-Time (JIT) Compiler

A JIT compiler compiles code at runtime, right before it is executed — combining the benefits of both compilers and interpreters.

  • Starts executing quickly like an interpreter
  • Compiles hot (frequently-run) code paths into fast machine code
  • Gets faster the longer the program runs (warm-up period)
Bytecode / IR ↓ [JIT detects hot code] ↓ Compile to native machine code on the fly ↓ Run the native code directly

Example: JVM HotSpot (Java), V8 (JavaScript/Node.js), .NET CLR (C#)

Source-to-Source Compiler (Transpiler)

A transpiler (source-to-source compiler) translates source code in one high-level language into source code in another high-level language.

  • Output is still readable, human-editable code
  • Often used to add features to a language or target an older standard
Input LanguageTranspilerOutput Language
TypeScripttscJavaScript
CoffeeScriptcoffeeJavaScript
SassSass compilerCSS
C++ (modern)EmscriptenWebAssembly / JS

Example: TypeScript compiler (tsc), Babel (modern JS → older JS)

Incremental Compiler

An incremental compiler only recompiles the parts of a program that changed since the last build.

  • Saves a lot of time in large projects with many files
  • Tracks dependencies between files
  • Recompiles only affected modules

Example: Most IDE build systems (VS Code tasks, Gradle for Java), TypeScript in watch mode (tsc --watch)

Without incremental compilation, rebuilding a 1-million-line project after changing one line would recompile everything — which could take minutes.