FPGA開発日記

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

オリジナルLLVMバックエンド実装をまとめる(17. 関数からも戻るときのCalling Convention)

次に、戻り値に関するCalling Conventionの実装から始める。

以下の記述では、関数の戻り値が渡される場合に、CCAssignToRegで示されるレジスタのどれかに引数が格納されるルールが追加されている。 そしてCalling ConventionであるRetCC_MYRISCVXを定義する。

commit:84bc7aa2b1b Add MYRISCVX Calling Convention LP32/STACK32/LP64/STACK64

  • llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXCallingConv.td
//===----------------------------------------------------------------------===//
// MYRISCVX LP32/STACK32 Return Convention
//===----------------------------------------------------------------------===//

def RetCC_LP32 : CallingConv<[
  CCIfType<[i32], CCAssignToReg<[A0, A1]>>
]>;

def RetCC_STACK32 : CallingConv<[
  CCIfType<[i32], CCAssignToStack<4, 4>>
]>;


//===----------------------------------------------------------------------===//
// MYRISCVX LP64/STACK64 Return Convention
//===----------------------------------------------------------------------===//

def RetCC_LP64 : CallingConv<[
  CCIfType<[i64], CCAssignToReg<[A0, A1]>>
]>;

def RetCC_STACK64 : CallingConv<[
  CCIfType<[i64], CCAssignToStack<8, 8>>
]>;

def RetCC_MYRISCVX : CallingConv<[
  CCIfSubtarget<"isABI_STACK64()", CCDelegateTo<RetCC_STACK64>>,
  CCIfSubtarget<"isABI_LP64   ()", CCDelegateTo<RetCC_LP64>>,
  CCIfSubtarget<"isABI_STACK32()", CCDelegateTo<RetCC_STACK32>>,
  CCDelegateTo<RetCC_LP32>
]>;

このRetCC_MYRISCVXはInstruction Selection時のAnalyzeReturnで使用される。AnalyzeReturnLowerReturnから呼び出される。

それぞれの中身は、見てみると簡単です。LP32/LP64のCalling Conventionの場合は戻り値をレジスタA0, A1に割り当てる。 これをCCAssignToRegという命令によって表現している。 また、STACK32/STACK64のCalling Conventionの場合は、CCAssignToStackという命令を使用してスタックに割り当てる。 文法はCCAssignToStack<Size, Align>となっており、最初の数字はデータサイズ、次の数字はアドレスアラインを示している。

f:id:msyksphinz:20191007022122p:plain
関数戻り時の戻り値を特定のレジスタ経由で行う仕組み。

そして、LP64/STACK64, LP32/STACK32をそれぞれオプションで切り替えるための、大元となるRetCC_MYRISCVXを定義している。

ちなみに、生成されたMYRISCVXGenCallingConv.incの一部を切り取る。 このように、CCIfSubTargetによりサブターゲットの条件分岐が行われ、さらにCC_LP{32,64}, CC_STACK{32, 64}の中で引数のチェックが行われている。

  • build-myriscvx80/lib/Target/MYRISCVX/MYRISCVXGenCallingConv.inc
// おおもとのCalling Convention。内部でCC_STACK32, CC_LP32, CC_STACK64, CC_LP64を条件分岐で使い分ける。
static bool RetCC_MYRISCVX(unsigned ValNo, MVT ValVT,
                           MVT LocVT, CCValAssign::LocInfo LocInfo,
                           ISD::ArgFlagsTy ArgFlags, CCState &State) {

  if (static_cast<const MYRISCVXSubtarget&>(State.getMachineFunction().getSubtarget()).isABI_STACK64()) {
    if (!RetCC_STACK64(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State))
      return false;
  }

  if (static_cast<const MYRISCVXSubtarget&>(State.getMachineFunction().getSubtarget()).isABI_LP64   ()) {
    if (!RetCC_LP64(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State))
      return false;
  }

  if (static_cast<const MYRISCVXSubtarget&>(State.getMachineFunction().getSubtarget()).isABI_STACK32()) {
    if (!RetCC_STACK32(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State))
      return false;
  }

  if (!RetCC_LP32(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State))
    return false;

  return true;  // CC didn't match.
}


// RetCC_STACK32の実装。
// スタックのアロケーションを行っている。
static bool RetCC_STACK32(unsigned ValNo, MVT ValVT,
                          MVT LocVT, CCValAssign::LocInfo LocInfo,
                          ISD::ArgFlagsTy ArgFlags, CCState &State) {

  if (LocVT == MVT::i32) {
    unsigned Offset1 = State.AllocateStack(4, 4);
    State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset1, LocVT, LocInfo));
    return false;
  }

  return true;  // CC didn't match.
}


// RetCC_LP32の実装
static bool RetCC_LP32(unsigned ValNo, MVT ValVT,
                       MVT LocVT, CCValAssign::LocInfo LocInfo,
                       ISD::ArgFlagsTy ArgFlags, CCState &State) {

  if (LocVT == MVT::i32) {
    static const MCPhysReg RegList1[] = {
      MYRISCVX::A0, MYRISCVX::A1
    };
    if (unsigned Reg = State.AllocateReg(RegList1)) {
      State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
      return false;
    }
  }

  return true;  // CC didn't match.
}
  • llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXISelLowering.cpp
template<typename Ty>
void MYRISCVXTargetLowering::MYRISCVXCC::
analyzeReturn(const SmallVectorImpl<Ty> &RetVals, bool IsSoftFloat,
              const SDNode *CallNode, const Type *RetTy) const {
  CCAssignFn *Fn;

  Fn = RetCC_MYRISCVX;

...
    if (Fn(I, VT, RegVT, CCValAssign::Full, Flags, this->CCInfo)) {
      LLVM_DEBUG(dbgs() << "Call result #" << I << " has unhandled type "
                 << EVT(VT).getEVTString() << '\n');
      llvm_unreachable(nullptr);
    }
...