Cpu0のバックエンドをLLVMに追加するチュートリアル、第3章(?)のBackendの追加の章をやり終えた。 やり終えたといっても、ひたすら写経して、ビルドして、テストパタンを動かして終わりである。 これではさすがにもったいないので、もう少しまとめておきたい。
この辺りまで読んだ。が、最後の方はあまり理解できていない。
- Backend structure
Add Cpu0DAGToDAGISel class
lbdex/chapters/Chapter2/Cpu0InstrInfo.td
: Cpu0InstrIndo.tdにはADDiuの実装と、Load/Store命令の定義が含まれる。lbdex/chapters/Chapter3_3/Cpu0TargetMachine.cpp
:createCpu0SEISelDag()
を通じてPassを追加する。lbdex/chapters/Chapter3_3/Cpu0ISelDAGToDAG.h
,lbdex/chapters/Chapter3_3/Cpu0ISelDAGToDAG.cpp
: DAGの変換を行う関数群。Cpu0DAGToDAGISel::Select()
は、Cpu0DAGToDAGISel::SelectAddr()
(アドレス型時のデータDAG)のための"OP code DAG node"を選択する。
Handle return register $lr
MIPSは"jr $ra"で関数から戻ってくるため、$raをリターンアドレスとして使用する。そして$2に戻り値を保存している。しかし、LLVMは$2だけでなく任意の場所を戻り値として使用する。また、"jr $ra"も$raではなく任意の場所をリターンアドレスとして使用する可能性がある。$raを指定することでリターンアドレスは固定できるが、"jal $x"は任意の場所を指定できる。
lbdex/chapters/Chapter3_4/Cpu0CallingConv.td
lbdex/chapters/Chapter3_4/Cpu0InstrFormats.td
lbdex/chapters/Chapter3_4/Cpu0InstrInfo.td
lbdex/chapters/Chapter3_4/Cpu0ISelLowering.h
lbdex/chapters/Chapter3_4/Cpu0ISelLowering.cpp
lbdex/chapters/Chapter3_4/Cpu0MachineFunction.h
lbdex/chapters/Chapter3_4/Cpu0SEInstrInfo.cpp
しかしこのままではLocal変数のスタックが確保されていないためエラーとなる。
プロローグとエピローグの追加
Callee-savedレジスタを保存する。予約されているレジスタについてのケアを行う。
- emitPrologue()
はプロローグコードを追加する。フレームポインタがある場合hあフレームレジスタ%a14に、古いスタックポインタを設定する。
- emitEpilogue()
はスタックフレームの破壊とすべての保存したレジスタを復旧する。スタックポインタ%a10、リターンアドレス%a11、フレームポインタ%a14はひとつ前のコンテキストに復旧するが、これはemitEpilogue()
の役目ではない。
- eliminateFrameIndex()
は、スタックスロットにアクセスする命令のために生成される。これまでの関数内で、スタック上の変数にアクセスする場合は、全てフレームポインタと即値オフセットでのアクセスであった。この関数により、レジスタとオフセットのペアに変換する。
実際にプロローグとエピローグを追加する。
lbdex/chapters/Chapter3_5/Cpu0SEFrameLowering.cpp
lbdex/chapters/Chapter3_5/Cpu0MachineFunction.h
簡単なコードから見てみる。以下のBitCodeにPrologueとEpilogueを追加すると、以下のようなコードが生成される。プロローグとしてスタックポインタの減算と、エピローグとしてスタックポインタの加算が組み込まれている。
define i32 @main() #0 { %1 = alloca i32, align 4 store i32 0, i32* %1 ret i32 0 }
# BB#0: addiu $sp, $sp, -8 $tmp1: .cfi_def_cfa_offset 8 addiu $2, $zero, 0 st $2, 4($sp) addiu $sp, $sp, 8 ret $lr
Handle stack slot for local variables
局所変数のスタック操作を行う。以下のスタックフレームオブジェクトは、$spを経由して参照される。 1. 外部引数 2. スタック領域に動的に割り当てられるポインタ 3. Callee-savedレジスタの場所
つぎに、保存すべきレジスタを決定する。これはdetermineCalleeSaves()
により決定される。
最後に、storeRegToStack()
によりレジスタをスタックに退避する。
Large Stack
単純な関数ではうまくいくが、32ビット長のスタック領域を考慮する。大きなサイズのスタックを処理するためには、再帰呼び出しを行う。 スタックサイズが、"0 ~ 0x7ff8", "0x8000 ~ 0xfff8" , "x10000 ~ 0xfffffff8", "x10000 ~ 0xfffffff8" に応じて使用するプロローグとエピローグのコードが異なる。
Data operands DAGs
DAGを追加するためには、既存のコードにはどのようなDAGが追加されているのかについて理解する必要がある。