crustc: entirety of `rustc`, translated to C

crustc is a fully functional Rust compiler translated entirely into C, allowing it to be built using GCC and make. Powered by the 'cilly' toolchain, this project aims to bring Rust support to legacy and obscure platforms that lack LLVM backend support.
This is a functional Rust compiler you can build with GCC & make.
# We need to provide a path to LLVM(`libLLVM.so.22.1-rust-1.98.0-nightly`)
# I *could* include pre-built LLVM in the project, but I'd rather not embed random binaries in the project.
make -j20 LLVM_LIB_DIR=~/.rustup/toolchains/nightly-2026-06-16-aarch64-unknown-linux-gnu/lib
It is just C code, which, when compiled, gives you a functional Rust compiler.
# It works - (library path to point to libLLVM.so.22.1-rust-1.98.0-nightly - rustc uses llvm)
LD_LIBRARY_PATH=~/.rustup/toolchains/nightly-2026-06-16-aarch64-unknown-linux-gnu/lib:./rustc_driver ./rustc/rustc --version
rustc 1.98.0-nightly (c712ea946 2026-06-16)
That Rust compiler can compile code - build core, alloc, std - you name it!
This is a demo/teaser for my new Rust to C compiler toolchain. The full cilly toolchain compiles your own Rust to C for arbitrary targets. This repo just shows the compiler compiling itself, as I believe this is the flashiest showcase I could do.
For the past 3 years, I have been working on compiling Rust to C. I made a few public attempts, like rustc_codegen_clr, and a lot of private ones.
This is, by my count, the 14th attempt: cilly. It is a Rust library for generating C code and a Rust compiler backend (read: plugin) that allows you to compile Rust to C.
The main innovation behind cilly is that it adapts to C compilers.
It can generate "witness" programs, which check what a given compiler and platform support:
/* This compiles if and only if our C compiler supports _Thread_local. */
_Thread_local int KEYWORD_TLS_SUPPORTED;
This means Cilly will generate C code, which will make your specific, weird "Shminky C compiler for Blorbo OS" happy.
/* This will pass in some C compilers. */
assert(sizeof(float) == sizeof(double));
All type layouts, sizes, alignments, character encodings (ASCII), and integer formats (two's complement) are queried for.
With fallbacks, where possible.
I try my very best not to assume anything outside of ANSI C - including workarounds for things in "modern" C standards, like strict aliasing.
Sadly, this means the output of cilly is compiler-specific (i.e., you can't take the cilly C generated for Arm64 and run it on riscv32, but you can generate cilly C specifically for riscv32).
This build of rustc (the generated C) is "targeting" ARM64 Linux because that is the ISA of my workstation.
The primary goal of this is support for old/obscure hardware with no LLVM/GCC support. There are still some systems out there that don't support Rust but support C.
Whenever some project moves from Rust to C, or a Rust alternative of a C project is made, support for those targets is validly raised as a downside of Rust.
The goal of this project is to remove that problem.
cilly wraps rustc and a C compiler and translates the Rust code to C on the fly. From the user perspective, this is as simple as defining what C compiler to use for a given target.
"triple": [
"sdcc_z180-unknown-none"
],
"tool_def": {
"kind": "local",
"compile": {
"base": {
"executable": "/usr/bin/sdcc",
"base_args": [
"-mz180",
"--std-c89",
"-c"
],
"input_arg_template": [
"{input}"
]
}
}
}
cilly is network transparent, and can talk to C compilers over TCP (may be extended to weird things like UART if need be).
This is a solution to the bootstrap paradox / platforms without C cross compilers.
You build a small C server on your Blorbo OS, run rustc on some normal platform like Linux, and let cilly talk over the wire.
I have successfully used this to compile small Rust programs for x86 Plan9 VMs, while running rustc on Arm64 linux.
term% echo `{cat /dev/sysname} osversion `{cat /dev/osversion} cputype $cputype
gnot osversion 2000 cputype 386
term% /tmp/hello_plan9
Hello, world!
term% nm /tmp/hello_plan9 | grep rust_begin_unwind
1020 T _RNvCshfEkAwg4zv6_7___rustc17rust_begin_unwind
cilly can optionally embed markers within its object files, and save its IR to a cache directory. It can then read those markers, split functions / globals by their definition location, and generate a directory with makefiles - to allow you to build Rust with a C compiler and make.
cilly generated code is mostly ABI compatible with normal rustc compiled code. I say mostly, because on some platforms(... like arm64) rustc choose an ABI not representable from C.
This rust compiler was built on:
uname -a
Linux spark-2773 6.17.0-1021-nvidia #21-Ubuntu SMP PREEMPT_DYNAMIC Wed May 27 19:14:05 UTC 2026 aarch64 aarch64 aarch64 GNU/Linux
This is the C compiler I used:
readelf -p .comment ./rustc/rustc
String dump of section '.comment':
[ 1] GCC: (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
[ 2e] Linker: Ubuntu LLD 18.1.3
In order to build the demo, you will need to provide it with the right LLVM libs. The easies way to do so is to just use the LLVM rustc ships:
rustup install nightly-2026-06-16
With the right GCC(more modern GCC versions should work too, untested), right LLVM version, and GNU make installed, run:
# We need to provide a path to LLVM(`libLLVM.so.22.1-rust-1.98.0-nightly`)
# I *could* include pre-built LLVM in the project, but I'd rather not embed random binaries in the project.
make -j20 LLVM_LIB_DIR=~/.rustup/toolchains/nightly-2026-06-16-aarch64-unknown-linux-gnu/lib
# CFLAGS work(CAVEAT: some flags will slow compilation down)
make -j20 CFLAGS=-g
And... voilà!
I strongly recommend not enabling optimizations: both because the may break stuff(this is just a demo, and it's... ee... rough around the edges) AND because optimizations take time at this scale.
Without opts, my machine builds the project in a few minutes:
make -j20 937.98s user 123.77s system 1352% cpu 1:18.48 total
With opts, expect to choke on some specific larger rust files. You will blitz through most code, and then get stuck on those behemoths.
Run:
LD_LIBRARY_PATH=~/.rustup/toolchains/nightly-2026-06-16-aarch64-unknown-linux-gnu/lib:./rustc_driver ./rustc/rustc --version
You should see the rustc version printed.
For building programs, you will need to build std.
LD_LIBRARY_PATH=~/.rustup/toolchains/nightly-2026-06-16-aarch64-unknown-linux-gnu/lib:./rustc_driver ./rustc/rustc main.rs
error[E0463]: can't find crate for `std`
|
= note: the `aarch64-unknown-linux-gnu` target may not be installed
= help: consider downloading the target with `rustup target add aarch64-unknown-linux-gnu`
= help: consider building the standard library from source with `cargo build -Zbuild-std`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0463`.
Read BUILDING_STD.md for that.
For some weird path-canonicalization-reasons, crustc can crash when run in the directory it was built in(repo root, crustc). Works fine in other places.
...
I am confused too.
Nah, not yet. It is not ready for public consumption. I will release it as soon as possible - but
- I got a job(which means I no longer write code on a laptop with it's G & C keys broken, yay!)
- I got uni(well I am on summer break, but thesis-s don't tend to write themselves).
- I put my left hand in a blender. The blender won. (Still have all my fingers, just some stitches). I will not elaborate further.
Source: Hacker News











