crustc: rustc全体をCに変換
(github.com/FractalFir)crustcは、rustc 1.98.0-nightly (c712ea946 2026-06-16)全体を 4,600万行のCコード に変換したデモで、GCCとmakeでビルドすると動作する Rust コンパイラが生成される- 基盤ツールである
cillyは、Rust を C にコンパイルする Rust コンパイラバックエンド であり、このリポジトリはコンパイラが自分自身をコンパイルする最も目立つショーケースとして構成されている cillyは対象の C コンパイラとプラットフォームの型レイアウト、サイズ、アラインメント、文字エンコーディング、整数形式などを witness プログラム で問い合わせ、特定の C コンパイラが受理できる C コードを生成する- 主な目標は、LLVM/GCC のサポートはないが C コンパイラはある 古い、または特殊なハードウェア で Rust を使えるようにすることで、TCP 経由でリモートの C コンパイラと通信するネットワーク透過性も含まれる
- 現在生成される C は、作者のワークステーションの ISA である ARM64 Linux を対象としており、
cillyのツールチェーン全体はまだ一般公開向けの準備ができておらず、最適化関連のバグも追跡中である
rustcをCに変換したデモ
crustcは、rustc 1.98.0-nightly (c712ea946 2026-06-16)を 4,600万行のCコード に変換したリポジトリである- この C コードは
GCCとmakeでビルドでき、ビルド結果は動作する Rust コンパイラ になる - 実行例では LLVM ライブラリのパスを指定したうえで
./rustc/rustc --versionを実行し、同じrustc 1.98.0-nightlyバージョンを出力している - 生成された Rust コンパイラは、コードのコンパイル、
core、alloc、stdのビルドが可能である - コードには C コードのほかに一部の C++ LLVM ラッパー も含まれる
- Rust が LLVM の一部機能を公開するために C++ を使っている
- そのラッパーは LLVM バージョンに依存しており、単独ビルドが煩雑なため、事前コンパイル済みの状態で提供される
cillyの役割
crustcは、新しい Rust-to-C コンパイラツールチェーンcillyのデモ / ティーザーであるcillyツールチェーン全体は、ユーザーの Rust コードを任意のターゲット向けに C へコンパイルすることを目指している- このリポジトリは、
cillyがコンパイラ自身をコンパイルする様子を示すために構成されている cillyは Rust ライブラリであり Rust コンパイラバックエンドでもあり、つまりプラグインの形で Rust を C にコンパイルする- 作者は過去 3 年間 Rust を C にコンパイルする作業を続けており、
rustc_codegen_clrのような公開された試みや複数の非公開の試みを経て、cillyが 14 回目の試みだと述べている
Cコンパイラに合わせてコードを生成する仕組み
cillyの主な特徴は、C コンパイラに適応 する点である- 特定のコンパイラとプラットフォームが何をサポートしているかを確認する witness プログラム を生成できる
- 例として
_Thread_local int KEYWORD_TLS_SUPPORTED;があり、その C コンパイラが_Thread_localをサポートする場合にのみコンパイルされる
- 例として
cillyは、特定の C コンパイラが受理できる C コードを生成しようとする- 型レイアウト、サイズ、アラインメント、文字エンコーディング、整数形式が問い合わせ対象である
- 文字エンコーディングについては ASCII かどうかを確認する
- 整数形式については two's complement かどうかを確認する
- 可能な場合はフォールバックを使う
- ANSI C の外にある仮定を避けようとしており、strict aliasing のような現代 C 標準に関わる動作にも回避策を用意している
- まれに
(void*)(uintptr_t)(ptr)の往復変換のような 合理的な仮定 が必要になる場合がある- そのような仮定は文書化し、可能なら
CHAR_BIT = 8のような assert も追加する
- そのような仮定は文書化し、可能なら
ターゲットごとのCコードとABI制約
cillyの出力する C コードは コンパイラごと である- Arm64 向けに生成した
cillyの C を riscv32 でそのまま実行することはできない - riscv32 向けの
cillyの C を別途生成することは可能である
- Arm64 向けに生成した
- このリポジトリの
rustc生成 C は、作者のワークステーションの ISA の都合で ARM64 Linux を対象としている cillyが生成したコードは、通常のrustcがコンパイルしたコードとおおむね ABI 互換である- 一部のプラットフォームでは、
rustcが C で表現できない ABI を選ぶため、完全な互換性は難しい - Arm64 では、構造体返却ポインタである
sretのために制約がある- 多くのプラットフォームでは
sretは第 1 引数と同じレジスタで渡されるため、第 1 引数を出力ポインタにする方式が可能である - Arm64 では
sretポインタは別のレジスタで渡される - ネイティブ C コンパイラが小さな構造体に対して return-by-sret を選ぶ必要があるが、16 バイト未満の小さな構造体ではそうしないと説明している
- 多くのプラットフォームでは
古い、または特殊なターゲットのサポート
- このプロジェクトの主な目標は、LLVM/GCC のサポートはないが C はサポートしている 古い、または特殊なハードウェア で Rust を使えるようにすることである
- あるプロジェクトが Rust から C へ移行したり、C プロジェクトの Rust 代替が作られたりする際、そうしたターゲットのサポート不足は Rust の弱点として挙げられることがある
cillyはrustcと C コンパイラをラップし、Rust コードをその場で C に変換する- ユーザー視点では、特定ターゲット向けに使う C コンパイラを定義する方式に近い
- 設定例では
sdcc_z180-unknown-noneトリプルと/usr/bin/sdcc、-mz180、--std-c89、-c引数を使用している
ネットワーク透過性とリモートCコンパイラ
cillyは ネットワーク透過性 を備え、TCP 経由で C コンパイラと通信できる- 必要であれば、UART のようなさらに特殊な通信方式にも拡張できる
- この方式は、C クロスコンパイラが存在しないプラットフォームにおける ブートストラップのパラドックス を解決するための手法である
- ターゲット OS 上で小さな C サーバーをビルドして実行し、Linux のような一般的なプラットフォームで
rustcを動かしたうえで、cillyがネットワーク越しに通信するようにできる - 作者は Arm64 Linux 上で
rustcを実行しながら、x86 Plan 9 VM 向けの小さな Rust プログラムのコンパイルに成功している- Plan 9 環境の出力は
gnot osversion 2000 cputype 386である /tmp/hello_plan9の実行結果はHello, world!であるnmの結果にはrust_begin_unwindシンボルが表示される
- Plan 9 環境の出力は
makefile生成機能
cillyはオプションでオブジェクトファイル内に マーカー を挿入し、IR をキャッシュディレクトリに保存できる- その後、そのマーカーを読み取って関数とグローバルを定義位置ごとに分割できる
- この情報を基に makefile を含むディレクトリを生成し、C コンパイラと
makeだけで Rust をビルドできるようにする
ビルドと実行条件
- デモのビルドに使われたシステムは Ubuntu ベースの ARM64 Linux である
- カーネル文字列は
Linux spark-2773 6.17.0-1021-nvidia ... aarch64である
- カーネル文字列は
- 使用された C コンパイラ情報は GCC 13.3.0 と Ubuntu LLD 18.1.3 である
- ビルドには正しい LLVM ライブラリが必要で、最も簡単な方法は
rustup install nightly-2026-06-16で該当 nightly をインストールすることである - ビルドコマンドは、
LLVM_LIB_DIRでlibLLVM.so.22.1-rust-1.98.0-nightlyのパスを指定してmake -j20を実行する CFLAGSは動作するが、一部のフラグはコンパイルを遅くする可能性がある- 最適化は推奨されない
- デモはまだ粗削りで、最適化が問題を引き起こす可能性がある
- この規模では最適化に非常に時間がかかる
- 最適化なしなら作者のマシンでは数分以内でビルドできる
- 計測値は
937.98s user,123.77s system,1352% cpu,1:18.48 totalである
- 計測値は
- 最適化を有効にすると、大半のコードは速く処理されるが、一部の大きな Rust ファイルで詰まる可能性がある
テストと既知の問題
- ビルドテストは、
LD_LIBRARY_PATHに nightly の LLVM ライブラリと./rustc_driverを指定し、./rustc/rustc --versionを実行する方法で行う - 通常のプログラムをビルドするには
stdをビルドする必要があるstdがないとerror[E0463]: can't find crate for stdエラーが発生する- 標準ライブラリのビルドについては
BUILDING_STD.mdを参照する必要がある
- 既知のバグとして、奇妙なパス正規化の問題により、
crustcはビルドされたディレクトリ、つまりリポジトリのルートで実行するとクラッシュする場合がある - 別の場所では正常に動作する
cillyの公開状況
cillyはまだ 一般公開向けの準備ができていない- 作者はできるだけ早く公開する予定だと述べている
- 公開が遅れている理由として、仕事、大学論文、手のけがを挙げている
cillyツールチェーン全体がまだ公開されていない理由の 1 つは、最適化関連のバグ を追跡中であるためである
1件のコメント
Lobste.rs のコメント
伝統的な C の
configureビルドチェーンがだいたいこの方式で動くというのはかなり奇妙に感じるが、このコンパイラ、あるいはトランスパイラがそのパターンに従うのは納得できる。「14回目の試み: cilly」とは、すごい執念で、その粘り強さがうらやましい。
規模は 460万行で、このプロジェクトよりほぼちょうど一桁小さい。生成される C コードはターゲットごとに異なるが、コンパイラごとには異ならない。
Zig の利点には、さまざまなクロスコンパイルターゲットのサポート、セルフホストのツールチェーン、LLVM が選択依存であることが含まれるが、Rust を希少なプラットフォーム向けの C にコンパイルできるという約束は、Zig 側を意識したシグナルのようにも見える。
crustcを使うだけではそうはならない。変換されたコンパイラも元のコンパイラと同じコンパイルターゲットしかサポートしないため。ただし、別の Rust プロジェクトを C に変換して、C しかサポートしないターゲット上で実行・コンパイルすることは可能。
とはいえ影響を過大評価すべきではない。かなりニッチな問題に対する、やや手間のかかる解法に近い。
rustcはすでにターゲット対応が広く、gccバックエンドによってさらに広がる予定。Zig のクロスコンパイルツールは素晴らしいが、特に Zig や Rust より扱いの難しい C の部分で利便性を高める性格が強く、より多くのターゲットをサポートするという意味合いは相対的に小さい。
結局、Zig と Rust のターゲット対応はすでにかなり似ていて決定的要因にはなりにくく、2つの言語の間にはもっと重要な違いがたくさんある。
crustcは Rust を C に変換するツールチェーンであるcillyに何ができるかを示す例にすぎない。cillyツールチェーン全体は、ユーザーの Rust コードを任意のターゲット向けの C にコンパイルする。このリポジトリは最も派手なデモだと思うので、コンパイラが自分自身をコンパイルする様子を見せているのだろう。rustcも C に変換できる。