GCCベースのRustコンパイラ開発が進展
(lwn.net)- gccrsは、GCC内にRustコンパイラを実装しようとする2014年開始のプロジェクトで、まだ完成には遠いものの、標準ライブラリのコンパイルとGCC 14への取り込みに向けて進展している
- プロジェクトは変化の速い最新Rustではなく Rust 1.49 を目標にしたが、標準ライブラリ内部の依存関係のため、
const genericsのような機能も最終的には実装する必要があった coreとallocのコンパイルは、マクロ名解決、デコレータマクロ、GCCにないLLVMのコンパイラintrinsic、borrow checkerの不在により、まだ行き詰まっている- rustc_codegen_gccは、rustcの一部を活用し、GCCを後段のコード生成に使う、より成熟したアプローチで、2023年10月時点で追加パッチなしにRust for Linuxをコンパイルできる
- gccrsには、GCCのセキュリティプラグイン、静的解析、LLVM未対応アーキテクチャ、Rust for Linuxという明確な動機があるが、実用性はまだ
rustc_codegen_gccより限定的である
gccrsの目標と現在地
- gccrsは、GNU Compiler Collection(GCC) の中にRustコンパイラを実装しようとするプロジェクトである
- 2014年に始まり、LWNの以前の記事以降、プロジェクト報告ベースでは進展があった
- 2022年にはGCC 13への取り込みを目標にしていたが達成できず、2023年11月の月次報告時点ではGCC 14への取り込みを目標としている
- GCC 14は、2024年半ばにリリースされる可能性のあるバージョンとして扱われている
- 2023年10月のEuroRust 2023でArthur Cohenは、「The road to compiling the standard library with gccrs」という発表で、Rust標準ライブラリのコンパイル作業と、まだコンパイルできない理由を説明した
- 発表動画が公開されている
Rust 1.49を目標にした理由
- gccrsはRustの最新バージョンを追い続けるのではなく、Rust 1.49 を目標としている
- Rust 1.49は2020年末にリリースされ、Rust 1.50で一般利用可能になった
const generics対応より前の最後のバージョンである - プロジェクトは
const genericsを避けようとしたが、Rust 1.49の標準ライブラリ内部でもすでに使われていたため、結局無視できなかった const genericsはその後完全に実装され、もはや障害ではない- gccrsはRustの上位集合や別の「GNU Rust」言語を作ろうとはしていない
- 目標は
rustcの出力、バグ、特異な挙動まで再現することだ - そのためにRustのテストスイートとGCCのテストスイートの両方を使用している
- 目標は
標準ライブラリのコンパイルで行き詰まっている部分
- Rust標準ライブラリは複数の**クレート(crate)**で構成されている
- gccrsは最も重要な2つのクレートであるcoreとallocのコンパイル対応に取り組んでいる
coreはプリミティブ型やマクロなど、標準ライブラリの基盤機能を実装するallocはヒープメモリ割り当てと複数のコンテナ型を扱う
- 現在のgccrsは、以下の機能不足のため、これらのクレートをコンパイルできない
- マクロ名解決の動作が正しくない
- デコレータマクロのサポートが完全ではない
- borrow checkerがなく、コードの安全性を正しく検査できない
- GCCにないLLVMコンパイラintrinsicを実装する必要がある
- borrow checkerの不在はコンパイルそのものを妨げるわけではないが、Rustコードの安全性検査を正しく実行できなくする
手続き型マクロとGCC統合作業
- 2023年9月のGNU Tools CauldronでPierre-Emmanuel Patryは、GCC 14への取り込み状況とマクロ作業を中心に発表した
- 発表スライドが公開されている
- 手続き型マクロ(procedural macro) の実装方式は、GCCビルドシステムの変更を必要とする
- 手続き型マクロはCやC++のマクロのように単純なソーステキストを出力するのではなく、トークンストリームを出力する関数型マクロである
- Rustでは組み込みクレートproc_macroで実装される
- 手続き型マクロは実装が難しいが、強力な機能を可能にする
#[attribute]デコレータ#[derive()]デコレータ- コンパイル時評価に基づくドメイン固有言語の生成
- GNU Cauldronでの発表時点で、gccrsにはGCCへupstreamする必要のあるコミットが800件以上あった
GCCエコシステムをRustに活用しようとする理由
- gccrsの主な動機の一つは、GCCのセキュリティプラグインをRustコードにも活用することだ
- GCCにはデバッグ、静的解析、ハードニングを支援する多様なプラグインがあり、それらはGCCの中間表現上で動作する
- gccrsは、Rust開発者が既存のGCCプラグインを再利用するワークフローを支援しようとしている
- Cohenは、Cプログラマはファイルディスクリプタを閉じ忘れるミスを長年してきたため、それを検出するプラグインが多い、という例を挙げた
- Rustの
unsafeコードでバグを検出するために、既存のGCCプラグインと静的解析器を活用できるようにすることが目標である
すでに一部で使われている領域
- Cohenによると、Sega Dreamcastのホームブリューコミュニティは、gccrsを使ってDreamcastゲームコンソール向けの新しいゲームを作っている
- Dreamcastコミュニティが関心を持つ理由は、
rustcのLLVMバックエンドがコンソールのHitachi SH-4アーキテクチャをサポートしていない一方、GCCはサポートしているためだ - gccrsはまだ未完成だが、こうした組み込み用途では役立つ
- GCCプラグインを使った
unsafeRustコードの静的解析もすでに可能である - gccrsの作業過程で、
Derefやマクロ名解決のように仕様が十分に定まっていない言語機能が明らかになり、プロジェクトはRust仕様の追加にも貢献できた- Rustには現在公式仕様がないが、RFC 3355に従って仕様策定作業が進んでいる
まだ開発中の主要機能
- gccrsには依然としてRustコンパイラの中核機能が多く欠けている
- 主な未実装または開発中の機能は次のとおり
async/await- GCCにないLLVM intrinsic
println!()のような出力マクロが使うformat_args!()マクロ- Rustの参照規則を強制するborrow checker
- borrow checkerの有力な解決策は別プロジェクトPoloniusである
- Cohenは、gccrsが数カ月後にPoloniusを統合する可能性が高いと述べた
- Jakub Dupakはここ数カ月、この作業で進展を出している
- Poloniusは現在、
rustcのborrow checkerと意味的に同等のborrow checkerを実装するライブラリである- 参照ライフタイムの計算に別のアルゴリズムを使用する
- 長期的には、
rustcの現在のborrow checkerが抱える不足点やコーナーケースの解決を目指す - 成熟すれば、
rustcも将来的にPoloniusを採用する可能性がある
format_args!()が必要な理由
- 2023年11月のgccrs月次報告によると、
format_args!()マクロの作業が始まった format_args!()は、文字列フォーマット用マクロに渡す引数を構成する補助マクロである- この機能はDisplayとDebugトレイトに関係する
format!()やprintln!()のようなマクロへ渡す引数を準備するために必要であるformat_args!()がないと、Rustプログラムはフォーマット済み出力を作成できない- したがって、gccrsが「Hello, World」プログラムをコンパイルする前にも必要な機能である
format_args!()の詳説として、Mara Bosのブログ記事も取り上げられている
rustc_codegen_gccとの違い
rustc_codegen_gccはgccrsとは別のGCCベースRustプロジェクトである- gccrsより成熟しているが、範囲はより限定的である
- Rustコンパイラを最初から完全に実装する方式ではない
- libgccjitライブラリを使って、
rustcのLLVMバックエンドAPIに接続する - コンパイルの多くの部分は
rustcが実行し、その後の段階でGCCを使用する libgccjitという名前にはJITが含まれるが、rustc_codegen_gccはahead-of-timeコンパイルを目的としている- 主な目標は、LLVMがサポートしないプラットフォームでRustコード生成を可能にすることだ
- 2023年10月時点で、
rustc_codegen_gccは追加パッチなしにRust for Linuxをコンパイルできる - 過去1年でSIMDとリンク時最適化のサポートを追加した
- これら2機能は以前、テスト失敗の原因として指摘されていた
- CohenはEuroRustの発表で何度も、当面はgccrsではなく
rustc_codegen_gccを使うよう勧めた rustc_codegen_gccはすでにRust言語リポジトリのupstreamに入っている
Rust for Linuxとバージョン差
- Rust for Linuxは、LinuxカーネルにRust対応を追加しようとするイニシアチブである
- CohenはLinuxカーネルをgccrsプロジェクトの中核的な動機に挙げた
- 多くのカーネル関係者が、カーネルはGNUツールチェーンだけでコンパイルされることを好むためである
- 現在のRust for Linuxプロジェクトは、カーネルRustコードをビルドする方法としてrustcまたはrustc_codegen_gccを文書化している
- カーネルには複数のビルドツールの最小対応バージョンに関する文書もある
rustcは最小バージョンではなく、正確に一致しなければならないバージョンとして扱われる- 現在サポートされる
rustcバージョンは、2023年10月にリリースされた 1.73.0 である
- gccrsが目標とするRust 1.49と、Rust for Linuxが要求するRust 1.73.0の間には大きな差がある
- Rust for Linux対応はgccrsの明示された目標だが、このバージョン差のため、まだかなり遠い
全体評価
- gccrsリポジトリには、2023年1月1日以降3,000件を超えるコミットがある
- 過去1年でプロジェクトは大きく進展した
- しかし、最初から完全なRustコンパイラを実装する範囲は非常に大きいため、ほぼすべての実用目的ではまだ利用可能な状態ではない
rustc_codegen_gccはupstreamのRustリポジトリにマージされ、Rust for Linuxで実際に使われている- Rust言語に複数の独立したコンパイラ実装が存在する段階にはまだ達していないが、その方向へ近づきつつある
1件のコメント
Hacker News の意見
記事の主張だけでは、gccrs の動機はいささか弱く見える
GCC のセキュリティプラグインや Linux カーネル側の GNU ツールチェーン志向は、GCC をバックエンドとして使いたい理由は説明しているが、なぜ重複するフロントエンドが必要なのかは説明できていない
C++ のように、複数コンパイラのスイッチ、言語サポート水準の差、プラットフォーム別のバグによってクロスプラットフォーム開発が難しくなる過ちを、Rust には繰り返してほしくない
なので、gccrs が
rustc_codegen_gccよりなぜ優れたアプローチなのか説明してほしい。後者ははるかに少ない労力とリスクで同じ目標を達成できるように見えるMSVC でコンパイラバグに遭遇したら報告してから GCC に切り替えて作業を続けられるが、Rust では今のところそうした選択肢がない
引用されているように、Rust の上位集合にならないよう注意していることがその例である
C/C++ の問題は、コンパイラベンダー同士が「より良い」と競い合ったことから生じたもので、複数のフロントエンドには通常、バグや誤った実装を多く露呈させる利点がある
Rust が何らかの意味で神聖だからフロントエンドの再実装が禁じられているわけでもないし、新しいアーキテクチャで Rust をブートストラップするのはいまだに苦痛である
LLVM がサポートしていないアーキテクチャには、動作する Rust コンパイラも存在しない
codegen_rust_gccにも既存の Rust コンパイラと同じブートストラップ問題があり、Rust のさまざまな部分にアーキテクチャサポートを入れる必要があるが、Rust のメンテナーたちはそれを嫌がってきただから近い将来、すぐに使える Rust コンパイラが登場し、Alpha のようなアーキテクチャでも、Rust 化されたライブラリを大きな苦労なく再びビルドできるようになるなら非常に喜ばしい
すでに GCC、G++、libstdc++、EDG C++ フロントエンドがあった
GCC、Clang、MSVC などのコンパイラは互いに補完し合い、異なる目的と市場を担い、言語が単一実装の偶然の特性ではなく仕様に沿って堅牢になるようにしてくれる
GNU Toolchain Project、LLVM Project、Rust プロジェクトはいずれも問題を経験してきたので、単一障害点に依存しないほうがよく、冗長性と反脆弱性は味方である
言語標準を悪いものと見なし、「このコードは C99/C++11 だ」とは言わず、「このコードは SHA256 ハッシュ
e49d560cd008344edf745b8052ef714b07595808898c835f17f962a10012f964の rustc バイナリ/ソースで動作する」と言うほうを好むのは混乱を招くRust には言語標準が必要である
https://blog.m-ou.se/rust-standard/
https://rust-lang.github.io/rfcs/3355-rust-spec.html
https://github.com/rust-lang/rfcs/pull/3355
標準ができるまで Rust を採用しない組織や産業は多い
C、C++、C#、さらには JavaScript(ECMAScript)にも言語標準があるのだから、Rust だけが持つべきでない理由はない
C: https://www.iso.org/standard/74528.html
C++: https://isocpp.org/std/the-standard
C#: https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
JavaScript / ECMAScript: https://ecma-international.org/publications-and-standards/st...
進捗はがっかりするほど遅いものの、プロジェクトは生きており、来年には速度が上がる可能性がある
https://blog.rust-lang.org/inside-rust/2023/11/15/spec-visio...
https://go.dev/ref/spec
うまく機能しているやり方を、なぜ変えなければならないのかわからない
GCC-RSに否定的な反応が多いのは驚き
言語に複数の実装がないなら、かなり貧弱な言語だと思う
Rustコミュニティの合意は、現在の方式、つまり定義上標準であるコンパイラが1つ、多くの文書、安全性が必須の産業向け最小仕様、一部モジュール下位領域の仕様が、複数実装の利点の大半を得つつ欠点を避ける、という方向にある
Cでは実際に経験しているし、C++は途方もなく奇怪な複雑さのせいで、すでに救済不能に近い
これがなければ、現実的な代替実装は出にくい
GNUツールチェーンはめちゃくちゃで、実際その上でどう開発するのか分からない
イデオロギーの話ではなく、文字どおりGCC自体の開発環境をどう整えるのか理解できない
何度かブートストラップした不運な経験があるが、これまで見たソフトウェアの中で最もひどい挙動をする代物だった
Sega Dreamcastの自作ソフトコミュニティがgccrsで新しいゲームを作り、GCCプラグインでunsafe Rustコードの静的解析ができるというくだりは楽しい
DreamcastのHitachi SH-4アーキテクチャは
rustcのLLVMバックエンドではサポートされていないが、GCCはサポートしているので、未完成状態のgccrsでもこうした組み込み用途には役立つそのためにGCCフロントエンドが必要なのではなく、GCCバックエンドだけあればよい
これでLLVMがサポートせずGCCがサポートするAlpha、SuperH、VAXのようなアーキテクチャでもRustサポートが見られそう
rustcが最近Loongsonから資金/リソース支援を引き出そうとして失敗した後にサポートを捨てたmips64も該当するhttps://github.com/rust-lang/compiler-team/issues/648
LLVM式の考え方の最大の問題は、アーキテクチャサポートをハードウェア企業から支援、つまり有給開発者のポストを引き出す手段として使う点にある
GNUは変更のマージ基準が面倒なほど高いことがあるが、いったん入ってサポートされれば長期的に維持してくれる
買うことと借りることの違いのようなもので、GCCにサポートを入れるには開発時間がはるかに多くかかるが、一度入れば残る
例えば最近、RISC-VでGCCはRV32EターゲットをサポートしているがLLVMはサポートしていないことを知った
最後に確認したとき、GCCではスタンドアロンのCコンパイルはまだ動いていた
gccrsが「GNU Rust」という特別な言語を作らず、
rustcの出力、バグや癖まで再現しようとしている部分は、経験上大きな間違いだと思うRustには仕様がなく、参考文書はあるが明示的に規範的ではない
単一の参照実装以外に文書化されていない言語には、長期的な弱点がある
既存コードが両方の実装で動作するよう保証しようという目標は合理的だが、バグ互換性まで約束すれば、誤った決定やバグを化石化させることになる
Microsoftは古いプログラムを動かし続けながらセキュリティと信頼性のバグを直すために多くの人員を使っているが、Rustが寿命の初期にそのような負担を負う必要はない
言語を進化させたいなら、品質保証と品質管理を受け入れなければならない
品質はテストで後から注入できるものではなく、アーキテクチャや設計、設計・コードレビューのようなプロセスによって正しく動作するようにし、失敗しても正しい方向に失敗するようにしなければならない
Common Lisp、C++、FORTRANのような強い標準はこの信念を受け入れており、Pythonのように事実上標準に近い弱い言語も人気は得られるが、変化が難しいことはPython 2から3への長い移行と実装数の少なさに表れている
このスレッドの後半に遅れて書くが、これは必ずしも良いことではないかもしれない
ディストリビューションは数か月ごとに新バージョンが出る言語についていけず、すでに
rustcやGoをディストリビューションのパッケージとして使うのは難しい今では驚くことに、GCCは不要になって削除し、既存ソフトウェアの更新のためにアップストリームのGoとRustだけを維持するシステムがある
数か月前、CVEのためにGoを更新したとき、Goベースのアプリが独自のGo環境を4か所も保存していたのを見て悪夢のようだった
Linuxは望めばすでにClangでコンパイルして、すべてLLVMベースのツールチェーンを使える
GNUの「純粋性」のためにこれを開発・維持する重複した努力に、それだけの価値があるようには見えない
ClangBuiltLinuxコミュニティは、Linuxが単一のコンパイラに依存すべきではないと主張していたが、Rustが来ると同じ人たちの多くが突然、単一コンパイラでも構わないと考え始めた
カーネルをGNU専用に縛り付けようとしているのではなく、純粋なGNUツールチェーンを選べるようにしようとしているだけ