FPGA開発日記

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

RISC-V 64-bit LLVM Backendを試す (13. Return文生成の解析)

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

前回までで、簡単なReturn命令は生成されるようになったが、まだ-O2オプションを付加しないとエラーが発生する。 EmitPrologueとEmitEpilogueを実装したのだが、まだ途中でスタックダンプしてしまい、正しく動作していない様だ。

とりあえず、bin/llcを実行した際に-march=riscv64-march=myriscvxでどのように挙動が変わるのかを観測した。

Legalizing node: t7: i32 = Register $a0
Ignoring node results
Legally typed node: t7: i32 = Register $a0

Legalizing node: t6: i32 = Constant<4>
Analyzing result type: i32
Legal result type
Legally typed node: t6: i32 = Constant<4>

Legalizing node: t4: i64 = undef
Analyzing result type: i64
Expand integer result: t4: i64 = undef

Creating new node: t10: i32 = undef
Legalizing node: t10: i32 = undef
Analyzing result type: i32
Legal result type
Legally typed node: t10: i32 = undef

Legalizing node: t2: i64 = FrameIndex<0>
Analyzing result type: i64
Expand integer result: t2: i64 = FrameIndex<0>

ExpandIntegerResult #0: t2: i64 = FrameIndex<0>

Do not know how to expand the result of this operator!
UNREACHABLE executed at /home/msyksphinz/work/riscv/llvm-myriscvx64/lib/CodeGen/SelectionDAG/LegalizeIntegerTypes.cpp:1369!

-O2でコンパイルしたものはllvm-disで逆アセンブルすると以下のようになる。

; Function Attrs: norecurse nounwind readnone
define dso_local signext i32 @main() local_unnamed_addr #0 {
entry:
  ret i32 0
}

一方で、-O2を付加しないものはllvm-disで逆アセンブルすると以下のようになる。

; Function Attrs: noinline norecurse nounwind optnone
define dso_local signext i32 @main() #0 {
entry:
  %retval = alloca i32, align 4
  store i32 0, i32* %retval, align 4
  ret i32 0
}

FrameIndexというNodeを変換するときに失敗する。FrameIndexについていろいろ調べてみると、どうも関数フレームを処理するためのポインタの処理を行っているらしい。しかもExpandIntegerResultということは値の拡張をしようとしているので、どうもそこで引っかかっている気がする。

まさかと思って、ポインタのサイズの定義を64bitから32bitに変えてみた。

diff --git a/lib/Target/MYRISCVX/MYRISCVXTargetMachine.cpp b/lib/Target/MYRISCVX/MYRISCVXTargetMachine.cpp
index 1053f2428d8..0cadaed5cc5 100644
--- a/lib/Target/MYRISCVX/MYRISCVXTargetMachine.cpp
+++ b/lib/Target/MYRISCVX/MYRISCVXTargetMachine.cpp
@@ -36,7 +36,7 @@ static std::string computeDataLayout(const Triple &TT, StringRef CPU,
   std::string Ret = "";
   Ret += "e";
   Ret += "-m:m";
-  Ret += "-p:64:64-i64:64-i128:128-n64-S128";
+  Ret += "-p:32:32-i64:64-i128:128-n64-S128";
   return Ret;
 }

これでビルドを行った。すると問題となっていた部分は通過した。やはり、32bitから64bitへの拡張ができていなかったのか。 とりあえず、32bitのアドレッシングで進めることにしよう。あとで修正する。

ただ、llcの途中でまだ固まってしまう。

1: nullptr
2: %bb.0
Found roots: %bb.0
        discovered a new reachable node nullptr
        discovered a new reachable node %bb.0
Skipping pass 'Shrink Wrapping analysis' on function main
alloc FI(0) at SP[-4]

フレームについてもう少し実装を進めてみる。

  • lib/Target/MYRISCVX/MYRISCVXRegisterInfo.cpp
// FrameIndex represent objects inside a abstract stack.
// We must replace FrameIndex with an stack/frame pointer
// direct reference.
void MYRISCVXRegisterInfo::
eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj,
                    unsigned FIOperandNum, RegScavenger *RS) const {
...
  // The following stack frame objects are always referenced relative to $sp:
  //  1. Outgoing arguments.
  //  2. Pointer to dynamically allocated stack space.
  //  3. Locations for callee-saved registers.
  // Everything else is referenced relative to whatever register
  // getFrameRegister() returns.
  unsigned FrameReg;

  FrameReg = MYRISCVX::SP;

  // Calculate final offset.
  // - There is no need to change the offset if the frame object is one of the
  //   following: an outgoing argument, pointer to a dynamically allocated
  //   stack space or a $gp restore location,
  // - If the frame object is any of the following, its offset must be adjusted
  //   by adding the size of the stack:
  //   incoming argument, callee-saved register location or local variable.
  int64_t Offset;
  Offset = spOffset + (int64_t)stackSize;

  Offset    += MI.getOperand(i+1).getImm();

  dbgs() << "Offset     : " << Offset << "\n" << "<--------->\n";

  // If MI is not a debug value, make sure Offset fits in the 16-bit immediate
  // field.
  if (!MI.isDebugValue() && !isInt<16>(Offset)) {
    assert("(!MI.isDebugValue() && !isInt<16>(Offset))");
  }

  MI.getOperand(i).ChangeToRegister(FrameReg, false);
  MI.getOperand(i+1).ChangeToImmediate(Offset);

これで無事に命令を生成することができた。

./bin/llc -march=myriscvx -relocation-model=pic --debug -filetype=asm ch3.bc -o -
********** COMPUTING STACKMAP LIVENESS: main **********
        .globl  main                    # -- Begin function main
        .type   main,@function
        .ent    main                    # @main
main:
        .frame  $x8,8,$x1
        .mask   0x00000000,0
        .set    noreorder
        .set    nomacro
        discovered a new reachable node %bb.0
# %bb.0:                                # %entry
        addi    $x2, $x2, -8
        addi    $x10, $x0, 0
        st      $x10, 4($x2)
        addi    $x10, $x0, 4
        addi    $x2, $x2, 8
        ret     $x1
        .set    macro
        .set    reorder
        .end    main
$func_end0:
        .size   main, ($func_end0)-main
                                        # -- End function

        .ident  "clang version 7.0.1 (https://git.llvm.org/git/clang.git/ 65f84326edb6105fb0263f0b023719b491f8cf1a) (https://github.com/msyksphinz/llvm.git 232d729230027419ef0c460c2289d5f1147e61a3)"
        .section        ".note.GNU-stack","",@progbits
f:id:msyksphinz:20190120194335p:plain