LLVMにはすでにRISC-Vのバックエンドサポートが追加されている。しかし、勉強のために独自のRISC-V実装をLLVMに追加している。
第9章の後半では、様々なイントリンジックを挿入する。
9.6.5 Function related Intrinsics support
LLVM固有のIRの組み込みを行う。大体は例外のサポートのためだ。C++での例外ハンドラのプログラムを実装するために、フレームアドレスとリターンアドレスを記録する必要があり、このためのLLVM IRを追加する。
lbdex/chapters/Chapter9_3/Cpu0ISelLowering.cpp
case ISD::FRAMEADDR: return lowerFRAMEADDR(Op, DAG); case ISD::RETURNADDR: return lowerRETURNADDR(Op, DAG); case ISD::EH_RETURN: return lowerEH_RETURN(Op, DAG); case ISD::ADD: return lowerADD(Op, DAG);
... // include/llvm/CodeGen/SelectionDAG.h SDValue getCopyFromReg(SDValue Chain, const SDLoc &dl, unsigned Reg, EVT VT) { SDVTList VTs = getVTList(VT, MVT::Other); SDValue Ops[] = { Chain, getRegister(Reg, VT) }; return getNode(ISD::CopyFromReg, dl, VTs, Ops); } ... SDValue Cpu0TargetLowering:: lowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const { ... // FPレジスタの値を返す。 SDValue FrameAddr = DAG.getCopyFromReg( DAG.getEntryNode(), DL, Cpu0::FP, VT); return FrameAddr;
SDValue MYRISCVXTargetLowering::lowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const { // 戻りアドレスを格納しているRAレジスタを返す。暗黙的なLive-Inであることをマークする。 unsigned Reg = MF.addLiveIn(RA, getRegClassFor(VT)); return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), Reg, VT);
// EH_RETRUNはllvm.eh.retrun IRの結果を返す。これは__buildin_eh_return(offset, handler) から生成される。 // このIRの効果は、"offset"によってスタックポインタの位置を調整し、"handler"に移動する。 SDValue MYRISCVXTargetLowering::lowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const { ... // スタックのオフセットをA1に保存し、A0にジャンプターゲットを格納する。 // CopyToRegとEH_RETURNのノードを接続するので、これらの命令が連続して生成されるようになる。 unsigned OffsetReg = MYRISCVX::A1; unsigned AddrReg = MYRISCVX::A0; Chain = DAG.getCopyToReg(Chain, DL, OffsetReg, Offset, SDValue()); Chain = DAG.getCopyToReg(Chain, DL, AddrReg, Handler, Chain.getValue(1)); return DAG.getNode(MYRISCVXISD::EH_RETURN, DL, MVT::Other, Chain, DAG.getRegister(OffsetReg, Ty), DAG.getRegister(AddrReg, getPointerTy(MF.getDataLayout())), Chain.getValue(1));
SDValue MYRISCVXTargetLowering::lowerADD(SDValue Op, SelectionDAG &DAG) const { ... // この関数がどのような意味を持っているのかはよく分からない... MYRISCVXFI->setCallsEhDwarf(); return Op;
テストパタンを実行した結果は以下となった。
_Z21display_returnaddressv: ... # %bb.0: # %entry sw x1, 8(x2) # fn()を呼び出す前に、RAレジスタをメモリに退避する。 lw x3, %call16(_Z2fnv)(x3) jalr x3 lw x3, 8(x2) lw x10, 8(x2) # RAレジスタの値をReturn Valueレジスタにロードする。 _Z20display_frameaddressv: ... # %bb.0: # %entry addi x2, x2, -8 sw x8, 4(x2) # 4-byte Folded Spill move x8, x2 addi x10, x8, 0 # FPレジスタの値をReturn Valueレジスタにロードする。 move x2, x8 lw x8, 4(x2) # 4-byte Folded Reload addi x2, x2, 8 jalr x1
次に、lbdex/input/ch9_3_detect_exception.cpp
に実行して、例外ハンドラ(eh_return
)を生成させてみる。
./bin/clang -c -target mips-unknown-linux-gnu ../lbdex/input/ch9_3_detect_exception.cpp -emit-llvm ./bin/llc -march=myriscvx32 -relocation-model=pic -filetype=asm ch9_3_detect_exception.bc -o -
bswap
イントリンジックのサポート
bswapというイントリンジックがあるらしい。これも初めて知った。
../lbdex/input/ch9_3_bswap.cpp
int test_bswap16() { volatile int a = 0x1234; int result = (__builtin_bswap16(a) ^ 0x3412); return result; }
./bin/clang -c -target mips-unknown-linux-gnu ../lbdex/input/ch9_3_bswap.cpp -emit-llvm ./bin/llc -march=myriscvx32 -relocation-model=pic -filetype=asm ch9_3_bswap.bc -o -
_Z12test_bswap16v: # %bb.0: # %entry addi x2, x2, -16 sw x8, 12(x2) # 4-byte Folded Spill move x8, x2 ori x10, x0, 4660 sw x10, 8(x2) lw x10, 8(x2) slli x11, x10, 8 lui x12, 4080 and x11, x11, x12 slli x10, x10, 24 or x10, x10, x11 shli x10, x10, 16 xori x10, x10, 1042 sw x10, 4(x2) lw x10, 4(x2) move x2, x8 lw x8, 12(x2) # 4-byte Folded Reload addi x2, x2, 16 jalr x1