DEV Community

Cover image for How I Built a Systems Programming Language in 100% Pure x86-64 Assembly (And Made It Self-Host)
Tek Raj Awasthi
Tek Raj Awasthi

Posted on

How I Built a Systems Programming Language in 100% Pure x86-64 Assembly (And Made It Self-Host)

Modern systems programming languages like Rust, Go, Zig, and Swift rely heavily on massive compilation infrastructures—primarily LLVM (millions of lines of C++) or vendor C runtimes (libc, glibc, msvcrt).

While these toolchains provide deep optimizations, they also bring noticeable baggage: multi-gigabyte compiler installations, slow compilation times, non-trivial binary bloat, and dependency layers that obscure what the CPU is actually doing.

I wanted to explore an alternative question:

Can we build a fast, modern, statically typed systems programming language with zero C, zero libc, zero LLVM, and a compiler written in 100% pure x86-64 assembly?

That experiment became Rubix (.ix).


What Makes Rubix Different?

1. Zero C / Zero libc Dependency

Rubix does not link against libc. All console I/O, process management, and memory allocation talk directly to the OS kernel:

  • Linux: Native syscall instructions (sys_read, sys_write, sys_open, sys_mmap, sys_exit).
  • Windows: Cross-compiles native PE64 .exe binaries with direct Win32 kernel32.dll API resolution (VirtualAlloc, WriteFile, ExitProcess) using the Microsoft x64 calling convention.
  • macOS: Darwin 64-bit kernel syscall convention.

2. The 55 KB Compiler

Because the compiler has zero external baggage:

  • The entire native compiler binary is ~55 KB (smaller than a typical "Hello World" executable in Go or Rust).
  • It compiles from source assembly in under 0.15 seconds.
  • Emitted binaries start up in 0.3 ms with less than 2 MB of RAM.

What Rubix Code Looks Like

Rubix combines the clean, expressive syntax of modern languages with low-level systems control:

struct Vec2 {
    x: float,
    y: float
}

fn length_squared(v: Vec2): float {
    return (v.x * v.x) + (v.y * v.y);
}

fn main() {
    let point = Vec2 { x: 3.0, y: 4.0 };
    let d2 = length_squared(point);

    print_str("Distance squared: ");
    print_float(d2);
    print_str("\n");
    return 0;
}

Enter fullscreen mode Exit fullscreen mode

Features already implemented and verified in the test suite:

64-bit Integers & IEEE 754 Doubles: Hardware SSE2 floating-point math (xmm0-xmm7).
Named Structs: Multi-field structs with dot access (point.x).
Dynamic Heap Memory: alloc(bytes) and free(ptr, bytes) using direct OS virtual memory.
Control Flow: if/else, while, and C-style for loops with break/continue.
Debugging: Full DWARF debug directives emitted for step-through debugging in GDB and LLDB.
Modules: Multi-file imports (import "lib.ix";).
The Big Milestone: Self-Hosting Bootstrap Alpha
Writing a compiler in assembly is fun, but the ultimate test of any language is self-hosting—can the language compile itself?

Inside rubixc_bootstrap/, we wrote the compiler pipeline (lexer, symbol table with lexical scoping, recursive-descent parser, and assembly emitter) in pure Rubix (.ix).

The bootstrap loop works end-to-end:

The native assembly compiler compiles rubixc_bootstrap/src/main.ix into a native compiler executable (rubixc_native).
rubixc_native parses user source code and emits functional x86-64 assembly.
The resulting binary executes natively and runs calculations accurately.
All 17/17 core regression test suites pass cleanly.

Where We Are Going & How to Contribute
Rubix is 100% open-source under the MIT license: 👉 GitHub: https://github.com/tek-sys-hub/Rubix-lang

We are looking for systems developers, compiler engineers, and low-level enthusiasts to collaborate on:

Linear-Scan / Graph-Coloring Register Allocator: Moving our expression evaluator from the stack to hardware registers (rax-r15).
Foreign Function Interface (C-FFI): Adding extern fn support (System V / Win64 calling conventions) to link with Raylib, SDL2, and SQLite for native 2D/3D games and GUIs.
Memory Allocator & Standard Containers: Writing custom Arena/Slab allocators, dynamic Vector, and HashMap in pure Rubix.
WebAssembly (WASM) Backend: Emitting standalone .wasm binary bytecode.

If you enjoy low-level programming or compilers, clone the repo, run ./test.sh, and feel free to open a PR or share your thoughts!

Publishing this on Dev.to takes under 60 seconds and gets your project in front of thousands of developers immediately!

Top comments (0)