MYRISCVXRegisterInfo::eliminateFrameIndex
これも必須の関数だ。関数のスタックフレーム内での参照について、計算できていなかったオフセットを計算するための関数だ。
このeliminateFrameIndex
が呼ばれるまでは、スタックポインタをベースに参照されるデータのオフセットは計算できていない。このeliminateFrameIndex
によって、最終的なオフセットを計算する。
そもそもどのようにしてスタックオフセットを計算するかというと、まず、関数のプロローグでスタックポインタをデクリメントすることでスタックを割り当てる。 スタックポインタがデクリメントされると、すべてのスタックへの参照はスタック・フレームポインタからの+方向へのオフセット参照となる。
ここでは、MIPSに倣いスタックフレームを以下のように構成する。
// 0 ---------- // 4 引数一覧 // . PICで保存するGP // . allocaによる割り当て // . ローカル領域 // . CPUのCallee Savedレジスタ // . 保存されたFP // . 保存されたRA // . FPUのCallee Savedレジスタ // StackSize -----------
全体のスタックサイズはLowerFormalArgumentsではまだ未決定で、例えば関数の引数へのアクセスのために挿入されるスタックベースのメモリアクセス(ObjectOffset)は負数が挿入されている。
これにより、eliminateFrameIndex
はこの参照を検出し、実際のアクセスに変換する、というわけだ。
EmitPrologue, EmitEpilogue, EliminateFrameIndex
実行前のLLVM IR
bb.0.entry: $a0 = ADDI $zero, 0 SW $a0, %stack.0.retval, 0 :: (store 4 into %ir.retval) RetRA implicit $a0
EmitPrologue, EmitEpilogue, EliminateFrameIndex
実行後のLLVM IR
bb.0.entry: $sp = ADDI $sp, -8 CFI_INSTRUCTION def_cfa_offset 8 $a0 = ADDI $zero, 0 SW $a0, $sp, 4 :: (store 4 into %ir.retval) $sp = ADDI $sp, 8 RetRA implicit $a0
SW命令のアドレスがレジスタとオフセットに置き換えられていることが分かる。