最終的に関数をコンパイルして命令を出力するためには、関数のプロローグ・エピローグを生成する必要がある。 関数のプロローグは関数に入った時の最初に行う処理、関数のエピローグは関数から出るときに最後に行う処理である。
基本的には
- Callee Saved Registerの退避
- スタックポインタの移動
が必要になる。これらを実行するのが、LLVMの
MYRISCVXSEFrameLowering::emitPrologue()
MYRISCVXSEFrameLowering::emitEpilogue()
である。それぞれについて内部の実装を確認していく。
MYRISCVXSEFrameLowering::emitPrologue()
emitPrologue
は関数のエピローグのコードを生成する。
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXSEFrameLowering.cpp
void MYRISCVXSEFrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const {
引数としては2つ用意されている。MachineFunction MF
は変換対象となる関数の実体で、関数内部のBasicBlockなどを含んでいる。
MachieBasicBlockのインスタンスのリストを含んでおり、MachineFunction
にはMachineControlPool, MachineFrameInfo, MachineFunctionInfo, MachineRegisterInfo
クラスが含まれている。
MachineBasicBlock MBB
は変換後のBasicBlockだ。MachineBasicBlockにはマシン命令のリストであるMachineInstr
のリストが含まれている。
実際には、EmitPrologue内では関数内で使用されるスタックのサイズを計算し、そのその分だけスタックポインタの量を調整する。
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXSEFrameLowering.cpp
void MYRISCVXSEFrameLowering::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); ...
また、EmitEpilogueではその逆で、関数内のスタックサイズの分だけスタックポインタを元に戻す。
llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXSEFrameLowering.cpp
//@emitEpilogue { void MYRISCVXSEFrameLowering::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); ...