Architecture

Compiler Pipeline

ts-aot uses a multi-stage compilation pipeline:

TypeScript Source
    |
    v
Native C++ Parser       -- Hand-written recursive descent (~4,300 LOC)
    |
    v
AST                     -- TypeScript-compatible abstract syntax tree
    |
    v
Type Analyzer           -- Type inference, monomorphization, module resolution
    |
    v
HIR (High-level IR)     -- SSA-based intermediate representation
    |   |
    |   v
    |  HIR Optimization Passes (8 passes)
    |
    v
LLVM IR                 -- Generated via LLVM 18 C++ API
    |
    v
LLVM Optimization       -- O0-O3 pipeline with runtime-aware annotations
    |
    v
Native Object Code      -- x86-64 machine code
    |
    v
LLD Linker              -- Link with runtime + extensions
    |
    v
Standalone Executable

Parser

A hand-written C++ recursive descent parser (~4,300 lines) handles TypeScript and JavaScript syntax. It produces a TypeScript-compatible AST without depending on Node.js or the TypeScript compiler.

Type Analyzer

The analyzer performs type inference, resolves modules, and monomorphizes generic functions. TypeScript types drive code generation — typed code gets optimized paths while any falls back to boxed values.

HIR Pipeline

The High-level Intermediate Representation uses SSA (Static Single Assignment) form with 8 optimization passes:

PassPurpose
TypePropagationPropagate type information across SSA
IntegerOptimizationSpecialize number operations to i64
ConstantFoldingEvaluate compile-time constants
DeadCodeEliminationRemove unreachable code
InliningInline small functions
EscapeAnalysisStack-allocate non-escaping objects
MethodResolutionDevirtualize method calls
BuiltinResolutionLower built-in calls (Math, Array, etc.)

LLVM Backend

The HIR is lowered to LLVM IR using the LLVM 18 C++ API (opaque pointers). The full LLVM optimization pipeline (O0-O3) is available, with runtime-aware function annotations that help LLVM’s passes.

Runtime

Every compiled executable links against a C++ runtime library:

ComponentDescription
TsGCCustom mark-sweep GC with generational nursery (4MB), conservative stack scanning
TsStringICU-based Unicode strings with NFC normalization
TsArrayDynamic arrays with typed specialization
TsMap / TsSetHash-based collections
TsFlatObjectShape-based objects with NaN-boxed inline slots and vtable dispatch
TsClosureClosure capture with cell-based upvalues
TsPromisePromise/async-await via libuv event loop
TsRegExpICU-based regular expressions
TsBigIntArbitrary-precision integers via libtommath
TsProxy / TsReflectES6 Proxy and Reflect

Memory Management

ts-aot uses a custom garbage collector (not Boehm GC):

  • Mark-sweep collector with 16 size classes (8-4096 bytes)
  • Generational nursery (4MB) with pin-based promotion
  • Conservative stack scanning via setjmp register flushing
  • Escape analysis enables stack allocation of short-lived objects
  • NaN-boxing for compact value representation

Extension System

Node.js APIs are implemented as C++ extension libraries with JSON contracts:

extensions/node/
├── fs/          # File system (sync + async)
├── http/        # HTTP server and client
├── net/         # TCP sockets
├── crypto/      # Hashing, HMAC, random, KDF
├── stream/      # Readable, Writable, Duplex, Transform
└── ...          # 32 modules total

Each module has an .ext.json contract defining function signatures, which enables the compiler to generate direct native calls instead of dynamic dispatch.

Static Linking

All dependencies are statically linked:

LibraryPurpose
libuvEvent loop and async I/O
ICUUnicode string handling
OpenSSLTLS/crypto
llhttpHTTP parsing
c-aresAsync DNS
nghttp2HTTP/2
zlib + brotliCompression
libsodiumCryptographic primitives
libtommathBigInt support