FPGA開発日記

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

オリジナルLLVMバックエンド実装をまとめる(28. llvm-objdumpを実装する2)

前回の続き。命令ダンプのオペランド表記を一部改造する

このままでLLVMコンパイルして実行しても良いのだが、例えば以下のような命令はダンプの結果がおかしくなる。

        addi    x2, x2, -4
-->
        addi    x2, x2, 4092

マイナス値の表記がうまく行えない。オペランドの出力についてはprintOperand()が担っているのだが、実装部分を確認すると以下のようになっており、符号の有り無しを区別していない。

  • llvm-myriscvx80/lib/Target/MYRISCVX/InstPrinter/MYRISCVXInstPrinter.cpp
void MYRISCVXInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
                                       raw_ostream &O) {
  const MCOperand &Op = MI->getOperand(OpNo);
  if (Op.isReg()) {
    printRegName(O, Op.getReg());
    return;
  }

  // 即値のプリントについては、符号の有り無しを考慮していない。
  if (Op.isImm()) {
    O << Op.getImm();
    return;
  }

  assert(Op.isExpr() && "unknown operand kind in printOperand");
  Op.getExpr()->print(O, &MAI, true);
}

これではダンプ結果として少し読みにくいので、符号ありのオペランドについては出力関数を切り替えて、符号を扱えるようにしたい。

  • llvm-myriscvx80/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td
class ArithLogicI<bits<7> opcode, bits<3> funct3,
                  string instr_asm, SDNode OpNode,
                  Operand Od, PatLeaf imm_type, RegisterClass RC> :
  MYRISCVX_I<opcode, funct3, (outs RC:$rd), (ins RC:$rs1, Od:$imm12),
  !strconcat(instr_asm, "\t$rd, $rs1, $imm12"),
  [(set RC:$rd, (OpNode RC:$rs1, imm_type:$imm12))], IIAlu> {
    let isReMaterializable = 1;
}
...
def ADDI : ArithLogicI<0b0010011, 0b000, "addi", add, simm12, immSExt12, GPR>;

オペランドにはsimm12ノードを使用しているので、simm12ノードのDecodeMethod()を変更する。

// Signed Operand
def simm12 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isInt<12>(Imm);}]> {
  let DecoderMethod = "decodeSImmOperand<12>";
}
def simm20 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isInt<20>(Imm);}]> {
  let DecoderMethod = "decodeSImmOperand<20>";
}

decodeSImmOperand()MYRISCVXInstPrinter()に作り込んでいく。

  • llvm-myriscvx80/lib/Target/MYRISCVX/Disassembler/MYRISCVXDisassembler.cpp
template <unsigned N>
static DecodeStatus decodeSImmOperand(MCInst &Inst, uint64_t Imm,
                                      int64_t Address, const void *Decoder) {
  assert(isUInt<N>(Imm) && "Invalid immediate");
  // Sign-extend the number in the bottom N bits of Imm
  Inst.addOperand(MCOperand::createImm(SignExtend64<N>(Imm)));
  return MCDisassembler::Success;
}

即値を64ビット幅に拡張し、オペランドを追加する。

f:id:msyksphinz:20191019221631p:plain
符号付即値のノードについては、DecodeMethodを変更してDecodeSIMMOperand()を呼び出す。

コンパイルと実行

では、ここまで実装した状態でLLVMをリコンパイルし、実際にobjdumpを実行してみる。

  • if_ctrl.cpp
int test_ifctrl()
{
  unsigned int a = 0;

  if (a == 0) {
    a++; // a = 1
  }

  return a;
}
./bin/clang if_ctrl.cpp -emit-llvm
./bin/llc -mtriple=myriscvx32 -filetype=asm -o if_ctrl.my32.s

生成されたif_ctrl_my32.sは以下のようになった。

_Z11test_ifctrlv:                       # @_Z11test_ifctrlv

# %bb.0:                                # %entry
        addi    x2, x2, -4
        addi    x10, zero, 0
        sw      x10, 0(x2)
        lw      x10, 0(x2)
        bne     x10, zero, $BB0_2
# %bb.1:                                # %if.then
        lw      x10, 0(x2)
        addi    x10, x10, 1
        sw      x10, 0(x2)
$BB0_2:                                 # %if.end
        lw      x10, 0(x2)
        addi    x2, x2, 4
        ret

次は、オブジェクトファイルを生成してみる。

./bin/llc -mtriple=myriscvx32 -filetype=obj -o if_ctrl.my32.o
./bin/llvm-objdump -triple=myriscvx32 -d  if_ctrl.my32.o
Disassembly of section .text:
0000000000000000 _Z11test_ifctrlv:
       0:       13 01 c1 ff     addi    x2, x2, -4
       4:       13 05 00 00     addi    x10, zero, 0
       8:       23 20 a1 00     sw      x10, 0(x2)
       c:       03 25 a1 00     lw      x10, 10(x2)
      10:       63 10 00 00     bne     zero, zero, 0
      14:       03 25 a1 00     lw      x10, 10(x2)
      18:       13 05 15 00     addi    x10, x10, 1
      1c:       23 20 a1 00     sw      x10, 0(x2)
      20:       03 25 a1 00     lw      x10, 10(x2)
      24:       13 01 41 00     addi    x2, x2, 4
      28:       67 80 00 00     ret

バイトの表記が全部逆になっているので少し驚くかもしれない。 左から順に0バイト目、1バイト目、2バイト目、3バイト目となる。 つまり最初のaddi命令はffc10113であるのが正解で、念のためriscv-toolsのディスアセンブラで確認しておくと、

echo "DASM(ffc10113)" | spike-dasm
addi    sp, sp, -4

となり、命令として正しくダンプできていることが確認できると思う。