xref: /aosp_15_r20/art/disassembler/disassembler_riscv64.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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