FPGA開発日記

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

オリジナルLLVMバックエンド実装をまとめる(9. emitPrologue, emitEpilogueについて)

関数に関連する命令の生成の続き。関数のプロローグ・エピローグとはどのような処理をするのだろうか。

  • プロローグ : 関数の先頭で、関数のスタックフレームを生成する。関数が使用する変数のサイズだけスタックを移動し、メモリ領域を確保する。また、Calling ConventionにおけるCallee Savedレジスタを保存する。このために、emitPrologue()関数を実装する。
  • エピローグ : プロローグと逆の処理を実行する。つまり、使用したスタックフレームを元に戻す。このために、emitEpilogue()を実装する。

これらの処理のため、関数フレームを処理するためのクラスMYRISCVXFrameLoweringを作成していた。

f:id:msyksphinz:20190921023556p:plain
emitPrologue(), emitEpilogue()の役割

MYRISCVXFrameLowering::emitPrologue()

emitPrologueは関数のエピローグのコードを生成する。

  • llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXFrameLowering.cpp
void MYRISCVXFrameLowering::emitPrologue(MachineFunction &MF,
                                         MachineBasicBlock &MBB) const {

引数としては2つ用意されている。MachineFunction MFは変換対象となる関数の実体で、関数内部のBasicBlockなどを含んでいる。MachieBasicBlockのインスタンスのリストを含んでおり、MachineFunctionにはMachineControlPool, MachineFrameInfo, MachineFunctionInfo, MachineRegisterInfoクラスが含まれている。

MachineBasicBlock MBBは変換後のBasicBlockです。MachineBasicBlockにはマシン命令のリストであるMachineInstrのリストが含まれている。

emitPrologue()内では関数内で使用されるスタックのサイズを計算し、そのその分だけスタックポインタの量を調整する。

  • llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXFrameLowering.cpp
void MYRISCVXFrameLowering::emitPrologue(MachineFunction &MF,
                                         MachineBasicBlock &MBB) const {
...
  // First, compute final stack size.
  uint64_t StackSize = MFI.getStackSize();

  // No need to allocate space on the stack.
  if (StackSize == 0 && !MFI.adjustsStack()) return;

  MachineModuleInfo &MMI = MF.getMMI();
  const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo();

  // Adjust stack.
  TII.adjustStackPtr(SP, -StackSize, MBB, MBBI);
...

adjustStackPtr()関数により、実際にスタックポインタをデクリメントする命令を生成する。

  • llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXInstrInfo.cpp
/// Adjust SP by Amount bytes.
void MYRISCVXInstrInfo::adjustStackPtr(unsigned SP, int64_t Amount,
                                       MachineBasicBlock &MBB,
                                       MachineBasicBlock::iterator I) const {
  DebugLoc DL = I != MBB.end() ? I->getDebugLoc() : DebugLoc();
  unsigned ADD  = MYRISCVX::ADD;
  unsigned ADDI = MYRISCVX::ADDI;

  if (isInt<12>(Amount)) {
    // addiu sp, sp, amount
    BuildMI(MBB, I, DL, get(ADDI), SP).addReg(SP).addImm(Amount);
  } else { // Expand immediate that doesn't fit in 12-bit.
    // unsigned Reg = MRI.createVirtualRegister(&MYRISCVX::GPRRegClass);
    unsigned Reg = MYRISCVX::T0;
    loadImmediate(Amount, MBB, I, DL, Reg, nullptr);
    BuildMI(MBB, I, DL, get(ADD), SP).addReg(SP).addReg(Reg, RegState::Kill);
  }
}

adjustStackPtrの実装は見ればすぐにわかると思う。Amountの量が12ビット以内に収まる数値であれば、単純にADDI命令を生成して、スタックポインタを更新する。12ビットを超える値の場合は、loadImmediate()関数を呼び出して即値を生成し、この値をT0レジスタに格納する。T0レジスタの値をSPレジスタと加算してスタックポインタを更新する。

loadImmediate()の実装を以下に示する。MYRISCVXInstrInfoに実装しました。

  • llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXInstrInfo.cpp
/// This function generates the sequence of instructions needed to get the
/// result of adding register REG and immediate IMM.
void
MYRISCVXInstrInfo::loadImmediate(int64_t Imm, MachineBasicBlock &MBB,
                                 MachineBasicBlock::iterator II,
                                 const DebugLoc &DL, unsigned DstReg,
                                 unsigned *NewImm) const {
  uint64_t Hi20 = ((Imm + 0x800) >> 12) & 0xfffff;
  uint64_t Lo12 = SignExtend64<12>(Imm);
  BuildMI(MBB, II, DL, get(MYRISCVX::LUI), DstReg)
      .addImm(Hi20);
  BuildMI(MBB, II, DL, get(MYRISCVX::ADDI), DstReg)
      .addReg(DstReg, RegState::Kill)
      .addImm(Lo12);
}

最初にLUI命令により上位の20ビットを生成し、次に下位の12ビットを生成する。

MYRISCVXFrameLowering::emitEpilogue()

また、EmitEpilogueではその逆で、関数内のスタックサイズの分だけスタックポインタを元に戻す。

  • llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXFrameLowering.cpp
//@emitEpilogue {
void MYRISCVXFrameLowering::emitEpilogue(MachineFunction &MF,
                                         MachineBasicBlock &MBB) const {
...
  unsigned SP = MYRISCVX::SP;

  // Get the number of bytes from FrameInfo
  uint64_t StackSize = MFI.getStackSize();

  if (!StackSize)
    return;

  // Adjust stack.
  TII.adjustStackPtr(SP, StackSize, MBB, MBBI);
...