FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

LLVMのバックエンドを作るための第一歩 (19. 関数のスタックフレームの作り方)

f:id:msyksphinz:20190425001356p:plain

MYRISCVXRegisterInfo::eliminateFrameIndex

これも必須の関数だ。関数のスタックフレーム内での参照について、計算できていなかったオフセットを計算するための関数だ。

このeliminateFrameIndexが呼ばれるまでは、スタックポインタをベースに参照されるデータのオフセットは計算できていない。このeliminateFrameIndexによって、最終的なオフセットを計算する。

そもそもどのようにしてスタックオフセットを計算するかというと、まず、関数のプロローグでスタックポインタをデクリメントすることでスタックを割り当てる。 スタックポインタがデクリメントされると、すべてのスタックへの参照はスタック・フレームポインタからの+方向へのオフセット参照となる。

ここでは、MIPSに倣いスタックフレームを以下のように構成する。

//  0                 ----------
//  4                 引数一覧
//  .                 PICで保存するGP
//  .                 allocaによる割り当て
//  .                 ローカル領域
//  .                 CPUのCallee Savedレジスタ
//  .                 保存されたFP
//  .                 保存されたRA
//  .                 FPUのCallee Savedレジスタ
//  StackSize         -----------
f:id:msyksphinz:20190606003013p:plain
スタックが割り当てられる領域。

全体のスタックサイズは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命令のアドレスがレジスタとオフセットに置き換えられていることが分かる。