diff options
-rw-r--r-- | disassembler/disassembler_riscv64.cc | 402 |
1 files changed, 399 insertions, 3 deletions
diff --git a/disassembler/disassembler_riscv64.cc b/disassembler/disassembler_riscv64.cc index b92d1287b2..41a6b3fb31 100644 --- a/disassembler/disassembler_riscv64.cc +++ b/disassembler/disassembler_riscv64.cc @@ -76,11 +76,21 @@ class DisassemblerRiscv64::Printer { kOpCFG = 0b111, }; + class ScopedNewLinePrinter { + std::ostream& os_; + + public: + explicit ScopedNewLinePrinter(std::ostream& os) : os_(os) {} + ~ScopedNewLinePrinter() { os_ << '\n'; } + }; + static const char* XRegName(uint32_t regno); static const char* FRegName(uint32_t regno); static const char* VRegName(uint32_t regno); static const char* RoundingModeName(uint32_t rm); + // Regular instruction immediate utils + static int32_t Decode32Imm12(uint32_t insn32) { uint32_t sign = (insn32 >> 31); uint32_t imm12 = (insn32 >> 20); @@ -99,12 +109,89 @@ class DisassemblerRiscv64::Printer { return static_cast<int32_t>(imm) - static_cast<int32_t>(bit11 << 12); // Sign-extend. } + // Compressed instruction immediate utils + + // Extracts the offset from a compressed instruction + // where `offset[5:3]` is in bits `[12:10]` and `offset[2|6]` is in bits `[6:5]` + static uint32_t Decode16CMOffsetW(uint32_t insn16) { + DCHECK(IsUint<16>(insn16)); + return BitFieldExtract(insn16, 5, 1) << 6 | BitFieldExtract(insn16, 10, 3) << 3 | + BitFieldExtract(insn16, 6, 1) << 2; + } + + // Extracts the offset from a compressed instruction + // where `offset[5:3]` is in bits `[12:10]` and `offset[7:6]` is in bits `[6:5]` + static uint32_t Decode16CMOffsetD(uint32_t insn16) { + DCHECK(IsUint<16>(insn16)); + return BitFieldExtract(insn16, 5, 2) << 6 | BitFieldExtract(insn16, 10, 3) << 3; + } + + // Re-orders raw immediatate into real value + // where `imm[5:3]` is in bits `[5:3]` and `imm[8:6]` is in bits `[2:0]` + static uint32_t Uimm6ToOffsetD16(uint32_t uimm6) { + DCHECK(IsUint<6>(uimm6)); + return (BitFieldExtract(uimm6, 3, 3) << 3) | (BitFieldExtract(uimm6, 0, 3) << 6); + } + + // Re-orders raw immediatate to form real value + // where `imm[5:2]` is in bits `[5:2]` and `imm[7:6]` is in bits `[1:0]` + static uint32_t Uimm6ToOffsetW16(uint32_t uimm6) { + DCHECK(IsUint<6>(uimm6)); + return (BitFieldExtract(uimm6, 2, 4) << 2) | (BitFieldExtract(uimm6, 0, 2) << 6); + } + + // Re-orders raw immediatate to form real value + // where `imm[1]` is in bit `[0]` and `imm[0]` is in bit `[1]` + static uint32_t Uimm2ToOffset10(uint32_t uimm2) { + DCHECK(IsUint<2>(uimm2)); + return (uimm2 >> 1) | (uimm2 & 0x1u) << 1; + } + + // Re-orders raw immediatate to form real value + // where `imm[1]` is in bit `[0]` and `imm[0]` is `0` + static uint32_t Uimm2ToOffset1(uint32_t uimm2) { + DCHECK(IsUint<2>(uimm2)); + return (uimm2 & 0x1u) << 1; + } + + template <size_t kWidth> + static constexpr int32_t SignExtendBits(uint32_t bits) { + static_assert(kWidth < BitSizeOf<uint32_t>()); + const uint32_t sign_bit = (bits >> kWidth) & 1u; + return static_cast<int32_t>(bits) - static_cast<int32_t>(sign_bit << kWidth); + } + + // Extracts the immediate from a compressed instruction + // where `imm[5]` is in bit `[12]` and `imm[4:0]` is in bits `[6:2]` + // and performes sign-extension if required + template <typename T> + static T Decode16Imm6(uint32_t insn16) { + DCHECK(IsUint<16>(insn16)); + static_assert(std::is_integral_v<T>, "T must be integral"); + const T bits = + BitFieldInsert(BitFieldExtract(insn16, 2, 5), BitFieldExtract(insn16, 12, 1), 5, 1); + const T checked_bits = dchecked_integral_cast<T>(bits); + if (std::is_unsigned_v<T>) { + return checked_bits; + } + return SignExtendBits<6>(checked_bits); + } + + // Regular instruction register utils + static uint32_t GetRd(uint32_t insn32) { return (insn32 >> 7) & 0x1fu; } static uint32_t GetRs1(uint32_t insn32) { return (insn32 >> 15) & 0x1fu; } static uint32_t GetRs2(uint32_t insn32) { return (insn32 >> 20) & 0x1fu; } static uint32_t GetRs3(uint32_t insn32) { return insn32 >> 27; } static uint32_t GetRoundingMode(uint32_t insn32) { return (insn32 >> 12) & 7u; } + // Compressed instruction register utils + + static uint32_t GetRs1Short16(uint32_t insn16) { return BitFieldExtract(insn16, 7, 3) + 8u; } + static uint32_t GetRs2Short16(uint32_t insn16) { return BitFieldExtract(insn16, 2, 3) + 8u; } + static uint32_t GetRs1_16(uint32_t insn16) { return BitFieldExtract(insn16, 7, 5); } + static uint32_t GetRs2_16(uint32_t insn16) { return BitFieldExtract(insn16, 2, 5); } + void PrintBranchOffset(int32_t offset); void PrintLoadStoreAddress(uint32_t rs1, int32_t offset); @@ -1548,10 +1635,319 @@ void DisassemblerRiscv64::Printer::Dump32(const uint8_t* insn) { void DisassemblerRiscv64::Printer::Dump16(const uint8_t* insn) { uint32_t insn16 = static_cast<uint32_t>(insn[0]) + (static_cast<uint32_t>(insn[1]) << 8); + ScopedNewLinePrinter nl(os_); CHECK_NE(insn16 & 3u, 3u); - // TODO(riscv64): Disassemble instructions from the "C" extension. - os_ << disassembler_->FormatInstructionPointer(insn) - << StringPrintf(": %04x \t<unknown16>\n", insn16); + os_ << disassembler_->FormatInstructionPointer(insn) << StringPrintf(": %04x \t", insn16); + + uint32_t funct3 = BitFieldExtract(insn16, 13, 3); + int32_t offset = -1; + + switch (insn16 & 3u) { + case 0b00u: // Quadrant 0 + switch (funct3) { + case 0b000u: + if (insn16 == 0u) { + os_ << "c.unimp"; + } else { + uint32_t nzuimm = BitFieldExtract(insn16, 5, 8); + if (nzuimm != 0u) { + uint32_t decoded = + BitFieldExtract(nzuimm, 0, 1) << 3 | BitFieldExtract(nzuimm, 1, 1) << 2 | + BitFieldExtract(nzuimm, 2, 4) << 6 | BitFieldExtract(nzuimm, 6, 2) << 4; + os_ << "c.addi4spn " << XRegName(GetRs2Short16(insn16)) << ", sp, " << decoded; + } else { + os_ << "<unknown16>"; + } + } + return; + case 0b001u: + offset = Decode16CMOffsetD(insn16); + os_ << "c.fld " << FRegName(GetRs2Short16(insn16)); + break; + case 0b010u: + offset = Decode16CMOffsetW(insn16); + os_ << "c.lw " << XRegName(GetRs2Short16(insn16)); + break; + case 0b011u: + offset = Decode16CMOffsetD(insn16); + os_ << "c.ld " << XRegName(GetRs2Short16(insn16)); + break; + case 0b100u: { + uint32_t opcode2 = BitFieldExtract(insn16, 10, 3); + uint32_t imm = BitFieldExtract(insn16, 5, 2); + switch (opcode2) { + case 0b000: + offset = Uimm2ToOffset10(imm); + os_ << "c.lbu " << XRegName(GetRs2Short16(insn16)); + break; + case 0b001: + offset = Uimm2ToOffset1(imm); + os_ << (BitFieldExtract(imm, 1, 1) == 0u ? "c.lhu " : "c.lh "); + os_ << XRegName(GetRs2Short16(insn16)); + break; + case 0b010: + offset = Uimm2ToOffset10(imm); + os_ << "c.sb " << XRegName(GetRs2Short16(insn16)); + break; + case 0b011: + if (BitFieldExtract(imm, 1, 1) == 0u) { + offset = Uimm2ToOffset1(imm); + os_ << "c.sh " << XRegName(GetRs2Short16(insn16)); + break; + } + FALLTHROUGH_INTENDED; + default: + os_ << "<unknown16>"; + return; + } + break; + } + case 0b101u: + offset = Decode16CMOffsetD(insn16); + os_ << "c.fsd " << FRegName(GetRs2Short16(insn16)); + break; + case 0b110u: + offset = Decode16CMOffsetW(insn16); + os_ << "c.sw " << XRegName(GetRs2Short16(insn16)); + break; + case 0b111u: + offset = Decode16CMOffsetD(insn16); + os_ << "c.sd " << XRegName(GetRs2Short16(insn16)); + break; + default: + UNREACHABLE(); + } + os_ << ", "; + PrintLoadStoreAddress(GetRs1Short16(insn16), offset); + return; + case 0b01u: // Quadrant 1 + switch (funct3) { + case 0b000u: { + uint32_t rd = GetRs1_16(insn16); + if (rd == 0) { + if (Decode16Imm6<uint32_t>(insn16) != 0u) { + os_ << "<hint16>"; + } else { + os_ << "c.nop"; + } + } else { + int32_t imm = Decode16Imm6<int32_t>(insn16); + if (imm != 0) { + os_ << "c.addi " << XRegName(rd) << ", " << imm; + } else { + os_ << "<hint16>"; + } + } + break; + } + case 0b001u: { + uint32_t rd = GetRs1_16(insn16); + if (rd != 0) { + os_ << "c.addiw " << XRegName(rd) << ", " << Decode16Imm6<int32_t>(insn16); + } else { + os_ << "<unknown16>"; + } + break; + } + case 0b010u: { + uint32_t rd = GetRs1_16(insn16); + if (rd != 0) { + os_ << "c.li " << XRegName(rd) << ", " << Decode16Imm6<int32_t>(insn16); + } else { + os_ << "<hint16>"; + } + break; + } + case 0b011u: { + uint32_t rd = GetRs1_16(insn16); + uint32_t imm6_bits = Decode16Imm6<uint32_t>(insn16); + if (imm6_bits != 0u) { + if (rd == 2) { + int32_t nzimm = + BitFieldExtract(insn16, 6, 1) << 4 | BitFieldExtract(insn16, 2, 1) << 5 | + BitFieldExtract(insn16, 5, 1) << 6 | BitFieldExtract(insn16, 3, 2) << 7 | + BitFieldExtract(insn16, 12, 1) << 9; + os_ << "c.addi16sp sp, " << SignExtendBits<10>(nzimm); + } else if (rd != 0) { + // sign-extend bits and mask with 0xfffff as llvm-objdump does + uint32_t mask = MaskLeastSignificant<uint32_t>(20); + os_ << "c.lui " << XRegName(rd) << ", " << (SignExtendBits<6>(imm6_bits) & mask); + } else { + os_ << "<hint16>"; + } + } else { + os_ << "<unknown16>"; + } + break; + } + case 0b100u: { + uint32_t funct2 = BitFieldExtract(insn16, 10, 2); + switch (funct2) { + case 0b00: { + int32_t nzuimm = Decode16Imm6<uint32_t>(insn16); + if (nzuimm != 0) { + os_ << "c.srli " << XRegName(GetRs1Short16(insn16)) << ", " << nzuimm; + } else { + os_ << "<hint16>"; + } + break; + } + case 0b01: { + int32_t nzuimm = Decode16Imm6<uint32_t>(insn16); + if (nzuimm != 0) { + os_ << "c.srai " << XRegName(GetRs1Short16(insn16)) << ", " << nzuimm; + } else { + os_ << "<hint16>"; + } + break; + } + case 0b10: + os_ << "c.andi " << XRegName(GetRs1Short16(insn16)) << ", " + << Decode16Imm6<int32_t>(insn16); + break; + case 0b11: { + constexpr static const char* mnemonics[] = { + "c.sub", "c.xor", "c.or", "c.and", "c.subw", "c.addw", "c.mul", nullptr + }; + uint32_t opc = BitFieldInsert( + BitFieldExtract(insn16, 5, 2), BitFieldExtract(insn16, 12, 1), 2, 1); + DCHECK(IsUint<3>(opc)); + const char* mnem = mnemonics[opc]; + if (mnem != nullptr) { + os_ << mnem << " " << XRegName(GetRs1Short16(insn16)) << ", " + << XRegName(GetRs2Short16(insn16)); + } else { + constexpr static const char* zbc_mnemonics[] = { + "c.zext.b", "c.sext.b", "c.zext.h", "c.sext.h", + "c.zext.w", "c.not", nullptr, nullptr, + }; + mnem = zbc_mnemonics[BitFieldExtract(insn16, 2, 3)]; + if (mnem != nullptr) { + os_ << mnem << " " << XRegName(GetRs1Short16(insn16)); + } else { + os_ << "<unknown16>"; + } + } + break; + } + default: + UNREACHABLE(); + } + break; + } + case 0b101u: { + int32_t disp = BitFieldExtract(insn16, 3, 3) << 1 | BitFieldExtract(insn16, 11, 1) << 4 | + BitFieldExtract(insn16, 2, 1) << 5 | BitFieldExtract(insn16, 7, 1) << 6 | + BitFieldExtract(insn16, 6, 1) << 7 | BitFieldExtract(insn16, 9, 2) << 8 | + BitFieldExtract(insn16, 8, 1) << 10 | BitFieldExtract(insn16, 12, 1) << 11; + os_ << "c.j "; + PrintBranchOffset(SignExtendBits<12>(disp)); + break; + } + case 0b110u: + case 0b111u: { + int32_t disp = BitFieldExtract(insn16, 3, 2) << 1 | BitFieldExtract(insn16, 10, 2) << 3 | + BitFieldExtract(insn16, 2, 1) << 5 | BitFieldExtract(insn16, 5, 2) << 6 | + BitFieldExtract(insn16, 12, 1) << 8; + + os_ << (funct3 == 0b110u ? "c.beqz " : "c.bnez "); + os_ << XRegName(GetRs1Short16(insn16)) << ", "; + PrintBranchOffset(SignExtendBits<9>(disp)); + break; + } + default: + UNREACHABLE(); + } + break; + case 0b10u: // Quadrant 2 + switch (funct3) { + case 0b000u: { + uint32_t nzuimm = Decode16Imm6<uint32_t>(insn16); + uint32_t rd = GetRs1_16(insn16); + if (rd == 0 || nzuimm == 0) { + os_ << "<hint16>"; + } else { + os_ << "c.slli " << XRegName(rd) << ", " << nzuimm; + } + return; + } + case 0b001u: { + offset = Uimm6ToOffsetD16(Decode16Imm6<uint32_t>(insn16)); + os_ << "c.fldsp " << FRegName(GetRs1_16(insn16)); + break; + } + case 0b010u: { + uint32_t rd = GetRs1_16(insn16); + if (rd != 0) { + offset = Uimm6ToOffsetW16(Decode16Imm6<uint32_t>(insn16)); + os_ << "c.lwsp " << XRegName(GetRs1_16(insn16)); + } else { + os_ << "<unknown16>"; + return; + } + break; + } + case 0b011u: { + uint32_t rd = GetRs1_16(insn16); + if (rd != 0) { + offset = Uimm6ToOffsetD16(Decode16Imm6<uint32_t>(insn16)); + os_ << "c.ldsp " << XRegName(GetRs1_16(insn16)); + } else { + os_ << "<unknown16>"; + return; + } + break; + } + case 0b100u: { + uint32_t rd_rs1 = GetRs1_16(insn16); + uint32_t rs2 = GetRs2_16(insn16); + uint32_t b12 = BitFieldExtract(insn16, 12, 1); + if (b12 == 0) { + if (rd_rs1 != 0 && rs2 != 0) { + os_ << "c.mv " << XRegName(rd_rs1) << ", " << XRegName(rs2); + } else if (rd_rs1 != 0) { + os_ << "c.jr " << XRegName(rd_rs1); + } else if (rs2 != 0) { + os_ << "<hint16>"; + } else { + os_ << "<unknown16>"; + } + } else { + if (rd_rs1 != 0 && rs2 != 0) { + os_ << "c.add " << XRegName(rd_rs1) << ", " << XRegName(rs2); + } else if (rd_rs1 != 0) { + os_ << "c.jalr " << XRegName(rd_rs1); + } else if (rs2 != 0) { + os_ << "<hint16>"; + } else { + os_ << "c.ebreak"; + } + } + return; + } + case 0b101u: + offset = BitFieldExtract(insn16, 7, 3) << 6 | BitFieldExtract(insn16, 10, 3) << 3; + os_ << "c.fsdsp " << FRegName(GetRs2_16(insn16)); + break; + case 0b110u: + offset = BitFieldExtract(insn16, 7, 2) << 6 | BitFieldExtract(insn16, 9, 4) << 2; + os_ << "c.swsp " << XRegName(GetRs2_16(insn16)); + break; + case 0b111u: + offset = BitFieldExtract(insn16, 7, 3) << 6 | BitFieldExtract(insn16, 10, 3) << 3; + os_ << "c.sdsp " << XRegName(GetRs2_16(insn16)); + break; + default: + UNREACHABLE(); + } + + os_ << ", "; + PrintLoadStoreAddress(/* sp */ 2, offset); + + break; + default: + UNREACHABLE(); + } } void DisassemblerRiscv64::Printer::Dump2Byte(const uint8_t* data) { |