DEV Community

RC
RC

Posted on Originally published at randomchaos.us

crustc ports rustc to C and builds it with GCC

The crustc repository is C code that you compile with GCC and GNU make into a working Rust compiler. Point make at a matching LLVM build and the binary it produces reports rustc 1.98.0-nightly (c712ea946 2026-06-16), then goes on to build core, alloc and std. The author built it on ARM64 Linux with GCC 13.3.0 and the LLD 18.1.3 linker. An unoptimized build takes about 78 seconds of wall time (937 seconds of user time with make -j20). It still needs LLVM at runtime, because rustc uses LLVM, so the repo asks you to supply libLLVM.so.22.1-rust-1.98.0-nightly from the matching nightly toolchain rather than shipping a binary.

That is rustc itself, translated to C and built by a C compiler. The repo is a demo for cilly, the author's Rust-to-C toolchain and, by their count, the 14th attempt at the problem over three years (rustc_codegen_clr was an earlier public one). cilly is a Rust library that generates C plus a rustc backend plugin. Instead of handing MIR to LLVM for machine code, it emits C and passes that to whatever C compiler you nominate for the target. Compiling rustc with itself through that path is the flashiest thing to show, so that is what the repo does.

The useful engineering is that cilly does not assume a fixed C dialect. It emits "witness" programs and compiles them to learn what the target compiler and platform actually support: a file containing _Thread_local int KEYWORD_TLS_SUPPORTED; only compiles if the compiler has _Thread_local, so that tells cilly whether it can use the keyword. It queries type layouts, sizes, alignments, character encoding (ASCII) and integer representation (two's complement), with fallbacks where it can. The author aims to assume nothing outside ANSI C, works around newer-standard hazards like strict aliasing, and documents the few unavoidable assumptions ((void*)(uintptr_t)(ptr) round-trips, CHAR_BIT == 8) with runtime asserts where possible. The cost is that output is target-specific. C generated for Arm64 will not run on riscv32; you regenerate per target.

cilly can also talk to a C compiler over TCP, with UART floated as a possibility. You run rustc on an ordinary Linux box and let cilly drive a small C server on the target, which covers platforms that have no C cross-compiler at all. The author used this to build small Rust programs for x86 Plan 9 VMs from an Arm64 Linux host; the binary printed Hello, world! and carried a rust_begin_unwind symbol. cilly can embed markers in its object files, cache its IR, and emit a directory of makefiles, so the end product is a tree you build with a C compiler and make.

Compiling Rust through C does not undo Rust's safety guarantees. The borrow checker and type system run in rustc's frontend, which cilly does not touch; crustc is rustc, so those checks still happen before any C is emitted. The change is in the codegen trust base: you are now relying on cilly's translation and a C compiler to be correct, on top of the documented assumptions above. The author is candid that the demo is rough. They are chasing optimization-related bugs and recommend building without optimizations, both because enabling them "may break stuff" and because they choke on larger Rust files. Generated code is mostly ABI-compatible with normal rustc output, with a known exception on Arm64, where rustc picks an sret convention that C cannot express for small structs under 16 bytes.

The target user is anyone who maintains Rust that people keep asking to run on old or obscure hardware, the recurring "Rust doesn't support Plan 9" objection whenever a project moves off C. The stated goal is targets that have a C compiler but no LLVM or GCC Rust backend. It is not there yet. The author says cilly is not ready for public consumption and that real use waits on the full toolchain. crustc is the evidence it works: a Rust compiler that builds from C with stock GCC and make, self-hosting included.

Top comments (0)