今回は、グローバル変数を取り扱うための手法について調査する。 現状では、グローバル変数の入っているプログラムをコンパイルすると以下のようにエラーが発生する。
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モードでコンパイル必要があるわけだが、そうでない場合は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)