ジェネリックマシンIR¶
ジェネリックMIR(gMIR)は、MachineIR(MIR)と同じデータ構造を共有しますが、制約がより緩和された中間表現です。コンパイルパイプラインが進むにつれて、これらの制約は徐々に厳しくなり、gMIRがMIRになります。
このドキュメントの残りの部分では、MachineIR(MIR)の概念を理解していることを前提とし、MIRとgMIRの違いを強調します。
ジェネリックマシン命令¶
注記
このセクションでは、MIR言語リファレンスのマシン命令について詳しく説明します。
MIRは主にターゲット命令を扱い、COPY
、PHI
、REG_SEQUENCE
などのターゲットに依存しないオペコードの小さなセットしか持たないのに対し、gMIRは、ターゲットに依存せず、通常ターゲットによってサポートされる操作を記述するジェネリック オペコード
の豊富なコレクションを定義します。一例として、整数加算用のジェネリックオペコードであるG_ADD
があります。各ジェネリックオペコードの詳細については、ジェネリックオペコードを参照してください。
MachineIRBuilder
クラスは、MachineInstrBuilder
をラップし、これらのジェネリック命令を作成するための便利な方法を提供します。
ジェネリック仮想レジスタ¶
注記
このセクションでは、MIR言語リファレンスのレジスタについて詳しく説明します。
ジェネリック仮想レジスタは仮想レジスタのようなものですが、レジスタクラスの制約が割り当てられていません。代わりに、ジェネリック仮想レジスタは、低レベル型で始まり、レジスタバンクにさらに制約される、より制約の緩いものを持っています。最終的に、それらはレジスタクラスに制約され、その時点で通常の仮想レジスタになります。
ジェネリック仮想レジスタは、MachineRegisterInfo
によって提供されるすべての仮想レジスタAPIで使用できます。特に、def-useチェーンAPIは、非ジェネリック仮想レジスタと区別する必要なく使用できます。
簡略化のために、ほとんどのジェネリック命令は仮想レジスタ(ジェネリックと非ジェネリックの両方)のみを受け入れます。いくつかの例外はありますが、一般的には次のようになります。
即値の代わりに、即値を具体化する命令によって定義されたジェネリック仮想レジスタを使用します(定数の変換を参照してください)。通常、これはG_CONSTANTまたはG_FCONSTANTです。この規則の例外の1つとして、即値を持つことが必須であるG_SEXT_INREGがあります。
物理レジスタの代わりに、物理レジスタからの
COPY
によって定義されるか、物理レジスタを定義するCOPY
によって使用されるジェネリック仮想レジスタを使用します。
歴史的な注記
私たちは、MRIが各ジェネリック仮想レジスタのサイズを追跡し、命令が型のリストを持つ代替表現から始めました。それには2つの欠点がありました。型とサイズが冗長であること、および指定されたオペランドの型を取得する一般的な方法がなかったことです(命令型とオペランドの間に1対1のマッピングがなかったため)。代わりに、MCInstrDescのいくつかのバリアントに型を配置することを検討しました。 PR26576を参照してください:[GlobalISel]ジェネリックMachineInstrsには型が必要ですが、これにより関連オブジェクトのメモリフットプリントが増加します
レジスタバンク¶
レジスタバンクは、ターゲットによって定義されたレジスタクラスのセットです。この定義はかなり緩いので、それらが何を実現できるかについて話しましょう。
AとBの2つのレジスタファイルを持つプロセッサがあると仮定します。これらはあらゆる点で同じであり、同じコストで同じ命令をサポートしています。それらは物理的に別々に格納されており、各命令はAまたはBのレジスタからのみアクセスできますが、2つの混合にアクセスすることはできません。2つのレジスタファイルに分割されたデータに対して操作を実行する場合は、まずすべてのデータを単一のレジスタファイルにコピーする必要があります。
このようなプロセッサの場合、関連するデータを1つのレジスタファイルにまとめて、すべての命令の(競合する可能性のある)要件を満たすためにデータをコピーするコストを最小限に抑えることが役立ちます。レジスタバンクは、仮想レジスタに特定のレジスタファイルを使用するようにレジスタアロケータを制約する手段です。
実際には、レジスタファイルAとBは等しいことはめったにありません。それらは通常同じデータを格納できますが、各レジスタファイルで実行できる操作には通常いくつかの制限があります。非常に一般的なパターンは、それらの1つが整数演算にアクセス可能であり、もう1つが浮動小数点演算にアクセス可能であるということです。これを考慮するために、AとBをGPR(汎用レジスタ)とFPR(浮動小数点レジスタ)に名前変更しましょう。
これで、私たちを制限する追加の制約がいくつかできました。G_FMULのような操作はFPRで発生する必要があり、G_ADDはGPRで発生する必要があります。ただし、これにより多くの割り当てが規定されているにもかかわらず、まだある程度の自由があります。G_LOADはGPRとFPRの両方で発生する可能性があり、どちらを使用するかは、ロードされたデータを使用するユーザーによって異なります。同様に、G_FNEGはGPRとFPRの両方で発生する可能性があります。FPRに割り当てると、浮動小数点否定を使用します。ただし、GPRに割り当てると、符号ビットを1とG_XORして反転することで同等の操作ができます。
要約すると、レジスタバンクは、特定のコンテキストで各選択肢を適用した場合の違いの分析に基づいて、見かけ上等価な選択肢を曖昧にする手段です。
具体的な例をいくつか挙げます。
AArch64
AArch64には、3つの主要なバンクがあります。整数演算用のGPR、浮動小数点演算およびNEONベクター命令セット用のFPR。3番目はCCRであり、予測に使用される条件コードレジスタを記述します。
MIPS
MIPSには5つの主要なバンクがあり、多くのプログラムは実際には1つまたは2つしか使用しません。GPRは整数演算用の汎用バンクです。FGRまたはCP1は、浮動小数点演算、MSAベクター命令、およびその他のいくつかのアプリケーション固有の拡張機能用です。CP0はシステムレジスタ用であり、それを実際に使用するプログラムはほとんどありません。CP2とCP3は、チップに存在する可能性のあるアプリケーション固有のコプロセッサ用です。おそらく、LOおよびHIレジスタ用の6番目のバンクもありますが、これらはいくつかの操作の結果にのみ使用され、GPRとは区別してモデル化する価値は疑問の余地があります。
X86
X86は、汎用、x87、およびベクター(単精度命令と倍精度命令のドメインごとにバンクにさらに分割できる)の3つの主要なバンクを持つと見なすことができます。また、AVX512マスクレジスタ用のバンクなど、さらにいくつかの潜在的なバンクがあるとも考えられます。
レジスタバンクは、ターゲットが提供するAPIであるRegisterBankInfoによって記述されます。
低レベル型¶
さらに、すべてのジェネリック仮想レジスタには、LLT
クラスのインスタンスで表される型があります。
EVT
/MVT
/Type
と同様に、符号なし整数型と符号付き整数型の区別はありません。さらに、整数型と浮動小数点型の区別もありません。これは主に、サイズやベクターレーンの数など、絶対に必要となる情報を伝えます。
スカラーの場合は
sN
ポインタの場合は
pN
ベクターの場合は
<N x sM>
LLT
は、SelectionDAGでのEVT
の使用を置き換えることを目的としています。
以下に、いくつかのLLTの例と、それらのEVT
およびType
相当物を示します
根拠:命令はすでに型に関する特定の解釈をエンコードしている(例:add
vs. fadd
、またはsdiv
vs. udiv
)。また、型システムにその情報をエンコードすることは、セレクターにとって実際的な利点がないビットキャストの導入が必要となる。
ポインター型はアドレス空間によって区別される。これは、演算に対する属性としてアドレス空間を持つSelectionDAGとは対照的に、IRと一致する。この表現は、アドレス空間によってサイズが異なるポインターをより適切にサポートする。
注記
注意
これはまだ当てはまるだろうか?1要素ベクターの概念は削除したと思っていた。仮説としては、スカラーとは異なる可能性があるが、実際の発生は見つけられなかったと思う。
現在、LLTはベクター内に少なくとも2つの要素を必要とするが、一部のターゲットは「1要素ベクター」の概念を持っている。それらを基盤となるスカラー型として表現するのは良い簡略化である。
脚注
汎用オペコードリファレンス¶
利用可能な汎用オペコードは、汎用オペコードで説明されています。