FPGA開発日記

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

オリジナルLLVM Backendを追加しよう (16. 32-bitコアでの64bit整数演算の命令出力調査)

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

jonathan2251.github.io

第7章では、long long(64-bit)の対応も含まれている。解説の中では、MIPSのようなHIレジスタとLOレジスタを使って64bitの演算を実現する方法が解説してある。

あれ、じゃあ32bit版のRISC-Vで、64bitの乗算ってどうやってLLVMに命令を出力すればいいんだ。良く分からなかったので実際にriscv32-unknown-elf-gccにコードを出力させて確認した。 以下のようなコードを書いてコンパイル

#include <stdint.h>

int64_t test_longlong(int64_t a1, int64_t b1)
{
  return (int64_t)a1 * (int64_t)b1;
}
riscv32-unknown-elf-gcc -O3 -c ../lbdex/input/ch7_1_longlong_mult.cpp
riscv32-unknown-elf-objdump -d ch7_1_longlong_mult.o | less

なるほどね。mulhuを使うのか。mulhuは、32bitと32bitの値を乗算し、上位の32bitの結果をレジスタに格納する命令だ。これを使って、部分積を取った場合の繰り上がりの計算を行っている。

Disassembly of section .text:

00000000 <_Z13test_longlongxx>:
   0:   02a686b3                mul     a3,a3,a0
   4:   02c585b3                mul     a1,a1,a2
   8:   02c537b3                mulhu   a5,a0,a2
   c:   00d585b3                add     a1,a1,a3
  10:   02c50533                mul     a0,a0,a2
  14:   00f585b3                add     a1,a1,a5
  18:   00008067                ret

図にするとこんな感じかな。イメージはできたので、LLVMのバックエンドに実装してみよう。

f:id:msyksphinz:20190307010859p:plain

2019/03/08追記。以下のような記述を追加した。とりあえずMULとMULHでしのぐことにする。

--- a/lib/Target/MYRISCVX/MYRISCVXSEISelDAGToDAG.cpp
+++ b/lib/Target/MYRISCVX/MYRISCVXSEISelDAGToDAG.cpp
@@ -70,19 +70,16 @@ void MYRISCVXSEDAGToDAGISel::selectAddESubE(unsigned MOp, SDValue InFlag,
 std::pair<SDNode *, SDNode *>
 MYRISCVXSEDAGToDAGISel::selectMULT(SDNode *N, unsigned Opc, const SDLoc &DL, EVT Ty,
                                    bool HasLo, bool HasHi) {
-  SDNode *Lo = 0, *Hi = 0;
-  SDNode *Mul = CurDAG->getMachineNode(Opc, DL, MVT::Glue, N->getOperand(0),
-                                       N->getOperand(1));
-  SDValue InFlag = SDValue(Mul, 0);
-  if (HasLo) {
-    Lo = CurDAG->getMachineNode(MYRISCVX::MFLO, DL,
-                                Ty, MVT::Glue, InFlag);
-    InFlag = SDValue(Lo, 1);
+  SDNode *MulHi = 0;
+  SDNode *MulLo = CurDAG->getMachineNode(Opc, DL, MVT::Glue,
+                                         N->getOperand(0),
+                                         N->getOperand(1));
+  if (HasHi) {
+    MulHi = CurDAG->getMachineNode(MYRISCVX::MULH, DL, MVT::Glue,
+                                   N->getOperand(0),
+                                   N->getOperand(1));
   }
-  if (HasHi)
-    Hi = CurDAG->getMachineNode(MYRISCVX::MFHI, DL,
-                                Ty, InFlag);
-  return std::make_pair(Lo, Hi);
+  return std::make_pair(MulHi, MulLo);
 }
./bin/llc -march=myriscvx32 -relocation-model=pic -filetype=asm ch7_1_longlong_mult.bc -o -

llc: /home/msyksphinz/others/riscv/llvm/llvm-myriscvx/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp:9025: void llvm::SelectionDAGISel::LowerArguments(const llvm::Function &): Assertion `InVals.size() == Ins.size() && "LowerFormalArguments didn't emit the correct number of values!"' failed.
Stack dump:
0.      Program arguments: ./bin/llc -march=myriscvx32 -relocation-model=pic -filetype=asm ch7_1_longlong_mult.bc -o -
1.      Running pass 'Function Pass Manager' on module 'ch7_1_longlong_mult.bc'.
2.      Running pass 'MYRISCVX DAG->DAG Pattern Instruction Selection' on function '@_Z13test_longlongxx'
#0 0x00000000009be531 __interceptor_backtrace /home/msyksphinz/software/llvm/llvm/projects/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:4025:0
#1 0x0000000002a51293 llvm::sys::PrintStackTrace(llvm::raw_ostream&) /home/msyksphinz/others/riscv/llvm/llvm-myriscvx/lib/Support/Unix/Signals.inc:490:13

あれ?落ちてしまった。おかしいなあ。