FPGA開発日記

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

LLVMのバックエンドを作るための第一歩 (23. グローバル変数の取り扱い)

今回は、グローバル変数を取り扱うための手法について調査する。 現状では、グローバル変数の入っているプログラムをコンパイルすると以下のようにエラーが発生する。

int gStart = 3;
int gI = 100;
int test_global()
{
  int c = 0;

  c = gI;

  return c;
}
./bin/clang -target mips-unknown-linux-gnu -c ../lbdex/input/ch6_1.cpp -emit-llvm
./bin/llc -debug -march=myriscvx32 -mcpu=simple32 -mattr=+64bit -relocation-model=pic -filetype=asm ch6_1.bc
LLVM ERROR: Cannot select: t5: i32 = GlobalAddress<i32* @gI> 0
In function: _Z11test_globalv

GlobalAddressというIRを処理する必要がありそうだ。

このグローバル変数を処理するためには、コンパイラには2種類のポリシーがある。

  • PIC(Position Independent Code) : ポジション独立。グローバル変数のアドレスはランタイムに計算される。
  • Static : グローバル変数のアドレスは固定で計算される。

ライブラリとして提供する場合、グローバル変数はPICモードでコンパイル必要があるわけだが、そうでない場合はstaticでコンパイルしても良いわけだ。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelLowering.cpp まず、setOperationAction(Custom)で、GlobalAddress IRの場合はカスタム実装したLower処理を実行するように設定する。
//@MYRISCVXTargetLowering {
MYRISCVXTargetLowering::MYRISCVXTargetLowering(const MYRISCVXTargetMachine &TM,
                                               const MYRISCVXSubtarget &STI)
    : TargetLowering(TM), Subtarget(STI), ABI(TM.getABI()) {
  setOperationAction(ISD::GlobalAddress,      MVT::i32,   Custom);
}
SDValue MYRISCVXTargetLowering::
LowerOperation(SDValue Op, SelectionDAG &DAG) const
{
  switch (Op.getOpcode())
  {
    case ISD::GlobalAddress    : return lowerGlobalAddress(Op, DAG);
  }
  return SDValue();
}

lowerGlobalAddressでは、PICモードとStaticモードの両方に対してDAGの生成を行う。

SDValue MYRISCVXTargetLowering::lowerGlobalAddress(SDValue Op,
                                                   SelectionDAG &DAG) const {
  • PICモードの場合:
  const GlobalObject *GO = GV->getBaseObject();
  return getAddrGlobalLargeGOT(
      N, Ty, DAG, MYRISCVXII::MO_GOT_HI16, MYRISCVXII::MO_GOT_LO16,
      DAG.getEntryNode(),
      MachinePointerInfo::getGOT(DAG.getMachineFunction()));
  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelLowering.h
    //@getAddrGlobalLargeGOT {
    // This method creates the following nodes, which are necessary for
    // computing a global symbol's address in large-GOT mode:
    //
    // (load (wrapper (add %hi(sym), $gp), %lo(sym)))
    template<class NodeTy>
    SDValue getAddrGlobalLargeGOT(NodeTy *N, EVT Ty, SelectionDAG &DAG,
                                  unsigned HiFlag, unsigned LoFlag,
                                  SDValue Chain,
                                  const MachinePointerInfo &PtrInfo) const {
      SDLoc DL(N);
      SDValue Hi = DAG.getNode(MYRISCVXISD::Hi, DL, Ty,
                               getTargetNode(N, Ty, DAG, HiFlag));
      Hi = DAG.getNode(ISD::ADD, DL, Ty, Hi, getGlobalReg(DAG, Ty));
      SDValue Wrapper = DAG.getNode(MYRISCVXISD::Wrapper, DL, Ty, Hi,
                                    getTargetNode(N, Ty, DAG, LoFlag));
      return DAG.getLoad(Ty, DL, Chain, Wrapper, PtrInfo);
    }
    //@getAddrGlobalLargeGOT }
  • Staticモードの場合
    //@ %hi/%lo relocation
    return getAddrNonPIC(N, Ty, DAG);
  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelLowering.h
    //@getAddrNonPIC
    // This method creates the following nodes, which are necessary for
    // computing a symbol's address in non-PIC mode:
    //
    // (add %hi(sym), %lo(sym))
    template<class NodeTy>
    SDValue getAddrNonPIC(NodeTy *N, EVT Ty, SelectionDAG &DAG) const {
      SDLoc DL(N);
      SDValue Hi = getTargetNode(N, Ty, DAG, MYRISCVXII::MO_ABS_HI);
      SDValue Lo = getTargetNode(N, Ty, DAG, MYRISCVXII::MO_ABS_LO);
      return DAG.getNode(ISD::ADD, DL, Ty,
                         DAG.getNode(MYRISCVXISD::Hi, DL, Ty, Hi),
                         DAG.getNode(MYRISCVXISD::Lo, DL, Ty, Lo));
    }

それぞれのコードは以下のようになる。

  • PICモードの場合
        lui     x10, %got_hi(gI)
        add     x10, x10, x3
        lw      x10, %got_lo(gI)(x10)
        lw      x10, 0(x10)
  • Staticモードの場合
        lui     x10, %hi(gI)
        ori     x10, x10, %lo(gI)
        lw      x10, 0(x10)
f:id:msyksphinz:20190612003723p:plain
グローバル変数をPICで生成した場合。GOTに変数の情報が書き込まれる。