Open6
Clang のレジスタ割り付けをコードリーディングする
ついったの感覚でざつに呟く場所 あんまりまとまりはない
とりあえず実装箇所をDeepWikiに聞く アルゴリズムが3つありそう
一番実装が簡単そうな Fast から読んでみる 2000行弱
長すぎて見失っちゃうけど、とりあえずエントリポイントはここ
これは4ステップで構成されているぽい
1. 必要な情報を集める
MRI = &MF.getRegInfo();
const TargetSubtargetInfo &STI = MF.getSubtarget();
TRI = STI.getRegisterInfo();
TII = STI.getInstrInfo();
MFI = &MF.getFrameInfo();
MRI->freezeReservedRegs();
RegClassInfo.runOnMachineFunction(MF);
unsigned NumRegUnits = TRI->getNumRegUnits();
InstrGen = 0;
UsedInInstr.assign(NumRegUnits, 0);
2. 割り付けを初期化する
// initialize the virtual->physical register map to have a 'null'
// mapping for all virtual registers
unsigned NumVirtRegs = MRI->getNumVirtRegs();
StackSlotForVirtReg.resize(NumVirtRegs);
LiveVirtRegs.setUniverse(NumVirtRegs);
MayLiveAcrossBlocks.clear();
MayLiveAcrossBlocks.resize(NumVirtRegs);
3. 割り付ける
// Loop over all of the basic blocks, eliminating virtual register references
for (MachineBasicBlock &MBB : MF)
allocateBasicBlock(MBB);
4. 掃除
if (ClearVirtRegs) {
// All machine operands and other references to virtual registers have been
// replaced. Remove the virtual registers.
MRI->clearVirtRegs();
}
StackSlotForVirtReg.clear();
LiveDbgValueMap.clear();
1. 必要な情報を集める (1)
MRI (Machine Register Info)
クラスの定義はココ
関数につき1つあるっぽい? コードから抽出された抽象的なレジスタ割り付け用の情報って感じ
- 使われている仮想レジスタ
- それらの型
- それらがどこで定義・使用されているか
- 関数開始時点で既に値が入っている物理レジスタ (関数の引数など)
- 予約済みレジスタ (スタックポインタなど)
- 関数呼び出しなどによって破壊される可能性のあるレジスタ
- レジスタ割り付けのためのヒント
- ある仮想レジスタ (e.g.
%1) はなるべく特定の物理レジスタ (e.g.%rax) に割り付けて欲しい、など
- ある仮想レジスタ (e.g.
- その他デバッグのための情報
ざっくり言えば 仮想レジスタに関する情報
1. 必要な情報を集める (2)
TRI (Target Register Info)
コンパイルターゲットにつき1つあるぽい 物理レジスタに関する情報が入っている (クロスコンパイルのために抽象化して情報を持っているんだと思う)
x86_64 なら %rax, %rbx, ... みたいなレジスタがあって、%eax は %rax の下位32bitを指していて、、、みたいな情報が詰まっている。
x86 の場合の具体的な実装は↓にあった 抽象的なクラスであるからこそ結構複雑な定義になっていそう
1. 必要な情報を集める (3)
MFI (Machine Frame Info)
スタックに関する情報を持っているクラスらしい スタック領域を抽象化して持つことでクロスコンパイルを可能にしていそう (さらに最適化もかけられる)
レジスタのスピルに使っている気がする (まだ読んでない)