MIR からバイナリへ
このガイドのこれまでの章には、共通点が 1 つあります。 実行可能な機械語コードをまったく生成していなかったのです! この章で、そのすべてが変わります。
これまで、 コンパイラがテキスト形式の生のソースコードを受け取り、 それを MIR に変換する方法を示してきました。 また、コンパイラがコードに対してさまざまな解析を行い、 型エラーやライフタイムエラーのようなものを検出する方法も示してきました。 ここでついに、MIR を受け取って実行可能な機械語コードを生成します。
注: コンパイラのこの部分は、しばしば バックエンド と呼ばれます。 この用語は少し多義的です。というのも、コンパイラのソースでは、 通常「コード生成バックエンド」(つまり LLVM、Cranelift、または GCC)を指すからです。 通常、この部分で「バックエンド」という言葉を見かけた場合、 「コード生成バックエンド」を指しています。
では、何をする必要があるのでしょうか?
- まず、コード生成の対象となるものの集合を収集する必要があります。 特に、 ジェネリックな型に対してどの具象型を代入するかを見つける必要があります。 なぜなら、具象型に対してコードを生成する必要があるからです。 具象型に対してコードを生成すること (つまり、具象型ごとにコードのコピーを出力すること)は 単相化 と呼ばれるため、 すべての具象型を収集する処理は 単相化収集 と呼ばれます。
- 次に、収集した各具象型について、実際に MIR をコード生成 IR (通常は LLVM IR)へ下げる必要があります。
- 最後に、コード生成バックエンドを呼び出す必要があります。 これは多数の最適化パスを実行し、 実行可能コードを生成し、 実行可能バイナリをリンクします。
コード生成のためのコードは、いくつかの要因により実際には少し複雑です。
- 複数のコード生成バックエンド(LLVM、Cranelift、GCC)のサポート。 それらの間でできる限り多くのバックエンドコードを共有しようとしているため、 その多くはコード生成の実装に対してジェネリックになっています。 これは、多くの場合、抽象化の層が多数存在することを意味します。
- パフォーマンスのため、コード生成は別スレッドで非同期に行われます。
- 実際のコード生成は、サードパーティライブラリ(3 つのバックエンドのいずれか)によって行われます。
一般に、rustc_codegen_ssa クレートにはバックエンドに依存しないコードが含まれ、
rustc_codegen_llvm クレートには LLVM コード生成に固有のコードが含まれます。
非常に高いレベルでは、エントリーポイントは
rustc_codegen_ssa::base::codegen_crate です。
この関数は、この章の残りで説明するプロセスを開始します。