Back to blog

1/1/2026

Inside Ferret: Compiler Architecture and Design

A deep dive into Ferret's multi-phase compiler architecture, from source code to native binary.

architecturecompilertechnical

Ferret’s compiler is built from the ground up with a focus on correctness, performance, and maintainability. This post explores the architecture that powers Ferret’s compilation from .fer source files to native executables.

Design Principles

The Ferret compiler follows several key principles:

  1. Per-Module Phase Tracking: Each source file progresses through compilation phases independently
  2. Parallel Processing: Modules are parsed in parallel with unbounded goroutines
  3. Thread-Safe Diagnostics: Error collection is thread-safe with proper mutex synchronization
  4. Topological Ordering: Phases respect module dependencies for correct compilation
  5. Multi-IR Architecture: Three intermediate representations (AST → HIR → MIR) for progressive lowering

Architecture Overview

Here’s the complete architecture diagram showing all compilation phases and data flow:

Ferret Compiler Architecture

Design Decisions

Why Three IRs?

  • AST: Preserves source structure for error reporting
  • HIR: High-level IR maintains readability for optimization passes
  • MIR: Low-level SSA suitable for code generation

Why QBE?

  • Mature Backend: Production-ready register allocation and optimization
  • Multi-Architecture: Single IR for x86_64, aarch64, riscv64
  • Simple Integration: Clean C API and straightforward IR format
  • Fast Compilation: No LLVM overhead

Why Go for the Compiler?

  • Concurrency: Built-in goroutines and channels for parallel compilation
  • Memory Safety: GC eliminates memory bugs in compiler code
  • Fast Compilation: Go compiles quickly, keeping development iteration fast
  • Standard Library: Excellent regex, parsing, and file I/O support

Why Per-Module Phases?

  • Incremental Compilation: Only recompile changed modules (future)
  • Parallel Processing: Modules can progress independently
  • Correct Dependencies: Topological ordering ensures correctness
  • Cache Friendly: Each module’s artifacts can be cached

Performance Characteristics

  • Parallel Parsing: Scales with available CPU cores
  • Lock-Free Reads: RWMutex allows concurrent phase queries
  • Minimal Allocations: Reuses symbol tables and scopes where possible
  • Fast Type Checking: Constant evaluation uses big integers but is still fast
  • Zero-Copy: Import paths used as map keys without string copying

Future Enhancements

  • Incremental Compilation: Cache module artifacts between builds
  • Language Server: IDE support with incremental reparsing
  • Better Optimizations: More sophisticated MIR transformations
  • LLVM Backend: Alternative to QBE for maximum optimization
  • Build System Integration: First-class build tool support

Conclusion

Ferret’s architecture balances simplicity with sophistication. The multi-phase pipeline with proper concurrency control enables fast, correct compilation while maintaining clear separation of concerns. The three-IR design allows progressive lowering from high-level semantics to low-level machine code while preserving enough information for high-quality error messages.

The result is a compiler that’s fast, correct, and maintainable - just like the language it compiles.


Want to contribute to the compiler? Check out the GitHub repository and read the CONTRIBUTING.md guide.