summaryrefslogtreecommitdiff
path: root/disassembler/disassembler_riscv64.cc
diff options
context:
space:
mode:
Diffstat (limited to 'disassembler/disassembler_riscv64.cc')
-rw-r--r--disassembler/disassembler_riscv64.cc773
1 files changed, 754 insertions, 19 deletions
diff --git a/disassembler/disassembler_riscv64.cc b/disassembler/disassembler_riscv64.cc
index 3592cfe4a2..b92d1287b2 100644
--- a/disassembler/disassembler_riscv64.cc
+++ b/disassembler/disassembler_riscv64.cc
@@ -47,8 +47,38 @@ class DisassemblerRiscv64::Printer {
TR = 9,
};
+ enum class MemAddressMode : uint32_t {
+ kUnitStride = 0b00,
+ kIndexedUnordered = 0b01,
+ kStrided = 0b10,
+ kIndexedOrdered = 0b11,
+ };
+
+ enum class Nf : uint32_t {
+ k1 = 0b000,
+ k2 = 0b001,
+ k3 = 0b010,
+ k4 = 0b011,
+ k5 = 0b100,
+ k6 = 0b101,
+ k7 = 0b110,
+ k8 = 0b111,
+ };
+
+ enum class VAIEncodings : uint32_t {
+ kOpIVV = 0b000,
+ kOpFVV = 0b001,
+ kOpMVV = 0b010,
+ kOpIVI = 0b011,
+ kOpIVX = 0b100,
+ kOpFVF = 0b101,
+ kOpMVX = 0b110,
+ kOpCFG = 0b111,
+ };
+
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);
static int32_t Decode32Imm12(uint32_t insn32) {
@@ -57,6 +87,10 @@ class DisassemblerRiscv64::Printer {
return static_cast<int32_t>(imm12) - static_cast<int32_t>(sign << 12); // Sign-extend.
}
+ static uint32_t Decode32UImm7(uint32_t insn32) { return (insn32 >> 25) & 0x7Fu; }
+
+ static uint32_t Decode32UImm12(uint32_t insn32) { return (insn32 >> 20) & 0xFFFu; }
+
static int32_t Decode32StoreOffset(uint32_t insn32) {
uint32_t bit11 = insn32 >> 31;
uint32_t bits5_11 = insn32 >> 25;
@@ -87,10 +121,16 @@ class DisassemblerRiscv64::Printer {
void Print32BinOp(uint32_t insn32);
void Print32Atomic(uint32_t insn32);
void Print32FpOp(uint32_t insn32);
+ void Print32RVVOp(uint32_t insn32);
void Print32FpFma(uint32_t insn32);
void Print32Zicsr(uint32_t insn32);
void Print32Fence(uint32_t insn32);
+ void AppendVType(uint32_t zimm);
+ static const char* DecodeRVVMemMnemonic(const uint32_t insn32,
+ bool is_load,
+ /*out*/ const char** rs2);
+
DisassemblerRiscv64* const disassembler_;
std::ostream& os_;
};
@@ -175,6 +215,46 @@ const char* DisassemblerRiscv64::Printer::FRegName(uint32_t regno) {
return kFRegisterNames[regno];
}
+const char* DisassemblerRiscv64::Printer::VRegName(uint32_t regno) {
+ static const char* const kVRegisterNames[] = {
+ "V0",
+ "V1",
+ "V2",
+ "V3",
+ "V4",
+ "V5",
+ "V6",
+ "V7",
+ "V8",
+ "V9",
+ "V10",
+ "V11",
+ "V12",
+ "V13",
+ "V14",
+ "V15",
+ "V16",
+ "V17",
+ "V18",
+ "V19",
+ "V20",
+ "V21",
+ "V22",
+ "V23",
+ "V24",
+ "V25",
+ "V26",
+ "V27",
+ "V28",
+ "V29",
+ "V30",
+ "V31",
+ };
+ static_assert(std::size(kVRegisterNames) == 32);
+ DCHECK_LT(regno, 32u);
+ return kVRegisterNames[regno];
+}
+
const char* DisassemblerRiscv64::Printer::RoundingModeName(uint32_t rm) {
// Note: We do not print the rounding mode for DYN.
static const char* const kRoundingModeNames[] = {
@@ -335,20 +415,170 @@ void DisassemblerRiscv64::Printer::Print32Store(uint32_t insn32) {
PrintLoadStoreAddress(GetRs1(insn32), Decode32StoreOffset(insn32));
}
+const char* DisassemblerRiscv64::Printer::DecodeRVVMemMnemonic(const uint32_t insn32,
+ bool is_load,
+ /*out*/ const char** rs2) {
+ const uint32_t width_index = (insn32 >> 12) & 3u;
+ DCHECK_EQ(width_index != 0u, (insn32 & 0x4000u) != 0u);
+ const uint32_t imm7 = Decode32UImm7(insn32);
+ const enum Nf nf = static_cast<enum Nf>((imm7 >> 4) & 0x7u);
+ const enum MemAddressMode mop = static_cast<enum MemAddressMode>((imm7 >> 1) & 0x3u);
+ const uint32_t mew = (insn32 >> 28) & 1u;
+
+ if (mew == 1u) {
+ // 7.3. Vector Load/Store Width Encoding
+ // The mew bit (inst[28]) when set is expected to be used to encode
+ // expanded memory sizes of 128 bits and above,
+ // but these encodings are currently reserved.
+ return nullptr;
+ }
+
+ switch (mop) {
+ case MemAddressMode::kUnitStride: {
+ const uint32_t umop = GetRs2(insn32);
+ switch (umop) {
+ case 0b00000: // Vector Unit-Stride Load/Store
+ static constexpr const char* kVUSMnemonics[8][4] = {
+ {"e8.v", "e16.v", "e32.v", "e64.v"},
+ {"seg2e8.v", "seg2e16.v", "seg2e32.v", "seg2e64.v"},
+ {"seg3e8.v", "seg3e16.v", "seg3e32.v", "seg3e64.v"},
+ {"seg4e8.v", "seg4e16.v", "seg4e32.v", "seg4e64.v"},
+ {"seg5e8.v", "seg5e16.v", "seg5e32.v", "seg5e64.v"},
+ {"seg6e8.v", "seg6e16.v", "seg6e32.v", "seg6e64.v"},
+ {"seg7e8.v", "seg7e16.v", "seg7e32.v", "seg7e64.v"},
+ {"seg8e8.v", "seg8e16.v", "seg8e32.v", "seg8e64.v"},
+ };
+ return kVUSMnemonics[enum_cast<uint32_t>(nf)][width_index];
+ case 0b01000: { // Vector Whole Register Load/Store
+ if (is_load) {
+ static constexpr const char* kVWRLMnemonics[8][4] = {
+ {"1re8.v", "1re16.v", "1re32.v", "1re64.v"},
+ {"2re8.v", "2re16.v", "2re32.v", "2re64.v"},
+ {nullptr, nullptr, nullptr, nullptr},
+ {"4re8.v", "4re16.v", "4re32.v", "4re64.v"},
+ {nullptr, nullptr, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr},
+ {nullptr, nullptr, nullptr, nullptr},
+ {"8re8.v", "8re16.v", "8re32.v", "8re64.v"},
+ };
+ return kVWRLMnemonics[enum_cast<uint32_t>(nf)][width_index];
+ } else {
+ if (width_index != 0) {
+ return nullptr;
+ }
+ static constexpr const char* kVWRSMnemonics[8] = {
+ "1r", "2r", nullptr, "4r", nullptr, nullptr, nullptr, "8r"
+ };
+ return kVWRSMnemonics[enum_cast<uint32_t>(nf)];
+ }
+ }
+ case 0b01011: // Vector Unit-Stride Mask Load/Store
+ if (nf == Nf::k1 && width_index == 0 && (imm7 & 1u) == 1u) {
+ return "m.v";
+ } else {
+ return nullptr;
+ }
+ case 0b10000: // Vector Unit-Stride Fault-Only-First Load
+ static constexpr const char* kVUSFFLMnemonics[8][4] = {
+ {"e8ff.v", "e16ff.v", "e32ff.v", "e64ff.v"},
+ {"seg2e8ff.v", "seg2e16ff.v", "seg2e32ff.v", "seg2e64ff.v"},
+ {"seg3e8ff.v", "seg3e16ff.v", "seg3e32ff.v", "seg3e64ff.v"},
+ {"seg4e8ff.v", "seg4e16ff.v", "seg4e32ff.v", "seg4e64ff.v"},
+ {"seg5e8ff.v", "seg5e16ff.v", "seg5e32ff.v", "seg5e64ff.v"},
+ {"seg6e8ff.v", "seg6e16ff.v", "seg6e32ff.v", "seg6e64ff.v"},
+ {"seg7e8ff.v", "seg7e16ff.v", "seg7e32ff.v", "seg7e64ff.v"},
+ {"seg8e8ff.v", "seg8e16ff.v", "seg8e32ff.v", "seg8e64ff.v"},
+ };
+ return is_load ? kVUSFFLMnemonics[enum_cast<uint32_t>(nf)][width_index] : nullptr;
+ default: // Unknown
+ return nullptr;
+ }
+ }
+ case MemAddressMode::kIndexedUnordered: {
+ static constexpr const char* kVIUMnemonics[8][4] = {
+ {"uxei8.v", "uxei16.v", "uxei32.v", "uxei64.v"},
+ {"uxseg2ei8.v", "uxseg2ei16.v", "uxseg2ei32.v", "uxseg2ei64.v"},
+ {"uxseg3ei8.v", "uxseg3ei16.v", "uxseg3ei32.v", "uxseg3ei64.v"},
+ {"uxseg4ei8.v", "uxseg4ei16.v", "uxseg4ei32.v", "uxseg4ei64.v"},
+ {"uxseg5ei8.v", "uxseg5ei16.v", "uxseg5ei32.v", "uxseg5ei64.v"},
+ {"uxseg6ei8.v", "uxseg6ei16.v", "uxseg6ei32.v", "uxseg6ei64.v"},
+ {"uxseg7ei8.v", "uxseg7ei16.v", "uxseg7ei32.v", "uxseg7ei64.v"},
+ {"uxseg8ei8.v", "uxseg8ei16.v", "uxseg8ei32.v", "uxseg8ei64.v"},
+ };
+ *rs2 = VRegName(GetRs2(insn32));
+ return kVIUMnemonics[enum_cast<uint32_t>(nf)][width_index];
+ }
+ case MemAddressMode::kStrided: {
+ static constexpr const char* kVSMnemonics[8][4] = {
+ {"se8.v", "se16.v", "se32.v", "se64.v"},
+ {"sseg2e8.v", "sseg2e16.v", "sseg2e32.v", "sseg2e64.v"},
+ {"sseg3e8.v", "sseg3e16.v", "sseg3e32.v", "sseg3e64.v"},
+ {"sseg4e8.v", "sseg4e16.v", "sseg4e32.v", "sseg4e64.v"},
+ {"sseg5e8.v", "sseg5e16.v", "sseg5e32.v", "sseg5e64.v"},
+ {"sseg6e8.v", "sseg6e16.v", "sseg6e32.v", "sseg6e64.v"},
+ {"sseg7e8.v", "sseg7e16.v", "sseg7e32.v", "sseg7e64.v"},
+ {"sseg8e8.v", "sseg8e16.v", "sseg8e32.v", "sseg8e64.v"},
+ };
+ *rs2 = XRegName(GetRs2(insn32));
+ return kVSMnemonics[enum_cast<uint32_t>(nf)][width_index];
+ }
+ case MemAddressMode::kIndexedOrdered: {
+ static constexpr const char* kVIOMnemonics[8][4] = {
+ {"oxei8.v", "oxei16.v", "oxei32.v", "oxei64.v"},
+ {"oxseg2ei8.v", "oxseg2ei16.v", "oxseg2ei32.v", "oxseg2ei64.v"},
+ {"oxseg3ei8.v", "oxseg3ei16.v", "oxseg3ei32.v", "oxseg3ei64.v"},
+ {"oxseg4ei8.v", "oxseg4ei16.v", "oxseg4ei32.v", "oxseg4ei64.v"},
+ {"oxseg5ei8.v", "oxseg5ei16.v", "oxseg5ei32.v", "oxseg5ei64.v"},
+ {"oxseg6ei8.v", "oxseg6ei16.v", "oxseg6ei32.v", "oxseg6ei64.v"},
+ {"oxseg7ei8.v", "oxseg7ei16.v", "oxseg7ei32.v", "oxseg7ei64.v"},
+ {"oxseg8ei8.v", "oxseg8ei16.v", "oxseg8ei32.v", "oxseg8ei64.v"},
+ };
+ *rs2 = VRegName(GetRs2(insn32));
+ return kVIOMnemonics[enum_cast<uint32_t>(nf)][width_index];
+ }
+ }
+}
+
+static constexpr const char* kFpMemMnemonics[] = {
+ nullptr, "h", "w", "d", "q", nullptr, nullptr, nullptr
+};
+
void DisassemblerRiscv64::Printer::Print32FLoad(uint32_t insn32) {
DCHECK_EQ(insn32 & 0x7fu, 0x07u);
- static const char* const kOpcodes[] = {
- nullptr, nullptr, "flw", "fld", nullptr, nullptr, nullptr, nullptr
- };
- uint32_t funct3 = (insn32 >> 12) & 7u;
- const char* opcode = kOpcodes[funct3];
- if (opcode == nullptr) {
+ int32_t offset = 0;
+ const char* rd = nullptr;
+ const char* rs2 = nullptr;
+ const char* vm = "";
+ const uint32_t funct3 = (insn32 >> 12) & 7u;
+ const char* mnemonic = kFpMemMnemonics[funct3];
+ const char* prefix = "f";
+ if (mnemonic == nullptr) {
+ // Vector Loads
+ prefix = "v";
+ mnemonic = DecodeRVVMemMnemonic(insn32, /*is_load=*/true, &rs2);
+ rd = VRegName(GetRd(insn32));
+
+ if ((Decode32UImm7(insn32) & 0x1U) == 0) {
+ vm = ", v0.t";
+ }
+ } else {
+ rd = FRegName(GetRd(insn32));
+ offset = Decode32Imm12(insn32);
+ }
+
+ if (mnemonic == nullptr) {
os_ << "<unknown32>";
return;
}
- os_ << opcode << " " << FRegName(GetRd(insn32)) << ", ";
- PrintLoadStoreAddress(GetRs1(insn32), Decode32Imm12(insn32));
+ os_ << prefix << "l" << mnemonic << " " << rd << ", ";
+ PrintLoadStoreAddress(GetRs1(insn32), offset);
+
+ if (rs2) {
+ os_ << ", " << rs2;
+ }
+
+ os_ << vm;
// TODO(riscv64): If previous instruction is AUIPC for current `rs1` and we load
// from the range specified by assembler options, print the loaded literal.
@@ -356,18 +586,35 @@ void DisassemblerRiscv64::Printer::Print32FLoad(uint32_t insn32) {
void DisassemblerRiscv64::Printer::Print32FStore(uint32_t insn32) {
DCHECK_EQ(insn32 & 0x7fu, 0x27u);
- static const char* const kOpcodes[] = {
- nullptr, nullptr, "fsw", "fsd", nullptr, nullptr, nullptr, nullptr
- };
- uint32_t funct3 = (insn32 >> 12) & 7u;
- const char* opcode = kOpcodes[funct3];
- if (opcode == nullptr) {
- os_ << "<unknown32>";
- return;
- }
+ uint32_t funct3 = (insn32 >> 12) & 3u;
+ const char* prefix = "f";
+ const char* mnemonic = kFpMemMnemonics[funct3];
- os_ << opcode << " " << FRegName(GetRs2(insn32)) << ", ";
- PrintLoadStoreAddress(GetRs1(insn32), Decode32StoreOffset(insn32));
+ if (mnemonic == nullptr) {
+ // Vector Stores
+ const char* rs2 = nullptr;
+ prefix = "v";
+ mnemonic = DecodeRVVMemMnemonic(insn32, /*is_load=*/false, &rs2);
+
+ if (mnemonic == nullptr) {
+ os_ << "<unknown32>";
+ return;
+ }
+
+ os_ << prefix << "s" << mnemonic << " " << VRegName(GetRd(insn32)) << ", ";
+ PrintLoadStoreAddress(GetRs1(insn32), 0);
+
+ if (rs2) {
+ os_ << ", " << rs2;
+ }
+
+ if ((Decode32UImm7(insn32) & 0x1U) == 0) {
+ os_ << ", v0.t";
+ }
+ } else {
+ os_ << prefix << "s" << mnemonic << " " << FRegName(GetRs2(insn32)) << ", ";
+ PrintLoadStoreAddress(GetRs1(insn32), Decode32StoreOffset(insn32));
+ }
}
void DisassemblerRiscv64::Printer::Print32BinOpImm(uint32_t insn32) {
@@ -641,6 +888,491 @@ void DisassemblerRiscv64::Printer::Print32FpOp(uint32_t insn32) {
os_ << "<unknown32>";
}
+void DisassemblerRiscv64::Printer::AppendVType(uint32_t vtype) {
+ const uint32_t lmul_v = vtype & 0x7U;
+ const uint32_t vsew_v = (vtype >> 3) & 0x7U;
+ const uint32_t vta_v = (vtype >> 6) & 0x1U;
+ const uint32_t vma_v = (vtype >> 7) & 0x1U;
+
+ if ((vsew_v & 0x4U) == 0u) {
+ if (lmul_v != 0b100) {
+ static const char* const vsews[] = {"e8", "e16", "e32", "e64"};
+ static const char* const lmuls[] = {
+ "m1", "m2", "m4", "m8", nullptr, "mf8", "mf4", "mf2"
+ };
+
+ const char* vma = vma_v ? "ma" : "mu";
+ const char* vta = vta_v ? "ta" : "tu";
+ const char* vsew = vsews[vsew_v & 0x3u];
+ const char* lmul = lmuls[lmul_v];
+
+ os_ << vsew << ", " << lmul << ", " << vta << ", " << vma;
+ return;
+ }
+ }
+
+ os_ << StringPrintf("0x%08x", vtype) << "\t# incorrect VType literal";
+}
+
+static constexpr uint32_t VWXUNARY0 = 0b010000;
+static constexpr uint32_t VRXUNARY0 = 0b010000;
+static constexpr uint32_t VXUNARY0 = 0b010010;
+static constexpr uint32_t VMUNARY0 = 0b010100;
+
+static constexpr uint32_t VWFUNARY0 = 0b010000;
+static constexpr uint32_t VRFUNARY0 = 0b010000;
+static constexpr uint32_t VFUNARY0 = 0b010010;
+static constexpr uint32_t VFUNARY1 = 0b010011;
+
+static void MaybeSwapOperands(uint32_t funct6,
+ /*inout*/ const char*& rs1,
+ /*inout*/ const char*& rs2) {
+ if ((0x28u <= funct6 && funct6 < 0x30u) || funct6 >= 0x3Cu) {
+ std::swap(rs1, rs2);
+ }
+}
+
+void DisassemblerRiscv64::Printer::Print32RVVOp(uint32_t insn32) {
+ // TODO(riscv64): Print pseudo-instruction aliases when applicable.
+ DCHECK_EQ(insn32 & 0x7fu, 0x57u);
+ const enum VAIEncodings vai = static_cast<enum VAIEncodings>((insn32 >> 12) & 7u);
+ const uint32_t funct7 = Decode32UImm7(insn32);
+ const uint32_t funct6 = funct7 >> 1;
+ const bool masked = (funct7 & 1) == 0;
+ const char* vm = masked ? ", v0.t" : "";
+ const char* opcode = nullptr;
+ const char* rd = nullptr;
+ const char* rs1 = nullptr;
+ const char* rs2 = nullptr;
+
+ switch (vai) {
+ case VAIEncodings::kOpIVV: {
+ static constexpr const char* kOPIVVOpcodes[64] = {
+ "vadd.vv", nullptr, "vsub.vv", nullptr,
+ "vminu.vv", "vmin.vv", "vmaxu.vv", "vmax.vv",
+ nullptr, "vand.vv", "vor.vv", "vxor.vv",
+ "vrgather.vv", nullptr, "vrgatherei16.vv", nullptr,
+ "vadc.vvm", "vmadc.vvm", "vsbc.vvm", "vmsbc.vvm",
+ nullptr, nullptr, nullptr, "<vmerge/vmv>",
+ "vmseq.vv", "vmsne.vv", "vmsltu.vv", "vmslt.vv",
+ "vmsleu.vv", "vmsle.vv", nullptr, nullptr,
+ "vsaddu.vv", "vsadd.vv", "vssubu.vv", "vssub.vv",
+ nullptr, "vsll.vv", nullptr, "vsmul.vv",
+ "vsrl.vv", "vsra.vv", "vssrl.vv", "vssra.vv",
+ "vnsrl.wv", "vnsra.wv", "vnclipu.wv", "vnclip.wv",
+ "vwredsumu.vs", "vwredsum.vs", nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ };
+
+ rs2 = VRegName(GetRs2(insn32));
+ if (funct6 == 0b010111) {
+ // vmerge/vmv
+ if (masked) {
+ opcode = "vmerge.vvm";
+ vm = ", v0";
+ } else if (GetRs2(insn32) == 0) {
+ opcode = "vmv.v.v";
+ rs2 = nullptr;
+ } else {
+ opcode = nullptr;
+ }
+ } else {
+ opcode = kOPIVVOpcodes[funct6];
+ }
+
+ rd = VRegName(GetRd(insn32));
+ rs1 = VRegName(GetRs1(insn32));
+ break;
+ }
+ case VAIEncodings::kOpIVX: {
+ static constexpr const char* kOPIVXOpcodes[64] = {
+ "vadd.vx", nullptr, "vsub.vx", "vrsub.vx",
+ "vminu.vx", "vmin.vx", "vmaxu.vx", "vmax.vx",
+ nullptr, "vand.vx", "vor.vx", "vxor.vx",
+ "vrgather.vx", nullptr, "vslideup.vx", "vslidedown.vx",
+ "vadc.vxm", "vmadc.vxm", "vsbc.vxm", "vmsbc.vxm",
+ nullptr, nullptr, nullptr, "<vmerge/vmv>",
+ "vmseq.vx", "vmsne.vx", "vmsltu.vx", "vmslt.vx",
+ "vmsleu.vx", "vmsle.vx", "vmsgtu.vx", "vmsgt.vx",
+ "vsaddu.vx", "vsadd.vx", "vssubu.vx", "vssub.vx",
+ nullptr, "vsll.vx", nullptr, "vsmul.vx",
+ "vsrl.vx", "vsra.vx", "vssrl.vx", "vssra.vx",
+ "vnsrl.wx", "vnsra.wx", "vnclipu.wx", "vnclip.wx",
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ };
+
+ rs2 = VRegName(GetRs2(insn32));
+ if (funct6 == 0b010111) {
+ // vmerge/vmv
+ if (masked) {
+ opcode = "vmerge.vxm";
+ vm = ", v0";
+ } else if (GetRs2(insn32) == 0) {
+ opcode = "vmv.v.x";
+ rs2 = nullptr;
+ } else {
+ opcode = nullptr;
+ }
+ } else {
+ opcode = kOPIVXOpcodes[funct6];
+ }
+
+ rd = VRegName(GetRd(insn32));
+ rs1 = XRegName(GetRs1(insn32));
+ break;
+ }
+ case VAIEncodings::kOpIVI: {
+ static constexpr const char* kOPIVIOpcodes[64] = {
+ "vadd.vi", nullptr, nullptr, "vrsub.vi",
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, "vand.vi", "vor.vi", "vxor.vi",
+ "vrgather.vi", nullptr, "vslideup.vi", "vslidedown.vi",
+ "vadc.vim", "vmadc.vim", nullptr, nullptr,
+ nullptr, nullptr, nullptr, "<vmerge/vmv>",
+ "vmseq.vi", "vmsne.vi", nullptr, nullptr,
+ "vmsleu.vi", "vmsle.vi", "vmsgtu.vi", "vmsgt.vi",
+ "vsaddu.vi", "vsadd.vi", nullptr, nullptr,
+ nullptr, "vsll.vi", nullptr, "<vmvNr.v>",
+ "vsrl.vi", "vsra.vi", "vssrl.vi", "vssra.vi",
+ "vnsrl.wi", "vnsra.wi", "vnclipu.wi", "vnclip.wi",
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ };
+
+ rs2 = VRegName(GetRs2(insn32));
+
+ if (funct6 == 0b010111) {
+ // vmerge/vmv
+ if (masked) {
+ opcode = "vmerge.vim";
+ vm = ", v0";
+ } else if (GetRs2(insn32) == 0) {
+ opcode = "vmv.v.i";
+ rs2 = nullptr;
+ } else {
+ opcode = nullptr;
+ }
+ } else if (funct6 == 0b100111) {
+ uint32_t rs1V = GetRs1(insn32);
+ static constexpr const char* kVmvnrOpcodes[8] = {
+ "vmv1r.v", "vmv2r.v", nullptr, "vmv4r.v",
+ nullptr, nullptr, nullptr, "vmv8r.v",
+ };
+ if (IsUint<3>(rs1V)) {
+ opcode = kVmvnrOpcodes[rs1V];
+ }
+ } else {
+ opcode = kOPIVIOpcodes[funct6];
+ }
+
+ rd = VRegName(GetRd(insn32));
+ break;
+ }
+ case VAIEncodings::kOpMVV: {
+ switch (funct6) {
+ case VWXUNARY0: {
+ static constexpr const char* kVWXUNARY0Opcodes[32] = {
+ "vmv.x.s", nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ "vcpop.m", "vfirst.m", nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ };
+ opcode = kVWXUNARY0Opcodes[GetRs1(insn32)];
+ rd = XRegName(GetRd(insn32));
+ rs2 = VRegName(GetRs2(insn32));
+ break;
+ }
+ case VXUNARY0: {
+ static constexpr const char* kVXUNARY0Opcodes[32] = {
+ nullptr, nullptr, "vzext.vf8", "vsext.vf8",
+ "vzext.vf4", "vsext.vf4", "vzext.vf2", "vsext.vf2",
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ };
+ opcode = kVXUNARY0Opcodes[GetRs1(insn32)];
+ rd = VRegName(GetRd(insn32));
+ rs2 = VRegName(GetRs2(insn32));
+ break;
+ }
+ case VMUNARY0: {
+ static constexpr const char* kVMUNARY0Opcodes[32] = {
+ nullptr, "vmsbf.m", "vmsof.m", "vmsif.m",
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ "viota.m", "vid.v", nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ };
+ opcode = kVMUNARY0Opcodes[GetRs1(insn32)];
+ rd = VRegName(GetRd(insn32));
+ rs2 = VRegName(GetRs2(insn32));
+ break;
+ }
+ default: {
+ static constexpr const char* kOPMVVOpcodes[64] = {
+ "vredsum.vs", "vredand.vs", "vredor.vs", "vredxor.vs",
+ "vredminu.vs", "vredmin.vs", "vredmaxu.vs", "vredmax.vs",
+ "vaaddu.vv", "vaadd.vv", "vasubu.vv", "vasub.vv",
+ nullptr, nullptr, nullptr, nullptr,
+ "<VWXUNARY0>", nullptr, "<VXUNARY0>", nullptr,
+ "<VMUNARY0>", nullptr, nullptr, "vcompress.vm",
+ "vmandn.mm", "vmand.mm", "vmor.mm", "vmxor.mm",
+ "vmorn.mm", "vmnand.mm", "vmnor.mm", "vmxnor.mm",
+ "vdivu.vv", "vdiv.vv", "vremu.vv", "vrem.vv",
+ "vmulhu.vv", "vmul.vv", "vmulhsu.vv", "vmulh.vv",
+ nullptr, "vmadd.vv", nullptr, "vnmsub.vv",
+ nullptr, "vmacc.vv", nullptr, "vnmsac.vv",
+ "vwaddu.vv", "vwadd.vv", "vwsubu.vv", "vwsub.vv",
+ "vwaddu.wv", "vwadd.wv", "vwsubu.wv", "vwsub.wv",
+ "vwmulu.vv", nullptr, "vwmulsu.vv", "vwmul.vv",
+ "vwmaccu.vv", "vwmacc.vv", nullptr, "vwmaccsu.vv",
+ };
+
+ opcode = kOPMVVOpcodes[funct6];
+ rd = VRegName(GetRd(insn32));
+ rs1 = VRegName(GetRs1(insn32));
+ rs2 = VRegName(GetRs2(insn32));
+
+ if (0x17u <= funct6 && funct6 <= 0x1Fu) {
+ if (masked) {
+ // for vcompress.vm and *.mm encodings with vm=0 are reserved
+ opcode = nullptr;
+ }
+ }
+
+ MaybeSwapOperands(funct6, rs1, rs2);
+
+ break;
+ }
+ }
+ break;
+ }
+ case VAIEncodings::kOpMVX: {
+ switch (funct6) {
+ case VRXUNARY0: {
+ opcode = GetRs2(insn32) == 0u ? "vmv.s.x" : nullptr;
+ rd = VRegName(GetRd(insn32));
+ rs1 = XRegName(GetRs1(insn32));
+ break;
+ }
+ default: {
+ static constexpr const char* kOPMVXOpcodes[64] = {
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ "vaaddu.vx", "vaadd.vx", "vasubu.vx", "vasub.vx",
+ nullptr, nullptr, "vslide1up.vx", "vslide1down.vx",
+ "<VRXUNARY0>", nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ "vdivu.vx", "vdiv.vx", "vremu.vx", "vrem.vx",
+ "vmulhu.vx", "vmul.vx", "vmulhsu.vx", "vmulh.vx",
+ nullptr, "vmadd.vx", nullptr, "vnmsub.vx",
+ nullptr, "vmacc.vx", nullptr, "vnmsac.vx",
+ "vwaddu.vx", "vwadd.vx", "vwsubu.vx", "vwsub.vx",
+ "vwaddu.wv", "vwadd.wv", "vwsubu.wv", "vwsub.wv",
+ "vwmulu.vx", nullptr, "vwmulsu.vx", "vwmul.vx",
+ "vwmaccu.vx", "vwmacc.vx", "vwmaccus.vx", "vwmaccsu.vx",
+ };
+ opcode = kOPMVXOpcodes[funct6];
+ rd = VRegName(GetRd(insn32));
+ rs1 = XRegName(GetRs1(insn32));
+ rs2 = VRegName(GetRs2(insn32));
+
+ MaybeSwapOperands(funct6, rs1, rs2);
+
+ break;
+ }
+ }
+ break;
+ }
+ case VAIEncodings::kOpFVV: {
+ switch (funct6) {
+ case VWFUNARY0: {
+ opcode = GetRs1(insn32) == 0u ? "vfmv.f.s" : nullptr;
+ rd = XRegName(GetRd(insn32));
+ rs2 = VRegName(GetRs2(insn32));
+ break;
+ }
+ case VFUNARY0: {
+ static constexpr const char* kVFUNARY0Opcodes[32] = {
+ "vfcvt.xu.f.v", "vfcvt.x.f.v", "vfcvt.f.xu.v", "vfcvt.f.x.v",
+ nullptr, nullptr, "vfcvt.rtz.xu.f.v", "vfcvt.rtz.x.f.v",
+ "vfwcvt.xu.f.v", "vfwcvt.x.f.v", "vfwcvt.f.xu.v", "vfwcvt.f.x.v",
+ "vfwcvt.f.f.v", nullptr, "vfwcvt.rtz.xu.f.v", "vfwcvt.rtz.x.f.v",
+ "vfncvt.xu.f.w", "vfncvt.x.f.w", "vfncvt.f.xu.w", "vfncvt.f.x.w",
+ "vfncvt.f.f.w", "vfncvt.rod.f.f.w", "vfncvt.rtz.xu.f.w", "vfncvt.rtz.x.f.w",
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ };
+ opcode = kVFUNARY0Opcodes[GetRs1(insn32)];
+ rd = VRegName(GetRd(insn32));
+ rs2 = VRegName(GetRs2(insn32));
+ break;
+ }
+ case VFUNARY1: {
+ static constexpr const char* kVFUNARY1Opcodes[32] = {
+ "vfsqrt.v", nullptr, nullptr, nullptr,
+ "vfrsqrt7.v", "vfrec7.v", nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ "vfclass.v", nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ };
+ opcode = kVFUNARY1Opcodes[GetRs1(insn32)];
+ rd = VRegName(GetRd(insn32));
+ rs2 = VRegName(GetRs2(insn32));
+ break;
+ }
+ default: {
+ static constexpr const char* kOPFVVOpcodes[64] = {
+ "vfadd.vv", "vfredusum.vs", "vfsub.vv", "vfredosum.vs",
+ "vfmin.vv", "vfredmin.vs", "vfmax.vv", "vfredmax.vs",
+ "vfsgnj.vv", "vfsgnjn.vv", "vfsgnjx.vv", nullptr,
+ nullptr, nullptr, nullptr, nullptr,
+ "<VWFUNARY0>", nullptr, "<VFUNARY0>", "<VFUNARY1>",
+ nullptr, nullptr, nullptr, nullptr,
+ "vmfeq.vv", "vmfle.vv", nullptr, "vmflt.vv",
+ "vmfne.vv", nullptr, nullptr, nullptr,
+ "vfdiv.vv", nullptr, nullptr, nullptr,
+ "vfmul.vv", nullptr, nullptr, nullptr,
+ "vfmadd.vv", "vfnmadd.vv", "vfmsub.vv", "vfnmsub.vv",
+ "vfmacc.vv", "vfnmacc.vv", "vfmsac.vv", "vfnmsac.vv",
+ "vfwadd.vv", "vfwredusum.vs", "vfwsub.vv", "vfwredosum.vs",
+ "vfwadd.wv", nullptr, "vfwsub.wv", nullptr,
+ "vfwmul.vv", nullptr, nullptr, nullptr,
+ "vfwmacc.vv", "vfwnmacc.vv", "vfwmsac.vv", "vfwnmsac.vv",
+ };
+ opcode = kOPFVVOpcodes[funct6];
+ rd = VRegName(GetRd(insn32));
+ rs1 = VRegName(GetRs1(insn32));
+ rs2 = VRegName(GetRs2(insn32));
+
+ MaybeSwapOperands(funct6, rs1, rs2);
+
+ break;
+ }
+ }
+ break;
+ }
+ case VAIEncodings::kOpFVF: {
+ switch (funct6) {
+ case VRFUNARY0: {
+ opcode = GetRs2(insn32) == 0u ? "vfmv.s.f" : nullptr;
+ rd = VRegName(GetRd(insn32));
+ rs1 = FRegName(GetRs1(insn32));
+ break;
+ }
+ default: {
+ static constexpr const char* kOPFVFOpcodes[64] = {
+ "vfadd.vf", nullptr, "vfsub.vf", nullptr,
+ "vfmin.vf", nullptr, "vfmax.vf", nullptr,
+ "vfsgnj.vf", "vfsgnjn.vf", "vfsgnjx.vf", nullptr,
+ nullptr, nullptr, "vfslide1up.vf", "vfslide1down.vf",
+ "<VRFUNARY0>", nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr, "<vfmerge.vfm/vfmv>",
+ "vmfeq.vf", "vmfle.vf", nullptr, "vmflt.vf",
+ "vmfne.vf", "vmfgt.vf", nullptr, "vmfge.vf",
+ "vfdiv.vf", "vfrdiv.vf", nullptr, nullptr,
+ "vfmul.vf", nullptr, nullptr, "vfrsub.vf",
+ "vfmadd.vf", "vfnmadd.vf", "vfmsub.vf", "vfnmsub.vf",
+ "vfmacc.vf", "vfnmacc.vf", "vfmsac.vf", "vfnmsac.vf",
+ "vfwadd.vf", nullptr, "vfwsub.vf", nullptr,
+ "vfwadd.wf", nullptr, "vfwsub.wf", nullptr,
+ "vfwmul.vf", nullptr, nullptr, nullptr,
+ "vfwmacc.vf", "vfwnmacc.vf", "vfwmsac.vf", "vfwnmsac.vf",
+ };
+
+ rs2 = VRegName(GetRs2(insn32));
+ if (funct6 == 0b010111) {
+ // vfmerge/vfmv
+ if (masked) {
+ opcode = "vfmerge.vfm";
+ vm = ", v0";
+ } else if (GetRs2(insn32) == 0) {
+ opcode = "vfmv.v.f";
+ rs2 = nullptr;
+ } else {
+ opcode = nullptr;
+ }
+ } else {
+ opcode = kOPFVFOpcodes[funct6];
+ }
+
+ rd = VRegName(GetRd(insn32));
+ rs1 = FRegName(GetRs1(insn32));
+
+ MaybeSwapOperands(funct6, rs1, rs2);
+
+ break;
+ }
+ }
+ break;
+ }
+ case VAIEncodings::kOpCFG: { // vector ALU control instructions
+ if ((insn32 >> 31) != 0u) {
+ if (((insn32 >> 30) & 0x1U) != 0u) { // vsetivli
+ const uint32_t zimm = Decode32UImm12(insn32) & ~0xC00U;
+ const uint32_t imm5 = GetRs1(insn32);
+ os_ << "vsetivli " << XRegName(GetRd(insn32)) << ", " << StringPrintf("0x%08x", imm5)
+ << ", ";
+ AppendVType(zimm);
+ } else { // vsetvl
+ os_ << "vsetvl " << XRegName(GetRd(insn32)) << ", " << XRegName(GetRs1(insn32)) << ", "
+ << XRegName(GetRs2(insn32));
+ if ((Decode32UImm7(insn32) & 0x40u) != 0u) {
+ os_ << "\t# incorrect funct7 literal : "
+ << StringPrintf("0x%08x", Decode32UImm7(insn32));
+ }
+ }
+ } else { // vsetvli
+ const uint32_t zimm = Decode32UImm12(insn32) & ~0x800U;
+ os_ << "vsetvli " << XRegName(GetRd(insn32)) << ", " << XRegName(GetRs1(insn32)) << ", ";
+ AppendVType(zimm);
+ }
+ return;
+ }
+ }
+
+ if (opcode == nullptr) {
+ os_ << "<unknown32>";
+ return;
+ }
+
+ os_ << opcode << " " << rd;
+
+ if (rs2 != nullptr) {
+ os_ << ", " << rs2;
+ }
+
+ if (rs1 != nullptr) {
+ os_ << ", " << rs1;
+ } else if (vai == VAIEncodings::kOpIVI) {
+ os_ << StringPrintf(", 0x%08x", GetRs1(insn32));
+ }
+
+ os_ << vm;
+}
+
void DisassemblerRiscv64::Printer::Print32FpFma(uint32_t insn32) {
DCHECK_EQ(insn32 & 0x73u, 0x43u); // Note: Bits 0xc select the FMA opcode.
uint32_t funct2 = (insn32 >> 25) & 3u;
@@ -787,6 +1519,9 @@ void DisassemblerRiscv64::Printer::Dump32(const uint8_t* insn) {
case 0x53u:
Print32FpOp(insn32);
break;
+ case 0x57u:
+ Print32RVVOp(insn32);
+ break;
case 0x43u:
case 0x47u:
case 0x4bu: