Ferry: A C-to-RISC-V Compiler in Rust with a Two-Tier Optimizing Intermediate Representation
Anubhab Patnaik · Zenodo (CERN European Organization for Nuclear Research) · 2026
We present Ferry, a compiler for a subset of C targeting RISC-V assembly, implemented in approximately 9,500 lines of Rust. Ferry implements the full classical pipeline - lexical analysis, recursive-descent parsing, semantic analysis with a symbol table and type checker, lowering to a tree-structured intermediate representation, optimization, and RISC-V code generation - with no dependency on LLVM or any external compiler infrastructure. Its distinguishing design choice is a two-tier optimizer: a basic tier performing constant folding and propagation, dead code elimination, common subexpression elimination and loop analysis, followed by an advanced tier that adds reachability-based dead code elimination, loop-invariant code motion, strength reduction, loop unrolling, and deterministic-loop elimination, each applied under fixed-point iteration. We evaluate on nine C programs by instrumenting the IR at all three pipeline stages. The optimizer removes between 12.8% and 95.2% of IR nodes on programs with optimizable structure, with the advanced tier's deterministic-loop elimination reducing one program from 42 nodes to 2. We also report, and diagnose, a soundness defect that the evaluation exposed: on three of seven compiling programs the dead-store elimination removes the definition of a local variable that remains live at a later use, and the code generator then emits an address-of reference to a symbol that was never allocated, producing assembly that assembles but fails to link. We trace this to the absence of a use-def dependency between the optimizer's liveness analysis and the code generator's stack-slot allocator, and argue it is the principal structural argument for static single assignment form in a compiler of this design.