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 funct6 = (insn32 >> 26) & 0x3Fu;
712 uint32_t rd = GetRd(insn32);
713 uint32_t rs1 = GetRs1(insn32);
714 int32_t imm = Decode32Imm12(insn32);
715
716 // Print shorter macro instruction notation if available.
717 if (funct3 == /*ADDI*/ 0u && imm == 0u) {
718 if (narrow) {
719 os_ << "sextw " << XRegName(rd) << ", " << XRegName(rs1);
720 } else if (rd == Zero && rs1 == Zero) {
721 os_ << "nop"; // Only canonical nop. Non-Zero `rd == rs1` nops are printed as "mv".
722 } else {
723 os_ << "mv " << XRegName(rd) << ", " << XRegName(rs1);
724 }
725 } else if (!narrow && funct3 == /*XORI*/ 4u && imm == -1) {
726 os_ << "not " << XRegName(rd) << ", " << XRegName(rs1);
727 } else if (!narrow && funct3 == /*ANDI*/ 7u && imm == 0xff) {
728 os_ << "zextb " << XRegName(rd) << ", " << XRegName(rs1);
729 } else if (!narrow && funct3 == /*SLTIU*/ 3u && imm == 1) {
730 os_ << "seqz " << XRegName(rd) << ", " << XRegName(rs1);
731 } else if ((insn32 & 0xfc00707fu) == 0x0800101bu) {
732 os_ << "slli.uw " << XRegName(rd) << ", " << XRegName(rs1) << ", " << (imm & 0x3fu);
733 } else if ((imm ^ 0x600u) < 3u && funct3 == 1u) {
734 static const char* const kBitOpcodes[] = { "clz", "ctz", "cpop" };
735 os_ << kBitOpcodes[imm ^ 0x600u] << (narrow ? "w " : " ")
736 << XRegName(rd) << ", " << XRegName(rs1);
737 } else if ((imm ^ 0x600u) < (narrow ? 32 : 64) && funct3 == 5u) {
738 os_ << "rori" << (narrow ? "w " : " ")
739 << XRegName(rd) << ", " << XRegName(rs1) << ", " << (imm ^ 0x600u);
740 } else if (imm == 0x287u && !narrow && funct3 == 5u) {
741 os_ << "orc.b " << XRegName(rd) << ", " << XRegName(rs1);
742 } else if (imm == 0x6b8u && !narrow && funct3 == 5u) {
743 os_ << "rev8 " << XRegName(rd) << ", " << XRegName(rs1);
744 } else if (!narrow && funct6 == 0x12u && (funct3 == /*BCLRI*/ 1u || funct3 == /*BEXTI*/ 5u)) {
745 os_ << ((funct3 == /*BCLRI*/ 1u) ? "bclri" : "bexti");
746 } else if (!narrow && funct6 == 0x1Au && funct3 == 0x1u /*BINVI*/) {
747 os_ << "binvi";
748 } else if (!narrow && funct6 == 0xAu && funct3 == 0x1u /*BSETI*/) {
749 os_ << "bseti";
750 } else {
751 bool bad_high_bits = false;
752 if (funct3 == /*SLLI*/ 1u || funct3 == /*SRLI/SRAI*/ 5u) {
753 imm &= (narrow ? 0x1fu : 0x3fu);
754 uint32_t high_bits = insn32 & (narrow ? 0xfe000000u : 0xfc000000u);
755 if (high_bits == 0x40000000u && funct3 == /*SRAI*/ 5u) {
756 os_ << "srai";
757 } else {
758 os_ << ((funct3 == /*SRLI*/ 5u) ? "srli" : "slli");
759 bad_high_bits = (high_bits != 0u);
760 }
761 } else if (!narrow || funct3 == /*ADDI*/ 0u) {
762 static const char* const kOpcodes[] = {
763 "addi", nullptr, "slti", "sltiu", "xori", nullptr, "ori", "andi"
764 };
765 DCHECK(kOpcodes[funct3] != nullptr);
766 os_ << kOpcodes[funct3];
767 } else {
768 os_ << "<unknown32>"; // There is no SLTIW/SLTIUW/XORIW/ORIW/ANDIW.
769 return;
770 }
771 os_ << (narrow ? "w " : " ") << XRegName(rd) << ", " << XRegName(rs1) << ", " << imm;
772 if (bad_high_bits) {
773 os_ << " (invalid high bits)";
774 }
775 }
776 }
777
Print32BinOp(uint32_t insn32)778 void DisassemblerRiscv64::Printer::Print32BinOp(uint32_t insn32) {
779 DCHECK_EQ(insn32 & 0x77u, 0x33u); // Note: Bit 0x8 selects narrow binop.
780 bool narrow = (insn32 & 0x8u) != 0u;
781 uint32_t funct3 = (insn32 >> 12) & 7u;
782 uint32_t funct7 = (insn32 >> 25) & 0x7Fu;
783 uint32_t rd = GetRd(insn32);
784 uint32_t rs1 = GetRs1(insn32);
785 uint32_t rs2 = GetRs2(insn32);
786
787 // Print shorter macro instruction notation if available.
788 if (funct7 == 0x20u && funct3 == /*SUB*/ 0u && rs1 == Zero) {
789 os_ << (narrow ? "negw " : "neg ") << XRegName(rd) << ", " << XRegName(rs2);
790 } else if (!narrow && funct3 == /*SLT*/ 2u && rs2 == Zero) {
791 os_ << "sltz " << XRegName(rd) << ", " << XRegName(rs1);
792 } else if (!narrow && funct3 == /*SLT*/ 2u && rs1 == Zero) {
793 os_ << "sgtz " << XRegName(rd) << ", " << XRegName(rs2);
794 } else if (!narrow && funct3 == /*SLTU*/ 3u && rs1 == Zero) {
795 os_ << "snez " << XRegName(rd) << ", " << XRegName(rs2);
796 } else if (narrow && funct7 == 4u && funct3 == /*ADD.UW*/ 0u && rs2 == Zero) {
797 os_ << "zext.w " << XRegName(rd) << ", " << XRegName(rs1);
798 } else if (!narrow && funct7 == 0x24u &&
799 (funct3 == /*BCLR*/ 1u || funct3 == /*BEXT*/ 5u)) {
800 os_ << ((funct3 == /*BCLR*/ 1u) ? "bclr" : "bext");
801 } else if (!narrow && funct7 == 0x34u && funct3 == /*BINV*/ 1u) {
802 os_ << "binv";
803 } else if (!narrow && funct7 == 0x14u && funct3 == /*BSET*/ 1u) {
804 os_ << "bset";
805 } else {
806 bool bad_high_bits = false;
807 if (funct7 == 0x20u && (funct3 == /*SUB*/ 0u || funct3 == /*SRA*/ 5u)) {
808 os_ << ((funct3 == /*SUB*/ 0u) ? "sub" : "sra");
809 } else if (funct7 == 1u &&
810 (!narrow || (funct3 == /*MUL*/ 0u || funct3 >= /*DIV/DIVU/REM/REMU*/ 4u))) {
811 static const char* const kOpcodes[] = {
812 "mul", "mulh", "mulhsu", "mulhu", "div", "divu", "rem", "remu"
813 };
814 os_ << kOpcodes[funct3];
815 } else if (funct7 == 4u && narrow && funct3 == /*ADD.UW*/ 0u) {
816 os_ << "add.u"; // "w" is added below.
817 } else if (funct7 == 0x10u && (funct3 & 1u) == 0u && funct3 != 0u) {
818 static const char* const kZbaOpcodes[] = { nullptr, "sh1add", "sh2add", "sh3add" };
819 DCHECK(kZbaOpcodes[funct3 >> 1] != nullptr);
820 os_ << kZbaOpcodes[funct3 >> 1] << (narrow ? ".u" /* "w" is added below. */ : "");
821 } else if (funct7 == 0x20u && !narrow && funct3 >= 4u && funct3 != 5u) {
822 static const char* const kZbbNegOpcodes[] = { "xnor", nullptr, "orn", "andn" };
823 DCHECK(kZbbNegOpcodes[funct3 - 4u] != nullptr);
824 os_ << kZbbNegOpcodes[funct3 - 4u];
825 } else if (funct7 == 0x5u && !narrow && funct3 >= 4u) {
826 static const char* const kZbbMinMaxOpcodes[] = { "min", "minu", "max", "maxu" };
827 DCHECK(kZbbMinMaxOpcodes[funct3 - 4u] != nullptr);
828 os_ << kZbbMinMaxOpcodes[funct3 - 4u];
829 } else if (funct7 == 0x30u && (funct3 == /*ROL*/ 1u || funct3 == /*ROL*/ 5u)) {
830 os_ << (funct3 == /*ROL*/ 1u ? "rol" : "ror");
831 } else if (!narrow || (funct3 == /*ADD*/ 0u || funct3 == /*SLL*/ 1u || funct3 == /*SRL*/ 5u)) {
832 static const char* const kOpcodes[] = {
833 "add", "sll", "slt", "sltu", "xor", "srl", "or", "and"
834 };
835 os_ << kOpcodes[funct3];
836 bad_high_bits = funct7 != 0u;
837 } else {
838 DCHECK(narrow);
839 os_ << "<unknown32>"; // Some of the above instructions do not have a narrow version.
840 return;
841 }
842 os_ << (narrow ? "w " : " ") << XRegName(rd) << ", " << XRegName(rs1) << ", " << XRegName(rs2);
843 if (bad_high_bits) {
844 os_ << " (invalid high bits)";
845 }
846 }
847 }
848
Print32Atomic(uint32_t insn32)849 void DisassemblerRiscv64::Printer::Print32Atomic(uint32_t insn32) {
850 DCHECK_EQ(insn32 & 0x7fu, 0x2fu);
851 uint32_t funct3 = (insn32 >> 12) & 7u;
852 uint32_t funct5 = (insn32 >> 27);
853 if ((funct3 != 2u && funct3 != 3u) || // There are only 32-bit and 64-bit LR/SC/AMO*.
854 (((funct5 & 3u) != 0u) && funct5 >= 4u)) { // Only multiples of 4, or 1-3.
855 os_ << "<unknown32>";
856 return;
857 }
858 static const char* const kMul4Opcodes[] = {
859 "amoadd", "amoxor", "amoor", "amoand", "amomin", "amomax", "amominu", "amomaxu"
860 };
861 static const char* const kOtherOpcodes[] = {
862 nullptr, "amoswap", "lr", "sc"
863 };
864 const char* opcode = ((funct5 & 3u) == 0u) ? kMul4Opcodes[funct5 >> 2] : kOtherOpcodes[funct5];
865 DCHECK(opcode != nullptr);
866 uint32_t rd = GetRd(insn32);
867 uint32_t rs1 = GetRs1(insn32);
868 uint32_t rs2 = GetRs2(insn32);
869 const char* type = (funct3 == 2u) ? ".w" : ".d";
870 const char* aq = (((insn32 >> 26) & 1u) != 0u) ? ".aq" : "";
871 const char* rl = (((insn32 >> 25) & 1u) != 0u) ? ".rl" : "";
872 os_ << opcode << type << aq << rl << " " << XRegName(rd) << ", " << XRegName(rs1);
873 if (funct5 == /*LR*/ 2u) {
874 if (rs2 != 0u) {
875 os_ << " (bad rs2)";
876 }
877 } else {
878 os_ << ", " << XRegName(rs2);
879 }
880 }
881
Print32FpOp(uint32_t insn32)882 void DisassemblerRiscv64::Printer::Print32FpOp(uint32_t insn32) {
883 DCHECK_EQ(insn32 & 0x7fu, 0x53u);
884 uint32_t rd = GetRd(insn32);
885 uint32_t rs1 = GetRs1(insn32);
886 uint32_t rs2 = GetRs2(insn32); // Sometimes used to to differentiate opcodes.
887 uint32_t rm = GetRoundingMode(insn32); // Sometimes used to to differentiate opcodes.
888 uint32_t funct7 = insn32 >> 25;
889 const char* type = ((funct7 & 1u) != 0u) ? ".d" : ".s";
890 if ((funct7 & 2u) != 0u) {
891 os_ << "<unknown32>"; // Note: This includes the "H" and "Q" extensions.
892 return;
893 }
894 switch (funct7 >> 2) {
895 case 0u:
896 case 1u:
897 case 2u:
898 case 3u: {
899 static const char* const kOpcodes[] = { "fadd", "fsub", "fmul", "fdiv" };
900 os_ << kOpcodes[funct7 >> 2] << type << RoundingModeName(rm) << " "
901 << FRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
902 return;
903 }
904 case 4u: { // FSGN*
905 // Print shorter macro instruction notation if available.
906 static const char* const kOpcodes[] = { "fsgnj", "fsgnjn", "fsgnjx" };
907 if (rm < std::size(kOpcodes)) {
908 if (rs1 == rs2) {
909 static const char* const kAltOpcodes[] = { "fmv", "fneg", "fabs" };
910 static_assert(std::size(kOpcodes) == std::size(kAltOpcodes));
911 os_ << kAltOpcodes[rm] << type << " " << FRegName(rd) << ", " << FRegName(rs1);
912 } else {
913 os_ << kOpcodes[rm] << type << " "
914 << FRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
915 }
916 return;
917 }
918 break;
919 }
920 case 5u: { // FMIN/FMAX
921 static const char* const kOpcodes[] = { "fmin", "fmax" };
922 if (rm < std::size(kOpcodes)) {
923 os_ << kOpcodes[rm] << type << " "
924 << FRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
925 return;
926 }
927 break;
928 }
929 case 0x8u: // FCVT between FP numbers.
930 if ((rs2 ^ 1u) == (funct7 & 1u)) {
931 os_ << ((rs2 != 0u) ? "fcvt.s.d" : "fcvt.d.s") << RoundingModeName(rm) << " "
932 << FRegName(rd) << ", " << FRegName(rs1);
933 }
934 break;
935 case 0xbu:
936 if (rs2 == 0u) {
937 os_ << "fsqrt" << type << RoundingModeName(rm) << " "
938 << FRegName(rd) << ", " << FRegName(rs1);
939 return;
940 }
941 break;
942 case 0x14u: { // FLE/FLT/FEQ
943 static const char* const kOpcodes[] = { "fle", "flt", "feq" };
944 if (rm < std::size(kOpcodes)) {
945 os_ << kOpcodes[rm] << type << " "
946 << XRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
947 return;
948 }
949 break;
950 }
951 case 0x18u: { // FCVT from floating point numbers to integers
952 static const char* const kIntTypes[] = { "w", "wu", "l", "lu" };
953 if (rs2 < std::size(kIntTypes)) {
954 os_ << "fcvt." << kIntTypes[rs2] << type << RoundingModeName(rm) << " "
955 << XRegName(rd) << ", " << FRegName(rs1);
956 return;
957 }
958 break;
959 }
960 case 0x1au: { // FCVT from integers to floating point numbers
961 static const char* const kIntTypes[] = { "w", "wu", "l", "lu" };
962 if (rs2 < std::size(kIntTypes)) {
963 os_ << "fcvt" << type << "." << kIntTypes[rs2] << RoundingModeName(rm) << " "
964 << FRegName(rd) << ", " << XRegName(rs1);
965 return;
966 }
967 break;
968 }
969 case 0x1cu: // FMV from FPR to GPR, or FCLASS
970 if (rs2 == 0u && rm == 0u) {
971 os_ << (((funct7 & 1u) != 0u) ? "fmv.x.d " : "fmv.x.w ")
972 << XRegName(rd) << ", " << FRegName(rs1);
973 return;
974 } else if (rs2 == 0u && rm == 1u) {
975 os_ << "fclass" << type << " " << XRegName(rd) << ", " << FRegName(rs1);
976 return;
977 }
978 break;
979 case 0x1eu: // FMV from GPR to FPR
980 if (rs2 == 0u && rm == 0u) {
981 os_ << (((funct7 & 1u) != 0u) ? "fmv.d.x " : "fmv.w.x ")
982 << FRegName(rd) << ", " << XRegName(rs1);
983 return;
984 }
985 break;
986 default:
987 break;
988 }
989 os_ << "<unknown32>";
990 }
991
AppendVType(uint32_t vtype)992 void DisassemblerRiscv64::Printer::AppendVType(uint32_t vtype) {
993 const uint32_t lmul_v = vtype & 0x7U;
994 const uint32_t vsew_v = (vtype >> 3) & 0x7U;
995 const uint32_t vta_v = (vtype >> 6) & 0x1U;
996 const uint32_t vma_v = (vtype >> 7) & 0x1U;
997
998 if ((vsew_v & 0x4U) == 0u) {
999 if (lmul_v != 0b100) {
1000 static const char* const vsews[] = {"e8", "e16", "e32", "e64"};
1001 static const char* const lmuls[] = {
1002 "m1", "m2", "m4", "m8", nullptr, "mf8", "mf4", "mf2"
1003 };
1004
1005 const char* vma = vma_v ? "ma" : "mu";
1006 const char* vta = vta_v ? "ta" : "tu";
1007 const char* vsew = vsews[vsew_v & 0x3u];
1008 const char* lmul = lmuls[lmul_v];
1009
1010 os_ << vsew << ", " << lmul << ", " << vta << ", " << vma;
1011 return;
1012 }
1013 }
1014
1015 os_ << StringPrintf("0x%08x", vtype) << "\t# incorrect VType literal";
1016 }
1017
1018 static constexpr uint32_t VWXUNARY0 = 0b010000;
1019 static constexpr uint32_t VRXUNARY0 = 0b010000;
1020 static constexpr uint32_t VXUNARY0 = 0b010010;
1021 static constexpr uint32_t VMUNARY0 = 0b010100;
1022
1023 static constexpr uint32_t VWFUNARY0 = 0b010000;
1024 static constexpr uint32_t VRFUNARY0 = 0b010000;
1025 static constexpr uint32_t VFUNARY0 = 0b010010;
1026 static constexpr uint32_t VFUNARY1 = 0b010011;
1027
MaybeSwapOperands(uint32_t funct6,const char * & rs1,const char * & rs2)1028 static void MaybeSwapOperands(uint32_t funct6,
1029 /*inout*/ const char*& rs1,
1030 /*inout*/ const char*& rs2) {
1031 if ((0x28u <= funct6 && funct6 < 0x30u) || funct6 >= 0x3Cu) {
1032 std::swap(rs1, rs2);
1033 }
1034 }
1035
Print32RVVOp(uint32_t insn32)1036 void DisassemblerRiscv64::Printer::Print32RVVOp(uint32_t insn32) {
1037 // TODO(riscv64): Print pseudo-instruction aliases when applicable.
1038 DCHECK_EQ(insn32 & 0x7fu, 0x57u);
1039 const enum VAIEncodings vai = static_cast<enum VAIEncodings>((insn32 >> 12) & 7u);
1040 const uint32_t funct7 = Decode32UImm7(insn32);
1041 const uint32_t funct6 = funct7 >> 1;
1042 const bool masked = (funct7 & 1) == 0;
1043 const char* vm = masked ? ", v0.t" : "";
1044 const char* opcode = nullptr;
1045 const char* rd = nullptr;
1046 const char* rs1 = nullptr;
1047 const char* rs2 = nullptr;
1048
1049 switch (vai) {
1050 case VAIEncodings::kOpIVV: {
1051 static constexpr const char* kOPIVVOpcodes[64] = {
1052 "vadd.vv", nullptr, "vsub.vv", nullptr,
1053 "vminu.vv", "vmin.vv", "vmaxu.vv", "vmax.vv",
1054 nullptr, "vand.vv", "vor.vv", "vxor.vv",
1055 "vrgather.vv", nullptr, "vrgatherei16.vv", nullptr,
1056 "vadc.vvm", "vmadc.vvm", "vsbc.vvm", "vmsbc.vvm",
1057 nullptr, nullptr, nullptr, "<vmerge/vmv>",
1058 "vmseq.vv", "vmsne.vv", "vmsltu.vv", "vmslt.vv",
1059 "vmsleu.vv", "vmsle.vv", nullptr, nullptr,
1060 "vsaddu.vv", "vsadd.vv", "vssubu.vv", "vssub.vv",
1061 nullptr, "vsll.vv", nullptr, "vsmul.vv",
1062 "vsrl.vv", "vsra.vv", "vssrl.vv", "vssra.vv",
1063 "vnsrl.wv", "vnsra.wv", "vnclipu.wv", "vnclip.wv",
1064 "vwredsumu.vs", "vwredsum.vs", nullptr, nullptr,
1065 nullptr, nullptr, nullptr, nullptr,
1066 nullptr, nullptr, nullptr, nullptr,
1067 nullptr, nullptr, nullptr, nullptr,
1068 };
1069
1070 rs2 = VRegName(GetRs2(insn32));
1071 if (funct6 == 0b010111) {
1072 // vmerge/vmv
1073 if (masked) {
1074 opcode = "vmerge.vvm";
1075 vm = ", v0";
1076 } else if (GetRs2(insn32) == 0) {
1077 opcode = "vmv.v.v";
1078 rs2 = nullptr;
1079 } else {
1080 opcode = nullptr;
1081 }
1082 } else {
1083 opcode = kOPIVVOpcodes[funct6];
1084 }
1085
1086 rd = VRegName(GetRd(insn32));
1087 rs1 = VRegName(GetRs1(insn32));
1088 break;
1089 }
1090 case VAIEncodings::kOpIVX: {
1091 static constexpr const char* kOPIVXOpcodes[64] = {
1092 "vadd.vx", nullptr, "vsub.vx", "vrsub.vx",
1093 "vminu.vx", "vmin.vx", "vmaxu.vx", "vmax.vx",
1094 nullptr, "vand.vx", "vor.vx", "vxor.vx",
1095 "vrgather.vx", nullptr, "vslideup.vx", "vslidedown.vx",
1096 "vadc.vxm", "vmadc.vxm", "vsbc.vxm", "vmsbc.vxm",
1097 nullptr, nullptr, nullptr, "<vmerge/vmv>",
1098 "vmseq.vx", "vmsne.vx", "vmsltu.vx", "vmslt.vx",
1099 "vmsleu.vx", "vmsle.vx", "vmsgtu.vx", "vmsgt.vx",
1100 "vsaddu.vx", "vsadd.vx", "vssubu.vx", "vssub.vx",
1101 nullptr, "vsll.vx", nullptr, "vsmul.vx",
1102 "vsrl.vx", "vsra.vx", "vssrl.vx", "vssra.vx",
1103 "vnsrl.wx", "vnsra.wx", "vnclipu.wx", "vnclip.wx",
1104 nullptr, nullptr, nullptr, nullptr,
1105 nullptr, nullptr, nullptr, nullptr,
1106 nullptr, nullptr, nullptr, nullptr,
1107 nullptr, nullptr, nullptr, nullptr,
1108 };
1109
1110 rs2 = VRegName(GetRs2(insn32));
1111 if (funct6 == 0b010111) {
1112 // vmerge/vmv
1113 if (masked) {
1114 opcode = "vmerge.vxm";
1115 vm = ", v0";
1116 } else if (GetRs2(insn32) == 0) {
1117 opcode = "vmv.v.x";
1118 rs2 = nullptr;
1119 } else {
1120 opcode = nullptr;
1121 }
1122 } else {
1123 opcode = kOPIVXOpcodes[funct6];
1124 }
1125
1126 rd = VRegName(GetRd(insn32));
1127 rs1 = XRegName(GetRs1(insn32));
1128 break;
1129 }
1130 case VAIEncodings::kOpIVI: {
1131 static constexpr const char* kOPIVIOpcodes[64] = {
1132 "vadd.vi", nullptr, nullptr, "vrsub.vi",
1133 nullptr, nullptr, nullptr, nullptr,
1134 nullptr, "vand.vi", "vor.vi", "vxor.vi",
1135 "vrgather.vi", nullptr, "vslideup.vi", "vslidedown.vi",
1136 "vadc.vim", "vmadc.vim", nullptr, nullptr,
1137 nullptr, nullptr, nullptr, "<vmerge/vmv>",
1138 "vmseq.vi", "vmsne.vi", nullptr, nullptr,
1139 "vmsleu.vi", "vmsle.vi", "vmsgtu.vi", "vmsgt.vi",
1140 "vsaddu.vi", "vsadd.vi", nullptr, nullptr,
1141 nullptr, "vsll.vi", nullptr, "<vmvNr.v>",
1142 "vsrl.vi", "vsra.vi", "vssrl.vi", "vssra.vi",
1143 "vnsrl.wi", "vnsra.wi", "vnclipu.wi", "vnclip.wi",
1144 nullptr, nullptr, nullptr, nullptr,
1145 nullptr, nullptr, nullptr, nullptr,
1146 nullptr, nullptr, nullptr, nullptr,
1147 nullptr, nullptr, nullptr, nullptr,
1148 };
1149
1150 rs2 = VRegName(GetRs2(insn32));
1151
1152 if (funct6 == 0b010111) {
1153 // vmerge/vmv
1154 if (masked) {
1155 opcode = "vmerge.vim";
1156 vm = ", v0";
1157 } else if (GetRs2(insn32) == 0) {
1158 opcode = "vmv.v.i";
1159 rs2 = nullptr;
1160 } else {
1161 opcode = nullptr;
1162 }
1163 } else if (funct6 == 0b100111) {
1164 uint32_t rs1V = GetRs1(insn32);
1165 static constexpr const char* kVmvnrOpcodes[8] = {
1166 "vmv1r.v", "vmv2r.v", nullptr, "vmv4r.v",
1167 nullptr, nullptr, nullptr, "vmv8r.v",
1168 };
1169 if (IsUint<3>(rs1V)) {
1170 opcode = kVmvnrOpcodes[rs1V];
1171 }
1172 } else {
1173 opcode = kOPIVIOpcodes[funct6];
1174 }
1175
1176 rd = VRegName(GetRd(insn32));
1177 break;
1178 }
1179 case VAIEncodings::kOpMVV: {
1180 switch (funct6) {
1181 case VWXUNARY0: {
1182 static constexpr const char* kVWXUNARY0Opcodes[32] = {
1183 "vmv.x.s", nullptr, nullptr, nullptr,
1184 nullptr, nullptr, nullptr, nullptr,
1185 nullptr, nullptr, nullptr, nullptr,
1186 nullptr, nullptr, nullptr, nullptr,
1187 "vcpop.m", "vfirst.m", nullptr, nullptr,
1188 nullptr, nullptr, nullptr, nullptr,
1189 nullptr, nullptr, nullptr, nullptr,
1190 nullptr, nullptr, nullptr, nullptr,
1191 };
1192 opcode = kVWXUNARY0Opcodes[GetRs1(insn32)];
1193 rd = XRegName(GetRd(insn32));
1194 rs2 = VRegName(GetRs2(insn32));
1195 break;
1196 }
1197 case VXUNARY0: {
1198 static constexpr const char* kVXUNARY0Opcodes[32] = {
1199 nullptr, nullptr, "vzext.vf8", "vsext.vf8",
1200 "vzext.vf4", "vsext.vf4", "vzext.vf2", "vsext.vf2",
1201 nullptr, nullptr, nullptr, nullptr,
1202 nullptr, nullptr, nullptr, nullptr,
1203 nullptr, nullptr, nullptr, nullptr,
1204 nullptr, nullptr, nullptr, nullptr,
1205 nullptr, nullptr, nullptr, nullptr,
1206 nullptr, nullptr, nullptr, nullptr,
1207 };
1208 opcode = kVXUNARY0Opcodes[GetRs1(insn32)];
1209 rd = VRegName(GetRd(insn32));
1210 rs2 = VRegName(GetRs2(insn32));
1211 break;
1212 }
1213 case VMUNARY0: {
1214 static constexpr const char* kVMUNARY0Opcodes[32] = {
1215 nullptr, "vmsbf.m", "vmsof.m", "vmsif.m",
1216 nullptr, nullptr, nullptr, nullptr,
1217 nullptr, nullptr, nullptr, nullptr,
1218 nullptr, nullptr, nullptr, nullptr,
1219 "viota.m", "vid.v", nullptr, nullptr,
1220 nullptr, nullptr, nullptr, nullptr,
1221 nullptr, nullptr, nullptr, nullptr,
1222 nullptr, nullptr, nullptr, nullptr,
1223 };
1224 opcode = kVMUNARY0Opcodes[GetRs1(insn32)];
1225 rd = VRegName(GetRd(insn32));
1226 rs2 = VRegName(GetRs2(insn32));
1227 break;
1228 }
1229 default: {
1230 static constexpr const char* kOPMVVOpcodes[64] = {
1231 "vredsum.vs", "vredand.vs", "vredor.vs", "vredxor.vs",
1232 "vredminu.vs", "vredmin.vs", "vredmaxu.vs", "vredmax.vs",
1233 "vaaddu.vv", "vaadd.vv", "vasubu.vv", "vasub.vv",
1234 nullptr, nullptr, nullptr, nullptr,
1235 "<VWXUNARY0>", nullptr, "<VXUNARY0>", nullptr,
1236 "<VMUNARY0>", nullptr, nullptr, "vcompress.vm",
1237 "vmandn.mm", "vmand.mm", "vmor.mm", "vmxor.mm",
1238 "vmorn.mm", "vmnand.mm", "vmnor.mm", "vmxnor.mm",
1239 "vdivu.vv", "vdiv.vv", "vremu.vv", "vrem.vv",
1240 "vmulhu.vv", "vmul.vv", "vmulhsu.vv", "vmulh.vv",
1241 nullptr, "vmadd.vv", nullptr, "vnmsub.vv",
1242 nullptr, "vmacc.vv", nullptr, "vnmsac.vv",
1243 "vwaddu.vv", "vwadd.vv", "vwsubu.vv", "vwsub.vv",
1244 "vwaddu.wv", "vwadd.wv", "vwsubu.wv", "vwsub.wv",
1245 "vwmulu.vv", nullptr, "vwmulsu.vv", "vwmul.vv",
1246 "vwmaccu.vv", "vwmacc.vv", nullptr, "vwmaccsu.vv",
1247 };
1248
1249 opcode = kOPMVVOpcodes[funct6];
1250 rd = VRegName(GetRd(insn32));
1251 rs1 = VRegName(GetRs1(insn32));
1252 rs2 = VRegName(GetRs2(insn32));
1253
1254 if (0x17u <= funct6 && funct6 <= 0x1Fu) {
1255 if (masked) {
1256 // for vcompress.vm and *.mm encodings with vm=0 are reserved
1257 opcode = nullptr;
1258 }
1259 }
1260
1261 MaybeSwapOperands(funct6, rs1, rs2);
1262
1263 break;
1264 }
1265 }
1266 break;
1267 }
1268 case VAIEncodings::kOpMVX: {
1269 switch (funct6) {
1270 case VRXUNARY0: {
1271 opcode = GetRs2(insn32) == 0u ? "vmv.s.x" : nullptr;
1272 rd = VRegName(GetRd(insn32));
1273 rs1 = XRegName(GetRs1(insn32));
1274 break;
1275 }
1276 default: {
1277 static constexpr const char* kOPMVXOpcodes[64] = {
1278 nullptr, nullptr, nullptr, nullptr,
1279 nullptr, nullptr, nullptr, nullptr,
1280 "vaaddu.vx", "vaadd.vx", "vasubu.vx", "vasub.vx",
1281 nullptr, nullptr, "vslide1up.vx", "vslide1down.vx",
1282 "<VRXUNARY0>", nullptr, nullptr, nullptr,
1283 nullptr, nullptr, nullptr, nullptr,
1284 nullptr, nullptr, nullptr, nullptr,
1285 nullptr, nullptr, nullptr, nullptr,
1286 "vdivu.vx", "vdiv.vx", "vremu.vx", "vrem.vx",
1287 "vmulhu.vx", "vmul.vx", "vmulhsu.vx", "vmulh.vx",
1288 nullptr, "vmadd.vx", nullptr, "vnmsub.vx",
1289 nullptr, "vmacc.vx", nullptr, "vnmsac.vx",
1290 "vwaddu.vx", "vwadd.vx", "vwsubu.vx", "vwsub.vx",
1291 "vwaddu.wv", "vwadd.wv", "vwsubu.wv", "vwsub.wv",
1292 "vwmulu.vx", nullptr, "vwmulsu.vx", "vwmul.vx",
1293 "vwmaccu.vx", "vwmacc.vx", "vwmaccus.vx", "vwmaccsu.vx",
1294 };
1295 opcode = kOPMVXOpcodes[funct6];
1296 rd = VRegName(GetRd(insn32));
1297 rs1 = XRegName(GetRs1(insn32));
1298 rs2 = VRegName(GetRs2(insn32));
1299
1300 MaybeSwapOperands(funct6, rs1, rs2);
1301
1302 break;
1303 }
1304 }
1305 break;
1306 }
1307 case VAIEncodings::kOpFVV: {
1308 switch (funct6) {
1309 case VWFUNARY0: {
1310 opcode = GetRs1(insn32) == 0u ? "vfmv.f.s" : nullptr;
1311 rd = XRegName(GetRd(insn32));
1312 rs2 = VRegName(GetRs2(insn32));
1313 break;
1314 }
1315 case VFUNARY0: {
1316 static constexpr const char* kVFUNARY0Opcodes[32] = {
1317 "vfcvt.xu.f.v", "vfcvt.x.f.v", "vfcvt.f.xu.v", "vfcvt.f.x.v",
1318 nullptr, nullptr, "vfcvt.rtz.xu.f.v", "vfcvt.rtz.x.f.v",
1319 "vfwcvt.xu.f.v", "vfwcvt.x.f.v", "vfwcvt.f.xu.v", "vfwcvt.f.x.v",
1320 "vfwcvt.f.f.v", nullptr, "vfwcvt.rtz.xu.f.v", "vfwcvt.rtz.x.f.v",
1321 "vfncvt.xu.f.w", "vfncvt.x.f.w", "vfncvt.f.xu.w", "vfncvt.f.x.w",
1322 "vfncvt.f.f.w", "vfncvt.rod.f.f.w", "vfncvt.rtz.xu.f.w", "vfncvt.rtz.x.f.w",
1323 nullptr, nullptr, nullptr, nullptr,
1324 nullptr, nullptr, nullptr, nullptr,
1325 };
1326 opcode = kVFUNARY0Opcodes[GetRs1(insn32)];
1327 rd = VRegName(GetRd(insn32));
1328 rs2 = VRegName(GetRs2(insn32));
1329 break;
1330 }
1331 case VFUNARY1: {
1332 static constexpr const char* kVFUNARY1Opcodes[32] = {
1333 "vfsqrt.v", nullptr, nullptr, nullptr,
1334 "vfrsqrt7.v", "vfrec7.v", nullptr, nullptr,
1335 nullptr, nullptr, nullptr, nullptr,
1336 nullptr, nullptr, nullptr, nullptr,
1337 "vfclass.v", nullptr, nullptr, nullptr,
1338 nullptr, nullptr, nullptr, nullptr,
1339 nullptr, nullptr, nullptr, nullptr,
1340 nullptr, nullptr, nullptr, nullptr,
1341 };
1342 opcode = kVFUNARY1Opcodes[GetRs1(insn32)];
1343 rd = VRegName(GetRd(insn32));
1344 rs2 = VRegName(GetRs2(insn32));
1345 break;
1346 }
1347 default: {
1348 static constexpr const char* kOPFVVOpcodes[64] = {
1349 "vfadd.vv", "vfredusum.vs", "vfsub.vv", "vfredosum.vs",
1350 "vfmin.vv", "vfredmin.vs", "vfmax.vv", "vfredmax.vs",
1351 "vfsgnj.vv", "vfsgnjn.vv", "vfsgnjx.vv", nullptr,
1352 nullptr, nullptr, nullptr, nullptr,
1353 "<VWFUNARY0>", nullptr, "<VFUNARY0>", "<VFUNARY1>",
1354 nullptr, nullptr, nullptr, nullptr,
1355 "vmfeq.vv", "vmfle.vv", nullptr, "vmflt.vv",
1356 "vmfne.vv", nullptr, nullptr, nullptr,
1357 "vfdiv.vv", nullptr, nullptr, nullptr,
1358 "vfmul.vv", nullptr, nullptr, nullptr,
1359 "vfmadd.vv", "vfnmadd.vv", "vfmsub.vv", "vfnmsub.vv",
1360 "vfmacc.vv", "vfnmacc.vv", "vfmsac.vv", "vfnmsac.vv",
1361 "vfwadd.vv", "vfwredusum.vs", "vfwsub.vv", "vfwredosum.vs",
1362 "vfwadd.wv", nullptr, "vfwsub.wv", nullptr,
1363 "vfwmul.vv", nullptr, nullptr, nullptr,
1364 "vfwmacc.vv", "vfwnmacc.vv", "vfwmsac.vv", "vfwnmsac.vv",
1365 };
1366 opcode = kOPFVVOpcodes[funct6];
1367 rd = VRegName(GetRd(insn32));
1368 rs1 = VRegName(GetRs1(insn32));
1369 rs2 = VRegName(GetRs2(insn32));
1370
1371 MaybeSwapOperands(funct6, rs1, rs2);
1372
1373 break;
1374 }
1375 }
1376 break;
1377 }
1378 case VAIEncodings::kOpFVF: {
1379 switch (funct6) {
1380 case VRFUNARY0: {
1381 opcode = GetRs2(insn32) == 0u ? "vfmv.s.f" : nullptr;
1382 rd = VRegName(GetRd(insn32));
1383 rs1 = FRegName(GetRs1(insn32));
1384 break;
1385 }
1386 default: {
1387 static constexpr const char* kOPFVFOpcodes[64] = {
1388 "vfadd.vf", nullptr, "vfsub.vf", nullptr,
1389 "vfmin.vf", nullptr, "vfmax.vf", nullptr,
1390 "vfsgnj.vf", "vfsgnjn.vf", "vfsgnjx.vf", nullptr,
1391 nullptr, nullptr, "vfslide1up.vf", "vfslide1down.vf",
1392 "<VRFUNARY0>", nullptr, nullptr, nullptr,
1393 nullptr, nullptr, nullptr, "<vfmerge.vfm/vfmv>",
1394 "vmfeq.vf", "vmfle.vf", nullptr, "vmflt.vf",
1395 "vmfne.vf", "vmfgt.vf", nullptr, "vmfge.vf",
1396 "vfdiv.vf", "vfrdiv.vf", nullptr, nullptr,
1397 "vfmul.vf", nullptr, nullptr, "vfrsub.vf",
1398 "vfmadd.vf", "vfnmadd.vf", "vfmsub.vf", "vfnmsub.vf",
1399 "vfmacc.vf", "vfnmacc.vf", "vfmsac.vf", "vfnmsac.vf",
1400 "vfwadd.vf", nullptr, "vfwsub.vf", nullptr,
1401 "vfwadd.wf", nullptr, "vfwsub.wf", nullptr,
1402 "vfwmul.vf", nullptr, nullptr, nullptr,
1403 "vfwmacc.vf", "vfwnmacc.vf", "vfwmsac.vf", "vfwnmsac.vf",
1404 };
1405
1406 rs2 = VRegName(GetRs2(insn32));
1407 if (funct6 == 0b010111) {
1408 // vfmerge/vfmv
1409 if (masked) {
1410 opcode = "vfmerge.vfm";
1411 vm = ", v0";
1412 } else if (GetRs2(insn32) == 0) {
1413 opcode = "vfmv.v.f";
1414 rs2 = nullptr;
1415 } else {
1416 opcode = nullptr;
1417 }
1418 } else {
1419 opcode = kOPFVFOpcodes[funct6];
1420 }
1421
1422 rd = VRegName(GetRd(insn32));
1423 rs1 = FRegName(GetRs1(insn32));
1424
1425 MaybeSwapOperands(funct6, rs1, rs2);
1426
1427 break;
1428 }
1429 }
1430 break;
1431 }
1432 case VAIEncodings::kOpCFG: { // vector ALU control instructions
1433 if ((insn32 >> 31) != 0u) {
1434 if (((insn32 >> 30) & 0x1U) != 0u) { // vsetivli
1435 const uint32_t zimm = Decode32UImm12(insn32) & ~0xC00U;
1436 const uint32_t imm5 = GetRs1(insn32);
1437 os_ << "vsetivli " << XRegName(GetRd(insn32)) << ", " << StringPrintf("0x%08x", imm5)
1438 << ", ";
1439 AppendVType(zimm);
1440 } else { // vsetvl
1441 os_ << "vsetvl " << XRegName(GetRd(insn32)) << ", " << XRegName(GetRs1(insn32)) << ", "
1442 << XRegName(GetRs2(insn32));
1443 if ((Decode32UImm7(insn32) & 0x40u) != 0u) {
1444 os_ << "\t# incorrect funct7 literal : "
1445 << StringPrintf("0x%08x", Decode32UImm7(insn32));
1446 }
1447 }
1448 } else { // vsetvli
1449 const uint32_t zimm = Decode32UImm12(insn32) & ~0x800U;
1450 os_ << "vsetvli " << XRegName(GetRd(insn32)) << ", " << XRegName(GetRs1(insn32)) << ", ";
1451 AppendVType(zimm);
1452 }
1453 return;
1454 }
1455 }
1456
1457 if (opcode == nullptr) {
1458 os_ << "<unknown32>";
1459 return;
1460 }
1461
1462 os_ << opcode << " " << rd;
1463
1464 if (rs2 != nullptr) {
1465 os_ << ", " << rs2;
1466 }
1467
1468 if (rs1 != nullptr) {
1469 os_ << ", " << rs1;
1470 } else if (vai == VAIEncodings::kOpIVI) {
1471 os_ << StringPrintf(", 0x%08x", GetRs1(insn32));
1472 }
1473
1474 os_ << vm;
1475 }
1476
Print32FpFma(uint32_t insn32)1477 void DisassemblerRiscv64::Printer::Print32FpFma(uint32_t insn32) {
1478 DCHECK_EQ(insn32 & 0x73u, 0x43u); // Note: Bits 0xc select the FMA opcode.
1479 uint32_t funct2 = (insn32 >> 25) & 3u;
1480 if (funct2 >= 2u) {
1481 os_ << "<unknown32>"; // Note: This includes the "H" and "Q" extensions.
1482 return;
1483 }
1484 static const char* const kOpcodes[] = { "fmadd", "fmsub", "fnmsub", "fnmadd" };
1485 os_ << kOpcodes[(insn32 >> 2) & 3u] << ((funct2 != 0u) ? ".d" : ".s")
1486 << RoundingModeName(GetRoundingMode(insn32)) << " "
1487 << FRegName(GetRd(insn32)) << ", " << FRegName(GetRs1(insn32)) << ", "
1488 << FRegName(GetRs2(insn32)) << ", " << FRegName(GetRs3(insn32));
1489 }
1490
Print32Zicsr(uint32_t insn32)1491 void DisassemblerRiscv64::Printer::Print32Zicsr(uint32_t insn32) {
1492 DCHECK_EQ(insn32 & 0x7fu, 0x73u);
1493 uint32_t funct3 = (insn32 >> 12) & 7u;
1494 static const char* const kOpcodes[] = {
1495 nullptr, "csrrw", "csrrs", "csrrc", nullptr, "csrrwi", "csrrsi", "csrrci"
1496 };
1497 const char* opcode = kOpcodes[funct3];
1498 if (opcode == nullptr) {
1499 os_ << "<unknown32>";
1500 return;
1501 }
1502 uint32_t rd = GetRd(insn32);
1503 uint32_t rs1_or_uimm = GetRs1(insn32);
1504 uint32_t csr = insn32 >> 20;
1505 // Print shorter macro instruction notation if available.
1506 if (funct3 == /*CSRRW*/ 1u && rd == 0u && rs1_or_uimm == 0u && csr == 0xc00u) {
1507 os_ << "unimp";
1508 return;
1509 } else if (funct3 == /*CSRRS*/ 2u && rs1_or_uimm == 0u) {
1510 if (csr == 0xc00u) {
1511 os_ << "rdcycle " << XRegName(rd);
1512 } else if (csr == 0xc01u) {
1513 os_ << "rdtime " << XRegName(rd);
1514 } else if (csr == 0xc02u) {
1515 os_ << "rdinstret " << XRegName(rd);
1516 } else {
1517 os_ << "csrr " << XRegName(rd) << ", " << csr;
1518 }
1519 return;
1520 }
1521
1522 if (rd == 0u) {
1523 static const char* const kAltOpcodes[] = {
1524 nullptr, "csrw", "csrs", "csrc", nullptr, "csrwi", "csrsi", "csrci"
1525 };
1526 DCHECK(kAltOpcodes[funct3] != nullptr);
1527 os_ << kAltOpcodes[funct3] << " " << csr << ", ";
1528 } else {
1529 os_ << opcode << " " << XRegName(rd) << ", " << csr << ", ";
1530 }
1531 if (funct3 >= /*CSRRWI/CSRRSI/CSRRCI*/ 4u) {
1532 os_ << rs1_or_uimm;
1533 } else {
1534 os_ << XRegName(rs1_or_uimm);
1535 }
1536 }
1537
Print32Fence(uint32_t insn32)1538 void DisassemblerRiscv64::Printer::Print32Fence(uint32_t insn32) {
1539 DCHECK_EQ(insn32 & 0x7fu, 0x0fu);
1540 if ((insn32 & 0xf00fffffu) == 0x0000000fu) {
1541 auto print_flags = [&](uint32_t flags) {
1542 if (flags == 0u) {
1543 os_ << "0";
1544 } else {
1545 DCHECK_LT(flags, 0x10u);
1546 static const char kFlagNames[] = "wroi";
1547 for (size_t bit : { 3u, 2u, 1u, 0u }) { // Print in the "iorw" order.
1548 if ((flags & (1u << bit)) != 0u) {
1549 os_ << kFlagNames[bit];
1550 }
1551 }
1552 }
1553 };
1554 os_ << "fence.";
1555 print_flags((insn32 >> 24) & 0xfu);
1556 os_ << ".";
1557 print_flags((insn32 >> 20) & 0xfu);
1558 } else if (insn32 == 0x8330000fu) {
1559 os_ << "fence.tso";
1560 } else if (insn32 == 0x0000100fu) {
1561 os_ << "fence.i";
1562 } else {
1563 os_ << "<unknown32>";
1564 }
1565 }
1566
Dump32(const uint8_t * insn)1567 void DisassemblerRiscv64::Printer::Dump32(const uint8_t* insn) {
1568 uint32_t insn32 = static_cast<uint32_t>(insn[0]) +
1569 (static_cast<uint32_t>(insn[1]) << 8) +
1570 (static_cast<uint32_t>(insn[2]) << 16) +
1571 (static_cast<uint32_t>(insn[3]) << 24);
1572 CHECK_EQ(insn32 & 3u, 3u);
1573 os_ << disassembler_->FormatInstructionPointer(insn) << StringPrintf(": %08x\t", insn32);
1574 switch (insn32 & 0x7fu) {
1575 case 0x37u:
1576 Print32Lui(insn32);
1577 break;
1578 case 0x17u:
1579 Print32Auipc(insn, insn32);
1580 break;
1581 case 0x6fu:
1582 Print32Jal(insn, insn32);
1583 break;
1584 case 0x67u:
1585 switch ((insn32 >> 12) & 7u) { // funct3
1586 case 0:
1587 Print32Jalr(insn, insn32);
1588 break;
1589 default:
1590 os_ << "<unknown32>";
1591 break;
1592 }
1593 break;
1594 case 0x63u:
1595 Print32BCond(insn, insn32);
1596 break;
1597 case 0x03u:
1598 Print32Load(insn32);
1599 break;
1600 case 0x23u:
1601 Print32Store(insn32);
1602 break;
1603 case 0x07u:
1604 Print32FLoad(insn32);
1605 break;
1606 case 0x27u:
1607 Print32FStore(insn32);
1608 break;
1609 case 0x13u:
1610 case 0x1bu:
1611 Print32BinOpImm(insn32);
1612 break;
1613 case 0x33u:
1614 case 0x3bu:
1615 Print32BinOp(insn32);
1616 break;
1617 case 0x2fu:
1618 Print32Atomic(insn32);
1619 break;
1620 case 0x53u:
1621 Print32FpOp(insn32);
1622 break;
1623 case 0x57u:
1624 Print32RVVOp(insn32);
1625 break;
1626 case 0x43u:
1627 case 0x47u:
1628 case 0x4bu:
1629 case 0x4fu:
1630 Print32FpFma(insn32);
1631 break;
1632 case 0x73u:
1633 if ((insn32 & 0xffefffffu) == 0x00000073u) {
1634 os_ << ((insn32 == 0x00000073u) ? "ecall" : "ebreak");
1635 } else {
1636 Print32Zicsr(insn32);
1637 }
1638 break;
1639 case 0x0fu:
1640 Print32Fence(insn32);
1641 break;
1642 default:
1643 // TODO(riscv64): Disassemble more instructions.
1644 os_ << "<unknown32>";
1645 break;
1646 }
1647 os_ << "\n";
1648 }
1649
Dump16(const uint8_t * insn)1650 void DisassemblerRiscv64::Printer::Dump16(const uint8_t* insn) {
1651 uint32_t insn16 = static_cast<uint32_t>(insn[0]) + (static_cast<uint32_t>(insn[1]) << 8);
1652 ScopedNewLinePrinter nl(os_);
1653 CHECK_NE(insn16 & 3u, 3u);
1654 os_ << disassembler_->FormatInstructionPointer(insn) << StringPrintf(": %04x \t", insn16);
1655
1656 uint32_t funct3 = BitFieldExtract(insn16, 13, 3);
1657 int32_t offset = -1;
1658
1659 switch (insn16 & 3u) {
1660 case 0b00u: // Quadrant 0
1661 switch (funct3) {
1662 case 0b000u:
1663 if (insn16 == 0u) {
1664 os_ << "c.unimp";
1665 } else {
1666 uint32_t nzuimm = BitFieldExtract(insn16, 5, 8);
1667 if (nzuimm != 0u) {
1668 uint32_t decoded =
1669 BitFieldExtract(nzuimm, 0, 1) << 3 | BitFieldExtract(nzuimm, 1, 1) << 2 |
1670 BitFieldExtract(nzuimm, 2, 4) << 6 | BitFieldExtract(nzuimm, 6, 2) << 4;
1671 os_ << "c.addi4spn " << XRegName(GetRs2Short16(insn16)) << ", sp, " << decoded;
1672 } else {
1673 os_ << "<unknown16>";
1674 }
1675 }
1676 return;
1677 case 0b001u:
1678 offset = Decode16CMOffsetD(insn16);
1679 os_ << "c.fld " << FRegName(GetRs2Short16(insn16));
1680 break;
1681 case 0b010u:
1682 offset = Decode16CMOffsetW(insn16);
1683 os_ << "c.lw " << XRegName(GetRs2Short16(insn16));
1684 break;
1685 case 0b011u:
1686 offset = Decode16CMOffsetD(insn16);
1687 os_ << "c.ld " << XRegName(GetRs2Short16(insn16));
1688 break;
1689 case 0b100u: {
1690 uint32_t opcode2 = BitFieldExtract(insn16, 10, 3);
1691 uint32_t imm = BitFieldExtract(insn16, 5, 2);
1692 switch (opcode2) {
1693 case 0b000:
1694 offset = Uimm2ToOffset10(imm);
1695 os_ << "c.lbu " << XRegName(GetRs2Short16(insn16));
1696 break;
1697 case 0b001:
1698 offset = Uimm2ToOffset1(imm);
1699 os_ << (BitFieldExtract(imm, 1, 1) == 0u ? "c.lhu " : "c.lh ");
1700 os_ << XRegName(GetRs2Short16(insn16));
1701 break;
1702 case 0b010:
1703 offset = Uimm2ToOffset10(imm);
1704 os_ << "c.sb " << XRegName(GetRs2Short16(insn16));
1705 break;
1706 case 0b011:
1707 if (BitFieldExtract(imm, 1, 1) == 0u) {
1708 offset = Uimm2ToOffset1(imm);
1709 os_ << "c.sh " << XRegName(GetRs2Short16(insn16));
1710 break;
1711 }
1712 FALLTHROUGH_INTENDED;
1713 default:
1714 os_ << "<unknown16>";
1715 return;
1716 }
1717 break;
1718 }
1719 case 0b101u:
1720 offset = Decode16CMOffsetD(insn16);
1721 os_ << "c.fsd " << FRegName(GetRs2Short16(insn16));
1722 break;
1723 case 0b110u:
1724 offset = Decode16CMOffsetW(insn16);
1725 os_ << "c.sw " << XRegName(GetRs2Short16(insn16));
1726 break;
1727 case 0b111u:
1728 offset = Decode16CMOffsetD(insn16);
1729 os_ << "c.sd " << XRegName(GetRs2Short16(insn16));
1730 break;
1731 default:
1732 LOG(FATAL) << "Unreachable";
1733 UNREACHABLE();
1734 }
1735 os_ << ", ";
1736 PrintLoadStoreAddress(GetRs1Short16(insn16), offset);
1737 return;
1738 case 0b01u: // Quadrant 1
1739 switch (funct3) {
1740 case 0b000u: {
1741 uint32_t rd = GetRs1_16(insn16);
1742 if (rd == 0) {
1743 if (Decode16Imm6<uint32_t>(insn16) != 0u) {
1744 os_ << "<hint16>";
1745 } else {
1746 os_ << "c.nop";
1747 }
1748 } else {
1749 int32_t imm = Decode16Imm6<int32_t>(insn16);
1750 if (imm != 0) {
1751 os_ << "c.addi " << XRegName(rd) << ", " << imm;
1752 } else {
1753 os_ << "<hint16>";
1754 }
1755 }
1756 break;
1757 }
1758 case 0b001u: {
1759 uint32_t rd = GetRs1_16(insn16);
1760 if (rd != 0) {
1761 os_ << "c.addiw " << XRegName(rd) << ", " << Decode16Imm6<int32_t>(insn16);
1762 } else {
1763 os_ << "<unknown16>";
1764 }
1765 break;
1766 }
1767 case 0b010u: {
1768 uint32_t rd = GetRs1_16(insn16);
1769 if (rd != 0) {
1770 os_ << "c.li " << XRegName(rd) << ", " << Decode16Imm6<int32_t>(insn16);
1771 } else {
1772 os_ << "<hint16>";
1773 }
1774 break;
1775 }
1776 case 0b011u: {
1777 uint32_t rd = GetRs1_16(insn16);
1778 uint32_t imm6_bits = Decode16Imm6<uint32_t>(insn16);
1779 if (imm6_bits != 0u) {
1780 if (rd == 2) {
1781 int32_t nzimm =
1782 BitFieldExtract(insn16, 6, 1) << 4 | BitFieldExtract(insn16, 2, 1) << 5 |
1783 BitFieldExtract(insn16, 5, 1) << 6 | BitFieldExtract(insn16, 3, 2) << 7 |
1784 BitFieldExtract(insn16, 12, 1) << 9;
1785 os_ << "c.addi16sp sp, " << SignExtendBits<10>(nzimm);
1786 } else if (rd != 0) {
1787 // sign-extend bits and mask with 0xfffff as llvm-objdump does
1788 uint32_t mask = MaskLeastSignificant<uint32_t>(20);
1789 os_ << "c.lui " << XRegName(rd) << ", " << (SignExtendBits<6>(imm6_bits) & mask);
1790 } else {
1791 os_ << "<hint16>";
1792 }
1793 } else {
1794 os_ << "<unknown16>";
1795 }
1796 break;
1797 }
1798 case 0b100u: {
1799 uint32_t funct2 = BitFieldExtract(insn16, 10, 2);
1800 switch (funct2) {
1801 case 0b00: {
1802 int32_t nzuimm = Decode16Imm6<uint32_t>(insn16);
1803 if (nzuimm != 0) {
1804 os_ << "c.srli " << XRegName(GetRs1Short16(insn16)) << ", " << nzuimm;
1805 } else {
1806 os_ << "<hint16>";
1807 }
1808 break;
1809 }
1810 case 0b01: {
1811 int32_t nzuimm = Decode16Imm6<uint32_t>(insn16);
1812 if (nzuimm != 0) {
1813 os_ << "c.srai " << XRegName(GetRs1Short16(insn16)) << ", " << nzuimm;
1814 } else {
1815 os_ << "<hint16>";
1816 }
1817 break;
1818 }
1819 case 0b10:
1820 os_ << "c.andi " << XRegName(GetRs1Short16(insn16)) << ", "
1821 << Decode16Imm6<int32_t>(insn16);
1822 break;
1823 case 0b11: {
1824 constexpr static const char* mnemonics[] = {
1825 "c.sub", "c.xor", "c.or", "c.and", "c.subw", "c.addw", "c.mul", nullptr
1826 };
1827 uint32_t opc = BitFieldInsert(
1828 BitFieldExtract(insn16, 5, 2), BitFieldExtract(insn16, 12, 1), 2, 1);
1829 DCHECK(IsUint<3>(opc));
1830 const char* mnem = mnemonics[opc];
1831 if (mnem != nullptr) {
1832 os_ << mnem << " " << XRegName(GetRs1Short16(insn16)) << ", "
1833 << XRegName(GetRs2Short16(insn16));
1834 } else {
1835 constexpr static const char* zbc_mnemonics[] = {
1836 "c.zext.b", "c.sext.b", "c.zext.h", "c.sext.h",
1837 "c.zext.w", "c.not", nullptr, nullptr,
1838 };
1839 mnem = zbc_mnemonics[BitFieldExtract(insn16, 2, 3)];
1840 if (mnem != nullptr) {
1841 os_ << mnem << " " << XRegName(GetRs1Short16(insn16));
1842 } else {
1843 os_ << "<unknown16>";
1844 }
1845 }
1846 break;
1847 }
1848 default:
1849 LOG(FATAL) << "Unreachable";
1850 UNREACHABLE();
1851 }
1852 break;
1853 }
1854 case 0b101u: {
1855 int32_t disp = BitFieldExtract(insn16, 3, 3) << 1 | BitFieldExtract(insn16, 11, 1) << 4 |
1856 BitFieldExtract(insn16, 2, 1) << 5 | BitFieldExtract(insn16, 7, 1) << 6 |
1857 BitFieldExtract(insn16, 6, 1) << 7 | BitFieldExtract(insn16, 9, 2) << 8 |
1858 BitFieldExtract(insn16, 8, 1) << 10 | BitFieldExtract(insn16, 12, 1) << 11;
1859 os_ << "c.j ";
1860 PrintBranchOffset(SignExtendBits<12>(disp));
1861 break;
1862 }
1863 case 0b110u:
1864 case 0b111u: {
1865 int32_t disp = BitFieldExtract(insn16, 3, 2) << 1 | BitFieldExtract(insn16, 10, 2) << 3 |
1866 BitFieldExtract(insn16, 2, 1) << 5 | BitFieldExtract(insn16, 5, 2) << 6 |
1867 BitFieldExtract(insn16, 12, 1) << 8;
1868
1869 os_ << (funct3 == 0b110u ? "c.beqz " : "c.bnez ");
1870 os_ << XRegName(GetRs1Short16(insn16)) << ", ";
1871 PrintBranchOffset(SignExtendBits<9>(disp));
1872 break;
1873 }
1874 default:
1875 LOG(FATAL) << "Unreachable";
1876 UNREACHABLE();
1877 }
1878 break;
1879 case 0b10u: // Quadrant 2
1880 switch (funct3) {
1881 case 0b000u: {
1882 uint32_t nzuimm = Decode16Imm6<uint32_t>(insn16);
1883 uint32_t rd = GetRs1_16(insn16);
1884 if (rd == 0 || nzuimm == 0) {
1885 os_ << "<hint16>";
1886 } else {
1887 os_ << "c.slli " << XRegName(rd) << ", " << nzuimm;
1888 }
1889 return;
1890 }
1891 case 0b001u: {
1892 offset = Uimm6ToOffsetD16(Decode16Imm6<uint32_t>(insn16));
1893 os_ << "c.fldsp " << FRegName(GetRs1_16(insn16));
1894 break;
1895 }
1896 case 0b010u: {
1897 uint32_t rd = GetRs1_16(insn16);
1898 if (rd != 0) {
1899 offset = Uimm6ToOffsetW16(Decode16Imm6<uint32_t>(insn16));
1900 os_ << "c.lwsp " << XRegName(GetRs1_16(insn16));
1901 } else {
1902 os_ << "<unknown16>";
1903 return;
1904 }
1905 break;
1906 }
1907 case 0b011u: {
1908 uint32_t rd = GetRs1_16(insn16);
1909 if (rd != 0) {
1910 offset = Uimm6ToOffsetD16(Decode16Imm6<uint32_t>(insn16));
1911 os_ << "c.ldsp " << XRegName(GetRs1_16(insn16));
1912 } else {
1913 os_ << "<unknown16>";
1914 return;
1915 }
1916 break;
1917 }
1918 case 0b100u: {
1919 uint32_t rd_rs1 = GetRs1_16(insn16);
1920 uint32_t rs2 = GetRs2_16(insn16);
1921 uint32_t b12 = BitFieldExtract(insn16, 12, 1);
1922 if (b12 == 0) {
1923 if (rd_rs1 != 0 && rs2 != 0) {
1924 os_ << "c.mv " << XRegName(rd_rs1) << ", " << XRegName(rs2);
1925 } else if (rd_rs1 != 0) {
1926 os_ << "c.jr " << XRegName(rd_rs1);
1927 } else if (rs2 != 0) {
1928 os_ << "<hint16>";
1929 } else {
1930 os_ << "<unknown16>";
1931 }
1932 } else {
1933 if (rd_rs1 != 0 && rs2 != 0) {
1934 os_ << "c.add " << XRegName(rd_rs1) << ", " << XRegName(rs2);
1935 } else if (rd_rs1 != 0) {
1936 os_ << "c.jalr " << XRegName(rd_rs1);
1937 } else if (rs2 != 0) {
1938 os_ << "<hint16>";
1939 } else {
1940 os_ << "c.ebreak";
1941 }
1942 }
1943 return;
1944 }
1945 case 0b101u:
1946 offset = BitFieldExtract(insn16, 7, 3) << 6 | BitFieldExtract(insn16, 10, 3) << 3;
1947 os_ << "c.fsdsp " << FRegName(GetRs2_16(insn16));
1948 break;
1949 case 0b110u:
1950 offset = BitFieldExtract(insn16, 7, 2) << 6 | BitFieldExtract(insn16, 9, 4) << 2;
1951 os_ << "c.swsp " << XRegName(GetRs2_16(insn16));
1952 break;
1953 case 0b111u:
1954 offset = BitFieldExtract(insn16, 7, 3) << 6 | BitFieldExtract(insn16, 10, 3) << 3;
1955 os_ << "c.sdsp " << XRegName(GetRs2_16(insn16));
1956 break;
1957 default:
1958 LOG(FATAL) << "Unreachable";
1959 UNREACHABLE();
1960 }
1961
1962 os_ << ", ";
1963 PrintLoadStoreAddress(/* sp */ 2, offset);
1964
1965 break;
1966 default:
1967 LOG(FATAL) << "Unreachable";
1968 UNREACHABLE();
1969 }
1970 }
1971
Dump2Byte(const uint8_t * data)1972 void DisassemblerRiscv64::Printer::Dump2Byte(const uint8_t* data) {
1973 uint32_t value = data[0] + (data[1] << 8);
1974 os_ << disassembler_->FormatInstructionPointer(data)
1975 << StringPrintf(": %04x \t.2byte %u\n", value, value);
1976 }
1977
DumpByte(const uint8_t * data)1978 void DisassemblerRiscv64::Printer::DumpByte(const uint8_t* data) {
1979 uint32_t value = *data;
1980 os_ << disassembler_->FormatInstructionPointer(data)
1981 << StringPrintf(": %02x \t.byte %u\n", value, value);
1982 }
1983
Dump(std::ostream & os,const uint8_t * begin)1984 size_t DisassemblerRiscv64::Dump(std::ostream& os, const uint8_t* begin) {
1985 if (begin < GetDisassemblerOptions()->base_address_ ||
1986 begin >= GetDisassemblerOptions()->end_address_) {
1987 return 0u; // Outside the range.
1988 }
1989 Printer printer(this, os);
1990 if (!IsAligned<2u>(begin) || GetDisassemblerOptions()->end_address_ - begin == 1) {
1991 printer.DumpByte(begin);
1992 return 1u;
1993 }
1994 if ((*begin & 3u) == 3u) {
1995 if (GetDisassemblerOptions()->end_address_ - begin >= 4) {
1996 printer.Dump32(begin);
1997 return 4u;
1998 } else {
1999 printer.Dump2Byte(begin);
2000 return 2u;
2001 }
2002 } else {
2003 printer.Dump16(begin);
2004 return 2u;
2005 }
2006 }
2007
Dump(std::ostream & os,const uint8_t * begin,const uint8_t * end)2008 void DisassemblerRiscv64::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) {
2009 Printer printer(this, os);
2010 const uint8_t* cur = begin;
2011 if (cur < end && !IsAligned<2u>(cur)) {
2012 // Unaligned, dump as a `.byte` to get to an aligned address.
2013 printer.DumpByte(cur);
2014 cur += 1;
2015 }
2016 if (cur >= end) {
2017 return;
2018 }
2019 while (end - cur >= 4) {
2020 if ((*cur & 3u) == 3u) {
2021 printer.Dump32(cur);
2022 cur += 4;
2023 } else {
2024 printer.Dump16(cur);
2025 cur += 2;
2026 }
2027 }
2028 if (end - cur >= 2) {
2029 if ((*cur & 3u) == 3u) {
2030 // Not enough data for a 32-bit instruction. Dump as `.2byte`.
2031 printer.Dump2Byte(cur);
2032 } else {
2033 printer.Dump16(cur);
2034 }
2035 cur += 2;
2036 }
2037 if (end != cur) {
2038 CHECK_EQ(end - cur, 1);
2039 printer.DumpByte(cur);
2040 }
2041 }
2042
2043 } // namespace riscv64
2044 } // namespace art
2045