Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  Disasm-riscv64.cpp

  Sprache: C
 

// Copyright 2011 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// A Disassembler object is used to disassemble a block of code instruction by
// instruction. The default implementation of the NameConverter object can be
// overriden to modify register names or to do symbol lookup on addresses.
//
// The example below will disassemble a block of code and print it to stdout.
//
//   disasm::NameConverter converter;
//   disasm::Disassembler d(converter);
//   for (uint8_t* pc = begin; pc < end;) {
//     disasm::EmbeddedVector<char, disasm::ReasonableBufferSize> buffer;
//     uint8_t* prev_pc = pc;
//     pc += d.InstructionDecode(buffer, pc);
//     printf("%p    %08x      %s\n",
//            prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer);
//   }
//
// The Disassembler class also has a convenience method to disassemble a block
// of code into a FILE*, meaning that the above functionality could also be
// achieved by just calling Disassembler::Disassemble(stdout, begin, end);

#include "jit/riscv64/disasm/Disasm-riscv64.h"

#include <stdio.h>
#include <string.h>
#include <string_view>

#include "jit/riscv64/Assembler-riscv64.h"
#include "jit/riscv64/extension/extension-riscv-zfa.h"

namespace js {
namespace jit {
namespace disasm {

#define UNSUPPORTED_RISCV() printf("Unsupported instruction %d.\n", __LINE__)
//------------------------------------------------------------------------------

// Decoder decodes and disassembles instructions into an output buffer.
// It uses the converter to convert register names and call destinations into
// more informative description.
class Decoder {
 public:
  Decoder(const disasm::NameConverter& converter, V8Vector<char> out_buffer)
      : converter_(converter), out_buffer_(out_buffer), out_buffer_pos_(0) {
    out_buffer_[out_buffer_pos_] = '\0';
  }

  ~Decoder() {}

  // Writes one disassembled instruction into 'buffer' (0-terminated).
  // Returns the length of the disassembled machine instruction in bytes.
  int InstructionDecode(Instruction* instr);

  static bool IsConstantPoolAt(uint8_t* instr_ptr);
  static int ConstantPoolSizeAt(uint8_t* instr_ptr);

 private:
  // Bottleneck functions to print into the out_buffer.
  void PrintChar(const char ch);
  void Print(const char* str);

  // Printing of common values.
  void PrintRegister(int reg);
  void PrintFPURegister(int freg);
  void PrintVRegister(int reg);
  void PrintFPUStatusRegister(int freg);
  void PrintRs1(Instruction* instr);
  void PrintRs2(Instruction* instr);
  void PrintRd(Instruction* instr);
  void PrintUimm(Instruction* instr);
  void PrintVs1(Instruction* instr);
  void PrintVs2(Instruction* instr);
  void PrintVd(Instruction* instr);
  void PrintFRs1(Instruction* instr);
  void PrintFRs2(Instruction* instr);
  void PrintFRs3(Instruction* instr);
  void PrintFRd(Instruction* instr);
  void PrintImm12(Instruction* instr);
  void PrintImm12X(Instruction* instr);
  void PrintImm20U(Instruction* instr);
  void PrintImm20J(Instruction* instr);
  void PrintShamt(Instruction* instr);
  void PrintShamt32(Instruction* instr);
  void PrintRvcImm6(Instruction* instr);
  void PrintRvcImm6U(Instruction* instr);
  void PrintRvcImm6Addi16sp(Instruction* instr);
  void PrintRvcShamt(Instruction* instr);
  void PrintRvcImm6Ldsp(Instruction* instr);
  void PrintRvcImm6Lwsp(Instruction* instr);
  void PrintRvcImm6Sdsp(Instruction* instr);
  void PrintRvcImm6Swsp(Instruction* instr);
  void PrintRvcImm5W(Instruction* instr);
  void PrintRvcImm5D(Instruction* instr);
  void PrintRvcImm8Addi4spn(Instruction* instr);
  void PrintRvcImm11CJ(Instruction* instr);
  void PrintRvcImm8B(Instruction* instr);
  void PrintRvvVm(Instruction* instr);
  void PrintAcquireRelease(Instruction* instr);
  void PrintBranchOffset(Instruction* instr);
  void PrintStoreOffset(Instruction* instr);
  void PrintCSRReg(Instruction* instr);
  void PrintRvvSEW(Instruction* instr);
  void PrintRvvLMUL(Instruction* instr);
  void PrintRvvSimm5(Instruction* instr);
  void PrintRvvUimm5(Instruction* instr);
  void PrintRoundingMode(Instruction* instr);
  void PrintMemoryOrder(Instruction* instr, bool is_pred);
  void PrintFLISImm(Instruction* instr);
  void PrintFLIDImm(Instruction* instr);

  // Each of these functions decodes one particular instruction type.
  void DecodeRType(Instruction* instr);
  void DecodeR4Type(Instruction* instr);
  void DecodeRAType(Instruction* instr);
  void DecodeRFPType(Instruction* instr);
  void DecodeIType(Instruction* instr);
  void DecodeSType(Instruction* instr);
  void DecodeBType(Instruction* instr);
  void DecodeUType(Instruction* instr);
  void DecodeJType(Instruction* instr);
  void DecodeCRType(Instruction* instr);
  void DecodeCAType(Instruction* instr);
  void DecodeCIType(Instruction* instr);
  void DecodeCIWType(Instruction* instr);
  void DecodeCSSType(Instruction* instr);
  void DecodeCLType(Instruction* instr);
  void DecodeCSType(Instruction* instr);
  void DecodeCJType(Instruction* instr);
  void DecodeCBType(Instruction* instr);

  // Printing of instruction name.
  void PrintInstructionName(Instruction* instr);
  void PrintTarget(Instruction* instr);

  // Handle formatting of instructions and their options.
  int FormatRegister(Instruction* instr, const char* option);
  int FormatFPURegisterOrRoundMode(Instruction* instr, const char* option);
  int FormatRvcRegister(Instruction* instr, const char* option);
  int FormatRvcImm(Instruction* instr, const char* option);
  int FormatOption(Instruction* instr, const char* option);
  void Format(Instruction* instr, const char* format);
  void Unknown(Instruction* instr);

  int switch_sew(Instruction* instr);
  int switch_nf(Instruction* instr);

  const disasm::NameConverter& converter_;
  V8Vector<char> out_buffer_;
  int out_buffer_pos_;

  // Disallow copy and assign.
  Decoder(const Decoder&) = delete;
  void operator=(const Decoder&) = delete;
};

// Support for assertions in the Decoder formatting functions.
#define STRING_STARTS_WITH(string, compare_string) \
  (strncmp(string, compare_string, strlen(compare_string)) == 0)

// Append the ch to the output buffer.
void Decoder::PrintChar(const char ch) { out_buffer_[out_buffer_pos_++] = ch; }

// Append the str to the output buffer.
void Decoder::Print(const char* str) {
  char cur = *str++;
  while (cur != '\0' && (out_buffer_pos_ < int(out_buffer_.length() - 1))) {
    PrintChar(cur);
    cur = *str++;
  }
  out_buffer_[out_buffer_pos_] = 0;
}

int Decoder::switch_nf(Instruction* instr) {
  int nf = 0;
  switch (instr->InstructionBits() & kRvvNfMask) {
    case 0x20000000:
      nf = 2;
      break;
    case 0x40000000:
      nf = 3;
      break;
    case 0x60000000:
      nf = 4;
      break;
    case 0x80000000:
      nf = 5;
      break;
    case 0xa0000000:
      nf = 6;
      break;
    case 0xc0000000:
      nf = 7;
      break;
    case 0xe0000000:
      nf = 8;
      break;
  }
  return nf;
}

int Decoder::switch_sew(Instruction* instr) {
  int width = 0;
  if ((instr->InstructionBits() & kBaseOpcodeMask) != LOAD_FP &&
      (instr->InstructionBits() & kBaseOpcodeMask) != STORE_FP)
    return -1;
  switch (instr->InstructionBits() & (kRvvWidthMask | kRvvMewMask)) {
    case 0x0:
      width = 8;
      break;
    case 0x00005000:
      width = 16;
      break;
    case 0x00006000:
      width = 32;
      break;
    case 0x00007000:
      width = 64;
      break;
    case 0x10000000:
      width = 128;
      break;
    case 0x10005000:
      width = 256;
      break;
    case 0x10006000:
      width = 512;
      break;
    case 0x10007000:
      width = 1024;
      break;
    default:
      width = -1;
      break;
  }
  return width;
}

// Handle all register based formatting in this function to reduce the
// complexity of FormatOption.
int Decoder::FormatRegister(Instruction* instr, const char* option) {
  MOZ_ASSERT(option[0] == 'r');
  if (option[1] == 's') {  // 'rs[12]: Rs register.
    if (option[2] == '1') {
      int reg = instr->Rs1Value();
      PrintRegister(reg);
      return 3;
    } else if (option[2] == '2') {
      int reg = instr->Rs2Value();
      PrintRegister(reg);
      return 3;
    }
    MOZ_CRASH();
  } else if (option[1] == 'd') {  // 'rd: rd register.
    int reg = instr->RdValue();
    PrintRegister(reg);
    return 2;
  }
  MOZ_CRASH();
}

// Handle all FPUregister based formatting in this function to reduce the
// complexity of FormatOption.
int Decoder::FormatFPURegisterOrRoundMode(Instruction* instr,
                                          const char* option) {
  MOZ_ASSERT(option[0] == 'f');
  if (option[1] == 's') {  // 'fs[1-3]: Rs register.
    if (option[2] == '1') {
      int reg = instr->Rs1Value();
      PrintFPURegister(reg);
      return 3;
    } else if (option[2] == '2') {
      int reg = instr->Rs2Value();
      PrintFPURegister(reg);
      return 3;
    } else if (option[2] == '3') {
      int reg = instr->Rs3Value();
      PrintFPURegister(reg);
      return 3;
    }
    MOZ_CRASH();
  } else if (option[1] == 'd') {  // 'fd: fd register.
    int reg = instr->RdValue();
    PrintFPURegister(reg);
    return 2;
  } else if (option[1] == 'r') {  // 'frm
    MOZ_ASSERT(STRING_STARTS_WITH(option, "frm"));
    PrintRoundingMode(instr);
    return 3;
  } else if (option[1] == 'i') {  // 'fis or 'fid: FLI immediate values.
    if (option[2] == 's') {
      MOZ_ASSERT(STRING_STARTS_WITH(option, "fis"));
      PrintFLISImm(instr);
      return 3;
    } else if (option[2] == 'd') {
      MOZ_ASSERT(STRING_STARTS_WITH(option, "fid"));
      PrintFLIDImm(instr);
      return 3;
    }
    MOZ_CRASH();
  }
  MOZ_CRASH();
}

// Handle all C extension register based formatting in this function to reduce
// the complexity of FormatOption.
int Decoder::FormatRvcRegister(Instruction* instr, const char* option) {
  MOZ_ASSERT(option[0] == 'C');
  MOZ_ASSERT(option[1] == 'r' || option[1] == 'f');
  if (option[2] == 's') {  // 'Crs[12]: Rs register.
    if (option[3] == '1') {
      if (option[4] == 's') {  // 'Crs1s: 3-bits register
        int reg = instr->RvcRs1sValue();
        if (option[1] == 'r') {
          PrintRegister(reg);
        } else if (option[1] == 'f') {
          PrintFPURegister(reg);
        }
        return 5;
      }
      int reg = instr->RvcRs1Value();
      if (option[1] == 'r') {
        PrintRegister(reg);
      } else if (option[1] == 'f') {
        PrintFPURegister(reg);
      }
      return 4;
    } else if (option[3] == '2') {
      if (option[4] == 's') {  // 'Crs2s: 3-bits register
        int reg = instr->RvcRs2sValue();
        if (option[1] == 'r') {
          PrintRegister(reg);
        } else if (option[1] == 'f') {
          PrintFPURegister(reg);
        }
        return 5;
      }
      int reg = instr->RvcRs2Value();
      if (option[1] == 'r') {
        PrintRegister(reg);
      } else if (option[1] == 'f') {
        PrintFPURegister(reg);
      }
      return 4;
    }
    MOZ_CRASH();
  } else if (option[2] == 'd') {  // 'Crd: rd register.
    int reg = instr->RvcRdValue();
    if (option[1] == 'r') {
      PrintRegister(reg);
    } else if (option[1] == 'f') {
      PrintFPURegister(reg);
    }
    return 3;
  }
  MOZ_CRASH();
}

// Handle all C extension immediates based formatting in this function to reduce
// the complexity of FormatOption.
int Decoder::FormatRvcImm(Instruction* instr, const char* option) {
  // TODO(riscv): add other rvc imm format
  MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm"));
  if (option[4] == '6') {
    if (option[5] == 'U') {
      MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm6U"));
      PrintRvcImm6U(instr);
      return 6;
    } else if (option[5] == 'A') {
      if (option[9] == '1' && option[10] == '6') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm6Addi16sp"));
        PrintRvcImm6Addi16sp(instr);
        return 13;
      }
      MOZ_CRASH();
    } else if (option[5] == 'L') {
      if (option[6] == 'd') {
        if (option[7] == 's') {
          MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm6Ldsp"));
          PrintRvcImm6Ldsp(instr);
          return 9;
        }
      } else if (option[6] == 'w') {
        if (option[7] == 's') {
          MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm6Lwsp"));
          PrintRvcImm6Lwsp(instr);
          return 9;
        }
      }
      MOZ_CRASH();
    } else if (option[5] == 'S') {
      if (option[6] == 'w') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm6Swsp"));
        PrintRvcImm6Swsp(instr);
        return 9;
      } else if (option[6] == 'd') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm6Sdsp"));
        PrintRvcImm6Sdsp(instr);
        return 9;
      }
      MOZ_CRASH();
    }
    PrintRvcImm6(instr);
    return 5;
  } else if (option[4] == '5') {
    MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm5"));
    if (option[5] == 'W') {
      MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm5W"));
      PrintRvcImm5W(instr);
      return 6;
    } else if (option[5] == 'D') {
      MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm5D"));
      PrintRvcImm5D(instr);
      return 6;
    }
    MOZ_CRASH();
  } else if (option[4] == '8') {
    MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm8"));
    if (option[5] == 'A') {
      MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm8Addi4spn"));
      PrintRvcImm8Addi4spn(instr);
      return 13;
    } else if (option[5] == 'B') {
      MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm8B"));
      PrintRvcImm8B(instr);
      return 6;
    }
    MOZ_CRASH();
  } else if (option[4] == '1') {
    MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm1"));
    if (option[5] == '1') {
      MOZ_ASSERT(STRING_STARTS_WITH(option, "Cimm11CJ"));
      PrintRvcImm11CJ(instr);
      return 8;
    }
    MOZ_CRASH();
  }
  MOZ_CRASH();
}

// FormatOption takes a formatting string and interprets it based on
// the current instructions. The format string points to the first
// character of the option string (the option escape has already been
// consumed by the caller.)  FormatOption returns the number of
// characters that were consumed from the formatting string.
int Decoder::FormatOption(Instruction* instr, const char* option) {
  switch (option[0]) {
    case 'C': {  // `C extension
      if (option[1] == 'r' || option[1] == 'f') {
        return FormatRvcRegister(instr, option);
      } else if (option[1] == 'i') {
        return FormatRvcImm(instr, option);
      } else if (option[1] == 's') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "Cshamt"));
        PrintRvcShamt(instr);
        return 6;
      }
      MOZ_CRASH();
    }
    case 'c': {  // `csr: CSR registers
      if (option[1] == 's') {
        if (option[2] == 'r') {
          PrintCSRReg(instr);
          return 3;
        }
      }
      MOZ_CRASH();
    }
    case 'i': {  // 'imm12, 'imm12x, 'imm20U, or 'imm20J: Immediates.
      if (option[3] == '1') {
        if (option[4] == '2') {
          MOZ_ASSERT(STRING_STARTS_WITH(option, "imm12"));
          if (option[5] == 'x') {
            PrintImm12X(instr);
            return 6;
          }
          PrintImm12(instr);
          return 5;
        }
      } else if (option[3] == '2' && option[4] == '0') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "imm20"));
        switch (option[5]) {
          case 'U':
            MOZ_ASSERT(STRING_STARTS_WITH(option, "imm20U"));
            PrintImm20U(instr);
            break;
          case 'J':
            MOZ_ASSERT(STRING_STARTS_WITH(option, "imm20J"));
            PrintImm20J(instr);
            break;
        }
        return 6;
      }
      MOZ_CRASH();
    }
    case 'o': {  // 'offB or 'offS: Offsets.
      if (option[3] == 'B') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "offB"));
        PrintBranchOffset(instr);
        return 4;
      } else if (option[3] == 'S') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "offS"));
        PrintStoreOffset(instr);
        return 4;
      }
      MOZ_CRASH();
    }
    case 'r': {  // 'r: registers.
      return FormatRegister(instr, option);
    }
    case 'f': {  // 'f: FPUregisters or `frm
      return FormatFPURegisterOrRoundMode(instr, option);
    }
    case 'a': {  // 'a: Atomic acquire and release.
      PrintAcquireRelease(instr);
      return 1;
    }
    case 'p': {  // `pre
      MOZ_ASSERT(STRING_STARTS_WITH(option, "pre"));
      PrintMemoryOrder(instr, true);
      return 3;
    }
    case 's': {  // 's32 or 's64: Shift amount.
      if (option[1] == '3') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "s32"));
        PrintShamt32(instr);
        return 3;
      } else if (option[1] == '6') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "s64"));
        PrintShamt(instr);
        return 3;
      } else if (option[1] == 'u') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "suc"));
        PrintMemoryOrder(instr, false);
        return 3;
      } else if (option[1] == 'e') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "sew"));
        PrintRvvSEW(instr);
        return 3;
      } else if (option[1] == 'i') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "simm5"));
        PrintRvvSimm5(instr);
        return 5;
      }
      MOZ_CRASH();
    }
    case 'v': {
      if (option[1] == 'd') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "vd"));
        PrintVd(instr);
        return 2;
      } else if (option[2] == '1') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "vs1"));
        PrintVs1(instr);
        return 3;
      } else if (option[2] == '2') {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "vs2"));
        PrintVs2(instr);
        return 3;
      } else {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "vm"));
        PrintRvvVm(instr);
        return 2;
      }
    }
    case 'l': {
      MOZ_ASSERT(STRING_STARTS_WITH(option, "lmul"));
      PrintRvvLMUL(instr);
      return 4;
    }
    case 'u': {
      if (STRING_STARTS_WITH(option, "uimm5")) {
        PrintRvvUimm5(instr);
        return 5;
      } else {
        MOZ_ASSERT(STRING_STARTS_WITH(option, "uimm"));
        PrintUimm(instr);
        return 4;
      }
    }
    case 't': {  // 'target: target of branch instructions'
      MOZ_ASSERT(STRING_STARTS_WITH(option, "target"));
      PrintTarget(instr);
      return 6;
    }
  }
  MOZ_CRASH();
}

// Format takes a formatting string for a whole instruction and prints it into
// the output buffer. All escaped options are handed to FormatOption to be
// parsed further.
void Decoder::Format(Instruction* instr, const char* format) {
  char cur = *format++;
  while ((cur != 0) && (out_buffer_pos_ < (out_buffer_.length() - 1))) {
    if (cur == '\'') {  // Single quote is used as the formatting escape.
      format += FormatOption(instr, format);
    } else {
      out_buffer_[out_buffer_pos_++] = cur;
    }
    cur = *format++;
  }
  out_buffer_[out_buffer_pos_] = '\0';
}

// The disassembler may end up decoding data inlined in the code. We do not want
// it to crash if the data does not ressemble any known instruction.
#define VERIFY(condition) \
  if (!(condition)) {     \
    Unknown(instr);       \
    return;               \
  }

// For currently unimplemented decodings the disassembler calls Unknown(instr)
// which will just print "unknown" of the instruction bits.
void Decoder::Unknown(Instruction* instr) { Format(instr, "unknown"); }

// Print the register name according to the active name converter.
void Decoder::PrintRegister(int reg) {
  Print(converter_.NameOfCPURegister(reg));
}

void Decoder::PrintVRegister(int reg) { UNSUPPORTED_RISCV(); }

void Decoder::PrintRs1(Instruction* instr) {
  int reg = instr->Rs1Value();
  PrintRegister(reg);
}

void Decoder::PrintRs2(Instruction* instr) {
  int reg = instr->Rs2Value();
  PrintRegister(reg);
}

void Decoder::PrintRd(Instruction* instr) {
  int reg = instr->RdValue();
  PrintRegister(reg);
}

void Decoder::PrintUimm(Instruction* instr) {
  int val = instr->Rs1Value();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", val);
}

void Decoder::PrintVs1(Instruction* instr) {
  int reg = instr->Vs1Value();
  PrintVRegister(reg);
}

void Decoder::PrintVs2(Instruction* instr) {
  int reg = instr->Vs2Value();
  PrintVRegister(reg);
}

void Decoder::PrintVd(Instruction* instr) {
  int reg = instr->VdValue();
  PrintVRegister(reg);
}

// Print the FPUregister name according to the active name converter.
void Decoder::PrintFPURegister(int freg) {
  Print(converter_.NameOfXMMRegister(freg));
}

void Decoder::PrintFRs1(Instruction* instr) {
  int reg = instr->Rs1Value();
  PrintFPURegister(reg);
}

void Decoder::PrintFRs2(Instruction* instr) {
  int reg = instr->Rs2Value();
  PrintFPURegister(reg);
}

void Decoder::PrintFRs3(Instruction* instr) {
  int reg = instr->Rs3Value();
  PrintFPURegister(reg);
}

void Decoder::PrintFRd(Instruction* instr) {
  int reg = instr->RdValue();
  PrintFPURegister(reg);
}

void Decoder::PrintImm12X(Instruction* instr) {
  int32_t imm = instr->Imm12Value();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
}

void Decoder::PrintImm12(Instruction* instr) {
  int32_t imm = instr->Imm12Value();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintTarget(Instruction* instr) {
  // if (instr->IsJalr()) {
  //   if ((instr - 4)->IsAuipc() &&
  //       (instr - 4)->RdValue() == instr->Rs1Value()) {
  //     int32_t imm = Assembler::BrachlongOffset(
  //         (instr - 4)->InstructionBits(), instr->InstructionBits());
  //     const char* target =
  //         converter_.NameOfAddress(reinterpret_cast<byte*>(instr - 4) + imm);
  //     out_buffer_pos_ +=
  //         SNPrintF(out_buffer_ + out_buffer_pos_, " -> %s", target);
  //     return;
  //   }
  // }
}

void Decoder::PrintBranchOffset(Instruction* instr) {
  int32_t imm = instr->BranchOffset();
  const char* target =
      converter_.NameOfAddress(reinterpret_cast<byte*>(instr) + imm);
  out_buffer_pos_ +=
      SNPrintF(out_buffer_ + out_buffer_pos_, "%d -> %s", imm, target);
}

void Decoder::PrintStoreOffset(Instruction* instr) {
  int32_t imm = instr->StoreOffset();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvvSEW(Instruction* instr) {
  const char* sew = instr->RvvSEW();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", sew);
}

void Decoder::PrintRvvLMUL(Instruction* instr) {
  const char* lmul = instr->RvvLMUL();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", lmul);
}

void Decoder::PrintRvvSimm5(Instruction* instr) {
  const int simm5 = instr->RvvSimm5();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", simm5);
}

void Decoder::PrintRvvUimm5(Instruction* instr) {
  const uint32_t uimm5 = instr->RvvUimm5();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%u", uimm5);
}

void Decoder::PrintImm20U(Instruction* instr) {
  int32_t imm = instr->Imm20UValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
}

void Decoder::PrintImm20J(Instruction* instr) {
  int32_t imm = instr->Imm20JValue();
  const char* target =
      converter_.NameOfAddress(reinterpret_cast<byte*>(instr) + imm);
  out_buffer_pos_ +=
      SNPrintF(out_buffer_ + out_buffer_pos_, "%d -> %s", imm, target);
}

void Decoder::PrintShamt(Instruction* instr) {
  int32_t imm = instr->Shamt();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintShamt32(Instruction* instr) {
  int32_t imm = instr->Shamt32();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm6(Instruction* instr) {
  int32_t imm = instr->RvcImm6Value();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm6U(Instruction* instr) {
  int32_t imm = instr->RvcImm6Value() & 0xFFFFF;
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
}

void Decoder::PrintRvcImm6Addi16sp(Instruction* instr) {
  int32_t imm = instr->RvcImm6Addi16spValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcShamt(Instruction* instr) {
  int32_t imm = instr->RvcShamt6();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm6Ldsp(Instruction* instr) {
  int32_t imm = instr->RvcImm6LdspValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm6Lwsp(Instruction* instr) {
  int32_t imm = instr->RvcImm6LwspValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm6Swsp(Instruction* instr) {
  int32_t imm = instr->RvcImm6SwspValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm6Sdsp(Instruction* instr) {
  int32_t imm = instr->RvcImm6SdspValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm5W(Instruction* instr) {
  int32_t imm = instr->RvcImm5WValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm5D(Instruction* instr) {
  int32_t imm = instr->RvcImm5DValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm8Addi4spn(Instruction* instr) {
  int32_t imm = instr->RvcImm8Addi4spnValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm11CJ(Instruction* instr) {
  int32_t imm = instr->RvcImm11CJValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvcImm8B(Instruction* instr) {
  int32_t imm = instr->RvcImm8BValue();
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
}

void Decoder::PrintRvvVm(Instruction* instr) {
  uint8_t imm = instr->RvvVM();
  if (imm == 0) {
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "  v0.t");
  }
}

void Decoder::PrintAcquireRelease(Instruction* instr) {
  bool aq = instr->AqValue();
  bool rl = instr->RlValue();
  if (aq || rl) {
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, ".");
  }
  if (aq) {
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "aq");
  }
  if (rl) {
    out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "rl");
  }
}

void Decoder::PrintCSRReg(Instruction* instr) {
  int32_t csr_reg = instr->CsrValue();
  std::string_view s;
  switch (csr_reg) {
    case csr_fflags:  // Floating-Point Accrued Exceptions (RW)
      s = "csr_fflags";
      break;
    case csr_frm:  // Floating-Point Dynamic Rounding Mode (RW)
      s = "csr_frm";
      break;
    case csr_fcsr:  // Floating-Point Control and Status Register (RW)
      s = "csr_fcsr";
      break;
    case csr_cycle:
      s = "csr_cycle";
      break;
    case csr_time:
      s = "csr_time";
      break;
    case csr_instret:
      s = "csr_instret";
      break;
    case csr_cycleh:
      s = "csr_cycleh";
      break;
    case csr_timeh:
      s = "csr_timeh";
      break;
    case csr_instreth:
      s = "csr_instreth";
      break;
    default:
      MOZ_CRASH();
  }
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", s.data());
}

void Decoder::PrintRoundingMode(Instruction* instr) {
  int frm = instr->RoundMode();
  std::string_view s;
  switch (frm) {
    case RNE:
      s = "RNE";
      break;
    case RTZ:
      s = "RTZ";
      break;
    case RDN:
      s = "RDN";
      break;
    case RUP:
      s = "RUP";
      break;
    case RMM:
      s = "RMM";
      break;
    case DYN:
      s = "DYN";
      break;
    default:
      MOZ_CRASH();
  }
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", s.data());
}

void Decoder::PrintMemoryOrder(Instruction* instr, bool is_pred) {
  int memOrder = instr->MemoryOrder(is_pred);
  char s[5] = {};
  char* ps = s;
  if ((memOrder & PSI) == PSI) {
    *ps++ = 'i';
  }
  if ((memOrder & PSO) == PSO) {
    *ps++ = 'o';
  }
  if ((memOrder & PSR) == PSR) {
    *ps++ = 'r';
  }
  if ((memOrder & PSW) == PSW) {
    *ps++ = 'w';
  }
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", s);
}

// Print the FLI.S immediate value as a floating-point constant.
// The rs1 field encodes the imm5 value (0-31), which corresponds to
// predefined floating-point constants.
void Decoder::PrintFLISImm(Instruction* instr) {
  uint8_t imm5 = static_cast<uint8_t>(instr->Rs1Value());
  float value = GetFLISValue(imm5);

  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%.3e", value);
}
void Decoder::PrintFLIDImm(Instruction* instr) {
  uint8_t imm5 = static_cast<uint8_t>(instr->Rs1Value());
  double value = GetFLIDValue(imm5);
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%.3e", value);
}

// Printing of instruction name.
void Decoder::PrintInstructionName(Instruction* instr) {}

// RISCV Instruction Decode Routine
void Decoder::DecodeRType(Instruction* instr) {
  switch (instr->InstructionBits() & kRTypeMask) {
    case RO_ADD:
      Format(instr, "add       'rd, 'rs1, 'rs2");
      break;
    case RO_SUB:
      if (instr->Rs1Value() == zero.code())
        Format(instr, "neg       'rd, 'rs2");
      else
        Format(instr, "sub       'rd, 'rs1, 'rs2");
      break;
    case RO_SLL:
      Format(instr, "sll       'rd, 'rs1, 'rs2");
      break;
    case RO_SLT:
      if (instr->Rs2Value() == zero.code())
        Format(instr, "sltz      'rd, 'rs1");
      else if (instr->Rs1Value() == zero.code())
        Format(instr, "sgtz      'rd, 'rs2");
      else
        Format(instr, "slt       'rd, 'rs1, 'rs2");
      break;
    case RO_SLTU:
      if (instr->Rs1Value() == zero.code())
        Format(instr, "snez      'rd, 'rs2");
      else
        Format(instr, "sltu      'rd, 'rs1, 'rs2");
      break;
    case RO_XOR:
      Format(instr, "xor       'rd, 'rs1, 'rs2");
      break;
    case RO_SRL:
      Format(instr, "srl       'rd, 'rs1, 'rs2");
      break;
    case RO_SRA:
      Format(instr, "sra       'rd, 'rs1, 'rs2");
      break;
    case RO_OR:
      Format(instr, "or        'rd, 'rs1, 'rs2");
      break;
    case RO_AND:
      Format(instr, "and       'rd, 'rs1, 'rs2");
      break;
    case RO_ANDN:
      Format(instr, "andn      'rd, 'rs1, 'rs2");
      break;
    case RO_ORN:
      Format(instr, "orn       'rd, 'rs1, 'rs2");
      break;
    case RO_XNOR:
      Format(instr, "xnor      'rd, 'rs1, 'rs2");
      break;
    case RO_ADDW:
      Format(instr, "addw      'rd, 'rs1, 'rs2");
      break;
    case RO_ADDUW:
      if (instr->Rs2Value() == zero_reg.code()) {
        Format(instr, "zext.w    'rd, 'rs1");
      } else {
        Format(instr, "add.uw    'rd, 'rs1, 'rs2");
      }
      break;
    case RO_SUBW:
      if (instr->Rs1Value() == zero.code())
        Format(instr, "negw      'rd, 'rs2");
      else
        Format(instr, "subw      'rd, 'rs1, 'rs2");
      break;
    case RO_SLLW:
      Format(instr, "sllw      'rd, 'rs1, 'rs2");
      break;
    case RO_SRLW:
      Format(instr, "srlw      'rd, 'rs1, 'rs2");
      break;
    case RO_SRAW:
      Format(instr, "sraw      'rd, 'rs1, 'rs2");
      break;
    // TODO(riscv): Add RISCV M extension macro
    case RO_MUL:
      Format(instr, "mul       'rd, 'rs1, 'rs2");
      break;
    case RO_MULH:
      Format(instr, "mulh      'rd, 'rs1, 'rs2");
      break;
    case RO_MULHSU:
      Format(instr, "mulhsu    'rd, 'rs1, 'rs2");
      break;
    case RO_MULHU:
      Format(instr, "mulhu     'rd, 'rs1, 'rs2");
      break;
    case RO_DIV:
      Format(instr, "div       'rd, 'rs1, 'rs2");
      break;
    case RO_DIVU:
      Format(instr, "divu      'rd, 'rs1, 'rs2");
      break;
    case RO_REM:
      Format(instr, "rem       'rd, 'rs1, 'rs2");
      break;
    case RO_REMU:
      Format(instr, "remu      'rd, 'rs1, 'rs2");
      break;
    case RO_MULW:
      Format(instr, "mulw      'rd, 'rs1, 'rs2");
      break;
    case RO_DIVW:
      Format(instr, "divw      'rd, 'rs1, 'rs2");
      break;
    case RO_DIVUW:
      Format(instr, "divuw     'rd, 'rs1, 'rs2");
      break;
    case RO_REMW:
      Format(instr, "remw      'rd, 'rs1, 'rs2");
      break;
    case RO_REMUW:
      Format(instr, "remuw     'rd, 'rs1, 'rs2");
      break;
    case RO_SH1ADDUW:
      Format(instr, "sh1add.uw 'rd, 'rs1, 'rs2");
      break;
    case RO_SH2ADDUW:
      Format(instr, "sh2add.uw 'rd, 'rs1, 'rs2");
      break;
    case RO_SH3ADDUW:
      Format(instr, "sh3add.uw 'rd, 'rs1, 'rs2");
      break;
    case RO_ROLW:
      Format(instr, "rolw     'rd, 'rs1, 'rs2");
      break;
    case RO_RORW:
      Format(instr, "rorw     'rd, 'rs1, 'rs2");
      break;
    case RO_SH1ADD:
      Format(instr, "sh1add    'rd, 'rs1, 'rs2");
      break;
    case RO_SH2ADD:
      Format(instr, "sh2add    'rd, 'rs1, 'rs2");
      break;
    case RO_SH3ADD:
      Format(instr, "sh3add    'rd, 'rs1, 'rs2");
      break;
    case RO_MAX:
      Format(instr, "max       'rd, 'rs1, 'rs2");
      break;
    case RO_MAXU:
      Format(instr, "maxu      'rd, 'rs1, 'rs2");
      break;
    case RO_MIN:
      Format(instr, "min       'rd, 'rs1, 'rs2");
      break;
    case RO_MINU:
      Format(instr, "minu      'rd, 'rs1, 'rs2");
      break;
    case RO_ZEXTH:
      Format(instr, "zext.h    'rd, 'rs1");
      break;
    case RO_ROL:
      Format(instr, "rol       'rd, 'rs1, 'rs2");
      break;
    case RO_ROR:
      Format(instr, "ror       'rd, 'rs1, 'rs2");
      break;
    case RO_BCLR:
      Format(instr, "bclr      'rd, 'rs1, 'rs2");
      break;
    case RO_BEXT:
      Format(instr, "bext      'rd, 'rs1, 'rs2");
      break;
    case RO_BINV:
      Format(instr, "binv      'rd, 'rs1, 'rs2");
      break;
    case RO_BSET:
      Format(instr, "bset      'rd, 'rs1, 'rs2");
      break;
    case RO_CZERO_EQZ:
      Format(instr, "czero.eqz 'rd, 'rs1, 'rs2");
      break;
    case RO_CZERO_NEZ:
      Format(instr, "czero.nez 'rd, 'rs1, 'rs2");
      break;
    // TODO(riscv): End Add RISCV M extension macro
    default: {
      switch (instr->BaseOpcode()) {
        case AMO:
          DecodeRAType(instr);
          break;
        case OP_FP:
          DecodeRFPType(instr);
          break;
        default:
          UNSUPPORTED_RISCV();
      }
    }
  }
}

void Decoder::DecodeRAType(Instruction* instr) {
  // TODO(riscv): Add macro for RISCV A extension
  // Special handling for A extension instructions because it uses func5
  // For all A extension instruction, V8 simulator is pure sequential. No
  // Memory address lock or other synchronizaiton behaviors.
  switch (instr->InstructionBits() & kRATypeMask) {
    case RO_LR_W:
      Format(instr, "lr.w'a    'rd, ('rs1)");
      break;
    case RO_SC_W:
      Format(instr, "sc.w'a    'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOSWAP_W:
      Format(instr, "amoswap.w'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOADD_W:
      Format(instr, "amoadd.w'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOXOR_W:
      Format(instr, "amoxor.w'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOAND_W:
      Format(instr, "amoand.w'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOOR_W:
      Format(instr, "amoor.w'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOMIN_W:
      Format(instr, "amomin.w'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOMAX_W:
      Format(instr, "amomax.w'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOMINU_W:
      Format(instr, "amominu.w'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOMAXU_W:
      Format(instr, "amomaxu.w'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_LR_D:
      Format(instr, "lr.d'a 'rd, ('rs1)");
      break;
    case RO_SC_D:
      Format(instr, "sc.d'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOSWAP_D:
      Format(instr, "amoswap.d'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOADD_D:
      Format(instr, "amoadd.d'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOXOR_D:
      Format(instr, "amoxor.d'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOAND_D:
      Format(instr, "amoand.d'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOOR_D:
      Format(instr, "amoor.d'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOMIN_D:
      Format(instr, "amomin.d'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOMAX_D:
      Format(instr, "amomax.d'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOMINU_D:
      Format(instr, "amominu.d'a 'rd, 'rs2, ('rs1)");
      break;
    case RO_AMOMAXU_D:
      Format(instr, "amomaxu.d'a 'rd, 'rs2, ('rs1)");
      break;
    // TODO(riscv): End Add macro for RISCV A extension
    default: {
      UNSUPPORTED_RISCV();
    }
  }
}

void Decoder::DecodeRFPType(Instruction* instr) {
  // OP_FP instructions (F/D) uses func7 first. Some further uses fun3 and rs2()

  // kRATypeMask is only for func7
  switch (instr->InstructionBits() & kRFPTypeMask) {
    // TODO(riscv): Add macro for RISCV F extension
    case RO_FADD_S:
      Format(instr, "fadd.s    'fd, 'fs1, 'fs2");
      break;
    case RO_FSUB_S:
      Format(instr, "fsub.s    'fd, 'fs1, 'fs2");
      break;
    case RO_FMUL_S:
      Format(instr, "fmul.s    'fd, 'fs1, 'fs2");
      break;
    case RO_FDIV_S:
      Format(instr, "fdiv.s    'fd, 'fs1, 'fs2");
      break;
    case RO_FSQRT_S:
      Format(instr, "fsqrt.s   'fd, 'fs1");
      break;
    case RO_FSGNJ_S: {  // RO_FSGNJN_S  RO_FSGNJX_S
      switch (instr->Funct3Value()) {
        case 0b000:  // RO_FSGNJ_S
          if (instr->Rs1Value() == instr->Rs2Value())
            Format(instr, "fmv.s     'fd, 'fs1");
          else
            Format(instr, "fsgnj.s   'fd, 'fs1, 'fs2");
          break;
        case 0b001:  // RO_FSGNJN_S
          if (instr->Rs1Value() == instr->Rs2Value())
            Format(instr, "fneg.s    'fd, 'fs1");
          else
            Format(instr, "fsgnjn.s  'fd, 'fs1, 'fs2");
          break;
        case 0b010:  // RO_FSGNJX_S
          if (instr->Rs1Value() == instr->Rs2Value())
            Format(instr, "fabs.s    'fd, 'fs1");
          else
            Format(instr, "fsgnjx.s  'fd, 'fs1, 'fs2");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FMIN_S: {  // RO_FMAX_S
      switch (instr->Funct3Value()) {
        case 0b000:  // RO_FMIN_S
          Format(instr, "fmin.s    'fd, 'fs1, 'fs2");
          break;
        case 0b010:  // RO_FMINM_S
          Format(instr, "fminm.s   'fd, 'fs1, 'fs2");
          break;
        case 0b001:  // RO_FMAX_S
          Format(instr, "fmax.s    'fd, 'fs1, 'fs2");
          break;
        case 0b011:  // RO_FMAXM_S
          Format(instr, "fmaxm.s   'fd, 'fs1, 'fs2");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FCVT_W_S: {  // RO_FCVT_WU_S , 64F RO_FCVT_L_S RO_FCVT_LU_S
      switch (instr->Rs2Value()) {
        case 0b00000:  // RO_FCVT_W_S
          Format(instr, "fcvt.w.s  ['frm] 'rd, 'fs1");
          break;
        case 0b00001:  // RO_FCVT_WU_S
          Format(instr, "fcvt.wu.s ['frm] 'rd, 'fs1");
          break;
        case 0b00010:  // RO_FCVT_L_S
          Format(instr, "fcvt.l.s  ['frm] 'rd, 'fs1");
          break;
        case 0b00011:  // RO_FCVT_LU_S
          Format(instr, "fcvt.lu.s ['frm] 'rd, 'fs1");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FMV_X_W: {  // RO_FCLASS_S
      if (instr->Rs2Value() != 0b00000) {
        UNSUPPORTED_RISCV();
      }
      switch (instr->Funct3Value()) {
        case 0b000:  // RO_FMV_X_W
          Format(instr, "fmv.x.w   'rd, 'fs1");
          break;
        case 0b001:  // RO_FCLASS_S
          Format(instr, "fclass.s  'rd, 'fs1");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FLE_S: {  // RO_FEQ_S RO_FLT_S RO_FLE_S
      switch (instr->Funct3Value()) {
        case 0b010:  // RO_FEQ_S
          Format(instr, "feq.s     'rd, 'fs1, 'fs2");
          break;
        case 0b001:  // RO_FLT_S
          Format(instr, "flt.s     'rd, 'fs1, 'fs2");
          break;
        case 0b000:  // RO_FLE_S
          Format(instr, "fle.s     'rd, 'fs1, 'fs2");
          break;
        case 0b100:  // RO_FLEQ_S
          Format(instr, "fleq.s    'rd, 'fs1, 'fs2");
          break;
        case 0b101:  // RO_FLTQ_S
          Format(instr, "fltq.s    'rd, 'fs1, 'fs2");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FCVT_S_W: {  // RO_FCVT_S_WU , 64F RO_FCVT_S_L RO_FCVT_S_LU
      switch (instr->Rs2Value()) {
        case 0b00000:  // RO_FCVT_S_W
          Format(instr, "fcvt.s.w  'fd, 'rs1");
          break;
        case 0b00001:  // RO_FCVT_S_WU
          Format(instr, "fcvt.s.wu 'fd, 'rs1");
          break;
        case 0b00010:  // RO_FCVT_S_L
          Format(instr, "fcvt.s.l  'fd, 'rs1");
          break;
        case 0b00011:  // RO_FCVT_S_LU
          Format(instr, "fcvt.s.lu 'fd, 'rs1");
          break;
        default: {
          UNSUPPORTED_RISCV();
        }
      }
      break;
    }
    case RO_FMV_W_X: {
      if (instr->Funct3Value() == 0b000) {
        if (instr->Rs2Value() == 0) {
          Format(instr, "fmv.w.x   'fd, 'rs1");
        } else if (instr->Rs2Value() == 0b00001) {
          Format(instr, "fli.s     'fd, 'fis");
        } else {
          UNSUPPORTED_RISCV();
        }
      } else {
        UNSUPPORTED_RISCV();
      }
      break;
    }
    // TODO(riscv): Add macro for RISCV D extension
    case RO_FADD_D:
      Format(instr, "fadd.d    'fd, 'fs1, 'fs2");
      break;
    case RO_FSUB_D:
      Format(instr, "fsub.d    'fd, 'fs1, 'fs2");
      break;
    case RO_FMUL_D:
      Format(instr, "fmul.d    'fd, 'fs1, 'fs2");
      break;
    case RO_FDIV_D:
      Format(instr, "fdiv.d    'fd, 'fs1, 'fs2");
      break;
    case RO_FSQRT_D: {
      if (instr->Rs2Value() == 0b00000) {
        Format(instr, "fsqrt.d   'fd, 'fs1");
      } else {
        UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FSGNJ_D: {  // RO_FSGNJN_D RO_FSGNJX_D
      switch (instr->Funct3Value()) {
        case 0b000:  // RO_FSGNJ_D
          if (instr->Rs1Value() == instr->Rs2Value())
            Format(instr, "fmv.d     'fd, 'fs1");
          else
            Format(instr, "fsgnj.d   'fd, 'fs1, 'fs2");
          break;
        case 0b001:  // RO_FSGNJN_D
          if (instr->Rs1Value() == instr->Rs2Value())
            Format(instr, "fneg.d    'fd, 'fs1");
          else
            Format(instr, "fsgnjn.d  'fd, 'fs1, 'fs2");
          break;
        case 0b010:  // RO_FSGNJX_D
          if (instr->Rs1Value() == instr->Rs2Value())
            Format(instr, "fabs.d    'fd, 'fs1");
          else
            Format(instr, "fsgnjx.d  'fd, 'fs1, 'fs2");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FMIN_D: {  // RO_FMAX_D
      switch (instr->Funct3Value()) {
        case 0b000:  // RO_FMIN_D
          Format(instr, "fmin.d    'fd, 'fs1, 'fs2");
          break;
        case 0b010:  // RO_FMINM_D
          Format(instr, "fminm.d   'fd, 'fs1, 'fs2");
          break;
        case 0b001:  // RO_FMAX_D
          Format(instr, "fmax.d    'fd, 'fs1, 'fs2");
          break;
        case 0b011:  // RO_FMAXM_D
          Format(instr, "fmaxm.d   'fd, 'fs1, 'fs2");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case (RO_FCVT_S_D & kRFPTypeMask): {
      if (instr->Rs2Value() == 0b00001) {
        Format(instr, "fcvt.s.d  ['frm] 'fd, 'fs1");
      } else if (instr->Rs2Value() == 0b00010) {
        Format(instr, "fcvt.s.h ['frm] 'fd, 'fs1");
      } else if (instr->Rs2Value() == 0b00100) {
        Format(instr, "fround.s ['frm] 'fd, 'fs1");
      } else if (instr->Rs2Value() == 0b00101) {
        Format(instr, "froundnx.s ['frm] 'fd, 'fs1");
      } else {
        UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FCVT_D_S: {  // RO_FCVT_D_H
      if (instr->Rs2Value() == 0b00000) {
        Format(instr, "fcvt.d.s  'fd, 'fs1");
      } else if (instr->Rs2Value() == 0b00010) {
        Format(instr, "fcvt.d.h ['frm] 'fd, 'fs1");
      } else if (instr->Rs2Value() == 0b00100) {
        Format(instr, "fround.d ['frm] 'fd, 'fs1");
      } else if (instr->Rs2Value() == 0b00101) {
        Format(instr, "froundnx.d ['frm] 'fd, 'fs1");
      } else {
        UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FLE_D: {  // RO_FEQ_D RO_FLT_D RO_FLE_D
      switch (instr->Funct3Value()) {
        case 0b010:  // RO_FEQ_S
          Format(instr, "feq.d     'rd, 'fs1, 'fs2");
          break;
        case 0b001:  // RO_FLT_D
          Format(instr, "flt.d     'rd, 'fs1, 'fs2");
          break;
        case 0b000:  // RO_FLE_D
          Format(instr, "fle.d     'rd, 'fs1, 'fs2");
          break;
        case 0b100:  // RO_FLEQ_S
          Format(instr, "fleq.d    'rd, 'fs1, 'fs2");
          break;
        case 0b101:  // RO_FLTQ_S
          Format(instr, "fltq.d    'rd, 'fs1, 'fs2");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case (RO_FCLASS_D & kRFPTypeMask): {  // RO_FCLASS_D , 64D RO_FMV_X_D
      if (instr->Rs2Value() != 0b00000) {
        UNSUPPORTED_RISCV();
        break;
      }
      switch (instr->Funct3Value()) {
        case 0b001:  // RO_FCLASS_D
          Format(instr, "fclass.d  'rd, 'fs1");
          break;
        case 0b000:  // RO_FMV_X_D
          Format(instr, "fmv.x.d   'rd, 'fs1");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FCVT_W_D: {  // RO_FCVT_WU_D , 64F RO_FCVT_L_D RO_FCVT_LU_D
      switch (instr->Rs2Value()) {
        case 0b00000:  // RO_FCVT_W_D
          Format(instr, "fcvt.w.d  ['frm] 'rd, 'fs1");
          break;
        case 0b00001:  // RO_FCVT_WU_D
          Format(instr, "fcvt.wu.d ['frm] 'rd, 'fs1");
          break;
        case 0b01000:  // RO_FCVTMOD_W_D
          MOZ_ASSERT(instr->RoundMode() == FPURoundingMode::RTZ);
          Format(instr, "fcvtmod.w.d ['frm] 'rd, 'fs1");
          break;
        case 0b00010:  // RO_FCVT_L_D
          Format(instr, "fcvt.l.d  ['frm] 'rd, 'fs1");
          break;
        case 0b00011:  // RO_FCVT_LU_D
          Format(instr, "fcvt.lu.d ['frm] 'rd, 'fs1");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FCVT_D_W: {  // RO_FCVT_D_WU , 64F RO_FCVT_D_L RO_FCVT_D_LU
      switch (instr->Rs2Value()) {
        case 0b00000:  // RO_FCVT_D_W
          Format(instr, "fcvt.d.w  'fd, 'rs1");
          break;
        case 0b00001:  // RO_FCVT_D_WU
          Format(instr, "fcvt.d.wu 'fd, 'rs1");
          break;
        case 0b00010:  // RO_FCVT_D_L
          Format(instr, "fcvt.d.l  'fd, 'rs1");
          break;
        case 0b00011:  // RO_FCVT_D_LU
          Format(instr, "fcvt.d.lu 'fd, 'rs1");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FMV_D_X: {
      if (instr->Funct3Value() == 0b000) {
        if (instr->Rs2Value() == 0b00000) {
          Format(instr, "fmv.d.x   'fd, 'rs1");
        } else if (instr->Rs2Value() == 0b00001) {
          Format(instr, "fli.d     'fd, 'fid");
        } else {
          UNSUPPORTED_RISCV();
        }
      } else {
        UNSUPPORTED_RISCV();
      }
      break;
    }
    // TODO(riscv): Add macro for RISCV ZFH extension
    case RO_FADD_H:
      Format(instr, "fadd.h    'fd, 'fs1, 'fs2");
      break;
    case RO_FSUB_H:
      Format(instr, "fsub.h    'fd, 'fs1, 'fs2");
      break;
    case RO_FMUL_H:
      Format(instr, "fmul.h    'fd, 'fs1, 'fs2");
      break;
    case RO_FDIV_H:
      Format(instr, "fdiv.h    'fd, 'fs1, 'fs2");
      break;
    case RO_FSQRT_H:
      Format(instr, "fsqrt.h   'fd, 'fs1");
      break;
    case RO_FSGNJ_H: {  // RO_FSGNJN_H  RO_FSGNJX_H
      switch (instr->Funct3Value()) {
        case 0b000:  // RO_FSGNJ_H
          if (instr->Rs1Value() == instr->Rs2Value()) {
            Format(instr, "fmv.h     'fd, 'fs1");
          } else {
            Format(instr, "fsgnj.h   'fd, 'fs1, 'fs2");
          }
          break;
        case 0b001:  // RO_FSGNJN_H
          if (instr->Rs1Value() == instr->Rs2Value()) {
            Format(instr, "fneg.h    'fd, 'fs1");
          } else {
            Format(instr, "fsgnjn.h  'fd, 'fs1, 'fs2");
          }
          break;
        case 0b010:  // RO_FSGNJX_H
          if (instr->Rs1Value() == instr->Rs2Value()) {
            Format(instr, "fabs.h    'fd, 'fs1");
          } else {
            Format(instr, "fsgnjx.h  'fd, 'fs1, 'fs2");
          }
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FMIN_H: {  // RO_FMAX_H
      switch (instr->Funct3Value()) {
        case 0b000:  // RO_FMIN_H
          Format(instr, "fmin.h    'fd, 'fs1, 'fs2");
          break;
        case 0b001:  // RO_FMAX_H
          Format(instr, "fmax.h    'fd, 'fs1, 'fs2");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FCVT_W_H: {  // RO_FCVT_WU_H , 64F RO_FCVT_L_H RO_FCVT_LU_H
      switch (instr->Rs2Value()) {
        case 0b00000:  // RO_FCVT_W_H
          Format(instr, "fcvt.w.h ['frm] 'rd, 'fs1");
          break;
        case 0b00001:  // RO_FCVT_WU_H
          Format(instr, "fcvt.wu.h ['frm] 'rd, 'fs1");
          break;
        case 0b00010:  // RO_FCVT_L_H
          Format(instr, "fcvt.l.h ['frm] 'rd, 'fs1");
          break;
        case 0b00011:  // RO_FCVT_LU_H
          Format(instr, "fcvt.lu.h ['frm] 'rd, 'fs1");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FCVT_H_S: {
      if (instr->Rs2Value() == 0b00000) {
        Format(instr, "fcvt.h.s ['frm] 'fd, 'fs1");
      } else if (instr->Rs2Value() == 0b00001) {
        Format(instr, "fcvt.h.d ['frm] 'fd, 'fs1");
      } else if (instr->Rs2Value() == 0b00100) {
        Format(instr, "fround.h ['frm] 'fd, 'fs1");
      } else if (instr->Rs2Value() == 0b00101) {
        Format(instr, "froundnx.h ['frm] 'fd, 'fs1");
      } else {
        UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FMV_X_H: {  // RO_FCLASS_H
      if (instr->Rs2Value() != 0b00000) {
        UNSUPPORTED_RISCV();
      }
      switch (instr->Funct3Value()) {
        case 0b000:  // RO_FMV_X_H
          Format(instr, "fmv.x.h   'rd, 'fs1");
          break;
        case 0b001:  // RO_FCLASS_H
          Format(instr, "fclass.h  'rd, 'fs1");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FLE_H: {  // RO_FEQ_H RO_FLT_H RO_FLE_H
      switch (instr->Funct3Value()) {
        case 0b010:  // RO_FEQ_H
          Format(instr, "feq.h     'rd, 'fs1, 'fs2");
          break;
        case 0b001:  // RO_FLT_H
          Format(instr, "flt.h     'rd, 'fs1, 'fs2");
          break;
        case 0b000:  // RO_FLE_H
          Format(instr, "fle.h     'rd, 'fs1, 'fs2");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FCVT_H_W: {  // RO_FCVT_H_WU , 64F RO_FCVT_H_L RO_FCVT_H_LU
      switch (instr->Rs2Value()) {
        case 0b00000:  // RO_FCVT_H_W
          Format(instr, "fcvt.h.w ['frm] 'fd, 'rs1");
          break;
        case 0b00001:  // RO_FCVT_H_WU
          Format(instr, "fcvt.h.wu ['frm] 'fd, 'rs1");
          break;
        case 0b00010:  // RO_FCVT_H_L
          Format(instr, "fcvt.h.l ['frm] 'fd, 'rs1");
          break;
        case 0b00011:  // RO_FCVT_H_LU
          Format(instr, "fcvt.h.lu ['frm] 'fd, 'rs1");
          break;
        default: {
          UNSUPPORTED_RISCV();
        }
      }
      break;
    }
    case RO_FMV_H_X: {
      if (instr->Funct3Value() == 0b000) {
        if (instr->Rs2Value() == 0b00000) {
          Format(instr, "fmv.h.x   'fd, 'rs1");
        } else if (instr->Rs2Value() == 0b00001) {
          // fli.h not supported
          UNSUPPORTED_RISCV();
        } else {
          UNSUPPORTED_RISCV();
        }
      } else {
        UNSUPPORTED_RISCV();
      }
      break;
    }
    default: {
      UNSUPPORTED_RISCV();
    }
  }
}

void Decoder::DecodeR4Type(Instruction* instr) {
  switch (instr->InstructionBits() & kR4TypeMask) {
    // TODO(riscv): use F Extension macro block
    case RO_FMADD_S:
      Format(instr, "fmadd.s   'fd, 'fs1, 'fs2, 'fs3");
      break;
    case RO_FMSUB_S:
      Format(instr, "fmsub.s   'fd, 'fs1, 'fs2, 'fs3");
      break;
    case RO_FNMSUB_S:
      Format(instr, "fnmsub.s   'fd, 'fs1, 'fs2, 'fs3");
      break;
    case RO_FNMADD_S:
      Format(instr, "fnmadd.s   'fd, 'fs1, 'fs2, 'fs3");
      break;
    // TODO(riscv): use F Extension macro block
    case RO_FMADD_D:
      Format(instr, "fmadd.d   'fd, 'fs1, 'fs2, 'fs3");
      break;
    case RO_FMSUB_D:
      Format(instr, "fmsub.d   'fd, 'fs1, 'fs2, 'fs3");
      break;
    case RO_FNMSUB_D:
      Format(instr, "fnmsub.d  'fd, 'fs1, 'fs2, 'fs3");
      break;
    case RO_FNMADD_D:
      Format(instr, "fnmadd.d  'fd, 'fs1, 'fs2, 'fs3");
      break;
    // TODO(riscv): use ZFH Extension macro block
    case RO_FMADD_H:
      Format(instr, "fmadd.h   'fd, 'fs1, 'fs2, 'fs3");
      break;
    case RO_FMSUB_H:
      Format(instr, "fmsub.h   'fd, 'fs1, 'fs2, 'fs3");
      break;
    case RO_FNMSUB_H:
      Format(instr, "fnmsub.h   'fd, 'fs1, 'fs2, 'fs3");
      break;
    case RO_FNMADD_H:
      Format(instr, "fnmadd.h   'fd, 'fs1, 'fs2, 'fs3");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

void Decoder::DecodeIType(Instruction* instr) {
  switch (instr->InstructionBits() & kITypeMask) {
    case RO_JALR:
      if (instr->RdValue() == zero.code() && instr->Rs1Value() == ra.code() &&
          instr->Imm12Value() == 0)
        Format(instr, "ret");
      else if (instr->RdValue() == zero.code() && instr->Imm12Value() == 0)
        Format(instr, "jr        'rs1");
      else if (instr->RdValue() == ra.code() && instr->Imm12Value() == 0)
        Format(instr, "jalr      'rs1");
      else
        Format(instr, "jalr      'rd, 'imm12('rs1)");
      break;
    case RO_LB:
      Format(instr, "lb        'rd, 'imm12('rs1)");
      break;
    case RO_LH:
      Format(instr, "lh        'rd, 'imm12('rs1)");
      break;
    case RO_LW:
      Format(instr, "lw        'rd, 'imm12('rs1)");
      break;
    case RO_LBU:
      Format(instr, "lbu       'rd, 'imm12('rs1)");
      break;
    case RO_LHU:
      Format(instr, "lhu       'rd, 'imm12('rs1)");
      break;
    case RO_LWU:
      Format(instr, "lwu       'rd, 'imm12('rs1)");
      break;
    case RO_LD:
      Format(instr, "ld        'rd, 'imm12('rs1)");
      break;
    case RO_ADDI:
      if (instr->Imm12Value() == 0) {
        if (instr->RdValue() == zero.code() && instr->Rs1Value() == zero.code())
          Format(instr, "nop");
        else
          Format(instr, "mv        'rd, 'rs1");
      } else if (instr->Rs1Value() == zero.code()) {
        Format(instr, "li        'rd, 'imm12");
      } else {
        Format(instr, "addi      'rd, 'rs1, 'imm12");
      }
      break;
    case RO_SLTI:
      Format(instr, "slti      'rd, 'rs1, 'imm12");
      break;
    case RO_SLTIU:
      if (instr->Imm12Value() == 1)
        Format(instr, "seqz      'rd, 'rs1");
      else
        Format(instr, "sltiu     'rd, 'rs1, 'imm12");
      break;
    case RO_XORI:
      if (instr->Imm12Value() == -1)
        Format(instr, "not       'rd, 'rs1");
      else
        Format(instr, "xori      'rd, 'rs1, 'imm12x");
      break;
    case RO_ORI:
      Format(instr, "ori       'rd, 'rs1, 'imm12x");
      break;
    case RO_ANDI:
      Format(instr, "andi      'rd, 'rs1, 'imm12x");
      break;
    case OP_SHL:
      switch (instr->Funct6FieldRaw() | OP_SHL) {
        case RO_SLLI:
          Format(instr, "slli      'rd, 'rs1, 's64");
          break;
        case RO_BCLRI:
          Format(instr, "bclri     'rd, 'rs1, 's64");
          break;
        case RO_BINVI:
          Format(instr, "binvi     'rd, 'rs1, 's64");
          break;
        case RO_BSETI:
          Format(instr, "bseti     'rd, 'rs1, 's64");
          break;
        case OP_COUNT:
          switch (instr->Shamt()) {
            case 0:
              Format(instr, "clz       'rd, 'rs1");
              break;
            case 1:
              Format(instr, "ctz       'rd, 'rs1");
              break;
            case 2:
              Format(instr, "cpop      'rd, 'rs1");
              break;
            case 4:
              Format(instr, "sext.b    'rd, 'rs1");
              break;
            case 5:
              Format(instr, "sext.h    'rd, 'rs1");
              break;
            default:
              UNSUPPORTED_RISCV();
          }
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    case OP_SHR: {  //  RO_SRAI
      switch (instr->Funct6FieldRaw() | OP_SHR) {
        case RO_SRLI:
          Format(instr, "srli      'rd, 'rs1, 's64");
          break;
        case RO_SRAI:
          Format(instr, "srai      'rd, 'rs1, 's64");
          break;
        case RO_BEXTI:
          Format(instr, "bexti     'rd, 'rs1, 's64");
          break;
        case RO_ORCB&(kFunct6Mask | OP_SHR):
          Format(instr, "orc.b     'rd, 'rs1");
          break;
        case RO_RORI:
          Format(instr, "rori      'rd, 'rs1, 's64");
          break;
        case RO_REV8: {
          if (instr->Imm12Value() == RO_REV8_IMM12) {
            Format(instr, "rev8      'rd, 'rs1");
            break;
          }
          UNSUPPORTED_RISCV();
          break;
        }
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_ADDIW:
      if (instr->Imm12Value() == 0)
        Format(instr, "sext.w    'rd, 'rs1");
      else
        Format(instr, "addiw     'rd, 'rs1, 'imm12");
      break;
    case OP_SHLW:
      switch (instr->Funct7FieldRaw() | OP_SHLW) {
        case RO_SLLIW:
          Format(instr, "slliw     'rd, 'rs1, 's32");
          break;
        case RO_SLLIUW:
          Format(instr, "slli.uw   'rd, 'rs1, 's32");
          break;
        case OP_COUNTW: {
          switch (instr->Shamt()) {
            case 0:
              Format(instr, "clzw      'rd, 'rs1");
              break;
            case 1:
              Format(instr, "ctzw      'rd, 'rs1");
              break;
            case 2:
              Format(instr, "cpopw     'rd, 'rs1");
              break;
            default:
              UNSUPPORTED_RISCV();
          }
          break;
        }
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    case OP_SHRW: {  //  RO_SRAI
      switch (instr->Funct7FieldRaw() | OP_SHRW) {
        case RO_SRLIW:
          Format(instr, "srliw     'rd, 'rs1, 's32");
          break;
        case RO_SRAIW:
          Format(instr, "sraiw     'rd, 'rs1, 's32");
          break;
        case RO_RORIW:
          Format(instr, "roriw     'rd, 'rs1, 's32");
          break;
        default:
          UNSUPPORTED_RISCV();
      }
      break;
    }
    case RO_FENCE:
      if (instr->MemoryOrder(true) == PSIORW &&
          instr->MemoryOrder(false) == PSIORW)
        Format(instr, "fence");
      else
        Format(instr, "fence 'pre, 'suc");
      break;
    case RO_ECALL: {                   // RO_EBREAK
      if (instr->Imm12Value() == 0) {  // ECALL
        Format(instr, "ecall");
      } else if (instr->Imm12Value() == 1) {  // EBREAK
        Format(instr, "ebreak");
      } else {
        UNSUPPORTED_RISCV();
      }
      break;
    }
    // TODO(riscv): use Zifencei Standard Extension macro block
    case RO_FENCE_I:
      Format(instr, "fence.i");
      break;
    // TODO(riscv): use Zicsr Standard Extension macro block
    // FIXME(RISC-V): Add special formatting for CSR registers
    case RO_CSRRW:
      if (instr->CsrValue() == csr_fcsr) {
        if (instr->RdValue() == zero.code())
          Format(instr, "fscsr     'rs1");
        else
          Format(instr, "fscsr     'rd, 'rs1");
      } else if (instr->CsrValue() == csr_frm) {
        if (instr->RdValue() == zero.code())
          Format(instr, "fsrm      'rs1");
        else
          Format(instr, "fsrm      'rd, 'rs1");
      } else if (instr->CsrValue() == csr_fflags) {
        if (instr->RdValue() == zero.code())
          Format(instr, "fsflags   'rs1");
        else
          Format(instr, "fsflags   'rd, 'rs1");
      } else if (instr->RdValue() == zero.code()) {
        Format(instr, "csrw      'csr, 'rs1");
      } else {
        Format(instr, "csrrw     'rd, 'csr, 'rs1");
      }
      break;
    case RO_CSRRS:
      if (instr->Rs1Value() == zero.code()) {
        switch (instr->CsrValue()) {
          case csr_instret:
            Format(instr, "rdinstret 'rd");
            break;
          case csr_instreth:
            Format(instr, "rdinstreth 'rd");
            break;
          case csr_time:
            Format(instr, "rdtime    'rd");
            break;
          case csr_timeh:
            Format(instr, "rdtimeh   'rd");
            break;
          case csr_cycle:
            Format(instr, "rdcycle   'rd");
            break;
          case csr_cycleh:
            Format(instr, "rdcycleh  'rd");
            break;
          case csr_fflags:
            Format(instr, "frflags   'rd");
            break;
          case csr_frm:
            Format(instr, "frrm      'rd");
            break;
          case csr_fcsr:
            Format(instr, "frcsr     'rd");
            break;
          default:
            MOZ_CRASH();
        }
      } else if (instr->Rs1Value() == zero.code()) {
        Format(instr, "csrr      'rd, 'csr");
      } else if (instr->RdValue() == zero.code()) {
        Format(instr, "csrs      'csr, 'rs1");
      } else {
        Format(instr, "csrrs     'rd, 'csr, 'rs1");
      }
      break;
    case RO_CSRRC:
      if (instr->RdValue() == zero.code())
        Format(instr, "csrc      'csr, 'rs1");
      else
        Format(instr, "csrrc     'rd, 'csr, 'rs1");
      break;
    case RO_CSRRWI:
      if (instr->RdValue() == zero.code())
        Format(instr, "csrwi     'csr, 'uimm");
      else
        Format(instr, "csrrwi    'rd, 'csr, 'uimm");
      break;
    case RO_CSRRSI:
      if (instr->RdValue() == zero.code())
        Format(instr, "csrsi     'csr, 'uimm");
      else
        Format(instr, "csrrsi    'rd, 'csr, 'uimm");
      break;
    case RO_CSRRCI:
      if (instr->RdValue() == zero.code())
        Format(instr, "csrci     'csr, 'uimm");
      else
        Format(instr, "csrrci    'rd, 'csr, 'uimm");
      break;
    // TODO(riscv): use F Extension macro block
    case RO_FLW:
      Format(instr, "flw       'fd, 'imm12('rs1)");
      break;
    case RO_FLH:
      Format(instr, "flh       'fd, 'imm12('rs1)");
      break;
    // TODO(riscv): use D Extension macro block
    case RO_FLD:
      Format(instr, "fld       'fd, 'imm12('rs1)");
      break;
    default:
#ifdef CAN_USE_RVV_INSTRUCTIONS
      if (instr->vl_vs_width() != -1) {
        DecodeRvvVL(instr);
      } else {
        UNSUPPORTED_RISCV();
      }
      break;
#else
      UNSUPPORTED_RISCV();
#endif
  }
}

void Decoder::DecodeSType(Instruction* instr) {
  switch (instr->InstructionBits() & kSTypeMask) {
    case RO_SB:
      Format(instr, "sb        'rs2, 'offS('rs1)");
      break;
    case RO_SH:
      Format(instr, "sh        'rs2, 'offS('rs1)");
      break;
    case RO_SW:
      Format(instr, "sw        'rs2, 'offS('rs1)");
      break;
    case RO_SD:
      Format(instr, "sd        'rs2, 'offS('rs1)");
      break;
    // TODO(riscv): use F Extension macro block
    case RO_FSW:
      Format(instr, "fsw       'fs2, 'offS('rs1)");
      break;
    case RO_FSH:
      Format(instr, "fsh       'fs2, 'offS('rs1)");
      break;
    // TODO(riscv): use D Extension macro block
    case RO_FSD:
      Format(instr, "fsd       'fs2, 'offS('rs1)");
      break;
    default:
#ifdef CAN_USE_RVV_INSTRUCTIONS
      if (instr->vl_vs_width() != -1) {
        DecodeRvvVS(instr);
      } else {
        UNSUPPORTED_RISCV();
      }
      break;
#else
      UNSUPPORTED_RISCV();
#endif
  }
}

void Decoder::DecodeBType(Instruction* instr) {
  switch (instr->InstructionBits() & kBTypeMask) {
    case RO_BEQ:
      Format(instr, "beq       'rs1, 'rs2, 'offB");
      break;
    case RO_BNE:
      Format(instr, "bne       'rs1, 'rs2, 'offB");
      break;
    case RO_BLT:
      Format(instr, "blt       'rs1, 'rs2, 'offB");
      break;
    case RO_BGE:
      Format(instr, "bge       'rs1, 'rs2, 'offB");
      break;
    case RO_BLTU:
      Format(instr, "bltu      'rs1, 'rs2, 'offB");
      break;
    case RO_BGEU:
      Format(instr, "bgeu      'rs1, 'rs2, 'offB");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}
void Decoder::DecodeUType(Instruction* instr) {
  // U Type doesn't have additional mask
  switch (instr->BaseOpcodeFieldRaw()) {
    case LUI:
      Format(instr, "lui       'rd, 'imm20U");
      break;
    case AUIPC:
      Format(instr, "auipc     'rd, 'imm20U");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}
// namespace jit
void Decoder::DecodeJType(Instruction* instr) {
  // J Type doesn't have additional mask
  switch (instr->BaseOpcodeValue()) {
    case JAL:
      if (instr->RdValue() == zero.code())
        Format(instr, "j         'imm20J");
      else if (instr->RdValue() == ra.code())
        Format(instr, "jal       'imm20J");
      else
        Format(instr, "jal       'rd, 'imm20J");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

void Decoder::DecodeCRType(Instruction* instr) {
  switch (instr->RvcFunct4Value()) {
    case 0b1000:
      if (instr->RvcRs1Value() != 0 && instr->RvcRs2Value() == 0)
        Format(instr, "jr        'Crs1");
      else if (instr->RvcRdValue() != 0 && instr->RvcRs2Value() != 0)
        Format(instr, "mv        'Crd, 'Crs2");
      else
        UNSUPPORTED_RISCV();
      break;
    case 0b1001:
      if (instr->RvcRs1Value() == 0 && instr->RvcRs2Value() == 0)
        Format(instr, "ebreak");
      else if (instr->RvcRdValue() != 0 && instr->RvcRs2Value() == 0)
        Format(instr, "jalr      'Crs1");
      else if (instr->RvcRdValue() != 0 && instr->RvcRs2Value() != 0)
        Format(instr, "add       'Crd, 'Crd, 'Crs2");
      else
        UNSUPPORTED_RISCV();
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

void Decoder::DecodeCAType(Instruction* instr) {
  switch (instr->InstructionBits() & kCATypeMask) {
    case RO_C_SUB:
      Format(instr, "sub       'Crs1s, 'Crs1s, 'Crs2s");
      break;
    case RO_C_XOR:
      Format(instr, "xor       'Crs1s, 'Crs1s, 'Crs2s");
      break;
    case RO_C_OR:
      Format(instr, "or       'Crs1s, 'Crs1s, 'Crs2s");
      break;
    case RO_C_AND:
      Format(instr, "and       'Crs1s, 'Crs1s, 'Crs2s");
      break;
    case RO_C_SUBW:
      Format(instr, "subw       'Crs1s, 'Crs1s, 'Crs2s");
      break;
    case RO_C_ADDW:
      Format(instr, "addw       'Crs1s, 'Crs1s, 'Crs2s");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

void Decoder::DecodeCIType(Instruction* instr) {
  switch (instr->RvcOpcode()) {
    case RO_C_NOP_ADDI:
      if (instr->RvcRdValue() == 0)
        Format(instr, "nop");
      else
        Format(instr, "addi      'Crd, 'Crd, 'Cimm6");
      break;
    case RO_C_ADDIW:
      Format(instr, "addiw     'Crd, 'Crd, 'Cimm6");
      break;
    case RO_C_LI:
      Format(instr, "li        'Crd, 'Cimm6");
      break;
    case RO_C_LUI_ADD:
      if (instr->RvcRdValue() == 2)
        Format(instr, "addi      sp, sp, 'Cimm6Addi16sp");
      else if (instr->RvcRdValue() != 0 && instr->RvcRdValue() != 2)
        Format(instr, "lui       'Crd, 'Cimm6U");
      else
        UNSUPPORTED_RISCV();
      break;
    case RO_C_SLLI:
      Format(instr, "slli      'Crd, 'Crd, 'Cshamt");
      break;
    case RO_C_FLDSP:
      Format(instr, "fld       'Cfd, 'Cimm6Ldsp(sp)");
      break;
    case RO_C_LWSP:
      Format(instr, "lw        'Crd, 'Cimm6Lwsp(sp)");
      break;
    case RO_C_LDSP:
      Format(instr, "ld        'Crd, 'Cimm6Ldsp(sp)");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

void Decoder::DecodeCIWType(Instruction* instr) {
  switch (instr->RvcOpcode()) {
    case RO_C_ADDI4SPN:
      Format(instr, "addi       'Crs2s, sp, 'Cimm8Addi4spn");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

void Decoder::DecodeCSSType(Instruction* instr) {
  switch (instr->RvcOpcode()) {
    case RO_C_SWSP:
      Format(instr, "sw        'Crs2, 'Cimm6Swsp(sp)");
      break;
    case RO_C_SDSP:
      Format(instr, "sd        'Crs2, 'Cimm6Sdsp(sp)");
      break;
    case RO_C_FSDSP:
      Format(instr, "fsd       'Cfs2, 'Cimm6Sdsp(sp)");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

void Decoder::DecodeCLType(Instruction* instr) {
  switch (instr->RvcOpcode()) {
    case RO_C_FLD:
      Format(instr, "fld       'Cfs2s, 'Cimm5D('Crs1s)");
      break;
    case RO_C_LW:
      Format(instr, "lw       'Crs2s, 'Cimm5W('Crs1s)");
      break;
    case RO_C_LD:
      Format(instr, "ld       'Crs2s, 'Cimm5D('Crs1s)");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

void Decoder::DecodeCSType(Instruction* instr) {
  switch (instr->RvcOpcode()) {
    case RO_C_FSD:
      Format(instr, "fsd       'Cfs2s, 'Cimm5D('Crs1s)");
      break;
    case RO_C_SW:
      Format(instr, "sw       'Crs2s, 'Cimm5W('Crs1s)");
      break;
    case RO_C_SD:
      Format(instr, "sd       'Crs2s, 'Cimm5D('Crs1s)");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

void Decoder::DecodeCJType(Instruction* instr) {
  switch (instr->RvcOpcode()) {
    case RO_C_J:
      Format(instr, "j       'Cimm11CJ");
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

void Decoder::DecodeCBType(Instruction* instr) {
  switch (instr->RvcOpcode()) {
    case RO_C_BNEZ:
      Format(instr, "bnez       'Crs1s, x0, 'Cimm8B");
      break;
    case RO_C_BEQZ:
      Format(instr, "beqz       'Crs1s, x0, 'Cimm8B");
      break;
    case RO_C_MISC_ALU:
      if (instr->RvcFunct2BValue() == 0b00)
        Format(instr, "srli       'Crs1s, 'Crs1s, 'Cshamt");
      else if (instr->RvcFunct2BValue() == 0b01)
        Format(instr, "srai       'Crs1s, 'Crs1s, 'Cshamt");
      else if (instr->RvcFunct2BValue() == 0b10)
        Format(instr, "andi       'Crs1s, 'Crs1s, 'Cimm6");
      else
        UNSUPPORTED_RISCV();
      break;
    default:
      UNSUPPORTED_RISCV();
  }
}

#undef VERIFIY

bool Decoder::IsConstantPoolAt(uint8_t* instr_ptr) {
  UNSUPPORTED_RISCV();
  MOZ_CRASH();
}

int Decoder::ConstantPoolSizeAt(uint8_t* instr_ptr) {
  UNSUPPORTED_RISCV();
  MOZ_CRASH();
}

// Disassemble the instruction at *instr_ptr into the output buffer.
int Decoder::InstructionDecode(Instruction* instr) {
  // Print raw instruction bytes.
  out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%08x       ",
                              instr->InstructionBits());
  switch (instr->InstructionType()) {
    case Instruction::kRType:
      DecodeRType(instr);
      break;
    case Instruction::kR4Type:
      DecodeR4Type(instr);
      break;
    case Instruction::kIType:
      DecodeIType(instr);
      break;
    case Instruction::kSType:
      DecodeSType(instr);
      break;
    case Instruction::kBType:
      DecodeBType(instr);
      break;
    case Instruction::kUType:
      DecodeUType(instr);
      break;
    case Instruction::kJType:
      DecodeJType(instr);
      break;
    case Instruction::kCRType:
      DecodeCRType(instr);
      break;
    case Instruction::kCAType:
      DecodeCAType(instr);
      break;
    case Instruction::kCJType:
      DecodeCJType(instr);
      break;
    case Instruction::kCIType:
      DecodeCIType(instr);
      break;
    case Instruction::kCIWType:
      DecodeCIWType(instr);
      break;
    case Instruction::kCSSType:
      DecodeCSSType(instr);
      break;
    case Instruction::kCLType:
      DecodeCLType(instr);
      break;
    case Instruction::kCSType:
      DecodeCSType(instr);
      break;
    case Instruction::kCBType:
      DecodeCBType(instr);
      break;
#ifdef CAN_USE_RVV_INSTRUCTIONS
    case Instruction::kVType:
      DecodeVType(instr);
      break;
#endif
    default:
      Format(instr, "UNSUPPORTED");
      UNSUPPORTED_RISCV();
  }
  return instr->InstructionSize();
}

}  // namespace disasm

#undef STRING_STARTS_WITH
#undef VERIFY

//------------------------------------------------------------------------------

namespace disasm {

const char* NameConverter::NameOfAddress(uint8_t* addr) const {
  SNPrintF(tmp_buffer_, "%p", addr);
  return tmp_buffer_.start();
}

const char* NameConverter::NameOfConstant(uint8_t* addr) const {
  return NameOfAddress(addr);
}

const char* NameConverter::NameOfCPURegister(int reg) const {
  return Registers::GetName(reg);
}

const char* NameConverter::NameOfByteCPURegister(int reg) const {
  MOZ_CRASH(" RISC-V does not have the concept of a byte register.");
}

const char* NameConverter::NameOfXMMRegister(int reg) const {
  return FloatRegisters::GetName(reg);
}

const char* NameConverter::NameInCode(uint8_t* addr) const {
  // The default name converter is called for unknown code. So we will not try
  // to access any memory.
  return "";
}

//------------------------------------------------------------------------------

Disassembler::Disassembler(const NameConverter& converter)
    : converter_(converter) {}

Disassembler::~Disassembler() {}

int Disassembler::InstructionDecode(V8Vector<char> buffer, Instruction* instr) {
  Decoder d(converter_, buffer);
  return d.InstructionDecode(instr);
}

int Disassembler::ConstantPoolSizeAt(uint8_t* instruction) {
  return Decoder::ConstantPoolSizeAt(instruction);
}

void Disassembler::Disassemble(FILE* f, uint8_t* begin, uint8_t* end) {
  NameConverter converter;
  Disassembler d(converter);
  for (uint8_t* pc = begin; pc < end;) {
    EmbeddedVector<char, ReasonableBufferSize> buffer;
    buffer[0] = '\0';
    uint8_t* prev_pc = pc;
    pc += d.InstructionDecode(buffer, Instruction::At(pc));
    fprintf(f, "%p    %08x      %s\n", prev_pc,
            *reinterpret_cast<int32_t*>(prev_pc), buffer.start());
  }
}

}  // namespace disasm
}  // namespace jit
}  // namespace js

Messung V0.5 in Prozent
C=90 H=99 G=94

¤ Dauer der Verarbeitung: 0.41 Sekunden  ¤

*© 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002