LLVMにはすでにRISC-Vのバックエンドサポートが追加されている。しかし、勉強のために独自のRISC-V実装をLLVMに追加している。
第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のバックエンドに実装してみよう。
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
あれ?落ちてしまった。おかしいなあ。