FPGA開発日記

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

オリジナルLLVM Backendを追加しよう (26. ELFのサポートとobjdump)

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

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

jonathan2251.github.io

第10章は、ELF形式のサポートと、objdumpコマンドを動かす。

まずは、ELFの形式をサポートする。ELFの形式は、以下のように表現される。

https://jonathan2251.github.io/lbd/_images/12.png

まずは通常通りllcでオブジェクトファイルを生成して、ELFのヘッダを確認してみる。 Machineの項は<unknown>: 0xf8となっており、認識できない。

$ ./bin/llc -march=myriscvx32 -relocation-model=pic -filetype=obj ch6_1.bc -o ch6_1.myriscvx.o
$ readelf -h ch6_1.myriscvx.o
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           <unknown>: 0xf8
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          572 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         8
  Section header string table index: 1

次に、MIPSのオブジェクトファイルを生成してみる。Machineの項はMIPS R3000と認識されている。

$ ./bin/llc -march=mips -relocation-model=pic -filetype=obj ch6_1.bc -o ch6_1.mips.o
$ readelf -h ch6_1.mips.o
ELF Header:
  Magic:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, big endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           MIPS R3000
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          700 (bytes into file)
  Flags:                             0x50001007, noreorder, pic, cpic, o32, mips32
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         14
  Section header string table index: 1

これらのオブジェクトは実行ファイルではないので、セグメントは入っていない。

$ readelf -l ch6_1.myriscvx.o

There are no program headers in this file.

オブジェクトのセクション情報を確認してみる。

$ readelf -S ch6_1.myriscvx.o
There are 8 section headers, starting at offset 0x23c:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .strtab           STRTAB          00000000 0001d4 000068 00      0   0  1
  [ 2] .text             PROGBITS        00000000 000034 00004c 00  AX  0   0  4
  [ 3] .rel.text         REL             00000000 0001a4 000030 08      7   2  4
  [ 4] .data             PROGBITS        00000000 000080 000008 00  WA  0   0  4
  [ 5] .comment          PROGBITS        00000000 000088 0000bb 01  MS  0   0  1
  [ 6] .note.GNU-stack   PROGBITS        00000000 000143 000000 00      0   0  1
  [ 7] .symtab           SYMTAB          00000000 000144 000060 10      1   2  4
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)
$ objdump -s ch6_1.myriscvx.o

ch6_1.myriscvx.o:     file format elf32-little

Contents of section .text:
 0000 b7010000 93e10100 b3814100 37050000  ..........A.7...
 0010 13050500 130181ff 23224400 33010400  ........#"D.3...
 0020 23200000 37050000 33053500 03200500  # ..7...3.5.. ..
 0030 03200500 23200500 03200500 33040100  . ..# ... ..3...
 0040 03224400 13018100 67800000           ."D.....g...
Contents of section .data:
 0000 03000000 64000000                    ....d...
Contents of section .comment:
 0000 00636c61 6e672076 65727369 6f6e2037  .clang version 7
 0010 2e302e31 20286874 7470733a 2f2f6769  .0.1 (https://gi
 0020 74687562 2e636f6d 2f6c6c76 6d2d6d69  thub.com/llvm-mi
 0030 72726f72 2f636c61 6e672e67 69742034  rror/clang.git 4
 0040 35313965 32363337 66636334 62663665  519e2637fcc4bf6e
 0050 33303439 61306138 30653661 35653762  3049a0a80e6a5e7b
 0060 39373636 37636229 20286874 7470733a  97667cb) (https:
 0070 2f2f6769 74687562 2e636f6d 2f6d7379  //github.com/msy
 0080 6b737068 696e7a2f 6c6c766d 2e676974  ksphinz/llvm.git
 0090 20633431 36633731 33363062 61633966   c416c71360bac9f
 00a0 62336233 30396661 33643839 33326637  b3b309fa3d8932f7
 00b0 62633664 66363436 612900             bc6df646a).
$ readelf -tr ch6_1.myriscvx.o
There are 8 section headers, starting at offset 0x23c:

Section Headers:
  [Nr] Name
       Type            Addr     Off    Size   ES   Lk Inf Al
       Flags
  [ 0]
       NULL            00000000 000000 000000 00   0   0  0
       [00000000]:
  [ 1] .strtab
       STRTAB          00000000 0001d4 000068 00   0   0  1
       [00000000]:
  [ 2] .text
       PROGBITS        00000000 000034 00004c 00   0   0  4
       [00000006]: ALLOC, EXEC
  [ 3] .rel.text
       REL             00000000 0001a4 000030 08   7   2  4
       [00000000]:
  [ 4] .data
       PROGBITS        00000000 000080 000008 00   0   0  4
       [00000003]: WRITE, ALLOC
  [ 5] .comment
       PROGBITS        00000000 000088 0000bb 01   0   0  1
       [00000030]: MERGE, STRINGS
  [ 6] .note.GNU-stack
       PROGBITS        00000000 000143 000000 00   0   0  1
       [00000000]:
  [ 7] .symtab
       SYMTAB          00000000 000144 000060 10   1   2  4
       [00000000]:

Relocation section '.rel.text' at offset 0x1a4 contains 6 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000000  00000305 unrecognized: 5       00000000   _gp_disp
00000004  00000306 unrecognized: 6       00000000   _gp_disp
0000000c  00000305 unrecognized: 5       00000000   _gp_disp
00000010  00000306 unrecognized: 6       00000000   _gp_disp
00000024  00000416 unrecognized: 16      00000004   gI
0000002c  00000417 unrecognized: 17      00000004   gI

objdumpをサポートする

objdumpをサポートするためには、Disassemblerを追加する。

diff --git a/lib/Target/MYRISCVX/CMakeLists.txt b/lib/Target/MYRISCVX/CMakeLists.txt
index 2257f4cf37a..b55c0fbf701 100644
--- a/lib/Target/MYRISCVX/CMakeLists.txt
+++ b/lib/Target/MYRISCVX/CMakeLists.txt
@@ -13,6 +13,7 @@ tablegen(LLVM MYRISCVXGenCallingConv.inc -gen-callingconv)
 tablegen(LLVM MYRISCVXGenCodeEmitter.inc -gen-emitter)
 tablegen(LLVM MYRISCVXGenMCCodeEmitter.inc -gen-emitter)
 tablegen(LLVM MYRISCVXGenAsmWriter.inc -gen-asm-writer)
+tablegen(LLVM MYRISCVXGenDisassemblerTables.inc -gen-disassembler)

 # MYRISCVXCommonTableGen must be defined
 add_public_tablegen_target(MYRISCVXCommonTableGen)
@@ -45,3 +46,4 @@ add_llvm_target(MYRISCVXCodeGen
 add_subdirectory(TargetInfo)
 add_subdirectory(MCTargetDesc)
 add_subdirectory(InstPrinter)
+add_subdirectory(Disassembler)

Disassemblerには、いくつかのオペランドの出力についてケアをする必要がある。 例えば、ストア命令は3つのオペランド(書き込みレジスタ、ベースアドレス、測地アドレス)を取るが、Target Descriptionファイルには2つのオペランドしか記述していない。 よく見ると、AlignedStoreの項目に$val, $ptrオペランドが2つ入っており、これを合わせてメモリアクセス命令のディスアセンブリを出力する必要がある。

class AlignedStore<PatFrag Node> :
  PatFrag<(ops node:$val, node:$ptr), (Node node:$val, node:$ptr), [{
    StoreSDNode *SD = cast<StoreSDNode>(N);
    return SD->getMemoryVT().getSizeInBits()/8 <= SD->getAlignment();
  }]>;
...
defm SW  : StoreM32<0b0100011, 0b010, "sw", store_a        >;
multiclass StoreM32<bits<7> opcode, bits<3> funct3, string instr_asm, PatFrag OpNode,
                    bit Pseudo = 0> {
  def #NAME# : StoreM<opcode, funct3, instr_asm, OpNode, GPR, mem, Pseudo>;
}
...
class StoreM<bits<7> opcode, bits<3> funct3, string instr_asm, PatFrag OpNode, RegisterClass RC,
             Operand MemOpnd, bit Pseudo>:
  FS<opcode, funct3, (outs), (ins RC:$rs1, MemOpnd:$addr),
     !strconcat(instr_asm, "\t$rs1, $addr"),
     [(OpNode RC:$rs1, addr:$addr)], IIStore> {
  let isPseudo = Pseudo;
}

この表記を上手く順番を並べ替えてフォーマットする。

  • lib/Target/MYRISCVX/Disassembler/MYRISCVXDisassembler.cpp
// @DecodeStore {
static DecodeStatus DecodeStore(MCInst &Inst,
                                unsigned Insn,
                                uint64_t Address,
                                const void *Decoder) {
  // @DecodeStore body {
  int Offset = SignExtend32<12>((fieldFromInstruction(Insn,  25, 7) << 5) |
                                (fieldFromInstruction(Insn,   7, 5)));
  int Reg  = (int)fieldFromInstruction(Insn, 20, 5);
  int Base = (int)fieldFromInstruction(Insn, 15, 5);

  Inst.addOperand(MCOperand::createReg(CPURegsTable[Base]));
  Inst.addOperand(MCOperand::createReg(CPURegsTable[Reg]));
  Inst.addOperand(MCOperand::createImm(Offset));

  return MCDisassembler::Success;
}
...
// @DecodeLoad {
static DecodeStatus DecodeLoad (MCInst &Inst,
                                unsigned Insn,
                                uint64_t Address,
                                const void *Decoder) {
  // @DecodeLoad body {
  int Offset = SignExtend32<12>((Insn >> 20) & 0x0fff);
  int Dest = (int)fieldFromInstruction(Insn,  7, 5);
  int Base = (int)fieldFromInstruction(Insn, 15, 5);

  Inst.addOperand(MCOperand::createReg(CPURegsTable[Dest]));
  Inst.addOperand(MCOperand::createReg(CPURegsTable[Base]));
  Inst.addOperand(MCOperand::createImm(Offset));

  return MCDisassembler::Success;
}

これでLLVMを再ビルドし、llvm-objdumpを実行してみる。

./bin/clang -c -target mips-unknown-linux-gnu ../lbdex/input/ch6_1.cpp -emit-llvm
./bin/llvm-objdump -d ch6_1.myriscvx.o
ch6_1.myriscvx.o:       file format ELF32-unknown

Disassembly of section .text:
_Z11test_globalv:
       0:       b7 01 00 00     lui     x3, 0
       4:       93 e1 01 00     ori     x3, x3, 0
       8:       b3 81 41 00     add     x3, x3, x4
       c:       37 05 00 00     lui     x10, 0
      10:       13 05 05 00     addi    x10, x10, 0
      14:       13 01 81 ff     addi    x2, x2, -8
      18:       23 22 44 00     sw      x8, 4(x4)
      1c:       33 01 04 00     move    x2, x8
      20:       23 20 00 00     sw      x0, 0(x0)
...

一応出力できたようだが、オペランドの順番が逆だ。add x3, x3, x4add x4, x3, x3にならないといけないと気がする。おかしいなあ。