FPGA開発日記

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

オリジナルLLVM Backendを追加しよう (23. オブジェクトファイル形式の対応)

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

jonathan2251.github.io

Chapter-5を進めていこう。これまではアセンブラ形式のみを対応させていたが、今度はオブジェクトファイルも生成しなくてはならない。

主に追加するファイルは以下だ。結構量が多い。写経というか、コピペになってしまっているが、とりあえず動くものを作っていこう。

  • MYRISCVXAsmBackend.cpp
  • MYRISCVXMCCodeEmitter.cpp
  • MYRISCVXMCExpr.cpp
  • MYRISCVXELFObjectWriter.cpp
  • MYRISCVXTargetStreamer.cpp

なんだか大量にファイルが追加されるが、要するにターゲットを追加する、ということらしい。 ELFObjectWriterというターゲットを定義してこれを追加する。おそらく一番肝になっているのは以下のコード。

  • lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
//@2 {
extern "C" void LLVMInitializeMYRISCVXTargetMC() {
  for (Target *T : {&TheMYRISCVX32Target, &TheMYRISCVX64Target}) {
...
    // Register the MC Code Emitter
    TargetRegistry::RegisterMCCodeEmitter(*T,
                                          createMYRISCVXMCCodeEmitterEL);

    // Register the asm backend.
    TargetRegistry::RegisterMCAsmBackend(*T,
                                         createMYRISCVXAsmBackendEL32);

さらに、写経をしながらコードを追加していく。以下のコードはCpu0ではNOPとSHLが同じのためケアをする必要があるらしいが、RISC-Vではそんなことはないので割愛。面倒なので削除しておく。

/// encodeInstruction - Emit the instruction.
/// Size the instruction (currently only 4 bytes)
void Cpu0MCCodeEmitter::
encodeInstruction(const MCInst &MI, raw_ostream &OS,
                  SmallVectorImpl<MCFixup> &Fixups,
                  const MCSubtargetInfo &STI) const
{
...
  // Check for unimplemented opcodes.
  // Unfortunately in CPU0 both NOT and SLL will come in with Binary == 0
  // so we have to special check for them.
  unsigned Opcode = MI.getOpcode();
  if ((Opcode != Cpu0::NOP) && (Opcode != Cpu0::SHL) && !Binary)
    llvm_unreachable("unimplemented opcode in encodeInstruction()");

あとはLLVMの仕様が微妙に変わっていて少しずつコンパイルが通らないのを修正する。 基本的にMIPSの実装を参考にしながら、古くなっている部分を修正していった。

コンパイルを通して、さっそく実験してみる。

./bin/clang -c -target mips-unknown-linux-gnu ../lbdex/input/ch4_1_math.cpp -emit-llvm
./bin/llc -march=myriscvx32 -relocation-model=pic -filetype=obj ch4_1_math.bc -o ch4_1_math.myriscvx.o
...

Function : _Z9test_mathv
<--------->
$a1 = LW %stack.9.i, 0 :: (dereferenceable load 4 from %ir.i)
FrameIndex : 9
spOffset : -40
stackSize : 56
Offset : 16
<--------->

Function : _Z9test_mathv
<--------->
$a1 = LW %stack.13.i1, 0 :: (dereferenceable load 4 from %ir.i1)
FrameIndex : 13
spOffset : -56
stackSize : 56
Offset : 0

大量にデバッグメッセージが出てくるが、なんとか終了したようだ。 とりあえず正しく生成できているか確認しよう。

$ objdump -s ch4_1_math.myriscvx.o
ch4_1_math.myriscvx.o:     file format elf32-little

Contents of section .text:
 0000 130181fc 13005500 232a4503 13002500  ......U.#*E...%.
 0010 23280503 1300b5ff 2326c502 032a4503  #(......#&...*E.
 0020 03a80503 3305b500 23248502 032a4503  ....3...#$...*E.
 0030 03a80503 3305b500 23224502 032a4503  ....3...#"E..*E.
 0040 03a80503 3305b500 23200502 032a4503  ....3...# ...*E.
 0050 13152500 232ec501 0326c502 13151500  ..%.#....&......
 0060 2326c500 032a4503 13552500 232c8501  #&...*E..U%.#,..
f:id:msyksphinz:20190226004748p:plain
llcで生成したMYRISCVXオブジェクトファイルのダンプ

アセンブリと比較して、何となくそれっぽい命令が出力で来ているように思う。

gccが生成したものと比較して一致しているか確認してみたい。

riscv64-unknown-elf-gcc -c -march=rv64imafd ../lbdex/input/ch4_1_math.cpp
riscv64-unknown-elf-objdump -d ch4_1_math.o 
Contents of section .text:
 0000 130101fb 23348104 13040105 93075000  ....#4........P.
 0010 2326f4fe 93072000 2324f4fe 9307b0ff  #&.... .#$......
 0020 2322f4fe 0327c4fe 832784fe bb07f700  #"...'...'......
 0030 2320f4fe 0327c4fe 832784fe bb07f740  # ...'...'.....@
 0040 232ef4fc 0327c4fe 832784fe bb07f702  #....'...'......
 0050 232cf4fc 8327c4fe 9b972700 232af4fc  #,...'....'.#*..
 0060 832744fe 9b971700 2328f4fc 8327c4fe  .'D.....#(...'..
 0070 9bd72740 2326f4fc 832744fe 9bd7e701  ..'@#&...'D.....

ビット反転とかなく、特に問題はなさそう。とりあえずオブジェクトファイルは出力できるようになったので良しとする。