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