Native Compilation

The subc compiler is SUB's native code generator. It compiles .sb source directly to x86-64 machine code and writes a complete ELF64 executable — no assembler, no linker, no libc required.

Usage

$ subc program.sb -o program
$ ./program

The output is a self-contained static ELF binary, typically a few kilobytes in size.

The Native x86-64 Backend

Unlike traditional compilers that emit assembly text and delegate to an assembler and linker, subc emits machine code bytes directly into memory buffers. The entire compilation pipeline runs in a single process:

  1. Parse — build the AST from source
  2. Codegen — walk the AST and emit x86-64 machine code bytes
  3. ELF emit — write the ELF64 header, program headers, sections, and raw code to disk

Why no assembler? Emitting machine code directly eliminates toolchain dependencies. The resulting binary has zero runtime dependencies — no libc, no dynamic linker, nothing. It just runs.

ELF64 Output Format

The compiler writes a valid ELF64 executable that the Linux kernel can load directly. The ELF structure includes:

ComponentDescription
ELF header64-byte header identifying the file as an x86-64 ELF executable
Program headersLOAD segments for code (text) and data (BSS)
Text sectionExecutable machine code
Data sectionRead-only data (string literals, constants)
BSS sectionUninitialized data (heap, variables)
Entry pointAddress of the _start routine

Memory Layout

The compiled binary uses a fixed memory layout:

┌─────────────────────────────────┐ 0x400000
│         .text (code)            │  Machine code, read-only, executable
├─────────────────────────────────┤ 0x500000
│         .bss (data)             │  Heap + static variables
│     (64 MB bump heap)           │
└─────────────────────────────────┘
RegionBase addressDescription
Text0x400000Machine code. Loaded as read-only + executable.
BSS0x500000Data segment. Contains the bump-allocator heap.
Heap0x50000064 MB bump heap for dynamic allocations (strings, arrays, objects).

Limitation: The 64 MB bump heap is a simple allocator — it does not free memory. This is fine for small programs but means long-running programs with heavy allocation may exhaust the heap. A garbage collector is planned for a future release.

Runtime Routines

Since the compiled binary has no libc, the compiler emits small runtime routines directly into the text section. These handle:

These routines are written in x86-64 machine code and linked directly into every binary. They add only a few hundred bytes to the output.

C Fallback Backend

For platforms where the native x86-64 backend is not available (macOS, Windows, FreeBSD, non-x86 architectures), subc provides a C fallback:

$ subc program.sb --via-c -o program

This mode generates C source code, then invokes the system C compiler (GCC or Clang) to produce the final binary. The C backend is portable and works on any platform with a C compiler.

Tip: The --via-c flag is automatically selected on non-Linux-x86-64 platforms. You only need to specify it explicitly if you want C output on Linux.

How the C fallback works

  1. Parse the .sb source into the AST
  2. Walk the AST and emit C source code to a temporary file
  3. Invoke cc (or the compiler specified by $CC) to compile the C file
  4. Link the result into a native binary
  5. Clean up the temporary C file

Platform Support

PlatformNative backendC fallback
x86-64 Linux✓ (direct ELF emission)✓
x86-64 macOS—✓
ARM64 macOS—✓
x86-64 Windows—✓
x86-64 FreeBSD—✓
Other—✓ (if C compiler available)

Binary Size

Compiled SUB binaries are remarkably small because they include only the runtime routines they actually use:

$ subc hello.sb -o hello
$ ls -la hello
-rwxr-xr-x  1 user user  3842 Jan 15 10:30 hello

$ file hello
hello: ELF 64-bit LSB executable, x86-64, statically linked, stripped

A "Hello, World!" program compiles to under 4 KB.

Next Steps