前回の続き。命令ダンプのオペランド表記を一部改造する
このままで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ビット幅に拡張し、オペランドを追加する。
コンパイルと実行
では、ここまで実装した状態で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
となり、命令として正しくダンプできていることが確認できると思う。