FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

オリジナルLLVM Backendを追加しよう (25. 可変引数・動的スタック割り当て)

https://cdn-ak.f.st-hatena.com/images/fotolife/m/msyksphinz/20181123/20181123225150.png

LLVMにはすでにRISC-Vのバックエンドサポートが追加されている。しかし、勉強のために独自のRISC-V実装をLLVMに追加している。

jonathan2251.github.io

第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