LLVMにはすでにRISC-Vのバックエンドサポートが追加されている。しかし、勉強のために独自のRISC-V実装をLLVMに追加している。
前回までで、簡単なReturn命令は生成されるようになったが、まだ-O2オプションを付加しないとエラーが発生する。
次はCpu0 Backendの資料を見ながら、EmitPrologueとEmitEpilogueを実装していきたい。
EmitPrologueとEmitEpilogueは、その名の通り関数の最初のエントリと最後のエントリを生成するものだが、役割としてはスタックポインタとフレームポインタの準備だ。 とりあえずCpu0の資料を読みながらと、RISC-Vの実際の実装を見つつ進めていく。
EmitPrologueのソースコードはCpu0の資料からの引用と、RISC-VのLLVMの実装のミックスだ。
スタックサイズを取得して、それに基づいてスタックの調整を行う。
そのためにadjustReg()
という関数を呼び出している。
lib/Target/MYRISCVX/MYRISCVXSEFrameLowering.cpp
//@emitPrologue { void MYRISCVXSEFrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const { assert(&MF.front() == &MBB && "Shrink-wrapping not yet supported"); MachineFrameInfo &MFI = MF.getFrameInfo(); auto *MYRISCVXFI = MF.getInfo<MYRISCVXFunctionInfo>(); const MYRISCVXSEInstrInfo &TII = *static_cast<const MYRISCVXSEInstrInfo*>(STI.getInstrInfo()); const MYRISCVXRegisterInfo &RegInfo = *static_cast<const MYRISCVXRegisterInfo *>(STI.getRegisterInfo()); ... unsigned FPReg = MYRISCVX::S0; unsigned SPReg = MYRISCVX::SP; // 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(); MachineLocation DstML, SrcML; // Adjust stack. adjustReg(MBB, MBBI, dl, SPReg, SPReg, -StackSize, MachineInstr::FrameSetup); // emit ".cfi_def_cfa_offset StackSize" unsigned CFIIndex = MF.addFrameInst( MCCFIInstruction::createDefCfaOffset(nullptr, -StackSize)); BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) .addCFIIndex(CFIIndex); const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo(); ... } //}
adjustReg
は以下のように実装している。
中身を完璧に理解しているかというとそんなことは無いのだが、スタックのサイズが12bitよりも小さい範囲で収まる場合には単純にStackのサイズだけSPレジスタの位置をずらす命令を生成している(命令を生成するのはBuildMI()
という関数らしい?
そうでなければ、scratchReg
レジスタを新たに作成し、そこにスタックをずらす量を格納する(TII->movImm32()
、そしてBuildMIで加算命令・減算命令を生成してスタックレジスタを更新する、という手法だ。
void MYRISCVXSEFrameLowering::adjustReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, int64_t Val, MachineInstr::MIFlag Flag) const { MachineRegisterInfo &MRI = MBB.getParent()->getRegInfo(); const MYRISCVXInstrInfo *TII = STI.getInstrInfo(); if (DestReg == SrcReg && Val == 0) return; if (isInt<12>(Val)) { BuildMI(MBB, MBBI, DL, TII->get(MYRISCVX::ADDI), DestReg) .addReg(SrcReg) .addImm(Val) .setMIFlag(Flag); } else if (isInt<32>(Val)) { unsigned Opc = MYRISCVX::ADD; bool isSub = Val < 0; if (isSub) { Val = -Val; Opc = MYRISCVX::SUB; } unsigned ScratchReg = MRI.createVirtualRegister(&MYRISCVX::CPURegsRegClass); TII->movImm32(MBB, MBBI, DL, ScratchReg, Val, Flag); BuildMI(MBB, MBBI, DL, TII->get(Opc), DestReg) .addReg(SrcReg) .addReg(ScratchReg, RegState::Kill) .setMIFlag(Flag); } else { report_fatal_error("adjustReg cannot yet handle adjustments >32 bits"); } }
次にemitEpilogue()
だ。これは上記のemitPrologue()
の逆のことをやっている。
void MYRISCVXSEFrameLowering::emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const { ... // Get the number of bytes from FrameInfo uint64_t StackSize = MFI.getStackSize(); if (!StackSize) return; // Adjust stack. adjustReg(MBB, MBBI, dl, SPReg, SPReg, StackSize, MachineInstr::FrameDestroy); }
スタックサイズを取得し、その分だけスタックポインタを移動させる。emitPrologue
のときは-StackSize
でアドレスの低い方にアドレスを伸ばしたが、emitEpilogue
ではアドレスの高い方にスタックを伸ばす。
という訳でこれらの関数を追加してビルドし、再度単純なReturnだけのcppファイルを食わせてみたのだが、まだエラーが発生する。
Combining: t0: ch = EntryToken Optimized lowered selection DAG: %bb.0 'main:entry' SelectionDAG has 9 nodes: t0: ch = EntryToken t5: ch = store<(store 4 into %ir.retval)> t0, Constant:i32<0>, FrameIndex:i64<0>, undef:i64 t8: ch,glue = CopyToReg t5, Register:i32 $a0, Constant:i32<4> t9: ch = MYRISCVXISD::Ret t8, Register:i32 $a0, t8:1 Legalizing node: t2: i64 = FrameIndex<0> Analyzing result type: i64 Expand integer result: t2: i64 = FrameIndex<0> ExpandIntegerResult #0: t2: i64 = FrameIndex<0> Do not know how to expand the result of this operator! UNREACHABLE executed at /home/msyksphinz/work/riscv/llvm-myriscvx64/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp:1369! Stack dump: 0. Program arguments: ./bin/llc -march=myriscvx -relocation-model=pic --debug -filetype=asm ch3.4.bc -o - 1. Running pass 'Function Pass Manager' on module 'ch3.4.bc'. 2. Running pass 'MYRISCVX DAG->DAG Pattern Instruction Selection' on function '@main'
うーん、これはどうやって解析すればよいのだろう?引き続き見ていく。
ExpandIntegerResult()
という関数内で引っかかっているのは分かるが、どのDAGがどの変換で失敗しているのかがまだ理解できていない。
undef:i64
などと出ているのも気になるなあ。store命令で何かが足りていないんだろうか?