概要
- Rustコンパイラを GCCベース でブートストラップする試み
- LLVMを使わず、 cg_gcc を利用した3段階のビルド工程
- #[inline(always)] 属性による再帰関数の問題とその回避策
- 128ビット整数の SwitchInt サポート不備によるビルド障害
- パフォーマンスと正確性を両立するための実装上の工夫
RustコンパイラをGCCでブートストラップする挑戦
- Rustコンパイラの ブートストラップ とは、Rust自身でRustコンパイラをビルドする工程
- LLVMを使用せず、 GCCベース のコード生成(cg_gcc)を利用
- ビルド工程は3段階
- Stage 1: 既存のLLVMベースRustコンパイラでGCCベースのrustcとコード生成をビルド
- Stage 2: GCCベースのコード生成でRustコンパイラを再ビルド
- Stage 3: Stage2でビルドしたコンパイラで再度ビルドし、バイナリの同一性を確認
- Stage3到達が GSoCプロジェクト の目標
現状の課題とバグ
- 3つの主要なバグが Stage3ビルド の妨げ
- バグの特定には「コンパイラのロボトミー」と呼ぶ デバッグ手法 を使用
- 問題のあるクレートや関数のソースコードを直接修正し、ビルドを進める
- 例:128ビット整数未対応、インライン化属性の除去、最適化無効化
#[inline(always)]と再帰関数の問題
- #[inline(always)] 属性付きの再帰関数がGCCバックエンドでエラーとなる
- LLVMではこの属性は 「ヒント」扱い で、インライン不可能なら無視
- GCCでは「常にインライン化」を厳格に解釈し、自己呼び出しで失敗
- 対策1:全ての#[inline(always)]を#[inline]として扱う
- 簡単だが、 パフォーマンス低下 の懸念
- 対策2:関数が 再帰的 (直接・間接問わず)である場合のみ属性を弱める
- 直接再帰チェックだけでは不十分(間接再帰を見逃す)
- 間接再帰まで考慮すると 実装が複雑・非効率 となる
効率的な属性チェック手法
- #[inline(always)] を持つ関数が、同じ属性を持つ関数を呼び出しているかをMIRで調査
- MIR(Mid-level Intermediate Representation)を利用した低コストなチェック
- 基本ブロックの終端(Terminator)を走査し、呼び出し先の属性を確認
- 条件を満たす場合のみ#[inline(always)]→#[inline]へ変換
- この方法により、 必要最小限の修正 で問題を回避しつつ、パフォーマンスも維持
128ビットSwitchIntのバグ
- SwitchInt はMIRの条件分岐命令で、Cのswitch文に類似
- 128ビット整数を扱うSwitchIntが GCCバックエンド で未対応
- libgccjitがエラーを出し、ビルドが停止
- 根本原因は、GCCのIRで128ビット整数の定数生成が正しく扱えないこと
- 対策には libgccjitの拡張 や、128ビット整数サポートの追加が必要
まとめ
- RustコンパイラのGCCベースブートストラップは 多くの技術的課題 を含む
- #[inline(always)]と再帰、 128ビット整数の分岐 など、LLVMとは異なるGCCの特性を考慮した実装工夫が不可欠
- MIRや属性チェックによる 効率的な問題回避 が重要
- 今後も パフォーマンスと正確性のバランス を意識した開発が求められる