2 ポイント 投稿者 GN⁺ 2023-12-19 | 1件のコメント | WhatsAppで共有
  • gccrsは、GCC内にRustコンパイラを実装しようとする2014年開始のプロジェクトで、まだ完成には遠いものの、標準ライブラリのコンパイルとGCC 14への取り込みに向けて進展している
  • プロジェクトは変化の速い最新Rustではなく Rust 1.49 を目標にしたが、標準ライブラリ内部の依存関係のため、const genericsのような機能も最終的には実装する必要があった
  • coreallocのコンパイルは、マクロ名解決、デコレータマクロ、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つのクレートであるcoreallocのコンパイル対応に取り組んでいる
    • 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プラグインを使ったunsafe Rustコードの静的解析もすでに可能である
  • 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!()は、文字列フォーマット用マクロに渡す引数を構成する補助マクロである
  • この機能はDisplayDebugトレイトに関係する
  • 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件のコメント

 
GN⁺ 2023-12-19
Hacker News の意見
  • 記事の主張だけでは、gccrs の動機はいささか弱く見える
    GCC のセキュリティプラグインや Linux カーネル側の GNU ツールチェーン志向は、GCC をバックエンドとして使いたい理由は説明しているが、なぜ重複するフロントエンドが必要なのかは説明できていない
    C++ のように、複数コンパイラのスイッチ、言語サポート水準の差、プラットフォーム別のバグによってクロスプラットフォーム開発が難しくなる過ちを、Rust には繰り返してほしくない
    なので、gccrs が rustc_codegen_gcc よりなぜ優れたアプローチなのか説明してほしい。後者ははるかに少ない労力とリスクで同じ目標を達成できるように見える

    • Rust の実装がもう1つあれば、Rust の仕様を検証し、未定義動作を減らす監査の役割を果たせる
      MSVC でコンパイラバグに遭遇したら報告してから GCC に切り替えて作業を続けられるが、Rust では今のところそうした選択肢がない
    • Rust はすでに C++ の過ちから学んでいる
      引用されているように、Rust の上位集合にならないよう注意していることがその例である
      C/C++ の問題は、コンパイラベンダー同士が「より良い」と競い合ったことから生じたもので、複数のフロントエンドには通常、バグや誤った実装を多く露呈させる利点がある
    • なぜこの質問が何度も出てくるのか理解できない
      Rust が何らかの意味で神聖だからフロントエンドの再実装が禁じられているわけでもないし、新しいアーキテクチャで Rust をブートストラップするのはいまだに苦痛である
      LLVM がサポートしていないアーキテクチャには、動作する Rust コンパイラも存在しない
      codegen_rust_gcc にも既存の Rust コンパイラと同じブートストラップ問題があり、Rust のさまざまな部分にアーキテクチャサポートを入れる必要があるが、Rust のメンテナーたちはそれを嫌がってきた
      だから近い将来、すぐに使える Rust コンパイラが登場し、Alpha のようなアーキテクチャでも、Rust 化されたライブラリを大きな苦労なく再びビルドできるようになるなら非常に喜ばしい
    • その理屈なら、LLVM コミュニティはなぜ DragonEgg を使い続けず、Clang、Clang++、libc++ などを作ったのか、という話になる
      すでに GCC、G++、libstdc++、EDG C++ フロントエンドがあった
      GCC、Clang、MSVC などのコンパイラは互いに補完し合い、異なる目的と市場を担い、言語が単一実装の偶然の特性ではなく仕様に沿って堅牢になるようにしてくれる
      GNU Toolchain Project、LLVM Project、Rust プロジェクトはいずれも問題を経験してきたので、単一障害点に依存しないほうがよく、冗長性と反脆弱性は味方である
    • Rust が C++ と C の過ちから学ぶべきだというのは、歴史上もっとも長く存続し、影響力が大きく、広く配布されてきた言語たちの過ちから学べということなのかと思う
      言語標準を悪いものと見なし、「このコードは 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...

    • その RFC はすでに承認されており、実際に進み始めている
      進捗はがっかりするほど遅いものの、プロジェクトは生きており、来年には速度が上がる可能性がある
      https://blog.rust-lang.org/inside-rust/2023/11/15/spec-visio...
    • Mara のブログ記事、つまり最初のリンクは、Rust にはすでに機能追加と互換性維持の手段があるため、本質的には標準は必要ないという側に近い
    • Ferrocene 仕様のおかげで、そうした産業でも Rust を使える
    • Go には本当によくできた仕様と複数の実装がある
      https://go.dev/ref/spec
    • 標準がなければ採用しない組織や産業が多いという主張への反論として、Rust はそうした組織や産業がなくても順調にやっている
      うまく機能しているやり方を、なぜ変えなければならないのかわからない
  • GCC-RSに否定的な反応が多いのは驚き
    言語に複数の実装がないなら、かなり貧弱な言語だと思う

    • 以前は特にC/C++の影響でそれが通念だったが、最近はずっと議論の余地が大きい
      Rustコミュニティの合意は、現在の方式、つまり定義上標準であるコンパイラが1つ、多くの文書、安全性が必須の産業向け最小仕様、一部モジュール下位領域の仕様が、複数実装の利点の大半を得つつ欠点を避ける、という方向にある
    • 長期的には構文の安定性と、拡張/属性による機能膨張の防止が問題になる
      Cでは実際に経験しているし、C++は途方もなく奇怪な複雑さのせいで、すでに救済不能に近い
      これがなければ、現実的な代替実装は出にくい
    • 個人的には複数実装の価値は理解するが、問題はGCCの上に積むことにある
      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はサポートしていないことを知った
    • Rustから出力されたPDP-11機械語を早く見たい
      最後に確認したとき、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ツールチェーンを選べるようにしようとしているだけ