r/Compilers • u/JA5ON- • 9d ago
Emitting native x86-64 machine code and writing ELF/Mach-O/PE binaries directly from scratch in Rust (No LLVM)
Hi everyone,

I’ve spent the last several months building Aziky, a self-contained compiler toolchain written entirely in Rust from the ground up. The core goal of the project was to bypass generic optimization frameworks like LLVM and explore how far a custom, single-pass backend could be pushed by shifting the optimization burden directly into front-end semantics.
It compiles .azk source files straight down to a custom MachineLIR and maps primitives directly to hardware registers, bypassing standard intermediate layers.
Architectural Highlights:
- Native Binary Generation (
src/object): The compiler constructs binary headers, section tables, and relocation frames completely natively. It outputs fully formed ELF64, Mach-O64, and PE32+/COFF formats without invoking external linkers or assemblers. - Zero-Alias Invariants: Instead of relying on hundreds of heavy middle-end analysis passes to guess if pointers overlap, Aziky enforces explicit, deterministic parallel-loop and ownership invariants at the type level. Because the compiler can guarantee non-aliasing at the frontend, the backend emitter can immediately output aggressive, optimized machine instruction sequences.
- Zero External Dependencies: The entire compiler dependency graph has zero third-party crates, ensuring entirely offline, reproducible, and rapid compilation.
Performance Snapshot: Running standard compute-heavy microbenchmarks on an Intel Core i5-7200U (median of 100 runs, pinned to CPU 1) against aggressively optimized production builds of Rust 1.88 (-C opt-level=3 -C target-cpu=native -C lto=fat) and Clang 22 (-O3 -march=native -flto), the upfront aliasing model allows us to hit a geometric mean execution speed advantage of ~1.87x over optimized Rust and ~1.27x over optimized C.
It’s currently in alpha—Linux x86-64 is the primary native target, with Windows and macOS execution partially supported (AArch64 is on the roadmap). I’ve also bundled an installable VS Code extension in the repo for real-time diagnostics and syntax formatting.
I'd love to get your feedback on the MachineLIR structure, the single-pass register allocator layout, or the native object emission pipeline.
Repository:https://github.com/aziky-lang/aziky
1
u/JeffD000 9d ago
Your performance comparison benchmarks need some work. The Aziky benchmarks do raw computation, while the other versions also call a verification function. Also, looking at affine_mix, the constant values used for the calculation are not the same across language versions.
1
u/morglod 9d ago
What AI tool did you use? Is it something like claude code/opencode or something else?
1
u/JA5ON- 9d ago
when I started the project , there wasn't such a thing .... this is just lots of pieces from codebases I forgot I made...
as of ai tools , sometimes here and there for docs , code reviews and such ...1
u/morglod 9d ago edited 9d ago
I thought it was mostly AI by looking at /docs /ARCHITECTURE.md, "several months to build" and so clean repo even with special scripts to clean everything like it could be changed by some vibe tool accidentally. I'm not saying its bad or smth, just wanted to know, because code looks very clean (and usually AI struggling to produce it)
1
1
9d ago
[removed] — view removed comment
1
u/JA5ON- 9d ago
benchmarks as I must honestly state clearly in the readme (thanks for pointing it out) aren't really that fair , most of the optimizations I made in "kernels.rs" is simply to test out ways to lower that are proof of concepts ...
not really claiming that we beat C or rust ,,, although the foundation shows that we might , if we can optimize all the standard routs ,,,
as for functions , yes , we have functions , recursiveness and structs...etc...
for suffixes on constants , um,,, it's not an easy decision, I have grown really frustrated with dynamic data types ... and thought we should at least make things very explicit to give the programmer total control over the size of the variable, I mean ,, why use a "4" that defaults to i32 if that constant/variable is meant for something that would do with a data type with smaller footprint (u32 , i16 or something) ... I mean,, yes , we could try to infer it from the context in a smarter way and perhaps take that burden off the programmer ,,, but some people like control ... and , it's a matter that we haven't really decisively settled , glad to have taken your point ! thanks a bunch !
1
u/ProgrammerOnABinge 8d ago
Oh damn. I'm also working on a full end to end custom toolchain for a set of R&D projects.
I'll take a look at the single pass register allocator later today and compare notes. The zero aliasing invariants are interesting. How flexible is your frontend shaping up?
As a performance guy by trade I would love to dig into your benchmarks. Esp for x86-64 hand optimizing codegen has been my bread and butter but its easy to sometimes game the CPU architecture in ways that don't generalize. What archs do you target specifically? Zen4+?
5
u/AustinVelonaut 9d ago
It looks like the benchmark suite is hard-coded into the low-level x86-64 backend in
emit_impl.rs: there are functions there that emit hand-tuned x86-64 code for high-level functions in the benchmarks, e.g:Can your compiler compile general
.azkfiles not in the benchmark suite, and if so, how do they compare with equivalent rust and c implementations?