コード生成
コード生成(または「codegen」)は、実際に実行可能バイナリを生成する
コンパイラの一部です。
通常、rustc はコード生成に LLVM を使用しますが、
Cranelift と GCC のサポートもあります。
重要なのは、rustc 自身が codegen を実装しているわけではないということです。
ただし、Rust のソースコードでは、バックエンドの多くの部分の名前に
codegen が含まれている点は注目に値します
(明確な境界はありません)。
注: コード生成のバグをデバッグする方法のヒントを探している場合は、 デバッグの章のこのセクションを参照してください。
LLVM とは?
LLVM は、「モジュール化され再利用可能なコンパイラおよび
ツールチェーン技術の集合」です。特に、LLVM プロジェクトにはプラグイン可能な
コンパイラバックエンド(これも「LLVM」と呼ばれます)が含まれており、
clang C コンパイラや私たちの愛する rustc を含む多くのコンパイラプロジェクトで使用されています。
LLVM は LLVM IR 形式の入力を受け取ります。これは基本的には、追加の低レベル型と アノテーションが付与されたアセンブリコードです。これらのアノテーションは、 LLVM IR と出力される機械語コードに対して最適化を行うのに役立ちます。 これらすべての最終結果は、(ようやく)実行可能なもの(たとえば ELF オブジェクト、 EXE、または wasm)になります。
LLVM を使用することには、いくつかの利点があります。
- コンパイラバックエンド全体を書く必要がありません。これにより、実装と 保守の負担が軽減されます。
- LLVM プロジェクトが蓄積してきた高度な最適化の大規模なスイートの恩恵を受けられます。
- LLVM がサポートする任意のプラットフォーム向けに、Rust を自動的にコンパイルできます。 たとえば、LLVM が wasm のサポートを追加した途端、ほら!rustc、 clang、そして他の多くの言語が wasm にコンパイルできるようになりました!(まあ、 追加で行うべき作業はいくらかありましたが、いずれにせよ 90% はそこまで到達していました)。
- 私たちと他のコンパイラプロジェクトは互いに恩恵を受けます。たとえば、 Spectre と Meltdown のセキュリティ脆弱性が発見されたとき、 パッチを適用する必要があったのは LLVM だけでした。
LLVM の実行、リンク、およびメタデータ生成
すべての関数や static などの LLVM IR が構築されると、LLVM とその最適化パスを 実行し始める段階になります。LLVM IR は「モジュール」にグループ化されます。 マルチコアの利用を助けるため、複数の「モジュール」を同時に codegen できます。 これらの「モジュール」は、私たちが codegen units と呼ぶものです。 これらの unit は、はるか前の単相化収集フェーズ中に確立されています。
LLVM がこれらのモジュールからオブジェクトを生成すると、これらのオブジェクトは、 任意でメタデータオブジェクトとともにリンカに渡され、アーカイブまたは 実行可能ファイルが生成されます。
上で説明した codegen フェーズが必ずしも最適化を実行するわけではありません。 特定の種類の LTO では、最適化は代わりにリンク時に行われる場合があります。 また、一部の最適化がオブジェクトがリンカに渡される前に行われ、 一部がリンク中に行われることも可能です。
これらはすべてコンパイルのごく終盤に行われます。このためのコードは
rustc_codegen_ssa::back と
rustc_codegen_llvm::back にあります。残念ながら、このコード片は
LLVM 依存のコードとしてあまりきれいに分離されていません。rustc_codegen_ssa には、
LLVM バックエンド固有のコードがかなり含まれています。
これらのコンポーネントが作業を終えると、要求した出力に対応する多数のファイルが ファイルシステム上に得られます。