Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/art/art/dexopt_chroot_setup/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 1 kB image not shown  

Quelle  disassembler_riscv64.cc

  Sprache: C
 

/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "disassembler_riscv64.h"

#include "android-base/logging.h"
#include "android-base/stringprintf.h"

#include "base/bit_utils.h"
#include "base/casts.h"

using android::base::StringPrintf;

namespace art {
namespace riscv64 {

class DisassemblerRiscv64::Printer {
 public:
  Printer(DisassemblerRiscv64* disassembler, std::ostream& os)
      : disassembler_(disassembler), os_(os) {}

  void Dump32(const uint8_t* insn);
  void Dump16(const uint8_t* insn);
  void Dump2Byte(const uint8_t* data);
  void DumpByte(const uint8_t* data);

 private:
  // This enumeration should mirror the declarations in runtime/arch/riscv64/registers_riscv64.h.
  // We do not include that file to avoid a dependency on libart.
  enum {
    Zero = 0,
    RA = 1,
    FP  = 8,
    TR  = 9,
  };

  enum class MemAddressMode : uint32_t {
    kUnitStride = 0b00,
    kIndexedUnordered = 0b01,
    kStrided = 0b10,
    kIndexedOrdered = 0b11,
  };

  enum class Nf : uint32_t {
    k1 = 0b000,
    k2 = 0b001,
    k3 = 0b010,
    k4 = 0b011,
    k5 = 0b100,
    k6 = 0b101,
    k7 = 0b110,
    k8 = 0b111,
  };

  enum class VAIEncodings : uint32_t {
    kOpIVV = 0b000,
    kOpFVV = 0b001,
    kOpMVV = 0b010,
    kOpIVI = 0b011,
    kOpIVX = 0b100,
    kOpFVF = 0b101,
    kOpMVX = 0b110,
    kOpCFG = 0b111,
  };

  class ScopedNewLinePrinter {
    std::ostream& os_;

   public:
    explicit ScopedNewLinePrinter(std::ostream& os) : os_(os) {}
    ~ScopedNewLinePrinter() { os_ << '\n'; }
  };

  static const char* XRegName(uint32_t regno);
  static const char* FRegName(uint32_t regno);
  static const char* VRegName(uint32_t regno);
  static const char* RoundingModeName(uint32_t rm);

  // Regular instruction immediate utils

  static int32_t Decode32Imm12(uint32_t insn32) {
    uint32_t sign = (insn32 >> 31);
    uint32_t imm12 = (insn32 >> 20);
    return static_cast<int32_t>(imm12) - static_cast<int32_t>(sign << 12);  // Sign-extend.
  }

  static uint32_t Decode32UImm7(uint32_t insn32) { return (insn32 >> 25) & 0x7Fu; }

  static uint32_t Decode32UImm12(uint32_t insn32) { return (insn32 >> 20) & 0xFFFu; }

  static int32_t Decode32StoreOffset(uint32_t insn32) {
    uint32_t bit11 = insn32 >> 31;
    uint32_t bits5_11 = insn32 >> 25;
    uint32_t bits0_4 = (insn32 >> 7) & 0x1fu;
    uint32_t imm = (bits5_11 << 5) + bits0_4;
    return static_cast<int32_t>(imm) - static_cast<int32_t>(bit11 << 12);  // Sign-extend.
  }

  // Compressed instruction immediate utils

  // Extracts the offset from a compressed instruction
  // where `offset[5:3]` is in bits `[12:10]` and `offset[2|6]` is in bits `[6:5]`
  static uint32_t Decode16CMOffsetW(uint32_t insn16) {
    DCHECK(IsUint<16>(insn16));
    return BitFieldExtract(insn16, 51) << 6 | BitFieldExtract(insn16, 103) << 3 |
           BitFieldExtract(insn16, 61) << 2;
  }

  // Extracts the offset from a compressed instruction
  // where `offset[5:3]` is in bits `[12:10]` and `offset[7:6]` is in bits `[6:5]`
  static uint32_t Decode16CMOffsetD(uint32_t insn16) {
    DCHECK(IsUint<16>(insn16));
    return BitFieldExtract(insn16, 52) << 6 | BitFieldExtract(insn16, 103) << 3;
  }

  // Re-orders raw immediatate into real value
  // where `imm[5:3]` is in bits `[5:3]` and `imm[8:6]` is in bits `[2:0]`
  static uint32_t Uimm6ToOffsetD16(uint32_t uimm6) {
    DCHECK(IsUint<6>(uimm6));
    return (BitFieldExtract(uimm6, 33) << 3) | (BitFieldExtract(uimm6, 03) << 6);
  }

  // Re-orders raw immediatate to form real value
  // where `imm[5:2]` is in bits `[5:2]` and `imm[7:6]` is in bits `[1:0]`
  static uint32_t Uimm6ToOffsetW16(uint32_t uimm6) {
    DCHECK(IsUint<6>(uimm6));
    return (BitFieldExtract(uimm6, 24) << 2) | (BitFieldExtract(uimm6, 02) << 6);
  }

  // Re-orders raw immediatate to form real value
  // where `imm[1]` is in bit `[0]` and `imm[0]` is in bit `[1]`
  static uint32_t Uimm2ToOffset10(uint32_t uimm2) {
    DCHECK(IsUint<2>(uimm2));
    return (uimm2 >> 1) | (uimm2 & 0x1u) << 1;
  }

  // Re-orders raw immediatate to form real value
  // where `imm[1]` is in bit `[0]` and `imm[0]` is `0`
  static uint32_t Uimm2ToOffset1(uint32_t uimm2) {
    DCHECK(IsUint<2>(uimm2));
    return (uimm2 & 0x1u) << 1;
  }

  template <size_t kWidth>
  static constexpr int32_t SignExtendBits(uint32_t bits) {
    static_assert(kWidth < BitSizeOf<uint32_t>());
    const uint32_t sign_bit = (bits >> kWidth) & 1u;
    return static_cast<int32_t>(bits) - static_cast<int32_t>(sign_bit << kWidth);
  }

  // Extracts the immediate from a compressed instruction
  // where `imm[5]` is in bit `[12]` and `imm[4:0]` is in bits `[6:2]`
  // and performs sign-extension if required
  template <typename T>
  static T Decode16Imm6(uint32_t insn16) {
    DCHECK(IsUint<16>(insn16));
    static_assert(std::is_integral_v<T>, "T must be integral");
    const T bits =
        BitFieldInsert(BitFieldExtract(insn16, 25), BitFieldExtract(insn16, 121), 51);
    const T checked_bits = dchecked_integral_cast<T>(bits);
    if (std::is_unsigned_v<T>) {
      return checked_bits;
    }
    return SignExtendBits<6>(checked_bits);
  }

  // Regular instruction register utils

  static uint32_t GetRd(uint32_t insn32) { return (insn32 >> 7) & 0x1fu; }
  static uint32_t GetRs1(uint32_t insn32) { return (insn32 >> 15) & 0x1fu; }
  static uint32_t GetRs2(uint32_t insn32) { return (insn32 >> 20) & 0x1fu; }
  static uint32_t GetRs3(uint32_t insn32) { return insn32 >> 27; }
  static uint32_t GetRoundingMode(uint32_t insn32) { return (insn32 >> 12) & 7u; }

  // Compressed instruction register utils

  static uint32_t GetRs1Short16(uint32_t insn16) { return BitFieldExtract(insn16, 73) + 8u; }
  static uint32_t GetRs2Short16(uint32_t insn16) { return BitFieldExtract(insn16, 23) + 8u; }
  static uint32_t GetRs1_16(uint32_t insn16) { return BitFieldExtract(insn16, 75); }
  static uint32_t GetRs2_16(uint32_t insn16) { return BitFieldExtract(insn16, 25); }

  void PrintBranchOffset(int32_t offset);
  void PrintLoadStoreAddress(uint32_t rs1, int32_t offset);

  void Print32Lui(uint32_t insn32);
  void Print32Auipc(const uint8_t* insn, uint32_t insn32);
  void Print32Jal(const uint8_t* insn, uint32_t insn32);
  void Print32Jalr(const uint8_t* insn, uint32_t insn32);
  void Print32BCond(const uint8_t* insn, uint32_t insn32);
  void Print32Load(uint32_t insn32);
  void Print32Store(uint32_t insn32);
  void Print32FLoad(uint32_t insn32);
  void Print32FStore(uint32_t insn32);
  void Print32BinOpImm(uint32_t insn32);
  void Print32BinOp(uint32_t insn32);
  void Print32Atomic(uint32_t insn32);
  void Print32FpOp(uint32_t insn32);
  void Print32RVVOp(uint32_t insn32);
  void Print32FpFma(uint32_t insn32);
  void Print32Zicsr(uint32_t insn32);
  void Print32Fence(uint32_t insn32);

  void AppendVType(uint32_t zimm);
  static const char* DecodeRVVMemMnemonic(const uint32_t insn32,
                                          bool is_load,
                                          /*out*/ const char** rs2);

  DisassemblerRiscv64* const disassembler_;
  std::ostream& os_;
};

const char* DisassemblerRiscv64::Printer::XRegName(uint32_t regno) {
  static const charconst kXRegisterNames[] = {
      "zero",
      "ra",
      "sp",
      "gp",
      "tp",
      "t0",
      "t1",
      "t2",
      "fp",  // s0/fp
      "tr",  // s1/tr - ART thread register
      "a0",
      "a1",
      "a2",
      "a3",
      "a4",
      "a5",
      "a6",
      "a7",
      "s2",
      "s3",
      "s4",
      "s5",
      "s6",
      "s7",
      "s8",
      "s9",
      "s10",
      "s11",
      "t3",
      "t4",
      "t5",
      "t6",
  };
  static_assert(std::size(kXRegisterNames) == 32);
  DCHECK_LT(regno, 32u);
  return kXRegisterNames[regno];
}

const char* DisassemblerRiscv64::Printer::FRegName(uint32_t regno) {
  static const charconst kFRegisterNames[] = {
      "ft0",
      "ft1",
      "ft2",
      "ft3",
      "ft4",
      "ft5",
      "ft6",
      "ft7",
      "fs0",
      "fs1",
      "fa0",
      "fa1",
      "fa2",
      "fa3",
      "fa4",
      "fa5",
      "fa6",
      "fa7",
      "fs2",
      "fs3",
      "fs4",
      "fs5",
      "fs6",
      "fs7",
      "fs8",
      "fs9",
      "fs10",
      "fs11",
      "ft8",
      "ft9",
      "ft10",
      "ft11",
  };
  static_assert(std::size(kFRegisterNames) == 32);
  DCHECK_LT(regno, 32u);
  return kFRegisterNames[regno];
}

const char* DisassemblerRiscv64::Printer::VRegName(uint32_t regno) {
  static const charconst kVRegisterNames[] = {
      "V0",
      "V1",
      "V2",
      "V3",
      "V4",
      "V5",
      "V6",
      "V7",
      "V8",
      "V9",
      "V10",
      "V11",
      "V12",
      "V13",
      "V14",
      "V15",
      "V16",
      "V17",
      "V18",
      "V19",
      "V20",
      "V21",
      "V22",
      "V23",
      "V24",
      "V25",
      "V26",
      "V27",
      "V28",
      "V29",
      "V30",
      "V31",
  };
  static_assert(std::size(kVRegisterNames) == 32);
  DCHECK_LT(regno, 32u);
  return kVRegisterNames[regno];
}

const char* DisassemblerRiscv64::Printer::RoundingModeName(uint32_t rm) {
  // Note: We do not print the rounding mode for DYN.
  static const charconst kRoundingModeNames[] = {
      ".rne"".rtz"".rdn"".rup"".rmm"".<reserved-rm>"".<reserved-rm>"/*DYN*/ ""
  };
  static_assert(std::size(kRoundingModeNames) == 8);
  DCHECK_LT(rm, 8u);
  return kRoundingModeNames[rm];
}

void DisassemblerRiscv64::Printer::PrintBranchOffset(int32_t offset) {
  os_ << (offset >= 0 ? "+" : "") << offset;
}

void DisassemblerRiscv64::Printer::PrintLoadStoreAddress(uint32_t rs1, int32_t offset) {
  if (offset != 0) {
    os_ << StringPrintf("%d", offset);
  }
  os_ << "(" << XRegName(rs1) << ")";

  if (rs1 == TR && offset >= 0) {
    // Add entrypoint name.
    os_ << " ; ";
    disassembler_->GetDisassemblerOptions()->thread_offset_name_function_(
        os_, dchecked_integral_cast<uint32_t>(offset));
  }
}

void DisassemblerRiscv64::Printer::Print32Lui(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x37u);
  // TODO(riscv64): Should we also print the actual sign-extend value?
  os_ << StringPrintf("lui %s, %u", XRegName(GetRd(insn32)), insn32 >> 12);
}

void DisassemblerRiscv64::Printer::Print32Auipc([[maybe_unused]] const uint8_t* insn,
                                                uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x17u);
  // TODO(riscv64): Should we also print the calculated address?
  os_ << StringPrintf("auipc %s, %u", XRegName(GetRd(insn32)), insn32 >> 12);
}

void DisassemblerRiscv64::Printer::Print32Jal(const uint8_t* insn, uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x6fu);
  // Print an alias if available.
  uint32_t rd = GetRd(insn32);
  os_ << (rd == Zero ? "j " : "jal ");
  if (rd != Zero && rd != RA) {
    os_ << XRegName(rd) << ", ";
  }
  uint32_t bit20 = (insn32 >> 31);
  uint32_t bits1_10 = (insn32 >> 21) & 0x3ffu;
  uint32_t bit11 = (insn32 >> 20) & 1u;
  uint32_t bits12_19 = (insn32 >> 12) & 0xffu;
  uint32_t imm = (bits1_10 << 1) + (bit11 << 11) + (bits12_19 << 12) + (bit20 << 20);
  int32_t offset = static_cast<int32_t>(imm) - static_cast<int32_t>(bit20 << 21);  // Sign-extend.
  PrintBranchOffset(offset);
  os_ << " ; " << disassembler_->FormatInstructionPointer(insn + offset);

  // TODO(riscv64): When we implement shared thunks to reduce AOT slow-path code size,
  // check if this JAL lands at an entrypoint load from TR and, if so, print its name.
}

void DisassemblerRiscv64::Printer::Print32Jalr([[maybe_unused]] const uint8_t* insn,
                                               uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x67u);
  DCHECK_EQ((insn32 >> 12) & 7u, 0u);
  uint32_t rd = GetRd(insn32);
  uint32_t rs1 = GetRs1(insn32);
  int32_t imm12 = Decode32Imm12(insn32);
  // Print shorter macro instruction notation if available.
  if (rd == Zero && rs1 == RA && imm12 == 0) {
    os_ << "ret";
  } else if (rd == Zero && imm12 == 0) {
    os_ << "jr " << XRegName(rs1);
  } else if (rd == RA && imm12 == 0) {
    os_ << "jalr " << XRegName(rs1);
  } else {
    // TODO(riscv64): Should we also print the calculated address if the preceding
    // instruction is AUIPC? (We would need to record the previous instruction.)
    os_ << "jalr " << XRegName(rd) << ", ";
    // Use the same format as llvm-objdump: "rs1" if `imm12` is zero, otherwise "imm12(rs1)".
    if (imm12 == 0) {
      os_ << XRegName(rs1);
    } else {
      os_ << imm12 << "(" << XRegName(rs1) << ")";
    }
  }
}

void DisassemblerRiscv64::Printer::Print32BCond(const uint8_t* insn, uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x63u);
  static const charconst kOpcodes[] = {
      "beq""bne", nullptr, nullptr, "blt""bge""bltu""bgeu"
  };
  uint32_t funct3 = (insn32 >> 12) & 7u;
  const char* opcode = kOpcodes[funct3];
  if (opcode == nullptr) {
    os_ << "<unknown32>";
    return;
  }

  // Print shorter macro instruction notation if available.
  uint32_t rs1 = GetRs1(insn32);
  uint32_t rs2 = GetRs2(insn32);
  if (rs2 == Zero) {
    os_ << opcode << "z " << XRegName(rs1);
  } else if (rs1 == Zero && (funct3 == 4u || funct3 == 5u)) {
    // blt zero, rs2, offset ... bgtz rs2, offset
    // bge zero, rs2, offset ... blez rs2, offset
    os_ << (funct3 == 4u ? "bgtz " : "blez ") << XRegName(rs2);
  } else {
    os_ << opcode << " " << XRegName(rs1) << ", " << XRegName(rs2);
  }
  os_ << ", ";

  uint32_t bit12 = insn32 >> 31;
  uint32_t bits5_10 = (insn32 >> 25) & 0x3fu;
  uint32_t bits1_4 = (insn32 >> 8) & 0xfu;
  uint32_t bit11 = (insn32 >> 7) & 1u;
  uint32_t imm = (bit12 << 12) + (bit11 << 11) + (bits5_10 << 5) + (bits1_4 << 1);
  int32_t offset = static_cast<int32_t>(imm) - static_cast<int32_t>(bit12 << 13);  // Sign-extend.
  PrintBranchOffset(offset);
  os_ << " ; " << disassembler_->FormatInstructionPointer(insn + offset);
}

void DisassemblerRiscv64::Printer::Print32Load(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x03u);
  static const charconst kOpcodes[] = {
      "lb""lh""lw""ld""lbu""lhu""lwu", nullptr
  };
  uint32_t funct3 = (insn32 >> 12) & 7u;
  const char* opcode = kOpcodes[funct3];
  if (opcode == nullptr) {
    os_ << "<unknown32>";
    return;
  }

  os_ << opcode << " " << XRegName(GetRd(insn32)) << ", ";
  PrintLoadStoreAddress(GetRs1(insn32), Decode32Imm12(insn32));

  // TODO(riscv64): If previous instruction is AUIPC for current `rs1` and we load
  // from the range specified by assembler options, print the loaded literal.
}

void DisassemblerRiscv64::Printer::Print32Store(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x23u);
  static const charconst kOpcodes[] = {
      "sb""sh""sw""sd", nullptr, nullptr, nullptr, nullptr
  };
  uint32_t funct3 = (insn32 >> 12) & 7u;
  const char* opcode = kOpcodes[funct3];
  if (opcode == nullptr) {
    os_ << "<unknown32>";
    return;
  }

  os_ << opcode << " " << XRegName(GetRs2(insn32)) << ", ";
  PrintLoadStoreAddress(GetRs1(insn32), Decode32StoreOffset(insn32));
}

const char* DisassemblerRiscv64::Printer::DecodeRVVMemMnemonic(const uint32_t insn32,
                                                               bool is_load,
                                                               /*out*/ const char** rs2) {
  const uint32_t width_index = (insn32 >> 12) & 3u;
  DCHECK_EQ(width_index != 0u, (insn32 & 0x4000u) != 0u);
  const uint32_t imm7 = Decode32UImm7(insn32);
  const enum Nf nf = static_cast<enum Nf>((imm7 >> 4) & 0x7u);
  const enum MemAddressMode mop = static_cast<enum MemAddressMode>((imm7 >> 1) & 0x3u);
  const uint32_t mew = (insn32 >> 28) & 1u;

  if (mew == 1u) {
    // 7.3. Vector Load/Store Width Encoding
    // The mew bit (inst[28]) when set is expected to be used to encode
    // expanded memory sizes of 128 bits and above,
    // but these encodings are currently reserved.
    return nullptr;
  }

  switch (mop) {
    case MemAddressMode::kUnitStride: {
      const uint32_t umop = GetRs2(insn32);
      switch (umop) {
        case 0b00000:  // Vector Unit-Stride Load/Store
          static constexpr const char* kVUSMnemonics[8][4] = {
              {"e8.v""e16.v""e32.v""e64.v"},
              {"seg2e8.v""seg2e16.v""seg2e32.v""seg2e64.v"},
              {"seg3e8.v""seg3e16.v""seg3e32.v""seg3e64.v"},
              {"seg4e8.v""seg4e16.v""seg4e32.v""seg4e64.v"},
              {"seg5e8.v""seg5e16.v""seg5e32.v""seg5e64.v"},
              {"seg6e8.v""seg6e16.v""seg6e32.v""seg6e64.v"},
              {"seg7e8.v""seg7e16.v""seg7e32.v""seg7e64.v"},
              {"seg8e8.v""seg8e16.v""seg8e32.v""seg8e64.v"},
          };
          return kVUSMnemonics[enum_cast<uint32_t>(nf)][width_index];
        case 0b01000: {  // Vector Whole Register Load/Store
          if (is_load) {
            static constexpr const char* kVWRLMnemonics[8][4] = {
                {"1re8.v""1re16.v""1re32.v""1re64.v"},
                {"2re8.v""2re16.v""2re32.v""2re64.v"},
                {nullptr, nullptr, nullptr, nullptr},
                {"4re8.v""4re16.v""4re32.v""4re64.v"},
                {nullptr, nullptr, nullptr, nullptr},
                {nullptr, nullptr, nullptr, nullptr},
                {nullptr, nullptr, nullptr, nullptr},
                {"8re8.v""8re16.v""8re32.v""8re64.v"},
            };
            return kVWRLMnemonics[enum_cast<uint32_t>(nf)][width_index];
          } else {
            if (width_index != 0) {
              return nullptr;
            }
            static constexpr const char* kVWRSMnemonics[8] = {
                "1r""2r", nullptr, "4r", nullptr, nullptr, nullptr, "8r"
            };
            return kVWRSMnemonics[enum_cast<uint32_t>(nf)];
          }
        }
        case 0b01011:  // Vector Unit-Stride Mask Load/Store
          if (nf == Nf::k1 && width_index == 0 && (imm7 & 1u) == 1u) {
            return "m.v";
          } else {
            return nullptr;
          }
        case 0b10000:  // Vector Unit-Stride Fault-Only-First Load
          static constexpr const char* kVUSFFLMnemonics[8][4] = {
              {"e8ff.v""e16ff.v""e32ff.v""e64ff.v"},
              {"seg2e8ff.v""seg2e16ff.v""seg2e32ff.v""seg2e64ff.v"},
              {"seg3e8ff.v""seg3e16ff.v""seg3e32ff.v""seg3e64ff.v"},
              {"seg4e8ff.v""seg4e16ff.v""seg4e32ff.v""seg4e64ff.v"},
              {"seg5e8ff.v""seg5e16ff.v""seg5e32ff.v""seg5e64ff.v"},
              {"seg6e8ff.v""seg6e16ff.v""seg6e32ff.v""seg6e64ff.v"},
              {"seg7e8ff.v""seg7e16ff.v""seg7e32ff.v""seg7e64ff.v"},
              {"seg8e8ff.v""seg8e16ff.v""seg8e32ff.v""seg8e64ff.v"},
          };
          return is_load ? kVUSFFLMnemonics[enum_cast<uint32_t>(nf)][width_index] : nullptr;
        default:  // Unknown
          return nullptr;
      }
    }
    case MemAddressMode::kIndexedUnordered: {
      static constexpr const char* kVIUMnemonics[8][4] = {
          {"uxei8.v""uxei16.v""uxei32.v""uxei64.v"},
          {"uxseg2ei8.v""uxseg2ei16.v""uxseg2ei32.v""uxseg2ei64.v"},
          {"uxseg3ei8.v""uxseg3ei16.v""uxseg3ei32.v""uxseg3ei64.v"},
          {"uxseg4ei8.v""uxseg4ei16.v""uxseg4ei32.v""uxseg4ei64.v"},
          {"uxseg5ei8.v""uxseg5ei16.v""uxseg5ei32.v""uxseg5ei64.v"},
          {"uxseg6ei8.v""uxseg6ei16.v""uxseg6ei32.v""uxseg6ei64.v"},
          {"uxseg7ei8.v""uxseg7ei16.v""uxseg7ei32.v""uxseg7ei64.v"},
          {"uxseg8ei8.v""uxseg8ei16.v""uxseg8ei32.v""uxseg8ei64.v"},
      };
      *rs2 = VRegName(GetRs2(insn32));
      return kVIUMnemonics[enum_cast<uint32_t>(nf)][width_index];
    }
    case MemAddressMode::kStrided: {
      static constexpr const char* kVSMnemonics[8][4] = {
          {"se8.v""se16.v""se32.v""se64.v"},
          {"sseg2e8.v""sseg2e16.v""sseg2e32.v""sseg2e64.v"},
          {"sseg3e8.v""sseg3e16.v""sseg3e32.v""sseg3e64.v"},
          {"sseg4e8.v""sseg4e16.v""sseg4e32.v""sseg4e64.v"},
          {"sseg5e8.v""sseg5e16.v""sseg5e32.v""sseg5e64.v"},
          {"sseg6e8.v""sseg6e16.v""sseg6e32.v""sseg6e64.v"},
          {"sseg7e8.v""sseg7e16.v""sseg7e32.v""sseg7e64.v"},
          {"sseg8e8.v""sseg8e16.v""sseg8e32.v""sseg8e64.v"},
      };
      *rs2 = XRegName(GetRs2(insn32));
      return kVSMnemonics[enum_cast<uint32_t>(nf)][width_index];
    }
    case MemAddressMode::kIndexedOrdered: {
      static constexpr const char* kVIOMnemonics[8][4] = {
          {"oxei8.v""oxei16.v""oxei32.v""oxei64.v"},
          {"oxseg2ei8.v""oxseg2ei16.v""oxseg2ei32.v""oxseg2ei64.v"},
          {"oxseg3ei8.v""oxseg3ei16.v""oxseg3ei32.v""oxseg3ei64.v"},
          {"oxseg4ei8.v""oxseg4ei16.v""oxseg4ei32.v""oxseg4ei64.v"},
          {"oxseg5ei8.v""oxseg5ei16.v""oxseg5ei32.v""oxseg5ei64.v"},
          {"oxseg6ei8.v""oxseg6ei16.v""oxseg6ei32.v""oxseg6ei64.v"},
          {"oxseg7ei8.v""oxseg7ei16.v""oxseg7ei32.v""oxseg7ei64.v"},
          {"oxseg8ei8.v""oxseg8ei16.v""oxseg8ei32.v""oxseg8ei64.v"},
      };
      *rs2 = VRegName(GetRs2(insn32));
      return kVIOMnemonics[enum_cast<uint32_t>(nf)][width_index];
    }
  }
}

static constexpr const char* kFpMemMnemonics[] = {
    nullptr, "h""w""d""q", nullptr, nullptr, nullptr
};

void DisassemblerRiscv64::Printer::Print32FLoad(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x07u);
  int32_t offset = 0;
  const char* rd = nullptr;
  const char* rs2 = nullptr;
  const char* vm = "";
  const uint32_t funct3 = (insn32 >> 12) & 7u;
  const char* mnemonic = kFpMemMnemonics[funct3];
  const char* prefix = "f";
  if (mnemonic == nullptr) {
    // Vector Loads
    prefix = "v";
    mnemonic = DecodeRVVMemMnemonic(insn32, /*is_load=*/true, &rs2);
    rd = VRegName(GetRd(insn32));

    if ((Decode32UImm7(insn32) & 0x1U) == 0) {
      vm = ", v0.t";
    }
  } else {
    rd = FRegName(GetRd(insn32));
    offset = Decode32Imm12(insn32);
  }

  if (mnemonic == nullptr) {
    os_ << "<unknown32>";
    return;
  }

  os_ << prefix << "l" << mnemonic << " " << rd << ", ";
  PrintLoadStoreAddress(GetRs1(insn32), offset);

  if (rs2) {
    os_ << ", " << rs2;
  }

  os_ << vm;

  // TODO(riscv64): If previous instruction is AUIPC for current `rs1` and we load
  // from the range specified by assembler options, print the loaded literal.
}

void DisassemblerRiscv64::Printer::Print32FStore(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x27u);
  uint32_t funct3 = (insn32 >> 12) & 3u;
  const char* prefix = "f";
  const char* mnemonic = kFpMemMnemonics[funct3];

  if (mnemonic == nullptr) {
    // Vector Stores
    const char* rs2 = nullptr;
    prefix = "v";
    mnemonic = DecodeRVVMemMnemonic(insn32, /*is_load=*/false, &rs2);

    if (mnemonic == nullptr) {
      os_ << "<unknown32>";
      return;
    }

    os_ << prefix << "s" << mnemonic << " " << VRegName(GetRd(insn32)) << ", ";
    PrintLoadStoreAddress(GetRs1(insn32), 0);

    if (rs2) {
      os_ << ", " << rs2;
    }

    if ((Decode32UImm7(insn32) & 0x1U) == 0) {
      os_ << ", v0.t";
    }
  } else {
    os_ << prefix << "s" << mnemonic << " " << FRegName(GetRs2(insn32)) << ", ";
    PrintLoadStoreAddress(GetRs1(insn32), Decode32StoreOffset(insn32));
  }
}

void DisassemblerRiscv64::Printer::Print32BinOpImm(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x77u, 0x13u);  // Note: Bit 0x8 selects narrow binop.
  bool narrow = (insn32 & 0x8u) != 0u;
  uint32_t funct3 = (insn32 >> 12) & 7u;
  uint32_t funct6 = (insn32 >> 26) & 0x3Fu;
  uint32_t rd = GetRd(insn32);
  uint32_t rs1 = GetRs1(insn32);
  int32_t imm = Decode32Imm12(insn32);

  // Print shorter macro instruction notation if available.
  if (funct3 == /*ADDI*/ 0u && imm == 0u) {
    if (narrow) {
      os_ << "sextw " << XRegName(rd) << ", " << XRegName(rs1);
    } else if (rd == Zero && rs1 == Zero) {
      os_ << "nop";  // Only canonical nop. Non-Zero `rd == rs1` nops are printed as "mv".
    } else {
      os_ << "mv " << XRegName(rd) << ", " << XRegName(rs1);
    }
  } else if (!narrow && funct3 == /*XORI*/ 4u && imm == -1) {
    os_ << "not " << XRegName(rd) << ", " << XRegName(rs1);
  } else if (!narrow && funct3 == /*ANDI*/ 7u && imm == 0xff) {
    os_ << "zextb " << XRegName(rd) << ", " << XRegName(rs1);
  } else if (!narrow && funct3 == /*SLTIU*/ 3u && imm == 1) {
    os_ << "seqz " << XRegName(rd) << ", " << XRegName(rs1);
  } else if ((insn32 & 0xfc00707fu) == 0x0800101bu) {
    os_ << "slli.uw " << XRegName(rd) << ", " << XRegName(rs1) << ", " << (imm & 0x3fu);
  } else if ((imm ^ 0x600u) < 3u && funct3 == 1u) {
    static const charconst kBitOpcodes[] = { "clz""ctz""cpop" };
    os_ << kBitOpcodes[imm ^ 0x600u] << (narrow ? "w " : " ")
        << XRegName(rd) << ", " << XRegName(rs1);
  } else if ((imm ^ 0x600u) < (narrow ? 32 : 64) && funct3 == 5u) {
    os_ << "rori" << (narrow ? "w " : " ")
        << XRegName(rd) << ", " << XRegName(rs1) << ", " << (imm ^ 0x600u);
  } else if (imm == 0x287u && !narrow && funct3 == 5u) {
    os_ << "orc.b " << XRegName(rd) << ", " << XRegName(rs1);
  } else if (imm == 0x6b8u && !narrow && funct3 == 5u) {
    os_ << "rev8 " << XRegName(rd) << ", " << XRegName(rs1);
  } else if (!narrow && funct6 == 0x12u && (funct3 == /*BCLRI*/ 1u || funct3 == /*BEXTI*/ 5u)) {
    os_ << ((funct3 == /*BCLRI*/ 1u) ? "bclri" : "bexti");
  } else if (!narrow && funct6 == 0x1Au && funct3 == 0x1u /*BINVI*/) {
    os_ << "binvi";
  } else if (!narrow && funct6 == 0xAu && funct3 == 0x1u /*BSETI*/) {
    os_ << "bseti";
  } else {
    bool bad_high_bits = false;
    if (funct3 == /*SLLI*/ 1u || funct3 == /*SRLI/SRAI*/ 5u) {
      imm &= (narrow ? 0x1fu : 0x3fu);
      uint32_t high_bits = insn32 & (narrow ? 0xfe000000u : 0xfc000000u);
      if (high_bits == 0x40000000u && funct3 == /*SRAI*/ 5u) {
        os_ << "srai";
      } else {
        os_ << ((funct3 == /*SRLI*/ 5u) ? "srli" : "slli");
        bad_high_bits = (high_bits != 0u);
      }
    } else if (!narrow || funct3 == /*ADDI*/ 0u) {
      static const charconst kOpcodes[] = {
          "addi", nullptr, "slti""sltiu""xori", nullptr, "ori""andi"
      };
      DCHECK(kOpcodes[funct3] != nullptr);
      os_ << kOpcodes[funct3];
    } else {
      os_ << "<unknown32>";  // There is no SLTIW/SLTIUW/XORIW/ORIW/ANDIW.
      return;
    }
    os_ << (narrow ? "w " : " ") << XRegName(rd) << ", " << XRegName(rs1) << ", " << imm;
    if (bad_high_bits) {
      os_ << " (invalid high bits)";
    }
  }
}

void DisassemblerRiscv64::Printer::Print32BinOp(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x77u, 0x33u);  // Note: Bit 0x8 selects narrow binop.
  bool narrow = (insn32 & 0x8u) != 0u;
  uint32_t funct3 = (insn32 >> 12) & 7u;
  uint32_t funct7 = (insn32 >> 25) & 0x7Fu;
  uint32_t rd = GetRd(insn32);
  uint32_t rs1 = GetRs1(insn32);
  uint32_t rs2 = GetRs2(insn32);

  // Print shorter macro instruction notation if available.
  if (funct7 == 0x20u && funct3 == /*SUB*/ 0u && rs1 == Zero) {
    os_ << (narrow ? "negw " : "neg ") << XRegName(rd) << ", " << XRegName(rs2);
  } else if (!narrow && funct3 == /*SLT*/ 2u && rs2 == Zero) {
    os_ << "sltz " << XRegName(rd) << ", " << XRegName(rs1);
  } else if (!narrow && funct3 == /*SLT*/ 2u && rs1 == Zero) {
    os_ << "sgtz " << XRegName(rd) << ", " << XRegName(rs2);
  } else if (!narrow && funct3 == /*SLTU*/ 3u && rs1 == Zero) {
    os_ << "snez " << XRegName(rd) << ", " << XRegName(rs2);
  } else if (narrow && funct7 == 4u && funct3 == /*ADD.UW*/ 0u && rs2 == Zero) {
    os_ << "zext.w " << XRegName(rd) << ", " << XRegName(rs1);
  } else if (!narrow && funct7 == 0x24u &&
             (funct3 == /*BCLR*/ 1u || funct3 == /*BEXT*/ 5u)) {
    os_ << ((funct3 == /*BCLR*/ 1u) ? "bclr" : "bext");
  } else if (!narrow && funct7 == 0x34u && funct3 == /*BINV*/ 1u) {
    os_ << "binv";
  } else if (!narrow && funct7 == 0x14u && funct3 == /*BSET*/ 1u) {
    os_ << "bset";
  } else {
    bool bad_high_bits = false;
    if (funct7 == 0x20u && (funct3 == /*SUB*/ 0u || funct3 == /*SRA*/ 5u)) {
      os_ << ((funct3 == /*SUB*/ 0u) ? "sub" : "sra");
    } else if (funct7 == 1u &&
               (!narrow || (funct3 == /*MUL*/ 0u || funct3 >= /*DIV/DIVU/REM/REMU*/ 4u))) {
      static const charconst kOpcodes[] = {
          "mul""mulh""mulhsu""mulhu""div""divu""rem""remu"
      };
      os_ << kOpcodes[funct3];
    } else if (funct7 == 4u && narrow && funct3 == /*ADD.UW*/ 0u) {
      os_ << "add.u";  // "w" is added below.
    } else if (funct7 == 0x10u && (funct3 & 1u) == 0u && funct3 != 0u) {
      static const charconst kZbaOpcodes[] = { nullptr, "sh1add""sh2add""sh3add" };
      DCHECK(kZbaOpcodes[funct3 >> 1] != nullptr);
      os_ << kZbaOpcodes[funct3 >> 1] << (narrow ? ".u" /* "w" is added below. */ : "");
    } else if (funct7 == 0x20u && !narrow && funct3 >= 4u && funct3 != 5u) {
      static const charconst kZbbNegOpcodes[] = { "xnor", nullptr, "orn""andn" };
      DCHECK(kZbbNegOpcodes[funct3 - 4u] != nullptr);
      os_ << kZbbNegOpcodes[funct3 - 4u];
    } else if (funct7 == 0x5u && !narrow && funct3 >= 4u) {
      static const charconst kZbbMinMaxOpcodes[] = { "min""minu""max""maxu" };
      DCHECK(kZbbMinMaxOpcodes[funct3 - 4u] != nullptr);
      os_ << kZbbMinMaxOpcodes[funct3 - 4u];
    } else if (funct7 == 0x30u && (funct3 == /*ROL*/ 1u || funct3 == /*ROL*/ 5u)) {
      os_ << (funct3 == /*ROL*/ 1u ? "rol" : "ror");
    } else if (!narrow || (funct3 == /*ADD*/ 0u || funct3 == /*SLL*/ 1u || funct3 == /*SRL*/ 5u)) {
      static const charconst kOpcodes[] = {
          "add""sll""slt""sltu""xor""srl""or""and"
      };
      os_ << kOpcodes[funct3];
      bad_high_bits = funct7 != 0u;
    } else {
      DCHECK(narrow);
      os_ << "<unknown32>";  // Some of the above instructions do not have a narrow version.
      return;
    }
    os_ << (narrow ? "w " : " ") << XRegName(rd) << ", " << XRegName(rs1) << ", " << XRegName(rs2);
    if (bad_high_bits) {
      os_ << " (invalid high bits)";
    }
  }
}

void DisassemblerRiscv64::Printer::Print32Atomic(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x2fu);
  uint32_t funct3 = (insn32 >> 12) & 7u;
  uint32_t funct5 = (insn32 >> 27);
  if ((funct3 != 2u && funct3 != 3u) ||  // There are only 32-bit and 64-bit LR/SC/AMO*.
      (((funct5 & 3u) != 0u) && funct5 >= 4u)) {  // Only multiples of 4, or 1-3.
    os_ << "<unknown32>";
    return;
  }
  static const charconst kMul4Opcodes[] = {
      "amoadd""amoxor""amoor""amoand""amomin""amomax""amominu""amomaxu"
  };
  static const charconst kOtherOpcodes[] = {
      nullptr, "amoswap""lr""sc"
  };
  const char* opcode = ((funct5 & 3u) == 0u) ? kMul4Opcodes[funct5 >> 2] : kOtherOpcodes[funct5];
  DCHECK(opcode != nullptr);
  uint32_t rd = GetRd(insn32);
  uint32_t rs1 = GetRs1(insn32);
  uint32_t rs2 = GetRs2(insn32);
  const char* type = (funct3 == 2u) ? ".w" : ".d";
  const char* aq = (((insn32 >> 26) & 1u) != 0u) ? ".aq" : "";
  const char* rl = (((insn32 >> 25) & 1u) != 0u) ? ".rl" : "";
  os_ << opcode << type << aq << rl << " " << XRegName(rd) << ", " << XRegName(rs1);
  if (funct5 == /*LR*/ 2u) {
    if (rs2 != 0u) {
      os_ << " (bad rs2)";
    }
  } else {
    os_ << ", " << XRegName(rs2);
  }
}

void DisassemblerRiscv64::Printer::Print32FpOp(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x53u);
  uint32_t rd = GetRd(insn32);
  uint32_t rs1 = GetRs1(insn32);
  uint32_t rs2 = GetRs2(insn32);  // Sometimes used to to differentiate opcodes.
  uint32_t rm = GetRoundingMode(insn32);  // Sometimes used to to differentiate opcodes.
  uint32_t funct7 = insn32 >> 25;
  const char* type = ((funct7 & 1u) != 0u) ? ".d" : ".s";
  if ((funct7 & 2u) != 0u) {
    os_ << "<unknown32>";  // Note: This includes the "H" and "Q" extensions.
    return;
  }
  switch (funct7 >> 2) {
    case 0u:
    case 1u:
    case 2u:
    case 3u: {
      static const charconst kOpcodes[] = { "fadd""fsub""fmul""fdiv" };
      os_ << kOpcodes[funct7 >> 2] << type << RoundingModeName(rm) << " "
          << FRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
      return;
    }
    case 4u: {  // FSGN*
      // Print shorter macro instruction notation if available.
      static const charconst kOpcodes[] = { "fsgnj""fsgnjn""fsgnjx" };
      if (rm < std::size(kOpcodes)) {
        if (rs1 == rs2) {
          static const charconst kAltOpcodes[] = { "fmv""fneg""fabs" };
          static_assert(std::size(kOpcodes) == std::size(kAltOpcodes));
          os_ << kAltOpcodes[rm] << type << " " << FRegName(rd) << ", " << FRegName(rs1);
        } else {
          os_ << kOpcodes[rm] << type << " "
              << FRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
        }
        return;
      }
      break;
    }
    case 5u: {  // FMIN/FMAX
      static const charconst kOpcodes[] = { "fmin""fmax" };
      if (rm < std::size(kOpcodes)) {
        os_ << kOpcodes[rm] << type << " "
            << FRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
        return;
      }
      break;
    }
    case 0x8u:  // FCVT between FP numbers.
      if ((rs2 ^ 1u) == (funct7 & 1u)) {
        os_ << ((rs2 != 0u) ? "fcvt.s.d" : "fcvt.d.s") << RoundingModeName(rm) << " "
            << FRegName(rd) << ", " << FRegName(rs1);
      }
      break;
    case 0xbu:
      if (rs2 == 0u) {
        os_ << "fsqrt" << type << RoundingModeName(rm) << " "
            << FRegName(rd) << ", " << FRegName(rs1);
        return;
      }
      break;
    case 0x14u: {  // FLE/FLT/FEQ
      static const charconst kOpcodes[] = { "fle""flt""feq" };
      if (rm < std::size(kOpcodes)) {
        os_ << kOpcodes[rm] << type << " "
            << XRegName(rd) << ", " << FRegName(rs1) << ", " << FRegName(rs2);
        return;
      }
      break;
    }
    case 0x18u: {  // FCVT from floating point numbers to integers
      static const charconst kIntTypes[] = { "w""wu""l""lu" };
      if (rs2 < std::size(kIntTypes)) {
        os_ << "fcvt." << kIntTypes[rs2] << type << RoundingModeName(rm) << " "
            << XRegName(rd) << ", " << FRegName(rs1);
        return;
      }
      break;
    }
    case 0x1au: {  // FCVT from integers to floating point numbers
      static const charconst kIntTypes[] = { "w""wu""l""lu" };
      if (rs2 < std::size(kIntTypes)) {
        os_ << "fcvt" << type << "." << kIntTypes[rs2] << RoundingModeName(rm) << " "
            << FRegName(rd) << ", " << XRegName(rs1);
        return;
      }
      break;
    }
    case 0x1cu:  // FMV from FPR to GPR, or FCLASS
      if (rs2 == 0u && rm == 0u) {
        os_ << (((funct7 & 1u) != 0u) ? "fmv.x.d " : "fmv.x.w ")
            << XRegName(rd) << ", " << FRegName(rs1);
        return;
      } else if (rs2 == 0u && rm == 1u) {
        os_ << "fclass" << type << " " << XRegName(rd) << ", " << FRegName(rs1);
        return;
      }
      break;
    case 0x1eu:  // FMV from GPR to FPR
      if (rs2 == 0u && rm == 0u) {
        os_ << (((funct7 & 1u) != 0u) ? "fmv.d.x " : "fmv.w.x ")
            << FRegName(rd) << ", " << XRegName(rs1);
        return;
      }
      break;
    default:
      break;
  }
  os_ << "<unknown32>";
}

void DisassemblerRiscv64::Printer::AppendVType(uint32_t vtype) {
  const uint32_t lmul_v = vtype & 0x7U;
  const uint32_t vsew_v = (vtype >> 3) & 0x7U;
  const uint32_t vta_v = (vtype >> 6) & 0x1U;
  const uint32_t vma_v = (vtype >> 7) & 0x1U;

  if ((vsew_v & 0x4U) == 0u) {
    if (lmul_v != 0b100) {
      static const charconst vsews[] = {"e8""e16""e32""e64"};
      static const charconst lmuls[] = {
          "m1""m2""m4""m8", nullptr, "mf8""mf4""mf2"
      };

      const char* vma = vma_v ? "ma" : "mu";
      const char* vta = vta_v ? "ta" : "tu";
      const char* vsew = vsews[vsew_v & 0x3u];
      const char* lmul = lmuls[lmul_v];

      os_ << vsew << ", " << lmul << ", " << vta << ", " << vma;
      return;
    }
  }

  os_ << StringPrintf("0x%08x", vtype) << "\t# incorrect VType literal";
}

static constexpr uint32_t VWXUNARY0 = 0b010000;
static constexpr uint32_t VRXUNARY0 = 0b010000;
static constexpr uint32_t VXUNARY0 = 0b010010;
static constexpr uint32_t VMUNARY0 = 0b010100;

static constexpr uint32_t VWFUNARY0 = 0b010000;
static constexpr uint32_t VRFUNARY0 = 0b010000;
static constexpr uint32_t VFUNARY0 = 0b010010;
static constexpr uint32_t VFUNARY1 = 0b010011;

static void MaybeSwapOperands(uint32_t funct6,
                              /*inout*/ const char*& rs1,
                              /*inout*/ const char*& rs2) {
  if ((0x28u <= funct6 && funct6 < 0x30u) || funct6 >= 0x3Cu) {
    std::swap(rs1, rs2);
  }
}

void DisassemblerRiscv64::Printer::Print32RVVOp(uint32_t insn32) {
  // TODO(riscv64): Print pseudo-instruction aliases when applicable.
  DCHECK_EQ(insn32 & 0x7fu, 0x57u);
  const enum VAIEncodings vai = static_cast<enum VAIEncodings>((insn32 >> 12) & 7u);
  const uint32_t funct7 = Decode32UImm7(insn32);
  const uint32_t funct6 = funct7 >> 1;
  const bool masked = (funct7 & 1) == 0;
  const char* vm = masked ? ", v0.t" : "";
  const char* opcode = nullptr;
  const char* rd = nullptr;
  const char* rs1 = nullptr;
  const char* rs2 = nullptr;

  switch (vai) {
    case VAIEncodings::kOpIVV: {
      static constexpr const char* kOPIVVOpcodes[64] = {
          "vadd.vv",      nullptr,       "vsub.vv",         nullptr,
          "vminu.vv",     "vmin.vv",     "vmaxu.vv",        "vmax.vv",
          nullptr,        "vand.vv",     "vor.vv",          "vxor.vv",
          "vrgather.vv",  nullptr,       "vrgatherei16.vv", nullptr,
          "vadc.vvm",     "vmadc.vvm",   "vsbc.vvm",        "vmsbc.vvm",
          nullptr,        nullptr,       nullptr,           "<vmerge/vmv>",
          "vmseq.vv",     "vmsne.vv",    "vmsltu.vv",       "vmslt.vv",
          "vmsleu.vv",    "vmsle.vv",    nullptr,           nullptr,
          "vsaddu.vv",    "vsadd.vv",    "vssubu.vv",       "vssub.vv",
          nullptr,        "vsll.vv",     nullptr,           "vsmul.vv",
          "vsrl.vv",      "vsra.vv",     "vssrl.vv",        "vssra.vv",
          "vnsrl.wv",     "vnsra.wv",    "vnclipu.wv",      "vnclip.wv",
          "vwredsumu.vs""vwredsum.vs", nullptr,           nullptr,
          nullptr,        nullptr,       nullptr,           nullptr,
          nullptr,        nullptr,       nullptr,           nullptr,
          nullptr,        nullptr,       nullptr,           nullptr,
      };

      rs2 = VRegName(GetRs2(insn32));
      if (funct6 == 0b010111) {
        // vmerge/vmv
        if (masked) {
          opcode = "vmerge.vvm";
          vm = ", v0";
        } else if (GetRs2(insn32) == 0) {
          opcode = "vmv.v.v";
          rs2 = nullptr;
        } else {
          opcode = nullptr;
        }
      } else {
        opcode = kOPIVVOpcodes[funct6];
      }

      rd = VRegName(GetRd(insn32));
      rs1 = VRegName(GetRs1(insn32));
      break;
    }
    case VAIEncodings::kOpIVX: {
      static constexpr const char* kOPIVXOpcodes[64] = {
          "vadd.vx",     nullptr,     "vsub.vx",     "vrsub.vx",
          "vminu.vx",    "vmin.vx",   "vmaxu.vx",    "vmax.vx",
          nullptr,       "vand.vx",   "vor.vx",      "vxor.vx",
          "vrgather.vx", nullptr,     "vslideup.vx""vslidedown.vx",
          "vadc.vxm",    "vmadc.vxm""vsbc.vxm",    "vmsbc.vxm",
          nullptr,       nullptr,     nullptr,       "<vmerge/vmv>",
          "vmseq.vx",    "vmsne.vx",  "vmsltu.vx",   "vmslt.vx",
          "vmsleu.vx",   "vmsle.vx",  "vmsgtu.vx",   "vmsgt.vx",
          "vsaddu.vx",   "vsadd.vx",  "vssubu.vx",   "vssub.vx",
          nullptr,       "vsll.vx",   nullptr,       "vsmul.vx",
          "vsrl.vx",     "vsra.vx",   "vssrl.vx",    "vssra.vx",
          "vnsrl.wx",    "vnsra.wx",  "vnclipu.wx",  "vnclip.wx",
          nullptr,       nullptr,     nullptr,       nullptr,
          nullptr,       nullptr,     nullptr,       nullptr,
          nullptr,       nullptr,     nullptr,       nullptr,
          nullptr,       nullptr,     nullptr,       nullptr,
      };

      rs2 = VRegName(GetRs2(insn32));
      if (funct6 == 0b010111) {
        // vmerge/vmv
        if (masked) {
          opcode = "vmerge.vxm";
          vm = ", v0";
        } else if (GetRs2(insn32) == 0) {
          opcode = "vmv.v.x";
          rs2 = nullptr;
        } else {
          opcode = nullptr;
        }
      } else {
        opcode = kOPIVXOpcodes[funct6];
      }

      rd = VRegName(GetRd(insn32));
      rs1 = XRegName(GetRs1(insn32));
      break;
    }
    case VAIEncodings::kOpIVI: {
      static constexpr const char* kOPIVIOpcodes[64] = {
          "vadd.vi",     nullptr,     nullptr,       "vrsub.vi",
          nullptr,       nullptr,     nullptr,       nullptr,
          nullptr,       "vand.vi",   "vor.vi",      "vxor.vi",
          "vrgather.vi", nullptr,     "vslideup.vi""vslidedown.vi",
          "vadc.vim",    "vmadc.vim", nullptr,       nullptr,
          nullptr,       nullptr,     nullptr,       "<vmerge/vmv>",
          "vmseq.vi",    "vmsne.vi",  nullptr,       nullptr,
          "vmsleu.vi",   "vmsle.vi",  "vmsgtu.vi",   "vmsgt.vi",
          "vsaddu.vi",   "vsadd.vi",  nullptr,       nullptr,
          nullptr,       "vsll.vi",   nullptr,       "<vmvNr.v>",
          "vsrl.vi",     "vsra.vi",   "vssrl.vi",    "vssra.vi",
          "vnsrl.wi",    "vnsra.wi",  "vnclipu.wi",  "vnclip.wi",
          nullptr,       nullptr,     nullptr,       nullptr,
          nullptr,       nullptr,     nullptr,       nullptr,
          nullptr,       nullptr,     nullptr,       nullptr,
          nullptr,       nullptr,     nullptr,       nullptr,
      };

      rs2 = VRegName(GetRs2(insn32));

      if (funct6 == 0b010111) {
        // vmerge/vmv
        if (masked) {
          opcode = "vmerge.vim";
          vm = ", v0";
        } else if (GetRs2(insn32) == 0) {
          opcode = "vmv.v.i";
          rs2 = nullptr;
        } else {
          opcode = nullptr;
        }
      } else if (funct6 == 0b100111) {
        uint32_t rs1V = GetRs1(insn32);
        static constexpr const char* kVmvnrOpcodes[8] = {
            "vmv1r.v""vmv2r.v", nullptr, "vmv4r.v",
            nullptr,   nullptr,   nullptr, "vmv8r.v",
        };
        if (IsUint<3>(rs1V)) {
          opcode = kVmvnrOpcodes[rs1V];
        }
      } else {
        opcode = kOPIVIOpcodes[funct6];
      }

      rd = VRegName(GetRd(insn32));
      break;
    }
    case VAIEncodings::kOpMVV: {
      switch (funct6) {
        case VWXUNARY0: {
          static constexpr const char* kVWXUNARY0Opcodes[32] = {
              "vmv.x.s", nullptr,    nullptr, nullptr,
              nullptr,   nullptr,    nullptr, nullptr,
              nullptr,   nullptr,    nullptr, nullptr,
              nullptr,   nullptr,    nullptr, nullptr,
              "vcpop.m""vfirst.m", nullptr, nullptr,
              nullptr,   nullptr,    nullptr, nullptr,
              nullptr,   nullptr,    nullptr, nullptr,
              nullptr,   nullptr,    nullptr, nullptr,
          };
          opcode = kVWXUNARY0Opcodes[GetRs1(insn32)];
          rd = XRegName(GetRd(insn32));
          rs2 = VRegName(GetRs2(insn32));
          break;
        }
        case VXUNARY0: {
          static constexpr const char* kVXUNARY0Opcodes[32] = {
              nullptr,     nullptr,     "vzext.vf8""vsext.vf8",
              "vzext.vf4""vsext.vf4""vzext.vf2""vsext.vf2",
              nullptr,     nullptr,     nullptr,     nullptr,
              nullptr,     nullptr,     nullptr,     nullptr,
              nullptr,     nullptr,     nullptr,     nullptr,
              nullptr,     nullptr,     nullptr,     nullptr,
              nullptr,     nullptr,     nullptr,     nullptr,
              nullptr,     nullptr,     nullptr,     nullptr,
          };
          opcode = kVXUNARY0Opcodes[GetRs1(insn32)];
          rd = VRegName(GetRd(insn32));
          rs2 = VRegName(GetRs2(insn32));
          break;
        }
        case VMUNARY0: {
          static constexpr const char* kVMUNARY0Opcodes[32] = {
              nullptr,   "vmsbf.m""vmsof.m""vmsif.m",
              nullptr,   nullptr,   nullptr,   nullptr,
              nullptr,   nullptr,   nullptr,   nullptr,
              nullptr,   nullptr,   nullptr,   nullptr,
              "viota.m""vid.v",   nullptr,   nullptr,
              nullptr,   nullptr,   nullptr,   nullptr,
              nullptr,   nullptr,   nullptr,   nullptr,
              nullptr,   nullptr,   nullptr,   nullptr,
          };
          opcode = kVMUNARY0Opcodes[GetRs1(insn32)];
          rd = VRegName(GetRd(insn32));
          rs2 = VRegName(GetRs2(insn32));
          break;
        }
        default: {
          static constexpr const char* kOPMVVOpcodes[64] = {
              "vredsum.vs",  "vredand.vs""vredor.vs",   "vredxor.vs",
              "vredminu.vs""vredmin.vs""vredmaxu.vs""vredmax.vs",
              "vaaddu.vv",   "vaadd.vv",   "vasubu.vv",   "vasub.vv",
              nullptr,       nullptr,      nullptr,       nullptr,
              "<VWXUNARY0>", nullptr,      "<VXUNARY0>",  nullptr,
              "<VMUNARY0>",  nullptr,      nullptr,       "vcompress.vm",
              "vmandn.mm",   "vmand.mm",   "vmor.mm",     "vmxor.mm",
              "vmorn.mm",    "vmnand.mm",  "vmnor.mm",    "vmxnor.mm",
              "vdivu.vv",    "vdiv.vv",    "vremu.vv",    "vrem.vv",
              "vmulhu.vv",   "vmul.vv",    "vmulhsu.vv",  "vmulh.vv",
              nullptr,       "vmadd.vv",   nullptr,       "vnmsub.vv",
              nullptr,       "vmacc.vv",   nullptr,       "vnmsac.vv",
              "vwaddu.vv",   "vwadd.vv",   "vwsubu.vv",   "vwsub.vv",
              "vwaddu.wv",   "vwadd.wv",   "vwsubu.wv",   "vwsub.wv",
              "vwmulu.vv",   nullptr,      "vwmulsu.vv",  "vwmul.vv",
              "vwmaccu.vv""vwmacc.vv",   nullptr,       "vwmaccsu.vv",
          };

          opcode = kOPMVVOpcodes[funct6];
          rd = VRegName(GetRd(insn32));
          rs1 = VRegName(GetRs1(insn32));
          rs2 = VRegName(GetRs2(insn32));

          if (0x17u <= funct6 && funct6 <= 0x1Fu) {
            if (masked) {
              // for vcompress.vm and *.mm encodings with vm=0 are reserved
              opcode = nullptr;
            }
          }

          MaybeSwapOperands(funct6, rs1, rs2);

          break;
        }
      }
      break;
    }
    case VAIEncodings::kOpMVX: {
      switch (funct6) {
        case VRXUNARY0: {
          opcode = GetRs2(insn32) == 0u ? "vmv.s.x" : nullptr;
          rd = VRegName(GetRd(insn32));
          rs1 = XRegName(GetRs1(insn32));
          break;
        }
        default: {
          static constexpr const char* kOPMVXOpcodes[64] = {
              nullptr,      nullptr,      nullptr,        nullptr,
              nullptr,      nullptr,      nullptr,        nullptr,
              "vaaddu.vx",  "vaadd.vx",   "vasubu.vx",    "vasub.vx",
              nullptr,       nullptr,     "vslide1up.vx""vslide1down.vx",
              "<VRXUNARY0>", nullptr,     nullptr,        nullptr,
              nullptr,       nullptr,     nullptr,        nullptr,
              nullptr,       nullptr,     nullptr,        nullptr,
              nullptr,       nullptr,     nullptr,        nullptr,
              "vdivu.vx",    "vdiv.vx",   "vremu.vx",     "vrem.vx",
              "vmulhu.vx",   "vmul.vx",   "vmulhsu.vx",   "vmulh.vx",
              nullptr,       "vmadd.vx",  nullptr,        "vnmsub.vx",
              nullptr,       "vmacc.vx",  nullptr,        "vnmsac.vx",
              "vwaddu.vx",   "vwadd.vx",  "vwsubu.vx",    "vwsub.vx",
              "vwaddu.wv",   "vwadd.wv",  "vwsubu.wv",    "vwsub.wv",
              "vwmulu.vx",   nullptr,     "vwmulsu.vx",   "vwmul.vx",
              "vwmaccu.vx",  "vwmacc.vx""vwmaccus.vx",  "vwmaccsu.vx",
          };
          opcode = kOPMVXOpcodes[funct6];
          rd = VRegName(GetRd(insn32));
          rs1 = XRegName(GetRs1(insn32));
          rs2 = VRegName(GetRs2(insn32));

          MaybeSwapOperands(funct6, rs1, rs2);

          break;
        }
      }
      break;
    }
    case VAIEncodings::kOpFVV: {
      switch (funct6) {
        case VWFUNARY0: {
          opcode = GetRs1(insn32) == 0u ? "vfmv.f.s" : nullptr;
          rd = XRegName(GetRd(insn32));
          rs2 = VRegName(GetRs2(insn32));
          break;
        }
        case VFUNARY0: {
          static constexpr const char* kVFUNARY0Opcodes[32] = {
              "vfcvt.xu.f.v",  "vfcvt.x.f.v",      "vfcvt.f.xu.v",      "vfcvt.f.x.v",
              nullptr,         nullptr,            "vfcvt.rtz.xu.f.v",  "vfcvt.rtz.x.f.v",
              "vfwcvt.xu.f.v""vfwcvt.x.f.v",     "vfwcvt.f.xu.v",     "vfwcvt.f.x.v",
              "vfwcvt.f.f.v",  nullptr,            "vfwcvt.rtz.xu.f.v""vfwcvt.rtz.x.f.v",
              "vfncvt.xu.f.w""vfncvt.x.f.w",     "vfncvt.f.xu.w",     "vfncvt.f.x.w",
              "vfncvt.f.f.w",  "vfncvt.rod.f.f.w""vfncvt.rtz.xu.f.w""vfncvt.rtz.x.f.w",
              nullptr,         nullptr,            nullptr,             nullptr,
              nullptr,         nullptr,            nullptr,             nullptr,
          };
          opcode = kVFUNARY0Opcodes[GetRs1(insn32)];
          rd = VRegName(GetRd(insn32));
          rs2 = VRegName(GetRs2(insn32));
          break;
        }
        case VFUNARY1: {
          static constexpr const char* kVFUNARY1Opcodes[32] = {
              "vfsqrt.v",   nullptr,    nullptr, nullptr,
              "vfrsqrt7.v""vfrec7.v", nullptr, nullptr,
              nullptr,      nullptr,    nullptr, nullptr,
              nullptr,      nullptr,    nullptr, nullptr,
              "vfclass.v",  nullptr,    nullptr, nullptr,
              nullptr,      nullptr,    nullptr, nullptr,
              nullptr,      nullptr,    nullptr, nullptr,
              nullptr,      nullptr,    nullptr, nullptr,
          };
          opcode = kVFUNARY1Opcodes[GetRs1(insn32)];
          rd = VRegName(GetRd(insn32));
          rs2 = VRegName(GetRs2(insn32));
          break;
        }
        default: {
          static constexpr const char* kOPFVVOpcodes[64] = {
              "vfadd.vv",    "vfredusum.vs",  "vfsub.vv",   "vfredosum.vs",
              "vfmin.vv",    "vfredmin.vs",   "vfmax.vv",   "vfredmax.vs",
              "vfsgnj.vv",   "vfsgnjn.vv",    "vfsgnjx.vv", nullptr,
              nullptr,       nullptr,         nullptr,      nullptr,
              "<VWFUNARY0>", nullptr,         "<VFUNARY0>""<VFUNARY1>",
              nullptr,       nullptr,         nullptr,      nullptr,
              "vmfeq.vv",    "vmfle.vv",      nullptr,      "vmflt.vv",
              "vmfne.vv",    nullptr,         nullptr,      nullptr,
              "vfdiv.vv",    nullptr,         nullptr,      nullptr,
              "vfmul.vv",    nullptr,         nullptr,      nullptr,
              "vfmadd.vv",   "vfnmadd.vv",    "vfmsub.vv",  "vfnmsub.vv",
              "vfmacc.vv",   "vfnmacc.vv",    "vfmsac.vv",  "vfnmsac.vv",
              "vfwadd.vv",   "vfwredusum.vs""vfwsub.vv",  "vfwredosum.vs",
              "vfwadd.wv",   nullptr,         "vfwsub.wv",  nullptr,
              "vfwmul.vv",   nullptr,         nullptr,      nullptr,
              "vfwmacc.vv",  "vfwnmacc.vv",   "vfwmsac.vv""vfwnmsac.vv",
          };
          opcode = kOPFVVOpcodes[funct6];
          rd = VRegName(GetRd(insn32));
          rs1 = VRegName(GetRs1(insn32));
          rs2 = VRegName(GetRs2(insn32));

          MaybeSwapOperands(funct6, rs1, rs2);

          break;
        }
      }
      break;
    }
    case VAIEncodings::kOpFVF: {
      switch (funct6) {
        case VRFUNARY0: {
          opcode = GetRs2(insn32) == 0u ? "vfmv.s.f" : nullptr;
          rd = VRegName(GetRd(insn32));
          rs1 = FRegName(GetRs1(insn32));
          break;
        }
        default: {
          static constexpr const char* kOPFVFOpcodes[64] = {
              "vfadd.vf",    nullptr,       "vfsub.vf",      nullptr,
              "vfmin.vf",    nullptr,       "vfmax.vf",      nullptr,
              "vfsgnj.vf",   "vfsgnjn.vf",  "vfsgnjx.vf",    nullptr,
              nullptr,       nullptr,       "vfslide1up.vf""vfslide1down.vf",
              "<VRFUNARY0>", nullptr,       nullptr,         nullptr,
              nullptr,       nullptr,       nullptr,         "<vfmerge.vfm/vfmv>",
              "vmfeq.vf",    "vmfle.vf",    nullptr,         "vmflt.vf",
              "vmfne.vf",    "vmfgt.vf",    nullptr,         "vmfge.vf",
              "vfdiv.vf",    "vfrdiv.vf",   nullptr,         nullptr,
              "vfmul.vf",    nullptr,       nullptr,         "vfrsub.vf",
              "vfmadd.vf",   "vfnmadd.vf",  "vfmsub.vf",     "vfnmsub.vf",
              "vfmacc.vf",   "vfnmacc.vf",  "vfmsac.vf",     "vfnmsac.vf",
              "vfwadd.vf",   nullptr,       "vfwsub.vf",     nullptr,
              "vfwadd.wf",   nullptr,       "vfwsub.wf",     nullptr,
              "vfwmul.vf",   nullptr,       nullptr,         nullptr,
              "vfwmacc.vf",  "vfwnmacc.vf""vfwmsac.vf",    "vfwnmsac.vf",
          };

          rs2 = VRegName(GetRs2(insn32));
          if (funct6 == 0b010111) {
            // vfmerge/vfmv
            if (masked) {
              opcode = "vfmerge.vfm";
              vm = ", v0";
            } else if (GetRs2(insn32) == 0) {
              opcode = "vfmv.v.f";
              rs2 = nullptr;
            } else {
              opcode = nullptr;
            }
          } else {
            opcode = kOPFVFOpcodes[funct6];
          }

          rd = VRegName(GetRd(insn32));
          rs1 = FRegName(GetRs1(insn32));

          MaybeSwapOperands(funct6, rs1, rs2);

          break;
        }
      }
      break;
    }
    case VAIEncodings::kOpCFG: {  // vector ALU control instructions
      if ((insn32 >> 31) != 0u) {
        if (((insn32 >> 30) & 0x1U) != 0u) {  // vsetivli
          const uint32_t zimm = Decode32UImm12(insn32) & ~0xC00U;
          const uint32_t imm5 = GetRs1(insn32);
          os_ << "vsetivli " << XRegName(GetRd(insn32)) << ", " << StringPrintf("0x%08x", imm5)
              << ", ";
          AppendVType(zimm);
        } else {  // vsetvl
          os_ << "vsetvl " << XRegName(GetRd(insn32)) << ", " << XRegName(GetRs1(insn32)) << ", "
              << XRegName(GetRs2(insn32));
          if ((Decode32UImm7(insn32) & 0x40u) != 0u) {
            os_ << "\t# incorrect funct7 literal : "
                << StringPrintf("0x%08x", Decode32UImm7(insn32));
          }
        }
      } else {  // vsetvli
        const uint32_t zimm = Decode32UImm12(insn32) & ~0x800U;
        os_ << "vsetvli " << XRegName(GetRd(insn32)) << ", " << XRegName(GetRs1(insn32)) << ", ";
        AppendVType(zimm);
      }
      return;
    }
  }

  if (opcode == nullptr) {
    os_ << "<unknown32>";
    return;
  }

  os_ << opcode << " " << rd;

  if (rs2 != nullptr) {
    os_ << ", " << rs2;
  }

  if (rs1 != nullptr) {
    os_ << ", " << rs1;
  } else if (vai == VAIEncodings::kOpIVI) {
    os_ << StringPrintf(", 0x%08x", GetRs1(insn32));
  }

  os_ << vm;
}

void DisassemblerRiscv64::Printer::Print32FpFma(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x73u, 0x43u);  // Note: Bits 0xc select the FMA opcode.
  uint32_t funct2 = (insn32 >> 25) & 3u;
  if (funct2 >= 2u) {
    os_ << "<unknown32>";  // Note: This includes the "H" and "Q" extensions.
    return;
  }
  static const charconst kOpcodes[] = { "fmadd""fmsub""fnmsub""fnmadd" };
  os_ << kOpcodes[(insn32 >> 2) & 3u] << ((funct2 != 0u) ? ".d" : ".s")
      << RoundingModeName(GetRoundingMode(insn32)) << " "
      << FRegName(GetRd(insn32)) << ", " << FRegName(GetRs1(insn32)) << ", "
      << FRegName(GetRs2(insn32)) << ", " << FRegName(GetRs3(insn32));
}

void DisassemblerRiscv64::Printer::Print32Zicsr(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x73u);
  uint32_t funct3 = (insn32 >> 12) & 7u;
  static const charconst kOpcodes[] = {
      nullptr, "csrrw""csrrs""csrrc", nullptr, "csrrwi""csrrsi""csrrci"
  };
  const char* opcode = kOpcodes[funct3];
  if (opcode == nullptr) {
    os_ << "<unknown32>";
    return;
  }
  uint32_t rd = GetRd(insn32);
  uint32_t rs1_or_uimm = GetRs1(insn32);
  uint32_t csr = insn32 >> 20;
  // Print shorter macro instruction notation if available.
  if (funct3 == /*CSRRW*/ 1u && rd == 0u && rs1_or_uimm == 0u && csr == 0xc00u) {
    os_ << "unimp";
    return;
  } else if (funct3 == /*CSRRS*/ 2u && rs1_or_uimm == 0u) {
    if (csr == 0xc00u) {
      os_ << "rdcycle " << XRegName(rd);
    } else if (csr == 0xc01u) {
      os_ << "rdtime " << XRegName(rd);
    } else if (csr == 0xc02u) {
      os_ << "rdinstret " << XRegName(rd);
    } else {
      os_ << "csrr " << XRegName(rd) << ", " << csr;
    }
    return;
  }

  if (rd == 0u) {
    static const charconst kAltOpcodes[] = {
        nullptr, "csrw""csrs""csrc", nullptr, "csrwi""csrsi""csrci"
    };
    DCHECK(kAltOpcodes[funct3] != nullptr);
    os_ << kAltOpcodes[funct3] << " " << csr << ", ";
  } else {
    os_ << opcode << " " << XRegName(rd) << ", " << csr << ", ";
  }
  if (funct3 >= /*CSRRWI/CSRRSI/CSRRCI*/ 4u) {
    os_ << rs1_or_uimm;
  } else {
    os_ << XRegName(rs1_or_uimm);
  }
}

void DisassemblerRiscv64::Printer::Print32Fence(uint32_t insn32) {
  DCHECK_EQ(insn32 & 0x7fu, 0x0fu);
  if ((insn32 & 0xf00fffffu) == 0x0000000fu) {
    auto print_flags = [&](uint32_t flags) {
      if (flags == 0u) {
        os_ << "0";
      } else {
        DCHECK_LT(flags, 0x10u);
        static const char kFlagNames[] = "wroi";
        for (size_t bit : { 3u, 2u, 1u, 0u }) {  // Print in the "iorw" order.
          if ((flags & (1u << bit)) != 0u) {
            os_ << kFlagNames[bit];
          }
        }
      }
    };
    os_ << "fence.";
    print_flags((insn32 >> 24) & 0xfu);
    os_ << ".";
    print_flags((insn32 >> 20) & 0xfu);
  } else if (insn32 == 0x8330000fu) {
    os_ << "fence.tso";
  } else if (insn32 == 0x0000100fu) {
    os_ << "fence.i";
  } else {
    os_ << "<unknown32>";
  }
}

void DisassemblerRiscv64::Printer::Dump32(const uint8_t* insn) {
  uint32_t insn32 = static_cast<uint32_t>(insn[0]) +
                    (static_cast<uint32_t>(insn[1]) << 8) +
                    (static_cast<uint32_t>(insn[2]) << 16) +
                    (static_cast<uint32_t>(insn[3]) << 24);
  CHECK_EQ(insn32 & 3u, 3u);
  os_ << disassembler_->FormatInstructionPointer(insn) << StringPrintf(": %08x\t", insn32);
  switch (insn32 & 0x7fu) {
    case 0x37u:
      Print32Lui(insn32);
      break;
    case 0x17u:
      Print32Auipc(insn, insn32);
      break;
    case 0x6fu:
      Print32Jal(insn, insn32);
      break;
    case 0x67u:
      switch ((insn32 >> 12) & 7u) {  // funct3
        case 0:
          Print32Jalr(insn, insn32);
          break;
        default:
          os_ << "<unknown32>";
          break;
      }
      break;
    case 0x63u:
      Print32BCond(insn, insn32);
      break;
    case 0x03u:
      Print32Load(insn32);
      break;
    case 0x23u:
      Print32Store(insn32);
      break;
    case 0x07u:
      Print32FLoad(insn32);
      break;
    case 0x27u:
      Print32FStore(insn32);
      break;
    case 0x13u:
    case 0x1bu:
      Print32BinOpImm(insn32);
      break;
    case 0x33u:
    case 0x3bu:
      Print32BinOp(insn32);
      break;
    case 0x2fu:
      Print32Atomic(insn32);
      break;
    case 0x53u:
      Print32FpOp(insn32);
      break;
    case 0x57u:
      Print32RVVOp(insn32);
      break;
    case 0x43u:
    case 0x47u:
    case 0x4bu:
    case 0x4fu:
      Print32FpFma(insn32);
      break;
    case 0x73u:
      if ((insn32 & 0xffefffffu) == 0x00000073u) {
        os_ << ((insn32 == 0x00000073u) ? "ecall" : "ebreak");
      } else {
        Print32Zicsr(insn32);
      }
      break;
    case 0x0fu:
      Print32Fence(insn32);
      break;
    default:
      // TODO(riscv64): Disassemble more instructions.
      os_ << "<unknown32>";
      break;
  }
  os_ << "\n";
}

void DisassemblerRiscv64::Printer::Dump16(const uint8_t* insn) {
  uint32_t insn16 = static_cast<uint32_t>(insn[0]) + (static_cast<uint32_t>(insn[1]) << 8);
  ScopedNewLinePrinter nl(os_);
  CHECK_NE(insn16 & 3u, 3u);
  os_ << disassembler_->FormatInstructionPointer(insn) << StringPrintf(": %04x    \t", insn16);

  uint32_t funct3 = BitFieldExtract(insn16, 133);
  int32_t offset = -1;

  switch (insn16 & 3u) {
    case 0b00u:  // Quadrant 0
      switch (funct3) {
        case 0b000u:
          if (insn16 == 0u) {
            os_ << "c.unimp";
          } else {
            uint32_t nzuimm = BitFieldExtract(insn16, 58);
            if (nzuimm != 0u) {
              uint32_t decoded =
                  BitFieldExtract(nzuimm, 01) << 3 | BitFieldExtract(nzuimm, 11) << 2 |
                  BitFieldExtract(nzuimm, 24) << 6 | BitFieldExtract(nzuimm, 62) << 4;
              os_ << "c.addi4spn " << XRegName(GetRs2Short16(insn16)) << ", sp, " << decoded;
            } else {
              os_ << "<unknown16>";
            }
          }
          return;
        case 0b001u:
          offset = Decode16CMOffsetD(insn16);
          os_ << "c.fld " << FRegName(GetRs2Short16(insn16));
          break;
        case 0b010u:
          offset = Decode16CMOffsetW(insn16);
          os_ << "c.lw " << XRegName(GetRs2Short16(insn16));
          break;
        case 0b011u:
          offset = Decode16CMOffsetD(insn16);
          os_ << "c.ld " << XRegName(GetRs2Short16(insn16));
          break;
        case 0b100u: {
          uint32_t opcode2 = BitFieldExtract(insn16, 103);
          uint32_t imm = BitFieldExtract(insn16, 52);
          switch (opcode2) {
            case 0b000:
              offset = Uimm2ToOffset10(imm);
              os_ << "c.lbu " << XRegName(GetRs2Short16(insn16));
              break;
            case 0b001:
              offset = Uimm2ToOffset1(imm);
              os_ << (BitFieldExtract(imm, 11) == 0u ? "c.lhu " : "c.lh ");
              os_ << XRegName(GetRs2Short16(insn16));
              break;
            case 0b010:
              offset = Uimm2ToOffset10(imm);
              os_ << "c.sb " << XRegName(GetRs2Short16(insn16));
              break;
            case 0b011:
              if (BitFieldExtract(imm, 11) == 0u) {
                offset = Uimm2ToOffset1(imm);
                os_ << "c.sh " << XRegName(GetRs2Short16(insn16));
                break;
              }
              FALLTHROUGH_INTENDED;
            default:
              os_ << "<unknown16>";
              return;
          }
          break;
        }
        case 0b101u:
          offset = Decode16CMOffsetD(insn16);
          os_ << "c.fsd " << FRegName(GetRs2Short16(insn16));
          break;
        case 0b110u:
          offset = Decode16CMOffsetW(insn16);
          os_ << "c.sw " << XRegName(GetRs2Short16(insn16));
          break;
        case 0b111u:
          offset = Decode16CMOffsetD(insn16);
          os_ << "c.sd " << XRegName(GetRs2Short16(insn16));
          break;
        default:
          LOG(FATAL) << "Unreachable";
          UNREACHABLE();
      }
      os_ << ", ";
      PrintLoadStoreAddress(GetRs1Short16(insn16), offset);
      return;
    case 0b01u:  // Quadrant 1
      switch (funct3) {
        case 0b000u: {
          uint32_t rd = GetRs1_16(insn16);
          if (rd == 0) {
            if (Decode16Imm6<uint32_t>(insn16) != 0u) {
              os_ << "<hint16>";
            } else {
              os_ << "c.nop";
            }
          } else {
            int32_t imm = Decode16Imm6<int32_t>(insn16);
            if (imm != 0) {
              os_ << "c.addi " << XRegName(rd) << ", " << imm;
            } else {
              os_ << "<hint16>";
            }
          }
          break;
        }
        case 0b001u: {
          uint32_t rd = GetRs1_16(insn16);
          if (rd != 0) {
            os_ << "c.addiw " << XRegName(rd) << ", " << Decode16Imm6<int32_t>(insn16);
          } else {
            os_ << "<unknown16>";
          }
          break;
        }
        case 0b010u: {
          uint32_t rd = GetRs1_16(insn16);
          if (rd != 0) {
            os_ << "c.li " << XRegName(rd) << ", " << Decode16Imm6<int32_t>(insn16);
          } else {
            os_ << "<hint16>";
          }
          break;
        }
        case 0b011u: {
          uint32_t rd = GetRs1_16(insn16);
          uint32_t imm6_bits = Decode16Imm6<uint32_t>(insn16);
          if (imm6_bits != 0u) {
            if (rd == 2) {
              int32_t nzimm =
                  BitFieldExtract(insn16, 61) << 4 | BitFieldExtract(insn16, 21) << 5 |
                  BitFieldExtract(insn16, 51) << 6 | BitFieldExtract(insn16, 32) << 7 |
                  BitFieldExtract(insn16, 121) << 9;
              os_ << "c.addi16sp sp, " << SignExtendBits<10>(nzimm);
            } else if (rd != 0) {
              // sign-extend bits and mask with 0xfffff as llvm-objdump does
              uint32_t mask = MaskLeastSignificant<uint32_t>(20);
              os_ << "c.lui " << XRegName(rd) << ", " << (SignExtendBits<6>(imm6_bits) & mask);
            } else {
              os_ << "<hint16>";
            }
          } else {
            os_ << "<unknown16>";
          }
          break;
        }
        case 0b100u: {
          uint32_t funct2 = BitFieldExtract(insn16, 102);
          switch (funct2) {
            case 0b00: {
              int32_t nzuimm = Decode16Imm6<uint32_t>(insn16);
              if (nzuimm != 0) {
                os_ << "c.srli " << XRegName(GetRs1Short16(insn16)) << ", " << nzuimm;
              } else {
                os_ << "<hint16>";
              }
              break;
            }
            case 0b01: {
              int32_t nzuimm = Decode16Imm6<uint32_t>(insn16);
              if (nzuimm != 0) {
                os_ << "c.srai " << XRegName(GetRs1Short16(insn16)) << ", " << nzuimm;
              } else {
                os_ << "<hint16>";
              }
              break;
            }
            case 0b10:
              os_ << "c.andi " << XRegName(GetRs1Short16(insn16)) << ", "
                  << Decode16Imm6<int32_t>(insn16);
              break;
            case 0b11: {
              constexpr static const char* mnemonics[] = {
                  "c.sub""c.xor""c.or""c.and""c.subw""c.addw""c.mul", nullptr
              };
              uint32_t opc = BitFieldInsert(
                  BitFieldExtract(insn16, 52), BitFieldExtract(insn16, 121), 21);
              DCHECK(IsUint<3>(opc));
              const char* mnem = mnemonics[opc];
              if (mnem != nullptr) {
                os_ << mnem << " " << XRegName(GetRs1Short16(insn16)) << ", "
                    << XRegName(GetRs2Short16(insn16));
              } else {
                constexpr static const char* zbc_mnemonics[] = {
                    "c.zext.b""c.sext.b""c.zext.h""c.sext.h",
                    "c.zext.w""c.not",    nullptr,    nullptr,
                };
                mnem = zbc_mnemonics[BitFieldExtract(insn16, 23)];
                if (mnem != nullptr) {
                  os_ << mnem << " " << XRegName(GetRs1Short16(insn16));
                } else {
                  os_ << "<unknown16>";
                }
              }
              break;
            }
            default:
              LOG(FATAL) << "Unreachable";
              UNREACHABLE();
          }
          break;
        }
        case 0b101u: {
          int32_t disp = BitFieldExtract(insn16, 33) << 1  | BitFieldExtract(insn16, 111) << 4 |
                         BitFieldExtract(insn16, 21) << 5  | BitFieldExtract(insn16, 7,  1) << 6 |
                         BitFieldExtract(insn16, 61) << 7  | BitFieldExtract(insn16, 9,  2) << 8 |
                         BitFieldExtract(insn16, 81) << 10 | BitFieldExtract(insn16, 121) << 11;
          os_ << "c.j ";
          PrintBranchOffset(SignExtendBits<12>(disp));
          break;
        }
        case 0b110u:
        case 0b111u: {
          int32_t disp = BitFieldExtract(insn16, 32) << 1 | BitFieldExtract(insn16, 102) << 3 |
                         BitFieldExtract(insn16, 21) << 5 | BitFieldExtract(insn16, 5,  2) << 6 |
                         BitFieldExtract(insn16, 121) << 8;

          os_ << (funct3 == 0b110u ? "c.beqz " : "c.bnez ");
          os_ << XRegName(GetRs1Short16(insn16)) << ", ";
          PrintBranchOffset(SignExtendBits<9>(disp));
          break;
        }
        default:
          LOG(FATAL) << "Unreachable";
          UNREACHABLE();
      }
      break;
    case 0b10u:  // Quadrant 2
      switch (funct3) {
        case 0b000u: {
          uint32_t nzuimm = Decode16Imm6<uint32_t>(insn16);
          uint32_t rd = GetRs1_16(insn16);
          if (rd == 0 || nzuimm == 0) {
            os_ << "<hint16>";
          } else {
            os_ << "c.slli " << XRegName(rd) << ", " << nzuimm;
          }
          return;
        }
        case 0b001u: {
          offset = Uimm6ToOffsetD16(Decode16Imm6<uint32_t>(insn16));
          os_ << "c.fldsp " << FRegName(GetRs1_16(insn16));
          break;
        }
        case 0b010u: {
          uint32_t rd = GetRs1_16(insn16);
          if (rd != 0) {
            offset = Uimm6ToOffsetW16(Decode16Imm6<uint32_t>(insn16));
            os_ << "c.lwsp " << XRegName(GetRs1_16(insn16));
          } else {
            os_ << "<unknown16>";
            return;
          }
          break;
        }
        case 0b011u: {
          uint32_t rd = GetRs1_16(insn16);
          if (rd != 0) {
            offset = Uimm6ToOffsetD16(Decode16Imm6<uint32_t>(insn16));
            os_ << "c.ldsp " << XRegName(GetRs1_16(insn16));
          } else {
            os_ << "<unknown16>";
            return;
          }
          break;
        }
        case 0b100u: {
          uint32_t rd_rs1 = GetRs1_16(insn16);
          uint32_t rs2 = GetRs2_16(insn16);
          uint32_t b12 = BitFieldExtract(insn16, 121);
          if (b12 == 0) {
            if (rd_rs1 != 0 && rs2 != 0) {
              os_ << "c.mv " << XRegName(rd_rs1) << ", " << XRegName(rs2);
            } else if (rd_rs1 != 0) {
              os_ << "c.jr " << XRegName(rd_rs1);
            } else if (rs2 != 0) {
              os_ << "<hint16>";
            } else {
              os_ << "<unknown16>";
            }
          } else {
            if (rd_rs1 != 0 && rs2 != 0) {
              os_ << "c.add " << XRegName(rd_rs1) << ", " << XRegName(rs2);
            } else if (rd_rs1 != 0) {
              os_ << "c.jalr " << XRegName(rd_rs1);
            } else if (rs2 != 0) {
              os_ << "<hint16>";
            } else {
              os_ << "c.ebreak";
            }
          }
          return;
        }
        case 0b101u:
          offset = BitFieldExtract(insn16, 73) << 6 | BitFieldExtract(insn16, 103) << 3;
          os_ << "c.fsdsp " << FRegName(GetRs2_16(insn16));
          break;
        case 0b110u:
          offset = BitFieldExtract(insn16, 72) << 6 | BitFieldExtract(insn16, 94) << 2;
          os_ << "c.swsp " << XRegName(GetRs2_16(insn16));
          break;
        case 0b111u:
          offset = BitFieldExtract(insn16, 73) << 6 | BitFieldExtract(insn16, 103) << 3;
          os_ << "c.sdsp " << XRegName(GetRs2_16(insn16));
          break;
        default:
          LOG(FATAL) << "Unreachable";
          UNREACHABLE();
      }

      os_ << ", ";
      PrintLoadStoreAddress(/* sp */ 2, offset);

      break;
    default:
      LOG(FATAL) << "Unreachable";
      UNREACHABLE();
  }
}

void DisassemblerRiscv64::Printer::Dump2Byte(const uint8_t* data) {
  uint32_t value = data[0] + (data[1] << 8);
  os_ << disassembler_->FormatInstructionPointer(data)
      << StringPrintf(": %04x    \t.2byte %u\n", value, value);
}

void DisassemblerRiscv64::Printer::DumpByte(const uint8_t* data) {
  uint32_t value = *data;
  os_ << disassembler_->FormatInstructionPointer(data)
      << StringPrintf(": %02x      \t.byte %u\n", value, value);
}

size_t DisassemblerRiscv64::Dump(std::ostream& os, const uint8_t* begin) {
  if (begin < GetDisassemblerOptions()->base_address_ ||
      begin >= GetDisassemblerOptions()->end_address_) {
    return 0u;  // Outside the range.
  }
  Printer printer(this, os);
  if (!IsAligned<2u>(begin) || GetDisassemblerOptions()->end_address_ - begin == 1) {
    printer.DumpByte(begin);
    return 1u;
  }
  if ((*begin & 3u) == 3u) {
    if (GetDisassemblerOptions()->end_address_ - begin >= 4) {
      printer.Dump32(begin);
      return 4u;
    } else {
      printer.Dump2Byte(begin);
      return 2u;
    }
  } else {
    printer.Dump16(begin);
    return 2u;
  }
}

void DisassemblerRiscv64::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) {
  Printer printer(this, os);
  const uint8_t* cur = begin;
  if (cur < end && !IsAligned<2u>(cur)) {
    // Unaligned, dump as a `.byte` to get to an aligned address.
    printer.DumpByte(cur);
    cur += 1;
  }
  if (cur >= end) {
    return;
  }
  while (end - cur >= 4) {
    if ((*cur & 3u) == 3u) {
      printer.Dump32(cur);
      cur += 4;
    } else {
      printer.Dump16(cur);
      cur += 2;
    }
  }
  if (end - cur >= 2) {
    if ((*cur & 3u) == 3u) {
      // Not enough data for a 32-bit instruction. Dump as `.2byte`.
      printer.Dump2Byte(cur);
    } else {
      printer.Dump16(cur);
    }
    cur += 2;
  }
  if (end != cur) {
    CHECK_EQ(end - cur, 1);
    printer.DumpByte(cur);
  }
}

}  // namespace riscv64
}  // namespace art

Messung V0.5 in Prozent
C=92 H=92 G=91

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.