現状、llvm-objdumpを実行すると以下のようになってしまう。これをサポートする。
% ./bin/llvm-objdump -d if_ctrl.o
if_ctrl.o: file format ELF32-myriscvx if_ctrl.o': can't find target: : error: unable to get target for 'unknown--', see --version and --triple.
まずは、MYRISCVXのオブジェクトファイルを認識させる必要がある。
MYRISCVXのディスアセンブラを実装する
ディスアセンブルをサポートするためには、lib/Target/MYRISCVX
の下に、さらにDisassembler
ディレクトリを掘る。その中にディスアセンブルに必要なコードを実装していく。
llvm-myriscvx/lib/Target/MYRISCVX/CMakeLists.txt
... tablegen(LLVM MYRISCVXGenDisassemblerTables.inc -gen-disassembler) ... add_subdirectory(Disassembler)
llvm-myriscvx/lib/Target/MYRISCVX/LLVMBuild.txt
[common] subdirectories = MCTargetDesc TargetInfo InstPrinter Disassembler /* これを追加 */ ... has_asmprinter = 1 has_disassembler = 1 /* これを追加 */ ...
まず、上記のtablegen
の追加により、新たにTarget Descriptionからディスアセンブラが生成される。Disassembler
の中では、このディスアセンブラのテンプレートに不足しているものを追加していく。
まずは、生成されたディスアセンブラのテンプレートを見てみる。
build-myriscvx/lib/Target/MYRISCVX/MYRISCVXGenDisassemblerTables.inc
static const uint8_t DecoderTableMYRISCVX32[] = { /* 0 */ MCD::OPC_ExtractField, 0, 7, // Inst{6-0} ... /* 3 */ MCD::OPC_FilterValue, 3, 48, 0, 0, // Skip to: 56 /* 8 */ MCD::OPC_ExtractField, 12, 3, // Inst{14-12} ... ... template<typename InsnType> static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], MCInst &MI, InsnType insn, uint64_t Address, const void *DisAsm, const MCSubtargetInfo &STI) { ...
insn
で渡される命令をデコードして、その結果をMCInst MI
に返す。DecodeTable[]
経由でMYRISCVXのデコードテーブルDecodeTableMYRISCVX32
を渡す。
while (true) { ptrdiff_t Loc = Ptr - DecodeTable; switch (*Ptr) { default: errs() << Loc << ": Unexpected decode table opcode!\n"; return MCDisassembler::Fail; case MCD::OPC_ExtractField: { /* 命令フィールドの切り出し */ CurFieldValue = fieldFromInstruction(insn, Start, Len); ... case MCD::OPC_FilterValue: { /* フィールドのデコード */ ... case MCD::OPC_CheckField: { ... case MCD::OPC_CheckPredicate: { ... case MCD::OPC_Decode: { unsigned Len; // Decode the Opcode value. unsigned Opc = decodeULEB128(++Ptr, &Len); Ptr += Len; unsigned DecodeIdx = decodeULEB128(Ptr, &Len); Ptr += Len; ... S = decodeToMCInst(S, DecodeIdx, insn, MI, Address, DisAsm, DecodeComplete); ... LLVM_DEBUG(dbgs() << Loc << ": OPC_Decode: opcode " << Opc << ", using decoder " << DecodeIdx << ": " << (S != MCDisassembler::Fail ? "PASS" : "FAIL") << "\n"); return S;
decodeInstruction
の内部では、デコードテーブルを先頭から読んでいき、命令フィールドを切り出しながら、次々とテーブルをジャンプする。最終的に1つの命令に到達すれば、それがデコード結果となる。
MCD::OPC_Decode
まで到達すると命令を1つに特定できる(Opc
に命令のデコードインデックスが格納される)。decodeToMCInst
では特定した命令をMCInstに変換し、decodeInstruction
から抜ける。MCInst
では命令の形状に応じてレジスタオペランドなどの情報を組み立てる。そのためにはDecodeIdx
でインデックスされる命令フォーマットを元にオペランドを組み立てる。それがdecodeToMCInst
だ。
template<typename InsnType> static DecodeStatus decodeToMCInst(DecodeStatus S, unsigned Idx, InsnType insn, MCInst &MI, uint64_t Address, const void *Decoder, bool &DecodeComplete) { ... switch (Idx) { default: llvm_unreachable("Invalid index!"); case 0: return S; case 1: /* Reg, Reg, Imm12 フォーマットの場合 */ tmp = fieldFromInstruction(insn, 7, 5); if (DecodeGPRRegisterClass(MI, tmp, Address, Decoder) == MCDisassembler::Fail) { return MCDisassembler::Fail; } tmp = fieldFromInstruction(insn, 15, 5); if (DecodeGPRRegisterClass(MI, tmp, Address, Decoder) == MCDisassembler::Fail) { return MCDisassembler::Fail; } tmp = fieldFromInstruction(insn, 20, 12); if (DecodeSimm12(MI, tmp, Address, Decoder) == MCDisassembler::Fail) { return MCDisassembler::Fail; } return S; ... case 5: /* Reg, Reg, Reg フォーマットの場合 */ tmp = fieldFromInstruction(insn, 7, 5); if (DecodeGPRRegisterClass(MI, tmp, Address, Decoder) == MCDisassembler::Fail) { return MCDisassembler::Fail; } tmp = fieldFromInstruction(insn, 15, 5); if (DecodeGPRRegisterClass(MI, tmp, Address, Decoder) == MCDisassembler::Fail) { return MCDisassembler::Fail; } tmp = fieldFromInstruction(insn, 20, 5); if (DecodeGPRRegisterClass(MI, tmp, Address, Decoder) == MCDisassembler::Fail) { return MCDisassembler::Fail; } return S;
decodeToMCInst
内で、定義されていない関数がある。各オペランドのデコードを行う関数群だ。例えば、
DecodeGPRRegisterClass
DecodeSimm12
DecodeBranch12Target
などが相当する。これらをlib/Target/MYRISCVX/MYRISCVXDisassembler.cpp
で実装することになる。
llvm-myriscvx/lib/Target/MYRISCVX/Disassembler/MYRISCVXDisassembler.cpp
.... #include "MYRISCVXGenDisassemblerTables.inc" ... /* 整数レジスタの場合のデコード */ static DecodeStatus DecodeCPURegsRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { if (RegNo > 15) return MCDisassembler::Fail; /* レジスタインスタンスを作成し、オペランドに登録する */ RegNo = getReg(Decoder, MYRISCVX::GPRRegClassID, RegNo); Inst.addOperand(MCOperand::createReg(RegNo)); return MCDisassembler::Success; } static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { return DecodeCPURegsRegisterClass(Inst, RegNo, Address, Decoder); } ... /* 分岐命令のPC相対即値アドレス生成 */ static DecodeStatus DecodeBranch12Target(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { /* RISC-Vの即値命令フィールド切り出しは若干複雑 */ int BranchOffset = SignExtend32<12>((fieldFromInstruction(Insn, 31, 1) << 12) | (fieldFromInstruction(Insn, 25, 6) << 5) | (fieldFromInstruction(Insn, 8, 4) << 1) | (fieldFromInstruction(Insn, 7, 1) << 11)); /* 即値オペランドを生成して登録する */ Inst.addOperand(MCOperand::createImm(BranchOffset)); return MCDisassembler::Success; }
このようにして実装されたdecodeInstruction()
およびデコード用ヘルパー関数は、実際にはより上位のMYRISCVXDisassembler::getInstruction
により呼び出される。
llvm-myriscvx/tools/llvm-objdump/llvm-objdump.cpp
static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { ... for (Index = Start; Index < End; Index += Size) { // Disassemble a real instruction or a data when disassemble all is // provided bool Disassembled = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), SectionAddr + Index, DebugOut, CommentStream);
では、ここまで実装した状態で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 -stats -debug -filetype=obj if_ctrl.bc -mcpu=simple32 -march=myriscvx32 -target-abi=lp64 -enable-MYRISCVX-tail-calls=false -relocation-model=pic
file if_ctrl.o
ELF 32-bit MSB relocatable, version 1 (SYSV), not stripped
./bin/objdump -d if_ctrl.o
Disassembly of section .text: 0000000000000000 _Z11test_ifctrlv: 0: 13 01 81 ff addi x11, x11, -8 4: 13 05 00 00 addi x6, zero, 0 8: 23 22 a1 00 sw x6, 4(x11) c: 03 25 41 00 lw x6, 4(x11) 10: 63 10 05 00 bne x6, zero, 0 14: 03 25 41 00 lw x6, 4(x11) 18: 13 05 15 00 addi x6, x6, 1 1c: 23 22 a1 00 sw x6, 4(x11) 0000000000000020 $BB0_2: 20: 03 25 41 00 lw x6, 4(x11) 24: 13 01 81 00 addi x11, x11, 8 28: 67 80 10 00 ret x10