1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "disassembler_riscv64.h"
18
19 #include "android-base/logging.h"
20 #include "android-base/stringprintf.h"
21
22 #include "base/bit_utils.h"
23 #include "base/casts.h"
24
25 using android::base::StringPrintf;
26
27 namespace art {
28 namespace riscv64 {
29
30 class DisassemblerRiscv64::Printer {
31 public:
Printer(DisassemblerRiscv64 * disassembler,std::ostream & os)32 Printer(DisassemblerRiscv64* disassembler, std::ostream& os)
33 : disassembler_(disassembler), os_(os) {}
34
35 void Dump32(const uint8_t* insn);
36 void Dump16(const uint8_t* insn);
37 void Dump2Byte(const uint8_t* data);
38 void DumpByte(const uint8_t* data);
39
40 private:
41 // This enumeration should mirror the declarations in runtime/arch/riscv64/registers_riscv64.h.
42 // We do not include that file to avoid a dependency on libart.
43 enum {
44 Zero = 0,
45 RA = 1,
46 FP = 8,
47 TR = 9,
48 };
49
50 enum class MemAddressMode : uint32_t {
51 kUnitStride = 0b00,
52 kIndexedUnordered = 0b01,
53 kStrided = 0b10,
54 kIndexedOrdered = 0b11,
55 };
56
57 enum class Nf : uint32_t {
58 k1 = 0b000,
59 k2 = 0b001,
60 k3 = 0b010,
61 k4 = 0b011,
62 k5 = 0b100,
63 k6 = 0b101,
64 k7 = 0b110,
65 k8 = 0b111,
66 };
67
68 enum class VAIEncodings : uint32_t {
69 kOpIVV = 0b000,
70 kOpFVV = 0b001,
71 kOpMVV = 0b010,
72 kOpIVI = 0b011,
73 kOpIVX = 0b100,
74 kOpFVF = 0b101,
75 kOpMVX = 0b110,
76 kOpCFG = 0b111,
77 };
78
79 class ScopedNewLinePrinter {
80 std::ostream& os_;
81
82 public:
ScopedNewLinePrinter(std::ostream & os)83 explicit ScopedNewLinePrinter(std::ostream& os) : os_(os) {}
~ScopedNewLinePrinter()84 ~ScopedNewLinePrinter() { os_ << '\n'; }
85 };
86
87 static const char* XRegName(uint32_t regno);
88 static const char* FRegName(uint32_t regno);
89 static const char* VRegName(uint32_t regno);
90 static const char* RoundingModeName(uint32_t rm);
91
92 // Regular instruction immediate utils
93
Decode32Imm12(uint32_t insn32)94 static int32_t Decode32Imm12(uint32_t insn32) {
95 uint32_t sign = (insn32 >> 31);
96 uint32_t imm12 = (insn32 >> 20);
97 return static_cast<int32_t>(imm12) - static_cast<int32_t>(sign << 12); // Sign-extend.
98 }
99
Decode32UImm7(uint32_t insn32)100 static uint32_t Decode32UImm7(uint32_t insn32) { return (insn32 >> 25) & 0x7Fu; }
101
Decode32UImm12(uint32_t insn32)102 static uint32_t Decode32UImm12(uint32_t insn32) { return (insn32 >> 20) & 0xFFFu; }
103
Decode32StoreOffset(uint32_t insn32)104 static int32_t Decode32StoreOffset(uint32_t insn32) {
105 uint32_t bit11 = insn32 >> 31;
106 uint32_t bits5_11 = insn32 >> 25;
107 uint32_t bits0_4 = (insn32 >> 7) & 0x1fu;
108 uint32_t imm = (bits5_11 << 5) + bits0_4;
109 return static_cast<int32_t>(imm) - static_cast<int32_t>(bit11 << 12); // Sign-extend.
110 }
111
112 // Compressed instruction immediate utils
113
114 // Extracts the offset from a compressed instruction
115 // where `offset[5:3]` is in bits `[12:10]` and `offset[2|6]` is in bits `[6:5]`
Decode16CMOffsetW(uint32_t insn16)116 static uint32_t Decode16CMOffsetW(uint32_t insn16) {
117 DCHECK(IsUint<16>(insn16));
118 return BitFieldExtract(insn16, 5, 1) << 6 | BitFieldExtract(insn16, 10, 3) << 3 |
119 BitFieldExtract(insn16, 6, 1) << 2;
120 }
121
122 // Extracts the offset from a compressed instruction
123 // where `offset[5:3]` is in bits `[12:10]` and `offset[7:6]` is in bits `[6:5]`
Decode16CMOffsetD(uint32_t insn16)124 static uint32_t Decode16CMOffsetD(uint32_t insn16) {
125 DCHECK(IsUint<16>(insn16));
126 return BitFieldExtract(insn16, 5, 2) << 6 | BitFieldExtract(insn16, 10, 3) << 3;
127 }
128
129 // Re-orders raw immediatate into real value
130 // where `imm[5:3]` is in bits `[5:3]` and `imm[8:6]` is in bits `[2:0]`
Uimm6ToOffsetD16(uint32_t uimm6)131 static uint32_t Uimm6ToOffsetD16(uint32_t uimm6) {
132 DCHECK(IsUint<6>(uimm6));
133 return (BitFieldExtract(uimm6, 3, 3) << 3) | (BitFieldExtract(uimm6, 0, 3) << 6);
134 }
135
136 // Re-orders raw immediatate to form real value
137 // where `imm[5:2]` is in bits `[5:2]` and `imm[7:6]` is in bits `[1:0]`
Uimm6ToOffsetW16(uint32_t uimm6)138 static uint32_t Uimm6ToOffsetW16(uint32_t uimm6) {
139 DCHECK(IsUint<6>(uimm6));
140 return (BitFieldExtract(uimm6, 2, 4) << 2) | (BitFieldExtract(uimm6, 0, 2) << 6);
141 }
142
143 // Re-orders raw immediatate to form real value
144 // where `imm[1]` is in bit `[0]` and `imm[0]` is in bit `[1]`
Uimm2ToOffset10(uint32_t uimm2)145 static uint32_t Uimm2ToOffset10(uint32_t uimm2) {
146 DCHECK(IsUint<2>(uimm2));
147 return (uimm2 >> 1) | (uimm2 & 0x1u) << 1;
148 }
149
150 // Re-orders raw immediatate to form real value
151 // where `imm[1]` is in bit `[0]` and `imm[0]` is `0`
Uimm2ToOffset1(uint32_t uimm2)152 static uint32_t Uimm2ToOffset1(uint32_t uimm2) {
153 DCHECK(IsUint<2>(uimm2));
154 return (uimm2 & 0x1u) << 1;
155 }
156
157 template <size_t kWidth>
SignExtendBits(uint32_t bits)158 static constexpr int32_t SignExtendBits(uint32_t bits) {
159 static_assert(kWidth < BitSizeOf<uint32_t>());
160 const uint32_t sign_bit = (bits >> kWidth) & 1u;
161 return static_cast<int32_t>(bits) - static_cast<int32_t>(sign_bit << kWidth);
162 }
163
164 // Extracts the immediate from a compressed instruction
165 // where `imm[5]` is in bit `[12]` and `imm[4:0]` is in bits `[6:2]`
166 // and performs sign-extension if required
167 template <typename T>
Decode16Imm6(uint32_t insn16)168 static T Decode16Imm6(uint32_t insn16) {
169 DCHECK(IsUint<16>(insn16));
170 static_assert(std::is_integral_v<T>, "T must be integral");
171 const T bits =
172 BitFieldInsert(BitFieldExtract(insn16, 2, 5), BitFieldExtract(insn16, 12, 1), 5, 1);
173 const T checked_bits = dchecked_integral_cast<T>(bits);
174 if (std::is_unsigned_v<T>) {
175 return checked_bits;
176 }
177 return SignExtendBits<6>(checked_bits);
178 }
179
180 // Regular instruction register utils
181
GetRd(uint32_t insn32)182 static uint32_t GetRd(uint32_t insn32) { return (insn32 >> 7) & 0x1fu; }
GetRs1(uint32_t insn32)183 static uint32_t GetRs1(uint32_t insn32) { return (insn32 >> 15) & 0x1fu; }
GetRs2(uint32_t insn32)184 static uint32_t GetRs2(uint32_t insn32) { return (insn32 >> 20) & 0x1fu; }
GetRs3(uint32_t insn32)185 static uint32_t GetRs3(uint32_t insn32) { return insn32 >> 27; }
GetRoundingMode(uint32_t insn32)186 static uint32_t GetRoundingMode(uint32_t insn32) { return (insn32 >> 12) & 7u; }
187
188 // Compressed instruction register utils
189
GetRs1Short16(uint32_t insn16)190 static uint32_t GetRs1Short16(uint32_t insn16) { return BitFieldExtract(insn16, 7, 3) + 8u; }
GetRs2Short16(uint32_t insn16)191 static uint32_t GetRs2Short16(uint32_t insn16) { return BitFieldExtract(insn16, 2, 3) + 8u; }
GetRs1_16(uint32_t insn16)192 static uint32_t GetRs1_16(uint32_t insn16) { return BitFieldExtract(insn16, 7, 5); }
GetRs2_16(uint32_t insn16)193 static uint32_t GetRs2_16(uint32_t insn16) { return BitFieldExtract(insn16, 2, 5); }
194
195 void PrintBranchOffset(int32_t offset);
196 void PrintLoadStoreAddress(uint32_t rs1, int32_t offset);
197
198 void Print32Lui(uint32_t insn32);
199 void Print32Auipc(const uint8_t* insn, uint32_t insn32);
200 void Print32Jal(const uint8_t* insn, uint32_t insn32);
201 void Print32Jalr(const uint8_t* insn, uint32_t insn32);
202 void Print32BCond(const uint8_t* insn, uint32_t insn32);
203 void Print32Load(uint32_t insn32);
204 void Print32Store(uint32_t insn32);
205 void Print32FLoad(uint32_t insn32);
206 void Print32FStore(uint32_t insn32);
207 void Print32BinOpImm(uint32_t insn32);
208 void Print32BinOp(uint32_t insn32);
209 void Print32Atomic(uint32_t insn32);
210 void Print32FpOp(uint32_t insn32);
211 void Print32RVVOp(uint32_t insn32);
212 void Print32FpFma(uint32_t insn32);
213 void Print32Zicsr(uint32_t insn32);
214 void Print32Fence(uint32_t insn32);
215
216 void AppendVType(uint32_t zimm);
217 static const char* DecodeRVVMemMnemonic(const uint32_t insn32,
218 bool is_load,
219 /*out*/ const char** rs2);
220
221 DisassemblerRiscv64* const disassembler_;
222 std::ostream& os_;
223 };
224
XRegName(uint32_t regno)225 const char* DisassemblerRiscv64::Printer::XRegName(uint32_t regno) {
226 static const char* const kXRegisterNames[] = {
227 "zero",
228 "ra",
229 "sp",
230 "gp",
231 "tp",
232 "t0",
233 "t1",
234 "t2",
235 "fp", // s0/fp
236 "tr", // s1/tr - ART thread register
237 "a0",
238 "a1",
239 "a2",
240 "a3",
241 "a4",
242 "a5",
243 "a6",
244 "a7",
245 "s2",
246 "s3",
247 "s4",
248 "s5",
249 "s6",
250 "s7",
251 "s8",
252 "s9",
253 "s10",
254 "s11",
255 "t3",
256 "t4",
257 "t5",
258 "t6",
259 };
260 static_assert(std::size(kXRegisterNames) == 32);
261 DCHECK_LT(regno, 32u);
262 return kXRegisterNames[regno];
263 }
264
FRegName(uint32_t regno)265 const char* DisassemblerRiscv64::Printer::FRegName(uint32_t regno) {
266 static const char* const kFRegisterNames[] = {
267 "ft0",
268 "ft1",
269 "ft2",
270 "ft3",
271 "ft4",
272 "ft5",
273 "ft6",
274 "ft7",
275 "fs0",
276 "fs1",
277 "fa0",
278 "fa1",
279 "fa2",
280 "fa3",
281 "fa4",
282 "fa5",
283 "fa6",
284 "fa7",
285 "fs2",
286 "fs3",
287 "fs4",
288 "fs5",
289 "fs6",
290 "fs7",
291 "fs8",
292 "fs9",
293 "fs10",
294 "fs11",
295 "ft8",
296 "ft9",
297 "ft10",
298 "ft11",
299 };
300 static_assert(std::size(kFRegisterNames) == 32);
301 DCHECK_LT(regno, 32u);
302 return kFRegisterNames[regno];
303 }
304
VRegName(uint32_t regno)305 const char* DisassemblerRiscv64::Printer::VRegName(uint32_t regno) {
306 static const char* const kVRegisterNames[] = {
307 "V0",
308 "V1",
309 "V2",
310 "V3",
311 "V4",
312 "V5",
313 "V6",
314 "V7",
315 "V8",
316 "V9",
317 "V10",
318 "V11",
319 "V12",
320 "V13",
321 "V14",
322 "V15",
323 "V16",
324 "V17",
325 "V18",
326 "V19",
327 "V20",
328 "V21",
329 "V22",
330 "V23",
331 "V24",
332 "V25",
333 "V26",
334 "V27",
335 "V28",
336 "V29",
337 "V30",
338 "V31",
339 };
340 static_assert(std::size(kVRegisterNames) == 32);
341 DCHECK_LT(regno, 32u);
342 return kVRegisterNames[regno];
343 }
344
RoundingModeName(uint32_t rm)345 const char* DisassemblerRiscv64::Printer::RoundingModeName(uint32_t rm) {
346 // Note: We do not print the rounding mode for DYN.
347 static const char* const kRoundingModeNames[] = {
348 ".rne", ".rtz", ".rdn", ".rup", ".rmm", ".<reserved-rm>", ".<reserved-rm>", /*DYN*/ ""
349 };
350 static_assert(std::size(kRoundingModeNames) == 8);
351 DCHECK_LT(rm, 8u);
352 return kRoundingModeNames[rm];
353 }
354
PrintBranchOffset(int32_t offset)355 void DisassemblerRiscv64::Printer::PrintBranchOffset(int32_t offset) {
356 os_ << (offset >= 0 ? "+" : "") << offset;
357 }
358
PrintLoadStoreAddress(uint32_t rs1,int32_t offset)359 void DisassemblerRiscv64::Printer::PrintLoadStoreAddress(uint32_t rs1, int32_t offset) {
360 if (offset != 0) {
361 os_ << StringPrintf("%d", offset);
362 }
363 os_ << "(" << XRegName(rs1) << ")";
364
365 if (rs1 == TR && offset >= 0) {
366 // Add entrypoint name.
367 os_ << " ; ";
368 disassembler_->GetDisassemblerOptions()->thread_offset_name_function_(
369 os_, dchecked_integral_cast<uint32_t>(offset));
370 }
371 }
372
Print32Lui(uint32_t insn32)373 void DisassemblerRiscv64::Printer::Print32Lui(uint32_t insn32) {
374 DCHECK_EQ(insn32 & 0x7fu, 0x37u);
375 // TODO(riscv64): Should we also print the actual sign-extend value?
376 os_ << StringPrintf("lui %s, %u", XRegName(GetRd(insn32)), insn32 >> 12);
377 }
378
Print32Auipc(const uint8_t * insn,uint32_t insn32)379 void DisassemblerRiscv64::Printer::Print32Auipc([[maybe_unused]] const uint8_t* insn,
380 uint32_t insn32) {
381 DCHECK_EQ(insn32 & 0x7fu, 0x17u);
382 // TODO(riscv64): Should we also print the calculated address?
383 os_ << StringPrintf("auipc %s, %u", XRegName(GetRd(insn32)), insn32 >> 12);
384 }
385
Print32Jal(const uint8_t * insn,uint32_t insn32)386 void DisassemblerRiscv64::Printer::Print32Jal(const uint8_t* insn, uint32_t insn32) {
387 DCHECK_EQ(insn32 & 0x7fu, 0x6fu);
388 // Print an alias if available.
389 uint32_t rd = GetRd(insn32);
390 os_ << (rd == Zero ? "j " : "jal ");
391 if (rd != Zero && rd != RA) {
392 os_ << XRegName(rd) << ", ";
393 }
394 uint32_t bit20 = (insn32 >> 31);
395 uint32_t bits1_10 = (insn32 >> 21) & 0x3ffu;
396 uint32_t bit11 = (insn32 >> 20) & 1u;
397 uint32_t bits12_19 = (insn32 >> 12) & 0xffu;
398 uint32_t imm = (bits1_10 << 1) + (bit11 << 11) + (bits12_19 << 12) + (bit20 << 20);
399 int32_t offset = static_cast<int32_t>(imm) - static_cast<int32_t>(bit20 << 21); // Sign-extend.
400 PrintBranchOffset(offset);
401 os_ << " ; " << disassembler_->FormatInstructionPointer(insn + offset);
402
403 // TODO(riscv64): When we implement shared thunks to reduce AOT slow-path code size,
404 // check if this JAL lands at an entrypoint load from TR and, if so, print its name.
405 }
406
Print32Jalr(const uint8_t * insn,uint32_t insn32)407 void DisassemblerRiscv64::Printer::Print32Jalr([[maybe_unused]] const uint8_t* insn,
408 uint32_t insn32) {
409 DCHECK_EQ(insn32 & 0x7fu, 0x67u);
410 DCHECK_EQ((insn32 >> 12) & 7u, 0u);
411 uint32_t rd = GetRd(insn32);
412 uint32_t rs1 = GetRs1(insn32);
413 int32_t imm12 = Decode32Imm12(insn32);
414 // Print shorter macro instruction notation if available.
415 if (rd == Zero && rs1 == RA && imm12 == 0) {
416 os_ << "ret";
417 } else if (rd == Zero && imm12 == 0) {
418 os_ << "jr " << XRegName(rs1);
419 } else if (rd == RA && imm12 == 0) {
420 os_ << "jalr " << XRegName(rs1);
421 } else {
422 // TODO(riscv64): Should we also print the calculated address if the preceding
423 // instruction is AUIPC? (We would need to record the previous instruction.)
424 os_ << "jalr " << XRegName(rd) << ", ";
425 // Use the same format as llvm-objdump: "rs1" if `imm12` is zero, otherwise "imm12(rs1)".
426 if (imm12 == 0) {
427 os_ << XRegName(rs1);
428 } else {
429 os_ << imm12 << "(" << XRegName(rs1) << ")";
430 }
431 }
432 }
433
Print32BCond(const uint8_t * insn,uint32_t insn32)434 void DisassemblerRiscv64::Printer::Print32BCond(const uint8_t* insn, uint32_t insn32) {
435 DCHECK_EQ(insn32 & 0x7fu, 0x63u);
436 static const char* const kOpcodes[] = {
437 "beq", "bne", nullptr, nullptr, "blt", "bge", "bltu", "bgeu"
438 };
439 uint32_t funct3 = (insn32 >> 12) & 7u;
440 const char* opcode = kOpcodes[funct3];
441 if (opcode == nullptr) {
442 os_ << "<unknown32>";
443 return;
444 }
445
446 // Print shorter macro instruction notation if available.
447 uint32_t rs1 = GetRs1(insn32);
448 uint32_t rs2 = GetRs2(insn32);
449 if (rs2 == Zero) {
450 os_ << opcode << "z " << XRegName(rs1);
451 } else if (rs1 == Zero && (funct3 == 4u || funct3 == 5u)) {
452 // blt zero, rs2, offset ... bgtz rs2, offset
453 // bge zero, rs2, offset ... blez rs2, offset
454 os_ << (funct3 == 4u ? "bgtz " : "blez ") << XRegName(rs2);
455 } else {
456 os_ << opcode << " " << XRegName(rs1) << ", " << XRegName(rs2);
457 }
458 os_ << ", ";
459
460 uint32_t bit12 = insn32 >> 31;
461 uint32_t bits5_10 = (insn32 >> 25) & 0x3fu;
462 uint32_t bits1_4 = (insn32 >> 8) & 0xfu;
463 uint32_t bit11 = (insn32 >> 7) & 1u;
464 uint32_t imm = (bit12 << 12) + (bit11 << 11) + (bits5_10 << 5) + (bits1_4 << 1);
465 int32_t offset = static_cast<int32_t>(imm) - static_cast<int32_t>(bit12 << 13); // Sign-extend.
466 PrintBranchOffset(offset);
467 os_ << " ; " << disassembler_->FormatInstructionPointer(insn + offset);
468 }
469
Print32Load(uint32_t insn32)470 void DisassemblerRiscv64::Printer::Print32Load(uint32_t insn32) {
471 DCHECK_EQ(insn32 & 0x7fu, 0x03u);
472 static const char* const kOpcodes[] = {
473 "lb", "lh", "lw", "ld", "lbu", "lhu", "lwu", nullptr
474 };
475 uint32_t funct3 = (insn32 >> 12) & 7u;
476 const char* opcode = kOpcodes[funct3];
477 if (opcode == nullptr) {
478 os_ << "<unknown32>";
479 return;
480 }
481
482 os_ << opcode << " " << XRegName(GetRd(insn32)) << ", ";
483 PrintLoadStoreAddress(GetRs1(insn32), Decode32Imm12(insn32));
484
485 // TODO(riscv64): If previous instruction is AUIPC for current `rs1` and we load
486 // from the range specified by assembler options, print the loaded literal.
487 }
488
Print32Store(uint32_t insn32)489 void DisassemblerRiscv64::Printer::Print32Store(uint32_t insn32) {
490 DCHECK_EQ(insn32 & 0x7fu, 0x23u);
491 static const char* const kOpcodes[] = {
492 "sb", "sh", "sw", "sd", nullptr, nullptr, nullptr, nullptr
493 };
494 uint32_t funct3 = (insn32 >> 12) & 7u;
495 const char* opcode = kOpcodes[funct3];
496 if (opcode == nullptr) {
497 os_ << "<unknown32>";
498 return;
499 }
500
501 os_ << opcode << " " << XRegName(GetRs2(insn32)) << ", ";
502 PrintLoadStoreAddress(GetRs1(insn32), Decode32StoreOffset(insn32));
503 }
504
DecodeRVVMemMnemonic(const uint32_t insn32,bool is_load,const char ** rs2)505 const char* DisassemblerRiscv64::Printer::DecodeRVVMemMnemonic(const uint32_t insn32,
506 bool is_load,
507 /*out*/ const char** rs2) {
508 const uint32_t width_index = (insn32 >> 12) & 3u;
509 DCHECK_EQ(width_index != 0u, (insn32 & 0x4000u) != 0u);
510 const uint32_t imm7 = Decode32UImm7(insn32);
511 const enum Nf nf = static_cast<enum Nf>((imm7 >> 4) & 0x7u);
512 const enum MemAddressMode mop = static_cast<enum MemAddressMode>((imm7 >> 1) & 0x3u);
513 const uint32_t mew = (insn32 >> 28) & 1u;
514
515 if (mew == 1u) {
516 // 7.3. Vector Load/Store Width Encoding
517 // The mew bit (inst[28]) when set is expected to be used to encode
518 // expanded memory sizes of 128 bits and above,
519 // but these encodings are currently reserved.
520 return nullptr;
521 }
522
523 switch (mop) {
524 case MemAddressMode::kUnitStride: {
525 const uint32_t umop = GetRs2(insn32);
526 switch (umop) {
527 case 0b00000: // Vector Unit-Stride Load/Store
528 static constexpr const char* kVUSMnemonics[8][4] = {
529 {"e8.v", "e16.v", "e32.v", "e64.v"},
530 {"seg2e8.v", "seg2e16.v", "seg2e32.v", "seg2e64.v"},
531 {"seg3e8.v", "seg3e16.v", "seg3e32.v", "seg3e64.v"},
532 {"seg4e8.v", "seg4e16.v", "seg4e32.v", "seg4e64.v"},
533 {"seg5e8.v", "seg5e16.v", "seg5e32.v", "seg5e64.v"},
534 {"seg6e8.v", "seg6e16.v", "seg6e32.v", "seg6e64.v"},
535 {"seg7e8.v", "seg7e16.v", "seg7e32.v", "seg7e64.v"},
536 {"seg8e8.v", "seg8e16.v", "seg8e32.v", "seg8e64.v"},
537 };
538 return kVUSMnemonics[enum_cast<uint32_t>(nf)][width_index];
539 case 0b01000: { // Vector Whole Register Load/Store
540 if (is_load) {
541 static constexpr const char* kVWRLMnemonics[8][4] = {
542 {"1re8.v", "1re16.v", "1re32.v", "1re64.v"},
543 {"2re8.v", "2re16.v", "2re32.v", "2re64.v"},
544 {nullptr, nullptr, nullptr, nullptr},
545 {"4re8.v", "4re16.v", "4re32.v", "4re64.v"},
546 {nullptr, nullptr, nullptr, nullptr},
547 {nullptr, nullptr, nullptr, nullptr},
548 {nullptr, nullptr, nullptr, nullptr},
549 {"8re8.v", "8re16.v", "8re32.v", "8re64.v"},
550 };
551 return kVWRLMnemonics[enum_cast<uint32_t>(nf)][width_index];
552 } else {
553 if (width_index != 0) {
554 return nullptr;
555 }
556 static constexpr const char* kVWRSMnemonics[8] = {
557 "1r", "2r", nullptr, "4r", nullptr, nullptr, nullptr, "8r"
558 };
559 return kVWRSMnemonics[enum_cast<uint32_t>(nf)];
560 }
561 }
562 case 0b01011: // Vector Unit-Stride Mask Load/Store
563 if (nf == Nf::k1 && width_index == 0 && (imm7 & 1u) == 1u) {
564 return "m.v";
565 } else {
566 return nullptr;
567 }
568 case 0b10000: // Vector Unit-Stride Fault-Only-First Load
569 static constexpr const char* kVUSFFLMnemonics[8][4] = {
570 {"e8ff.v", "e16ff.v", "e32ff.v", "e64ff.v"},
571 {"seg2e8ff.v", "seg2e16ff.v", "seg2e32ff.v", "seg2e64ff.v"},
572 {"seg3e8ff.v", "seg3e16ff.v", "seg3e32ff.v", "seg3e64ff.v"},
573 {"seg4e8ff.v", "seg4e16ff.v", "seg4e32ff.v", "seg4e64ff.v"},
574 {"seg5e8ff.v", "seg5e16ff.v", "seg5e32ff.v", "seg5e64ff.v"},
575 {"seg6e8ff.v", "seg6e16ff.v", "seg6e32ff.v", "seg6e64ff.v"},
576 {"seg7e8ff.v", "seg7e16ff.v", "seg7e32ff.v", "seg7e64ff.v"},
577 {"seg8e8ff.v", "seg8e16ff.v", "seg8e32ff.v", "seg8e64ff.v"},
578 };
579 return is_load ? kVUSFFLMnemonics[enum_cast<uint32_t>(nf)][width_index] : nullptr;
580 default: // Unknown
581 return nullptr;
582 }
583 }
584 case MemAddressMode::kIndexedUnordered: {
585 static constexpr const char* kVIUMnemonics[8][4] = {
586 {"uxei8.v", "uxei16.v", "uxei32.v", "uxei64.v"},
587 {"uxseg2ei8.v", "uxseg2ei16.v", "uxseg2ei32.v", "uxseg2ei64.v"},
588 {"uxseg3ei8.v", "uxseg3ei16.v", "uxseg3ei32.v", "uxseg3ei64.v"},
589 {"uxseg4ei8.v", "uxseg4ei16.v", "uxseg4ei32.v", "uxseg4ei64.v"},
590 {"uxseg5ei8.v", "uxseg5ei16.v", "uxseg5ei32.v", "uxseg5ei64.v"},
591 {"uxseg6ei8.v", "uxseg6ei16.v", "uxseg6ei32.v", "uxseg6ei64.v"},
592 {"uxseg7ei8.v", "uxseg7ei16.v", "uxseg7ei32.v", "uxseg7ei64.v"},
593 {"uxseg8ei8.v", "uxseg8ei16.v", "uxseg8ei32.v", "uxseg8ei64.v"},
594 };
595 *rs2 = VRegName(GetRs2(insn32));
596 return kVIUMnemonics[enum_cast<uint32_t>(nf)][width_index];
597 }
598 case MemAddressMode::kStrided: {
599 static constexpr const char* kVSMnemonics[8][4] = {
600 {"se8.v", "se16.v", "se32.v", "se64.v"},
601 {"sseg2e8.v", "sseg2e16.v", "sseg2e32.v", "sseg2e64.v"},
602 {"sseg3e8.v", "sseg3e16.v", "sseg3e32.v", "sseg3e64.v"},
603 {"sseg4e8.v", "sseg4e16.v", "sseg4e32.v", "sseg4e64.v"},
604 {"sseg5e8.v", "sseg5e16.v", "sseg5e32.v", "sseg5e64.v"},
605 {"sseg6e8.v", "sseg6e16.v", "sseg6e32.v", "sseg6e64.v"},
606 {"sseg7e8.v", "sseg7e16.v", "sseg7e32.v", "sseg7e64.v"},
607 {"sseg8e8.v", "sseg8e16.v", "sseg8e32.v", "sseg8e64.v"},
608 };
609 *rs2 = XRegName(GetRs2(insn32));
610 return kVSMnemonics[enum_cast<uint32_t>(nf)][width_index];
611 }
612 case MemAddressMode::kIndexedOrdered: {
613 static constexpr const char* kVIOMnemonics[8][4] = {
614 {"oxei8.v", "oxei16.v", "oxei32.v", "oxei64.v"},
615 {"oxseg2ei8.v", "oxseg2ei16.v", "oxseg2ei32.v", "oxseg2ei64.v"},
616 {"oxseg3ei8.v", "oxseg3ei16.v", "oxseg3ei32.v", "oxseg3ei64.v"},
617 {"oxseg4ei8.v", "oxseg4ei16.v", "oxseg4ei32.v", "oxseg4ei64.v"},
618 {"oxseg5ei8.v", "oxseg5ei16.v", "oxseg5ei32.v", "oxseg5ei64.v"},
619 {"oxseg6ei8.v", "oxseg6ei16.v", "oxseg6ei32.v", "oxseg6ei64.v"},
620 {"oxseg7ei8.v", "oxseg7ei16.v", "oxseg7ei32.v", "oxseg7ei64.v"},
621 {"oxseg8ei8.v", "oxseg8ei16.v", "oxseg8ei32.v", "oxseg8ei64.v"},
622 };
623 *rs2 = VRegName(GetRs2(insn32));
624 return kVIOMnemonics[enum_cast<uint32_t>(nf)][width_index];
625 }
626 }
627 }
628
629 static constexpr const char* kFpMemMnemonics[] = {
630 nullptr, "h", "w", "d", "q", nullptr, nullptr, nullptr
631 };
632
Print32FLoad(uint32_t insn32)633 void DisassemblerRiscv64::Printer::Print32FLoad(uint32_t insn32) {
634 DCHECK_EQ(insn32 & 0x7fu, 0x07u);
635 int32_t offset = 0;
636 const char* rd = nullptr;
637 const char* rs2 = nullptr;
638 const char* vm = "";
639 const uint32_t funct3 = (insn32 >> 12) & 7u;
640 const char* mnemonic = kFpMemMnemonics[funct3];
641 const char* prefix = "f";
642 if (mnemonic == nullptr) {
643 // Vector Loads
644 prefix = "v";
645 mnemonic = DecodeRVVMemMnemonic(insn32, /*is_load=*/true, &rs2);
646 rd = VRegName(GetRd(insn32));
647
648 if ((Decode32UImm7(insn32) & 0x1U) == 0) {
649 vm = ", v0.t";
650 }
651 } else {
652 rd = FRegName(GetRd(insn32));
653 offset = Decode32Imm12(insn32);
654 }
655
656 if (mnemonic == nullptr) {
657 os_ << "<unknown32>";
658 return;
659 }
660
661 os_ << prefix << "l" << mnemonic << " " << rd << ", ";
662 PrintLoadStoreAddress(GetRs1(insn32), offset);
663
664 if (rs2) {
665 os_ << ", " << rs2;
666 }
667
668 os_ << vm;
669
670 // TODO(riscv64): If previous instruction is AUIPC for current `rs1` and we load
671 // from the range specified by assembler options, print the loaded literal.
672 }
673
Print32FStore(uint32_t insn32)674 void DisassemblerRiscv64::Printer::Print32FStore(uint32_t insn32) {
675 DCHECK_EQ(insn32 & 0x7fu, 0x27u);
676 uint32_t funct3 = (insn32 >> 12) & 3u;
677 const char* prefix = "f";
678 const char* mnemonic = kFpMemMnemonics[funct3];
679
680 if (mnemonic == nullptr) {
681 // Vector Stores
682 const char* rs2 = nullptr;
683 prefix = "v";
684 mnemonic = DecodeRVVMemMnemonic(insn32, /*is_load=*/false, &rs2);
685
686 if (mnemonic == nullptr) {
687 os_ << "<unknown32>";
688 return;
689 }
690
691 os_ << prefix << "s" << mnemonic << " " << VRegName(GetRd(insn32)) << ", ";
692 PrintLoadStoreAddress(GetRs1(insn32), 0);
693
694 if (rs2) {
695 os_ << ", " << rs2;
696 }
697
698 if ((Decode32UImm7(insn32) & 0x1U) == 0) {
699 os_ << ", v0.t";
700 }
701 } else {
702 os_ << prefix << "s" << mnemonic << " " << FRegName(GetRs2(insn32)) << ", ";
703 PrintLoadStoreAddress(GetRs1(insn32), Decode32StoreOffset(insn32));
704 }
705 }
706
Print32BinOpImm(uint32_t insn32)707 void DisassemblerRiscv64::Printer::Print32BinOpImm(uint32_t insn32) {
708 DCHECK_EQ(insn32 & 0x77u, 0x13u); // Note: Bit 0x8 selects narrow binop.
709 bool narrow = (insn32 & 0x8u) != 0u;
710 uint32_t funct3 = (insn32 >> 12) & 7u;
711 uint32_t rd = GetRd(insn32);
712 uint32_t rs1 = GetRs1(insn32);
713 int32_t imm = Decode32Imm12(insn32);
714
715 // Print shorter macro instruction notation if available.
716 if (funct3 == /*ADDI*/ 0u && imm == 0u) {
717 if (narrow) {
718 os_ << "sextw " << XRegName(rd) << ", " << XRegName(rs1);
719 } else if (rd == Zero && rs1 == Zero) {
720 os_ << "nop"; // Only canonical nop. Non-Zero `rd == rs1` nops are printed as "mv".
721 } else {
722 os_ << "mv " << XRegName(rd) << ", " << XRegName(rs1);
723 }
724 } else if (!narrow && funct3 == /*XORI*/ 4u && imm == -1) {
725 os_ << "not " << XRegName(rd) << ", " << XRegName(rs1);
726 } else if (!narrow && funct3 == /*ANDI*/ 7u && imm == 0xff) {
727 os_ << "zextb " << XRegName(rd) << ", " << XRegName(rs1);
728 } else if (!narrow && funct3 == /*SLTIU*/ 3u && imm == 1) {
729 os_ << "seqz " << XRegName(rd) << ", " << XRegName(rs1);
730 } else if ((insn32 & 0xfc00707fu) == 0x0800101bu) {
731 os_ << "slli.uw " << XRegName(rd) << ", " << XRegName(rs1) << ", " << (imm & 0x3fu);
732 } else if ((imm ^ 0x600u) < 3u && funct3 == 1u) {
733 static const char* const kBitOpcodes[] = { "clz", "ctz", "cpop" };
734 os_ << kBitOpcodes[imm ^ 0x600u] << (narrow ? "w " : " ")
735 << XRegName(rd) << ", " << XRegName(rs1);
736 } else if ((imm ^ 0x600u) < (narrow ? 32 : 64) && funct3 == 5u) {
737 os_ << "rori" << (narrow ? "w " : " ")
738 << XRegName(rd) << ", " << XRegName(rs1) << ", " << (imm ^ 0x600u);
739 } else if (imm == 0x287u && !narrow && funct3 == 5u) {
740 os_ << "orc.b " << XRegName(rd) << ", " << XRegName(rs1);
741 } else if (imm == 0x6b8u && !narrow && funct3 == 5u) {
742 os_ << "rev8 " << XRegName(rd) << ", " << XRegName(rs1);
743 } else {
744 bool bad_high_bits = false;
745 if (funct3 == /*SLLI*/ 1u || funct3 == /*SRLI/SRAI*/ 5u) {
746 imm &= (narrow ? 0x1fu : 0x3fu);
747 uint32_t high_bits = insn32 & (narrow ? 0xfe000000u : 0xfc000000u);
748 if (high_bits == 0x40000000u && funct3 == /*SRAI*/ 5u) {
749 os_ << "srai";
750 } else {
751 os_ << ((funct3 == /*SRLI*/ 5u) ? "srli" : "slli");
752 bad_high_bits = (high_bits != 0u);
753 }
754 } else if (!narrow || funct3 == /*ADDI*/ 0u) {
755 static const char* const kOpcodes[] = {
756 "addi", nullptr, "slti", "sltiu", "xori", nullptr, "ori", "andi"
757 };
758 DCHECK(kOpcodes[funct3] != nullptr);
759 os_ << kOpcodes[funct3];
760 } else {
761 os_ << "<unknown32>"; // There is no SLTIW/SLTIUW/XORIW/ORIW/ANDIW.
762 return;
763 }
764 os_ << (narrow ? "w " : " ") << XRegName(rd) << ", " << XRegName(rs1) << ", " << imm;
765 if (bad_high_bits) {
766 os_ << " (invalid high bits)";
767 }
768 }
769 }
770
Print32BinOp(uint32_t insn32)771 void DisassemblerRiscv64::Printer::Print32BinOp(uint32_t insn32) {
772 DCHECK_EQ(insn32 & 0x77u, 0x33u); // Note: Bit 0x8 selects narrow binop.
773 bool narrow = (insn32 & 0x8u) != 0u;
774 uint32_t funct3 = (insn32 >> 12) & 7u;
775 uint32_t rd = GetRd(insn32);
776 uint32_t rs1 = GetRs1(insn32);
777 uint32_t rs2 = GetRs2(insn32);
778 uint32_t high_bits = insn32 & 0xfe000000u;
779
780 // Print shorter macro instruction notation if available.
781 if (high_bits == 0x40000000u && funct3 == /*SUB*/ 0u && rs1 == Zero) {
782 os_ << (narrow ? "negw " : "neg ") << XRegName(rd) << ", " << XRegName(rs2);
783 } else if (!narrow && funct3 == /*SLT*/ 2u && rs2 == Zero) {
784 os_ << "sltz " << XRegName(rd) << ", " << XRegName(rs1);
785 } else if (!narrow && funct3 == /*SLT*/ 2u && rs1 == Zero) {
786 os_ << "sgtz " << XRegName(rd) << ", " << XRegName(rs2);
787 } else if (!narrow && funct3 == /*SLTU*/ 3u && rs1 == Zero) {
788 os_ << "snez " << XRegName(rd) << ", " << XRegName(rs2);
789 } else if (narrow && high_bits == 0x08000000u && funct3 == /*ADD.UW*/ 0u && rs2 == Zero) {
790 os_ << "zext.w " << XRegName(rd) << ", " << XRegName(rs1);
791 } else {
792 bool bad_high_bits = false;
793 if (high_bits == 0x40000000u && (funct3 == /*SUB*/ 0u || funct3 == /*SRA*/ 5u)) {
794 os_ << ((funct3 == /*SUB*/ 0u) ? "sub" : "sra");
795 } else if (high_bits == 0x02000000u &&
796 (!narrow || (funct3 == /*MUL*/ 0u || funct3 >= /*DIV/DIVU/REM/REMU*/ 4u))) {
797 static const char* const kOpcodes[] = {
798 "mul", "mulh", "mulhsu", "mulhu", "div", "divu", "rem", "remu"
799 };
800 os_ << kOpcodes[funct3];
801 } else if (high_bits == 0x08000000u && narrow && funct3 == /*ADD.UW*/ 0u) {
802 os_ << "add.u"; // "w" is added below.
803 } else if (high_bits == 0x20000000u && (funct3 & 1u) == 0u && funct3 != 0u) {
804 static const char* const kZbaOpcodes[] = { nullptr, "sh1add", "sh2add", "sh3add" };
805 DCHECK(kZbaOpcodes[funct3 >> 1] != nullptr);
806 os_ << kZbaOpcodes[funct3 >> 1] << (narrow ? ".u" /* "w" is added below. */ : "");
807 } else if (high_bits == 0x40000000u && !narrow && funct3 >= 4u && funct3 != 5u) {
808 static const char* const kZbbNegOpcodes[] = { "xnor", nullptr, "orn", "andn" };
809 DCHECK(kZbbNegOpcodes[funct3 - 4u] != nullptr);
810 os_ << kZbbNegOpcodes[funct3 - 4u];
811 } else if (high_bits == 0x0a000000u && !narrow && funct3 >= 4u) {
812 static const char* const kZbbMinMaxOpcodes[] = { "min", "minu", "max", "maxu" };
813 DCHECK(kZbbMinMaxOpcodes[funct3 - 4u] != nullptr);
814 os_ << kZbbMinMaxOpcodes[funct3 - 4u];
815 } else if (high_bits == 0x60000000u && (funct3 == /*ROL*/ 1u || funct3 == /*ROL*/ 5u)) {
816 os_ << (funct3 == /*ROL*/ 1u ? "rol" : "ror");
817 } else if (!narrow || (funct3 == /*ADD*/ 0u || funct3 == /*SLL*/ 1u || funct3 == /*SRL*/ 5u)) {
818 static const char* const kOpcodes[] = {
819 "add", "sll", "slt", "sltu", "xor", "srl", "or", "and"
820 };
821 os_ << kOpcodes[funct3];
822 bad_high_bits = (high_bits != 0u);
823 } else {
824 DCHECK(narrow);
825 os_ << "<unknown32>"; // Some of the above instructions do not have a narrow version.
826 return;
827 }
828 os_ << (narrow ? "w " : " ") << XRegName(rd) << ", " << XRegName(rs1) << ", " << XRegName(rs2);
829 if (bad_high_bits) {
830 os_ << " (invalid high bits)";
831 }
832 }
833 }
834
Print32Atomic(uint32_t insn32)835 void DisassemblerRiscv64::Printer::Print32Atomic(uint32_t insn32) {
836 DCHECK_EQ(insn32 & 0x7fu, 0x2fu);
837 uint32_t funct3 = (insn32 >> 12) & 7u;
838 uint32_t funct5 = (insn32 >> 27);
839 if ((funct3 != 2u && funct3 != 3u) || // There are only 32-bit and 64-bit LR/SC/AMO*.
840 (((funct5 & 3u) != 0u) && funct5 >= 4u)) { // Only multiples of 4, or 1-3.
841 os_ << "<unknown32>";
842 return;
843 }
844 static const char* const kMul4Opcodes[] = {
845 "amoadd", "amoxor", "amoor", "amoand", "amomin", "amomax", "amominu", "amomaxu"
846 };
847 static const char* const kOtherOpcodes[] = {
848 nullptr, "amoswap", "lr", "sc"
849 };
850 const char* opcode = ((funct5 & 3u) == 0u) ? kMul4Opcodes[funct5 >> 2] : kOtherOpcodes[funct5];
851 DCHECK(opcode != nullptr);
852 uint32_t rd = GetRd(insn32);
853 uint32_t rs1 = GetRs1(insn32);
854 uint32_t rs2 = GetRs2(insn32);
855 const char* type = (funct3 == 2u) ? ".w" : ".d";
856 const char* aq = (((insn32 >> 26) & 1u) != 0u) ? ".aq" : "";
857 const char* rl = (((insn32 >> 25) & 1u) != 0u) ? ".rl" : "";
858 os_ << opcode << type << aq << rl << " " << XRegName(rd) << ", " << XRegName(rs1);
859 if (funct5 == /*LR*/ 2u) {
860 if (rs2 != 0u) {
861 os_ << " (bad rs2)";
862 }
863 } else {
864 os_ << ", " << XRegName(rs2);
865 }
866 }
867
Print32FpOp(uint32_t insn32)868 void DisassemblerRiscv64::Printer::Print32FpOp(uint32_t insn32) {
869 DCHECK_EQ(insn32 & 0x7fu, 0x53u);
870 uint32_t rd = GetRd(insn32);
871 uint32_t rs1 = GetRs1(insn32);
872 uint32_t rs2 = GetRs2(insn32); // Sometimes used to to differentiate opcodes.
873 uint32_t rm = GetRoundingMode(insn32); // Sometimes used to to differentiate opcodes.
874 uint32_t funct7 = insn32 >> 25;
875 const char* type = ((funct7 & 1u) != 0u) ? ".d" : ".s";
876 if ((funct7 & 2u) != 0u) {
877 os_ << "<unknown32>"; // Note: This includes the "H" and "Q" extensions.
878 return;
879 }
880 switch (funct7 >> 2) {
881 case 0u:
882 case 1u:
883 case 2u:
884 case 3u: {
885 static const char* const kOpcodes[] = { "fadd", "fsub", "fmul", "fdiv" };
886 os_ << kOpcodes[funct7 >> 2] << type << RoundingModeName(rm) << " "
887 << FRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
888 return;
889 }
890 case 4u: { // FSGN*
891 // Print shorter macro instruction notation if available.
892 static const char* const kOpcodes[] = { "fsgnj", "fsgnjn", "fsgnjx" };
893 if (rm < std::size(kOpcodes)) {
894 if (rs1 == rs2) {
895 static const char* const kAltOpcodes[] = { "fmv", "fneg", "fabs" };
896 static_assert(std::size(kOpcodes) == std::size(kAltOpcodes));
897 os_ << kAltOpcodes[rm] << type << " " << FRegName(rd) << ", " << FRegName(rs1);
898 } else {
899 os_ << kOpcodes[rm] << type << " "
900 << FRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
901 }
902 return;
903 }
904 break;
905 }
906 case 5u: { // FMIN/FMAX
907 static const char* const kOpcodes[] = { "fmin", "fmax" };
908 if (rm < std::size(kOpcodes)) {
909 os_ << kOpcodes[rm] << type << " "
910 << FRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
911 return;
912 }
913 break;
914 }
915 case 0x8u: // FCVT between FP numbers.
916 if ((rs2 ^ 1u) == (funct7 & 1u)) {
917 os_ << ((rs2 != 0u) ? "fcvt.s.d" : "fcvt.d.s") << RoundingModeName(rm) << " "
918 << FRegName(rd) << ", " << FRegName(rs1);
919 }
920 break;
921 case 0xbu:
922 if (rs2 == 0u) {
923 os_ << "fsqrt" << type << RoundingModeName(rm) << " "
924 << FRegName(rd) << ", " << FRegName(rs1);
925 return;
926 }
927 break;
928 case 0x14u: { // FLE/FLT/FEQ
929 static const char* const kOpcodes[] = { "fle", "flt", "feq" };
930 if (rm < std::size(kOpcodes)) {
931 os_ << kOpcodes[rm] << type << " "
932 << XRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
933 return;
934 }
935 break;
936 }
937 case 0x18u: { // FCVT from floating point numbers to integers
938 static const char* const kIntTypes[] = { "w", "wu", "l", "lu" };
939 if (rs2 < std::size(kIntTypes)) {
940 os_ << "fcvt." << kIntTypes[rs2] << type << RoundingModeName(rm) << " "
941 << XRegName(rd) << ", " << FRegName(rs1);
942 return;
943 }
944 break;
945 }
946 case 0x1au: { // FCVT from integers to floating point numbers
947 static const char* const kIntTypes[] = { "w", "wu", "l", "lu" };
948 if (rs2 < std::size(kIntTypes)) {
949 os_ << "fcvt" << type << "." << kIntTypes[rs2] << RoundingModeName(rm) << " "
950 << FRegName(rd) << ", " << XRegName(rs1);
951 return;
952 }
953 break;
954 }
955 case 0x1cu: // FMV from FPR to GPR, or FCLASS
956 if (rs2 == 0u && rm == 0u) {
957 os_ << (((funct7 & 1u) != 0u) ? "fmv.x.d " : "fmv.x.w ")
958 << XRegName(rd) << ", " << FRegName(rs1);
959 return;
960 } else if (rs2 == 0u && rm == 1u) {
961 os_ << "fclass" << type << " " << XRegName(rd) << ", " << FRegName(rs1);
962 return;
963 }
964 break;
965 case 0x1eu: // FMV from GPR to FPR
966 if (rs2 == 0u && rm == 0u) {
967 os_ << (((funct7 & 1u) != 0u) ? "fmv.d.x " : "fmv.w.x ")
968 << FRegName(rd) << ", " << XRegName(rs1);
969 return;
970 }
971 break;
972 default:
973 break;
974 }
975 os_ << "<unknown32>";
976 }
977
AppendVType(uint32_t vtype)978 void DisassemblerRiscv64::Printer::AppendVType(uint32_t vtype) {
979 const uint32_t lmul_v = vtype & 0x7U;
980 const uint32_t vsew_v = (vtype >> 3) & 0x7U;
981 const uint32_t vta_v = (vtype >> 6) & 0x1U;
982 const uint32_t vma_v = (vtype >> 7) & 0x1U;
983
984 if ((vsew_v & 0x4U) == 0u) {
985 if (lmul_v != 0b100) {
986 static const char* const vsews[] = {"e8", "e16", "e32", "e64"};
987 static const char* const lmuls[] = {
988 "m1", "m2", "m4", "m8", nullptr, "mf8", "mf4", "mf2"
989 };
990
991 const char* vma = vma_v ? "ma" : "mu";
992 const char* vta = vta_v ? "ta" : "tu";
993 const char* vsew = vsews[vsew_v & 0x3u];
994 const char* lmul = lmuls[lmul_v];
995
996 os_ << vsew << ", " << lmul << ", " << vta << ", " << vma;
997 return;
998 }
999 }
1000
1001 os_ << StringPrintf("0x%08x", vtype) << "\t# incorrect VType literal";
1002 }
1003
1004 static constexpr uint32_t VWXUNARY0 = 0b010000;
1005 static constexpr uint32_t VRXUNARY0 = 0b010000;
1006 static constexpr uint32_t VXUNARY0 = 0b010010;
1007 static constexpr uint32_t VMUNARY0 = 0b010100;
1008
1009 static constexpr uint32_t VWFUNARY0 = 0b010000;
1010 static constexpr uint32_t VRFUNARY0 = 0b010000;
1011 static constexpr uint32_t VFUNARY0 = 0b010010;
1012 static constexpr uint32_t VFUNARY1 = 0b010011;
1013
MaybeSwapOperands(uint32_t funct6,const char * & rs1,const char * & rs2)1014 static void MaybeSwapOperands(uint32_t funct6,
1015 /*inout*/ const char*& rs1,
1016 /*inout*/ const char*& rs2) {
1017 if ((0x28u <= funct6 && funct6 < 0x30u) || funct6 >= 0x3Cu) {
1018 std::swap(rs1, rs2);
1019 }
1020 }
1021
Print32RVVOp(uint32_t insn32)1022 void DisassemblerRiscv64::Printer::Print32RVVOp(uint32_t insn32) {
1023 // TODO(riscv64): Print pseudo-instruction aliases when applicable.
1024 DCHECK_EQ(insn32 & 0x7fu, 0x57u);
1025 const enum VAIEncodings vai = static_cast<enum VAIEncodings>((insn32 >> 12) & 7u);
1026 const uint32_t funct7 = Decode32UImm7(insn32);
1027 const uint32_t funct6 = funct7 >> 1;
1028 const bool masked = (funct7 & 1) == 0;
1029 const char* vm = masked ? ", v0.t" : "";
1030 const char* opcode = nullptr;
1031 const char* rd = nullptr;
1032 const char* rs1 = nullptr;
1033 const char* rs2 = nullptr;
1034
1035 switch (vai) {
1036 case VAIEncodings::kOpIVV: {
1037 static constexpr const char* kOPIVVOpcodes[64] = {
1038 "vadd.vv", nullptr, "vsub.vv", nullptr,
1039 "vminu.vv", "vmin.vv", "vmaxu.vv", "vmax.vv",
1040 nullptr, "vand.vv", "vor.vv", "vxor.vv",
1041 "vrgather.vv", nullptr, "vrgatherei16.vv", nullptr,
1042 "vadc.vvm", "vmadc.vvm", "vsbc.vvm", "vmsbc.vvm",
1043 nullptr, nullptr, nullptr, "<vmerge/vmv>",
1044 "vmseq.vv", "vmsne.vv", "vmsltu.vv", "vmslt.vv",
1045 "vmsleu.vv", "vmsle.vv", nullptr, nullptr,
1046 "vsaddu.vv", "vsadd.vv", "vssubu.vv", "vssub.vv",
1047 nullptr, "vsll.vv", nullptr, "vsmul.vv",
1048 "vsrl.vv", "vsra.vv", "vssrl.vv", "vssra.vv",
1049 "vnsrl.wv", "vnsra.wv", "vnclipu.wv", "vnclip.wv",
1050 "vwredsumu.vs", "vwredsum.vs", nullptr, nullptr,
1051 nullptr, nullptr, nullptr, nullptr,
1052 nullptr, nullptr, nullptr, nullptr,
1053 nullptr, nullptr, nullptr, nullptr,
1054 };
1055
1056 rs2 = VRegName(GetRs2(insn32));
1057 if (funct6 == 0b010111) {
1058 // vmerge/vmv
1059 if (masked) {
1060 opcode = "vmerge.vvm";
1061 vm = ", v0";
1062 } else if (GetRs2(insn32) == 0) {
1063 opcode = "vmv.v.v";
1064 rs2 = nullptr;
1065 } else {
1066 opcode = nullptr;
1067 }
1068 } else {
1069 opcode = kOPIVVOpcodes[funct6];
1070 }
1071
1072 rd = VRegName(GetRd(insn32));
1073 rs1 = VRegName(GetRs1(insn32));
1074 break;
1075 }
1076 case VAIEncodings::kOpIVX: {
1077 static constexpr const char* kOPIVXOpcodes[64] = {
1078 "vadd.vx", nullptr, "vsub.vx", "vrsub.vx",
1079 "vminu.vx", "vmin.vx", "vmaxu.vx", "vmax.vx",
1080 nullptr, "vand.vx", "vor.vx", "vxor.vx",
1081 "vrgather.vx", nullptr, "vslideup.vx", "vslidedown.vx",
1082 "vadc.vxm", "vmadc.vxm", "vsbc.vxm", "vmsbc.vxm",
1083 nullptr, nullptr, nullptr, "<vmerge/vmv>",
1084 "vmseq.vx", "vmsne.vx", "vmsltu.vx", "vmslt.vx",
1085 "vmsleu.vx", "vmsle.vx", "vmsgtu.vx", "vmsgt.vx",
1086 "vsaddu.vx", "vsadd.vx", "vssubu.vx", "vssub.vx",
1087 nullptr, "vsll.vx", nullptr, "vsmul.vx",
1088 "vsrl.vx", "vsra.vx", "vssrl.vx", "vssra.vx",
1089 "vnsrl.wx", "vnsra.wx", "vnclipu.wx", "vnclip.wx",
1090 nullptr, nullptr, nullptr, nullptr,
1091 nullptr, nullptr, nullptr, nullptr,
1092 nullptr, nullptr, nullptr, nullptr,
1093 nullptr, nullptr, nullptr, nullptr,
1094 };
1095
1096 rs2 = VRegName(GetRs2(insn32));
1097 if (funct6 == 0b010111) {
1098 // vmerge/vmv
1099 if (masked) {
1100 opcode = "vmerge.vxm";
1101 vm = ", v0";
1102 } else if (GetRs2(insn32) == 0) {
1103 opcode = "vmv.v.x";
1104 rs2 = nullptr;
1105 } else {
1106 opcode = nullptr;
1107 }
1108 } else {
1109 opcode = kOPIVXOpcodes[funct6];
1110 }
1111
1112 rd = VRegName(GetRd(insn32));
1113 rs1 = XRegName(GetRs1(insn32));
1114 break;
1115 }
1116 case VAIEncodings::kOpIVI: {
1117 static constexpr const char* kOPIVIOpcodes[64] = {
1118 "vadd.vi", nullptr, nullptr, "vrsub.vi",
1119 nullptr, nullptr, nullptr, nullptr,
1120 nullptr, "vand.vi", "vor.vi", "vxor.vi",
1121 "vrgather.vi", nullptr, "vslideup.vi", "vslidedown.vi",
1122 "vadc.vim", "vmadc.vim", nullptr, nullptr,
1123 nullptr, nullptr, nullptr, "<vmerge/vmv>",
1124 "vmseq.vi", "vmsne.vi", nullptr, nullptr,
1125 "vmsleu.vi", "vmsle.vi", "vmsgtu.vi", "vmsgt.vi",
1126 "vsaddu.vi", "vsadd.vi", nullptr, nullptr,
1127 nullptr, "vsll.vi", nullptr, "<vmvNr.v>",
1128 "vsrl.vi", "vsra.vi", "vssrl.vi", "vssra.vi",
1129 "vnsrl.wi", "vnsra.wi", "vnclipu.wi", "vnclip.wi",
1130 nullptr, nullptr, nullptr, nullptr,
1131 nullptr, nullptr, nullptr, nullptr,
1132 nullptr, nullptr, nullptr, nullptr,
1133 nullptr, nullptr, nullptr, nullptr,
1134 };
1135
1136 rs2 = VRegName(GetRs2(insn32));
1137
1138 if (funct6 == 0b010111) {
1139 // vmerge/vmv
1140 if (masked) {
1141 opcode = "vmerge.vim";
1142 vm = ", v0";
1143 } else if (GetRs2(insn32) == 0) {
1144 opcode = "vmv.v.i";
1145 rs2 = nullptr;
1146 } else {
1147 opcode = nullptr;
1148 }
1149 } else if (funct6 == 0b100111) {
1150 uint32_t rs1V = GetRs1(insn32);
1151 static constexpr const char* kVmvnrOpcodes[8] = {
1152 "vmv1r.v", "vmv2r.v", nullptr, "vmv4r.v",
1153 nullptr, nullptr, nullptr, "vmv8r.v",
1154 };
1155 if (IsUint<3>(rs1V)) {
1156 opcode = kVmvnrOpcodes[rs1V];
1157 }
1158 } else {
1159 opcode = kOPIVIOpcodes[funct6];
1160 }
1161
1162 rd = VRegName(GetRd(insn32));
1163 break;
1164 }
1165 case VAIEncodings::kOpMVV: {
1166 switch (funct6) {
1167 case VWXUNARY0: {
1168 static constexpr const char* kVWXUNARY0Opcodes[32] = {
1169 "vmv.x.s", nullptr, nullptr, nullptr,
1170 nullptr, nullptr, nullptr, nullptr,
1171 nullptr, nullptr, nullptr, nullptr,
1172 nullptr, nullptr, nullptr, nullptr,
1173 "vcpop.m", "vfirst.m", nullptr, nullptr,
1174 nullptr, nullptr, nullptr, nullptr,
1175 nullptr, nullptr, nullptr, nullptr,
1176 nullptr, nullptr, nullptr, nullptr,
1177 };
1178 opcode = kVWXUNARY0Opcodes[GetRs1(insn32)];
1179 rd = XRegName(GetRd(insn32));
1180 rs2 = VRegName(GetRs2(insn32));
1181 break;
1182 }
1183 case VXUNARY0: {
1184 static constexpr const char* kVXUNARY0Opcodes[32] = {
1185 nullptr, nullptr, "vzext.vf8", "vsext.vf8",
1186 "vzext.vf4", "vsext.vf4", "vzext.vf2", "vsext.vf2",
1187 nullptr, nullptr, nullptr, nullptr,
1188 nullptr, nullptr, nullptr, nullptr,
1189 nullptr, nullptr, nullptr, nullptr,
1190 nullptr, nullptr, nullptr, nullptr,
1191 nullptr, nullptr, nullptr, nullptr,
1192 nullptr, nullptr, nullptr, nullptr,
1193 };
1194 opcode = kVXUNARY0Opcodes[GetRs1(insn32)];
1195 rd = VRegName(GetRd(insn32));
1196 rs2 = VRegName(GetRs2(insn32));
1197 break;
1198 }
1199 case VMUNARY0: {
1200 static constexpr const char* kVMUNARY0Opcodes[32] = {
1201 nullptr, "vmsbf.m", "vmsof.m", "vmsif.m",
1202 nullptr, nullptr, nullptr, nullptr,
1203 nullptr, nullptr, nullptr, nullptr,
1204 nullptr, nullptr, nullptr, nullptr,
1205 "viota.m", "vid.v", nullptr, nullptr,
1206 nullptr, nullptr, nullptr, nullptr,
1207 nullptr, nullptr, nullptr, nullptr,
1208 nullptr, nullptr, nullptr, nullptr,
1209 };
1210 opcode = kVMUNARY0Opcodes[GetRs1(insn32)];
1211 rd = VRegName(GetRd(insn32));
1212 rs2 = VRegName(GetRs2(insn32));
1213 break;
1214 }
1215 default: {
1216 static constexpr const char* kOPMVVOpcodes[64] = {
1217 "vredsum.vs", "vredand.vs", "vredor.vs", "vredxor.vs",
1218 "vredminu.vs", "vredmin.vs", "vredmaxu.vs", "vredmax.vs",
1219 "vaaddu.vv", "vaadd.vv", "vasubu.vv", "vasub.vv",
1220 nullptr, nullptr, nullptr, nullptr,
1221 "<VWXUNARY0>", nullptr, "<VXUNARY0>", nullptr,
1222 "<VMUNARY0>", nullptr, nullptr, "vcompress.vm",
1223 "vmandn.mm", "vmand.mm", "vmor.mm", "vmxor.mm",
1224 "vmorn.mm", "vmnand.mm", "vmnor.mm", "vmxnor.mm",
1225 "vdivu.vv", "vdiv.vv", "vremu.vv", "vrem.vv",
1226 "vmulhu.vv", "vmul.vv", "vmulhsu.vv", "vmulh.vv",
1227 nullptr, "vmadd.vv", nullptr, "vnmsub.vv",
1228 nullptr, "vmacc.vv", nullptr, "vnmsac.vv",
1229 "vwaddu.vv", "vwadd.vv", "vwsubu.vv", "vwsub.vv",
1230 "vwaddu.wv", "vwadd.wv", "vwsubu.wv", "vwsub.wv",
1231 "vwmulu.vv", nullptr, "vwmulsu.vv", "vwmul.vv",
1232 "vwmaccu.vv", "vwmacc.vv", nullptr, "vwmaccsu.vv",
1233 };
1234
1235 opcode = kOPMVVOpcodes[funct6];
1236 rd = VRegName(GetRd(insn32));
1237 rs1 = VRegName(GetRs1(insn32));
1238 rs2 = VRegName(GetRs2(insn32));
1239
1240 if (0x17u <= funct6 && funct6 <= 0x1Fu) {
1241 if (masked) {
1242 // for vcompress.vm and *.mm encodings with vm=0 are reserved
1243 opcode = nullptr;
1244 }
1245 }
1246
1247 MaybeSwapOperands(funct6, rs1, rs2);
1248
1249 break;
1250 }
1251 }
1252 break;
1253 }
1254 case VAIEncodings::kOpMVX: {
1255 switch (funct6) {
1256 case VRXUNARY0: {
1257 opcode = GetRs2(insn32) == 0u ? "vmv.s.x" : nullptr;
1258 rd = VRegName(GetRd(insn32));
1259 rs1 = XRegName(GetRs1(insn32));
1260 break;
1261 }
1262 default: {
1263 static constexpr const char* kOPMVXOpcodes[64] = {
1264 nullptr, nullptr, nullptr, nullptr,
1265 nullptr, nullptr, nullptr, nullptr,
1266 "vaaddu.vx", "vaadd.vx", "vasubu.vx", "vasub.vx",
1267 nullptr, nullptr, "vslide1up.vx", "vslide1down.vx",
1268 "<VRXUNARY0>", nullptr, nullptr, nullptr,
1269 nullptr, nullptr, nullptr, nullptr,
1270 nullptr, nullptr, nullptr, nullptr,
1271 nullptr, nullptr, nullptr, nullptr,
1272 "vdivu.vx", "vdiv.vx", "vremu.vx", "vrem.vx",
1273 "vmulhu.vx", "vmul.vx", "vmulhsu.vx", "vmulh.vx",
1274 nullptr, "vmadd.vx", nullptr, "vnmsub.vx",
1275 nullptr, "vmacc.vx", nullptr, "vnmsac.vx",
1276 "vwaddu.vx", "vwadd.vx", "vwsubu.vx", "vwsub.vx",
1277 "vwaddu.wv", "vwadd.wv", "vwsubu.wv", "vwsub.wv",
1278 "vwmulu.vx", nullptr, "vwmulsu.vx", "vwmul.vx",
1279 "vwmaccu.vx", "vwmacc.vx", "vwmaccus.vx", "vwmaccsu.vx",
1280 };
1281 opcode = kOPMVXOpcodes[funct6];
1282 rd = VRegName(GetRd(insn32));
1283 rs1 = XRegName(GetRs1(insn32));
1284 rs2 = VRegName(GetRs2(insn32));
1285
1286 MaybeSwapOperands(funct6, rs1, rs2);
1287
1288 break;
1289 }
1290 }
1291 break;
1292 }
1293 case VAIEncodings::kOpFVV: {
1294 switch (funct6) {
1295 case VWFUNARY0: {
1296 opcode = GetRs1(insn32) == 0u ? "vfmv.f.s" : nullptr;
1297 rd = XRegName(GetRd(insn32));
1298 rs2 = VRegName(GetRs2(insn32));
1299 break;
1300 }
1301 case VFUNARY0: {
1302 static constexpr const char* kVFUNARY0Opcodes[32] = {
1303 "vfcvt.xu.f.v", "vfcvt.x.f.v", "vfcvt.f.xu.v", "vfcvt.f.x.v",
1304 nullptr, nullptr, "vfcvt.rtz.xu.f.v", "vfcvt.rtz.x.f.v",
1305 "vfwcvt.xu.f.v", "vfwcvt.x.f.v", "vfwcvt.f.xu.v", "vfwcvt.f.x.v",
1306 "vfwcvt.f.f.v", nullptr, "vfwcvt.rtz.xu.f.v", "vfwcvt.rtz.x.f.v",
1307 "vfncvt.xu.f.w", "vfncvt.x.f.w", "vfncvt.f.xu.w", "vfncvt.f.x.w",
1308 "vfncvt.f.f.w", "vfncvt.rod.f.f.w", "vfncvt.rtz.xu.f.w", "vfncvt.rtz.x.f.w",
1309 nullptr, nullptr, nullptr, nullptr,
1310 nullptr, nullptr, nullptr, nullptr,
1311 };
1312 opcode = kVFUNARY0Opcodes[GetRs1(insn32)];
1313 rd = VRegName(GetRd(insn32));
1314 rs2 = VRegName(GetRs2(insn32));
1315 break;
1316 }
1317 case VFUNARY1: {
1318 static constexpr const char* kVFUNARY1Opcodes[32] = {
1319 "vfsqrt.v", nullptr, nullptr, nullptr,
1320 "vfrsqrt7.v", "vfrec7.v", nullptr, nullptr,
1321 nullptr, nullptr, nullptr, nullptr,
1322 nullptr, nullptr, nullptr, nullptr,
1323 "vfclass.v", nullptr, nullptr, nullptr,
1324 nullptr, nullptr, nullptr, nullptr,
1325 nullptr, nullptr, nullptr, nullptr,
1326 nullptr, nullptr, nullptr, nullptr,
1327 };
1328 opcode = kVFUNARY1Opcodes[GetRs1(insn32)];
1329 rd = VRegName(GetRd(insn32));
1330 rs2 = VRegName(GetRs2(insn32));
1331 break;
1332 }
1333 default: {
1334 static constexpr const char* kOPFVVOpcodes[64] = {
1335 "vfadd.vv", "vfredusum.vs", "vfsub.vv", "vfredosum.vs",
1336 "vfmin.vv", "vfredmin.vs", "vfmax.vv", "vfredmax.vs",
1337 "vfsgnj.vv", "vfsgnjn.vv", "vfsgnjx.vv", nullptr,
1338 nullptr, nullptr, nullptr, nullptr,
1339 "<VWFUNARY0>", nullptr, "<VFUNARY0>", "<VFUNARY1>",
1340 nullptr, nullptr, nullptr, nullptr,
1341 "vmfeq.vv", "vmfle.vv", nullptr, "vmflt.vv",
1342 "vmfne.vv", nullptr, nullptr, nullptr,
1343 "vfdiv.vv", nullptr, nullptr, nullptr,
1344 "vfmul.vv", nullptr, nullptr, nullptr,
1345 "vfmadd.vv", "vfnmadd.vv", "vfmsub.vv", "vfnmsub.vv",
1346 "vfmacc.vv", "vfnmacc.vv", "vfmsac.vv", "vfnmsac.vv",
1347 "vfwadd.vv", "vfwredusum.vs", "vfwsub.vv", "vfwredosum.vs",
1348 "vfwadd.wv", nullptr, "vfwsub.wv", nullptr,
1349 "vfwmul.vv", nullptr, nullptr, nullptr,
1350 "vfwmacc.vv", "vfwnmacc.vv", "vfwmsac.vv", "vfwnmsac.vv",
1351 };
1352 opcode = kOPFVVOpcodes[funct6];
1353 rd = VRegName(GetRd(insn32));
1354 rs1 = VRegName(GetRs1(insn32));
1355 rs2 = VRegName(GetRs2(insn32));
1356
1357 MaybeSwapOperands(funct6, rs1, rs2);
1358
1359 break;
1360 }
1361 }
1362 break;
1363 }
1364 case VAIEncodings::kOpFVF: {
1365 switch (funct6) {
1366 case VRFUNARY0: {
1367 opcode = GetRs2(insn32) == 0u ? "vfmv.s.f" : nullptr;
1368 rd = VRegName(GetRd(insn32));
1369 rs1 = FRegName(GetRs1(insn32));
1370 break;
1371 }
1372 default: {
1373 static constexpr const char* kOPFVFOpcodes[64] = {
1374 "vfadd.vf", nullptr, "vfsub.vf", nullptr,
1375 "vfmin.vf", nullptr, "vfmax.vf", nullptr,
1376 "vfsgnj.vf", "vfsgnjn.vf", "vfsgnjx.vf", nullptr,
1377 nullptr, nullptr, "vfslide1up.vf", "vfslide1down.vf",
1378 "<VRFUNARY0>", nullptr, nullptr, nullptr,
1379 nullptr, nullptr, nullptr, "<vfmerge.vfm/vfmv>",
1380 "vmfeq.vf", "vmfle.vf", nullptr, "vmflt.vf",
1381 "vmfne.vf", "vmfgt.vf", nullptr, "vmfge.vf",
1382 "vfdiv.vf", "vfrdiv.vf", nullptr, nullptr,
1383 "vfmul.vf", nullptr, nullptr, "vfrsub.vf",
1384 "vfmadd.vf", "vfnmadd.vf", "vfmsub.vf", "vfnmsub.vf",
1385 "vfmacc.vf", "vfnmacc.vf", "vfmsac.vf", "vfnmsac.vf",
1386 "vfwadd.vf", nullptr, "vfwsub.vf", nullptr,
1387 "vfwadd.wf", nullptr, "vfwsub.wf", nullptr,
1388 "vfwmul.vf", nullptr, nullptr, nullptr,
1389 "vfwmacc.vf", "vfwnmacc.vf", "vfwmsac.vf", "vfwnmsac.vf",
1390 };
1391
1392 rs2 = VRegName(GetRs2(insn32));
1393 if (funct6 == 0b010111) {
1394 // vfmerge/vfmv
1395 if (masked) {
1396 opcode = "vfmerge.vfm";
1397 vm = ", v0";
1398 } else if (GetRs2(insn32) == 0) {
1399 opcode = "vfmv.v.f";
1400 rs2 = nullptr;
1401 } else {
1402 opcode = nullptr;
1403 }
1404 } else {
1405 opcode = kOPFVFOpcodes[funct6];
1406 }
1407
1408 rd = VRegName(GetRd(insn32));
1409 rs1 = FRegName(GetRs1(insn32));
1410
1411 MaybeSwapOperands(funct6, rs1, rs2);
1412
1413 break;
1414 }
1415 }
1416 break;
1417 }
1418 case VAIEncodings::kOpCFG: { // vector ALU control instructions
1419 if ((insn32 >> 31) != 0u) {
1420 if (((insn32 >> 30) & 0x1U) != 0u) { // vsetivli
1421 const uint32_t zimm = Decode32UImm12(insn32) & ~0xC00U;
1422 const uint32_t imm5 = GetRs1(insn32);
1423 os_ << "vsetivli " << XRegName(GetRd(insn32)) << ", " << StringPrintf("0x%08x", imm5)
1424 << ", ";
1425 AppendVType(zimm);
1426 } else { // vsetvl
1427 os_ << "vsetvl " << XRegName(GetRd(insn32)) << ", " << XRegName(GetRs1(insn32)) << ", "
1428 << XRegName(GetRs2(insn32));
1429 if ((Decode32UImm7(insn32) & 0x40u) != 0u) {
1430 os_ << "\t# incorrect funct7 literal : "
1431 << StringPrintf("0x%08x", Decode32UImm7(insn32));
1432 }
1433 }
1434 } else { // vsetvli
1435 const uint32_t zimm = Decode32UImm12(insn32) & ~0x800U;
1436 os_ << "vsetvli " << XRegName(GetRd(insn32)) << ", " << XRegName(GetRs1(insn32)) << ", ";
1437 AppendVType(zimm);
1438 }
1439 return;
1440 }
1441 }
1442
1443 if (opcode == nullptr) {
1444 os_ << "<unknown32>";
1445 return;
1446 }
1447
1448 os_ << opcode << " " << rd;
1449
1450 if (rs2 != nullptr) {
1451 os_ << ", " << rs2;
1452 }
1453
1454 if (rs1 != nullptr) {
1455 os_ << ", " << rs1;
1456 } else if (vai == VAIEncodings::kOpIVI) {
1457 os_ << StringPrintf(", 0x%08x", GetRs1(insn32));
1458 }
1459
1460 os_ << vm;
1461 }
1462
Print32FpFma(uint32_t insn32)1463 void DisassemblerRiscv64::Printer::Print32FpFma(uint32_t insn32) {
1464 DCHECK_EQ(insn32 & 0x73u, 0x43u); // Note: Bits 0xc select the FMA opcode.
1465 uint32_t funct2 = (insn32 >> 25) & 3u;
1466 if (funct2 >= 2u) {
1467 os_ << "<unknown32>"; // Note: This includes the "H" and "Q" extensions.
1468 return;
1469 }
1470 static const char* const kOpcodes[] = { "fmadd", "fmsub", "fnmsub", "fnmadd" };
1471 os_ << kOpcodes[(insn32 >> 2) & 3u] << ((funct2 != 0u) ? ".d" : ".s")
1472 << RoundingModeName(GetRoundingMode(insn32)) << " "
1473 << FRegName(GetRd(insn32)) << ", " << FRegName(GetRs1(insn32)) << ", "
1474 << FRegName(GetRs2(insn32)) << ", " << FRegName(GetRs3(insn32));
1475 }
1476
Print32Zicsr(uint32_t insn32)1477 void DisassemblerRiscv64::Printer::Print32Zicsr(uint32_t insn32) {
1478 DCHECK_EQ(insn32 & 0x7fu, 0x73u);
1479 uint32_t funct3 = (insn32 >> 12) & 7u;
1480 static const char* const kOpcodes[] = {
1481 nullptr, "csrrw", "csrrs", "csrrc", nullptr, "csrrwi", "csrrsi", "csrrci"
1482 };
1483 const char* opcode = kOpcodes[funct3];
1484 if (opcode == nullptr) {
1485 os_ << "<unknown32>";
1486 return;
1487 }
1488 uint32_t rd = GetRd(insn32);
1489 uint32_t rs1_or_uimm = GetRs1(insn32);
1490 uint32_t csr = insn32 >> 20;
1491 // Print shorter macro instruction notation if available.
1492 if (funct3 == /*CSRRW*/ 1u && rd == 0u && rs1_or_uimm == 0u && csr == 0xc00u) {
1493 os_ << "unimp";
1494 return;
1495 } else if (funct3 == /*CSRRS*/ 2u && rs1_or_uimm == 0u) {
1496 if (csr == 0xc00u) {
1497 os_ << "rdcycle " << XRegName(rd);
1498 } else if (csr == 0xc01u) {
1499 os_ << "rdtime " << XRegName(rd);
1500 } else if (csr == 0xc02u) {
1501 os_ << "rdinstret " << XRegName(rd);
1502 } else {
1503 os_ << "csrr " << XRegName(rd) << ", " << csr;
1504 }
1505 return;
1506 }
1507
1508 if (rd == 0u) {
1509 static const char* const kAltOpcodes[] = {
1510 nullptr, "csrw", "csrs", "csrc", nullptr, "csrwi", "csrsi", "csrci"
1511 };
1512 DCHECK(kAltOpcodes[funct3] != nullptr);
1513 os_ << kAltOpcodes[funct3] << " " << csr << ", ";
1514 } else {
1515 os_ << opcode << " " << XRegName(rd) << ", " << csr << ", ";
1516 }
1517 if (funct3 >= /*CSRRWI/CSRRSI/CSRRCI*/ 4u) {
1518 os_ << rs1_or_uimm;
1519 } else {
1520 os_ << XRegName(rs1_or_uimm);
1521 }
1522 }
1523
Print32Fence(uint32_t insn32)1524 void DisassemblerRiscv64::Printer::Print32Fence(uint32_t insn32) {
1525 DCHECK_EQ(insn32 & 0x7fu, 0x0fu);
1526 if ((insn32 & 0xf00fffffu) == 0x0000000fu) {
1527 auto print_flags = [&](uint32_t flags) {
1528 if (flags == 0u) {
1529 os_ << "0";
1530 } else {
1531 DCHECK_LT(flags, 0x10u);
1532 static const char kFlagNames[] = "wroi";
1533 for (size_t bit : { 3u, 2u, 1u, 0u }) { // Print in the "iorw" order.
1534 if ((flags & (1u << bit)) != 0u) {
1535 os_ << kFlagNames[bit];
1536 }
1537 }
1538 }
1539 };
1540 os_ << "fence.";
1541 print_flags((insn32 >> 24) & 0xfu);
1542 os_ << ".";
1543 print_flags((insn32 >> 20) & 0xfu);
1544 } else if (insn32 == 0x8330000fu) {
1545 os_ << "fence.tso";
1546 } else if (insn32 == 0x0000100fu) {
1547 os_ << "fence.i";
1548 } else {
1549 os_ << "<unknown32>";
1550 }
1551 }
1552
Dump32(const uint8_t * insn)1553 void DisassemblerRiscv64::Printer::Dump32(const uint8_t* insn) {
1554 uint32_t insn32 = static_cast<uint32_t>(insn[0]) +
1555 (static_cast<uint32_t>(insn[1]) << 8) +
1556 (static_cast<uint32_t>(insn[2]) << 16) +
1557 (static_cast<uint32_t>(insn[3]) << 24);
1558 CHECK_EQ(insn32 & 3u, 3u);
1559 os_ << disassembler_->FormatInstructionPointer(insn) << StringPrintf(": %08x\t", insn32);
1560 switch (insn32 & 0x7fu) {
1561 case 0x37u:
1562 Print32Lui(insn32);
1563 break;
1564 case 0x17u:
1565 Print32Auipc(insn, insn32);
1566 break;
1567 case 0x6fu:
1568 Print32Jal(insn, insn32);
1569 break;
1570 case 0x67u:
1571 switch ((insn32 >> 12) & 7u) { // funct3
1572 case 0:
1573 Print32Jalr(insn, insn32);
1574 break;
1575 default:
1576 os_ << "<unknown32>";
1577 break;
1578 }
1579 break;
1580 case 0x63u:
1581 Print32BCond(insn, insn32);
1582 break;
1583 case 0x03u:
1584 Print32Load(insn32);
1585 break;
1586 case 0x23u:
1587 Print32Store(insn32);
1588 break;
1589 case 0x07u:
1590 Print32FLoad(insn32);
1591 break;
1592 case 0x27u:
1593 Print32FStore(insn32);
1594 break;
1595 case 0x13u:
1596 case 0x1bu:
1597 Print32BinOpImm(insn32);
1598 break;
1599 case 0x33u:
1600 case 0x3bu:
1601 Print32BinOp(insn32);
1602 break;
1603 case 0x2fu:
1604 Print32Atomic(insn32);
1605 break;
1606 case 0x53u:
1607 Print32FpOp(insn32);
1608 break;
1609 case 0x57u:
1610 Print32RVVOp(insn32);
1611 break;
1612 case 0x43u:
1613 case 0x47u:
1614 case 0x4bu:
1615 case 0x4fu:
1616 Print32FpFma(insn32);
1617 break;
1618 case 0x73u:
1619 if ((insn32 & 0xffefffffu) == 0x00000073u) {
1620 os_ << ((insn32 == 0x00000073u) ? "ecall" : "ebreak");
1621 } else {
1622 Print32Zicsr(insn32);
1623 }
1624 break;
1625 case 0x0fu:
1626 Print32Fence(insn32);
1627 break;
1628 default:
1629 // TODO(riscv64): Disassemble more instructions.
1630 os_ << "<unknown32>";
1631 break;
1632 }
1633 os_ << "\n";
1634 }
1635
Dump16(const uint8_t * insn)1636 void DisassemblerRiscv64::Printer::Dump16(const uint8_t* insn) {
1637 uint32_t insn16 = static_cast<uint32_t>(insn[0]) + (static_cast<uint32_t>(insn[1]) << 8);
1638 ScopedNewLinePrinter nl(os_);
1639 CHECK_NE(insn16 & 3u, 3u);
1640 os_ << disassembler_->FormatInstructionPointer(insn) << StringPrintf(": %04x \t", insn16);
1641
1642 uint32_t funct3 = BitFieldExtract(insn16, 13, 3);
1643 int32_t offset = -1;
1644
1645 switch (insn16 & 3u) {
1646 case 0b00u: // Quadrant 0
1647 switch (funct3) {
1648 case 0b000u:
1649 if (insn16 == 0u) {
1650 os_ << "c.unimp";
1651 } else {
1652 uint32_t nzuimm = BitFieldExtract(insn16, 5, 8);
1653 if (nzuimm != 0u) {
1654 uint32_t decoded =
1655 BitFieldExtract(nzuimm, 0, 1) << 3 | BitFieldExtract(nzuimm, 1, 1) << 2 |
1656 BitFieldExtract(nzuimm, 2, 4) << 6 | BitFieldExtract(nzuimm, 6, 2) << 4;
1657 os_ << "c.addi4spn " << XRegName(GetRs2Short16(insn16)) << ", sp, " << decoded;
1658 } else {
1659 os_ << "<unknown16>";
1660 }
1661 }
1662 return;
1663 case 0b001u:
1664 offset = Decode16CMOffsetD(insn16);
1665 os_ << "c.fld " << FRegName(GetRs2Short16(insn16));
1666 break;
1667 case 0b010u:
1668 offset = Decode16CMOffsetW(insn16);
1669 os_ << "c.lw " << XRegName(GetRs2Short16(insn16));
1670 break;
1671 case 0b011u:
1672 offset = Decode16CMOffsetD(insn16);
1673 os_ << "c.ld " << XRegName(GetRs2Short16(insn16));
1674 break;
1675 case 0b100u: {
1676 uint32_t opcode2 = BitFieldExtract(insn16, 10, 3);
1677 uint32_t imm = BitFieldExtract(insn16, 5, 2);
1678 switch (opcode2) {
1679 case 0b000:
1680 offset = Uimm2ToOffset10(imm);
1681 os_ << "c.lbu " << XRegName(GetRs2Short16(insn16));
1682 break;
1683 case 0b001:
1684 offset = Uimm2ToOffset1(imm);
1685 os_ << (BitFieldExtract(imm, 1, 1) == 0u ? "c.lhu " : "c.lh ");
1686 os_ << XRegName(GetRs2Short16(insn16));
1687 break;
1688 case 0b010:
1689 offset = Uimm2ToOffset10(imm);
1690 os_ << "c.sb " << XRegName(GetRs2Short16(insn16));
1691 break;
1692 case 0b011:
1693 if (BitFieldExtract(imm, 1, 1) == 0u) {
1694 offset = Uimm2ToOffset1(imm);
1695 os_ << "c.sh " << XRegName(GetRs2Short16(insn16));
1696 break;
1697 }
1698 FALLTHROUGH_INTENDED;
1699 default:
1700 os_ << "<unknown16>";
1701 return;
1702 }
1703 break;
1704 }
1705 case 0b101u:
1706 offset = Decode16CMOffsetD(insn16);
1707 os_ << "c.fsd " << FRegName(GetRs2Short16(insn16));
1708 break;
1709 case 0b110u:
1710 offset = Decode16CMOffsetW(insn16);
1711 os_ << "c.sw " << XRegName(GetRs2Short16(insn16));
1712 break;
1713 case 0b111u:
1714 offset = Decode16CMOffsetD(insn16);
1715 os_ << "c.sd " << XRegName(GetRs2Short16(insn16));
1716 break;
1717 default:
1718 LOG(FATAL) << "Unreachable";
1719 UNREACHABLE();
1720 }
1721 os_ << ", ";
1722 PrintLoadStoreAddress(GetRs1Short16(insn16), offset);
1723 return;
1724 case 0b01u: // Quadrant 1
1725 switch (funct3) {
1726 case 0b000u: {
1727 uint32_t rd = GetRs1_16(insn16);
1728 if (rd == 0) {
1729 if (Decode16Imm6<uint32_t>(insn16) != 0u) {
1730 os_ << "<hint16>";
1731 } else {
1732 os_ << "c.nop";
1733 }
1734 } else {
1735 int32_t imm = Decode16Imm6<int32_t>(insn16);
1736 if (imm != 0) {
1737 os_ << "c.addi " << XRegName(rd) << ", " << imm;
1738 } else {
1739 os_ << "<hint16>";
1740 }
1741 }
1742 break;
1743 }
1744 case 0b001u: {
1745 uint32_t rd = GetRs1_16(insn16);
1746 if (rd != 0) {
1747 os_ << "c.addiw " << XRegName(rd) << ", " << Decode16Imm6<int32_t>(insn16);
1748 } else {
1749 os_ << "<unknown16>";
1750 }
1751 break;
1752 }
1753 case 0b010u: {
1754 uint32_t rd = GetRs1_16(insn16);
1755 if (rd != 0) {
1756 os_ << "c.li " << XRegName(rd) << ", " << Decode16Imm6<int32_t>(insn16);
1757 } else {
1758 os_ << "<hint16>";
1759 }
1760 break;
1761 }
1762 case 0b011u: {
1763 uint32_t rd = GetRs1_16(insn16);
1764 uint32_t imm6_bits = Decode16Imm6<uint32_t>(insn16);
1765 if (imm6_bits != 0u) {
1766 if (rd == 2) {
1767 int32_t nzimm =
1768 BitFieldExtract(insn16, 6, 1) << 4 | BitFieldExtract(insn16, 2, 1) << 5 |
1769 BitFieldExtract(insn16, 5, 1) << 6 | BitFieldExtract(insn16, 3, 2) << 7 |
1770 BitFieldExtract(insn16, 12, 1) << 9;
1771 os_ << "c.addi16sp sp, " << SignExtendBits<10>(nzimm);
1772 } else if (rd != 0) {
1773 // sign-extend bits and mask with 0xfffff as llvm-objdump does
1774 uint32_t mask = MaskLeastSignificant<uint32_t>(20);
1775 os_ << "c.lui " << XRegName(rd) << ", " << (SignExtendBits<6>(imm6_bits) & mask);
1776 } else {
1777 os_ << "<hint16>";
1778 }
1779 } else {
1780 os_ << "<unknown16>";
1781 }
1782 break;
1783 }
1784 case 0b100u: {
1785 uint32_t funct2 = BitFieldExtract(insn16, 10, 2);
1786 switch (funct2) {
1787 case 0b00: {
1788 int32_t nzuimm = Decode16Imm6<uint32_t>(insn16);
1789 if (nzuimm != 0) {
1790 os_ << "c.srli " << XRegName(GetRs1Short16(insn16)) << ", " << nzuimm;
1791 } else {
1792 os_ << "<hint16>";
1793 }
1794 break;
1795 }
1796 case 0b01: {
1797 int32_t nzuimm = Decode16Imm6<uint32_t>(insn16);
1798 if (nzuimm != 0) {
1799 os_ << "c.srai " << XRegName(GetRs1Short16(insn16)) << ", " << nzuimm;
1800 } else {
1801 os_ << "<hint16>";
1802 }
1803 break;
1804 }
1805 case 0b10:
1806 os_ << "c.andi " << XRegName(GetRs1Short16(insn16)) << ", "
1807 << Decode16Imm6<int32_t>(insn16);
1808 break;
1809 case 0b11: {
1810 constexpr static const char* mnemonics[] = {
1811 "c.sub", "c.xor", "c.or", "c.and", "c.subw", "c.addw", "c.mul", nullptr
1812 };
1813 uint32_t opc = BitFieldInsert(
1814 BitFieldExtract(insn16, 5, 2), BitFieldExtract(insn16, 12, 1), 2, 1);
1815 DCHECK(IsUint<3>(opc));
1816 const char* mnem = mnemonics[opc];
1817 if (mnem != nullptr) {
1818 os_ << mnem << " " << XRegName(GetRs1Short16(insn16)) << ", "
1819 << XRegName(GetRs2Short16(insn16));
1820 } else {
1821 constexpr static const char* zbc_mnemonics[] = {
1822 "c.zext.b", "c.sext.b", "c.zext.h", "c.sext.h",
1823 "c.zext.w", "c.not", nullptr, nullptr,
1824 };
1825 mnem = zbc_mnemonics[BitFieldExtract(insn16, 2, 3)];
1826 if (mnem != nullptr) {
1827 os_ << mnem << " " << XRegName(GetRs1Short16(insn16));
1828 } else {
1829 os_ << "<unknown16>";
1830 }
1831 }
1832 break;
1833 }
1834 default:
1835 LOG(FATAL) << "Unreachable";
1836 UNREACHABLE();
1837 }
1838 break;
1839 }
1840 case 0b101u: {
1841 int32_t disp = BitFieldExtract(insn16, 3, 3) << 1 | BitFieldExtract(insn16, 11, 1) << 4 |
1842 BitFieldExtract(insn16, 2, 1) << 5 | BitFieldExtract(insn16, 7, 1) << 6 |
1843 BitFieldExtract(insn16, 6, 1) << 7 | BitFieldExtract(insn16, 9, 2) << 8 |
1844 BitFieldExtract(insn16, 8, 1) << 10 | BitFieldExtract(insn16, 12, 1) << 11;
1845 os_ << "c.j ";
1846 PrintBranchOffset(SignExtendBits<12>(disp));
1847 break;
1848 }
1849 case 0b110u:
1850 case 0b111u: {
1851 int32_t disp = BitFieldExtract(insn16, 3, 2) << 1 | BitFieldExtract(insn16, 10, 2) << 3 |
1852 BitFieldExtract(insn16, 2, 1) << 5 | BitFieldExtract(insn16, 5, 2) << 6 |
1853 BitFieldExtract(insn16, 12, 1) << 8;
1854
1855 os_ << (funct3 == 0b110u ? "c.beqz " : "c.bnez ");
1856 os_ << XRegName(GetRs1Short16(insn16)) << ", ";
1857 PrintBranchOffset(SignExtendBits<9>(disp));
1858 break;
1859 }
1860 default:
1861 LOG(FATAL) << "Unreachable";
1862 UNREACHABLE();
1863 }
1864 break;
1865 case 0b10u: // Quadrant 2
1866 switch (funct3) {
1867 case 0b000u: {
1868 uint32_t nzuimm = Decode16Imm6<uint32_t>(insn16);
1869 uint32_t rd = GetRs1_16(insn16);
1870 if (rd == 0 || nzuimm == 0) {
1871 os_ << "<hint16>";
1872 } else {
1873 os_ << "c.slli " << XRegName(rd) << ", " << nzuimm;
1874 }
1875 return;
1876 }
1877 case 0b001u: {
1878 offset = Uimm6ToOffsetD16(Decode16Imm6<uint32_t>(insn16));
1879 os_ << "c.fldsp " << FRegName(GetRs1_16(insn16));
1880 break;
1881 }
1882 case 0b010u: {
1883 uint32_t rd = GetRs1_16(insn16);
1884 if (rd != 0) {
1885 offset = Uimm6ToOffsetW16(Decode16Imm6<uint32_t>(insn16));
1886 os_ << "c.lwsp " << XRegName(GetRs1_16(insn16));
1887 } else {
1888 os_ << "<unknown16>";
1889 return;
1890 }
1891 break;
1892 }
1893 case 0b011u: {
1894 uint32_t rd = GetRs1_16(insn16);
1895 if (rd != 0) {
1896 offset = Uimm6ToOffsetD16(Decode16Imm6<uint32_t>(insn16));
1897 os_ << "c.ldsp " << XRegName(GetRs1_16(insn16));
1898 } else {
1899 os_ << "<unknown16>";
1900 return;
1901 }
1902 break;
1903 }
1904 case 0b100u: {
1905 uint32_t rd_rs1 = GetRs1_16(insn16);
1906 uint32_t rs2 = GetRs2_16(insn16);
1907 uint32_t b12 = BitFieldExtract(insn16, 12, 1);
1908 if (b12 == 0) {
1909 if (rd_rs1 != 0 && rs2 != 0) {
1910 os_ << "c.mv " << XRegName(rd_rs1) << ", " << XRegName(rs2);
1911 } else if (rd_rs1 != 0) {
1912 os_ << "c.jr " << XRegName(rd_rs1);
1913 } else if (rs2 != 0) {
1914 os_ << "<hint16>";
1915 } else {
1916 os_ << "<unknown16>";
1917 }
1918 } else {
1919 if (rd_rs1 != 0 && rs2 != 0) {
1920 os_ << "c.add " << XRegName(rd_rs1) << ", " << XRegName(rs2);
1921 } else if (rd_rs1 != 0) {
1922 os_ << "c.jalr " << XRegName(rd_rs1);
1923 } else if (rs2 != 0) {
1924 os_ << "<hint16>";
1925 } else {
1926 os_ << "c.ebreak";
1927 }
1928 }
1929 return;
1930 }
1931 case 0b101u:
1932 offset = BitFieldExtract(insn16, 7, 3) << 6 | BitFieldExtract(insn16, 10, 3) << 3;
1933 os_ << "c.fsdsp " << FRegName(GetRs2_16(insn16));
1934 break;
1935 case 0b110u:
1936 offset = BitFieldExtract(insn16, 7, 2) << 6 | BitFieldExtract(insn16, 9, 4) << 2;
1937 os_ << "c.swsp " << XRegName(GetRs2_16(insn16));
1938 break;
1939 case 0b111u:
1940 offset = BitFieldExtract(insn16, 7, 3) << 6 | BitFieldExtract(insn16, 10, 3) << 3;
1941 os_ << "c.sdsp " << XRegName(GetRs2_16(insn16));
1942 break;
1943 default:
1944 LOG(FATAL) << "Unreachable";
1945 UNREACHABLE();
1946 }
1947
1948 os_ << ", ";
1949 PrintLoadStoreAddress(/* sp */ 2, offset);
1950
1951 break;
1952 default:
1953 LOG(FATAL) << "Unreachable";
1954 UNREACHABLE();
1955 }
1956 }
1957
Dump2Byte(const uint8_t * data)1958 void DisassemblerRiscv64::Printer::Dump2Byte(const uint8_t* data) {
1959 uint32_t value = data[0] + (data[1] << 8);
1960 os_ << disassembler_->FormatInstructionPointer(data)
1961 << StringPrintf(": %04x \t.2byte %u\n", value, value);
1962 }
1963
DumpByte(const uint8_t * data)1964 void DisassemblerRiscv64::Printer::DumpByte(const uint8_t* data) {
1965 uint32_t value = *data;
1966 os_ << disassembler_->FormatInstructionPointer(data)
1967 << StringPrintf(": %02x \t.byte %u\n", value, value);
1968 }
1969
Dump(std::ostream & os,const uint8_t * begin)1970 size_t DisassemblerRiscv64::Dump(std::ostream& os, const uint8_t* begin) {
1971 if (begin < GetDisassemblerOptions()->base_address_ ||
1972 begin >= GetDisassemblerOptions()->end_address_) {
1973 return 0u; // Outside the range.
1974 }
1975 Printer printer(this, os);
1976 if (!IsAligned<2u>(begin) || GetDisassemblerOptions()->end_address_ - begin == 1) {
1977 printer.DumpByte(begin);
1978 return 1u;
1979 }
1980 if ((*begin & 3u) == 3u) {
1981 if (GetDisassemblerOptions()->end_address_ - begin >= 4) {
1982 printer.Dump32(begin);
1983 return 4u;
1984 } else {
1985 printer.Dump2Byte(begin);
1986 return 2u;
1987 }
1988 } else {
1989 printer.Dump16(begin);
1990 return 2u;
1991 }
1992 }
1993
Dump(std::ostream & os,const uint8_t * begin,const uint8_t * end)1994 void DisassemblerRiscv64::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) {
1995 Printer printer(this, os);
1996 const uint8_t* cur = begin;
1997 if (cur < end && !IsAligned<2u>(cur)) {
1998 // Unaligned, dump as a `.byte` to get to an aligned address.
1999 printer.DumpByte(cur);
2000 cur += 1;
2001 }
2002 if (cur >= end) {
2003 return;
2004 }
2005 while (end - cur >= 4) {
2006 if ((*cur & 3u) == 3u) {
2007 printer.Dump32(cur);
2008 cur += 4;
2009 } else {
2010 printer.Dump16(cur);
2011 cur += 2;
2012 }
2013 }
2014 if (end - cur >= 2) {
2015 if ((*cur & 3u) == 3u) {
2016 // Not enough data for a 32-bit instruction. Dump as `.2byte`.
2017 printer.Dump2Byte(cur);
2018 } else {
2019 printer.Dump16(cur);
2020 }
2021 cur += 2;
2022 }
2023 if (end != cur) {
2024 CHECK_EQ(end - cur, 1);
2025 printer.DumpByte(cur);
2026 }
2027 }
2028
2029 } // namespace riscv64
2030 } // namespace art
2031