/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: */ // 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 buffer; // uint8_t* prev_pc = pc; // pc += d.InstructionDecode(buffer, pc); // printf("%p %08x %s\n", // prev_pc, *reinterpret_cast(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 #include #include #include "jit/riscv64/Assembler-riscv64.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 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(uint8_t* instruction); 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); // 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 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* format) { MOZ_ASSERT(format[0] == 'r'); if (format[1] == 's') { // 'rs[12]: Rs register. if (format[2] == '1') { int reg = instr->Rs1Value(); PrintRegister(reg); return 3; } else if (format[2] == '2') { int reg = instr->Rs2Value(); PrintRegister(reg); return 3; } MOZ_CRASH(); } else if (format[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* format) { MOZ_ASSERT(format[0] == 'f'); if (format[1] == 's') { // 'fs[1-3]: Rs register. if (format[2] == '1') { int reg = instr->Rs1Value(); PrintFPURegister(reg); return 3; } else if (format[2] == '2') { int reg = instr->Rs2Value(); PrintFPURegister(reg); return 3; } else if (format[2] == '3') { int reg = instr->Rs3Value(); PrintFPURegister(reg); return 3; } MOZ_CRASH(); } else if (format[1] == 'd') { // 'fd: fd register. int reg = instr->RdValue(); PrintFPURegister(reg); return 2; } else if (format[1] == 'r') { // 'frm MOZ_ASSERT(STRING_STARTS_WITH(format, "frm")); PrintRoundingMode(instr); return 3; } 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* format) { MOZ_ASSERT(format[0] == 'C'); MOZ_ASSERT(format[1] == 'r' || format[1] == 'f'); if (format[2] == 's') { // 'Crs[12]: Rs register. if (format[3] == '1') { if (format[4] == 's') { // 'Crs1s: 3-bits register int reg = instr->RvcRs1sValue(); if (format[1] == 'r') { PrintRegister(reg); } else if (format[1] == 'f') { PrintFPURegister(reg); } return 5; } int reg = instr->RvcRs1Value(); if (format[1] == 'r') { PrintRegister(reg); } else if (format[1] == 'f') { PrintFPURegister(reg); } return 4; } else if (format[3] == '2') { if (format[4] == 's') { // 'Crs2s: 3-bits register int reg = instr->RvcRs2sValue(); if (format[1] == 'r') { PrintRegister(reg); } else if (format[1] == 'f') { PrintFPURegister(reg); } return 5; } int reg = instr->RvcRs2Value(); if (format[1] == 'r') { PrintRegister(reg); } else if (format[1] == 'f') { PrintFPURegister(reg); } return 4; } MOZ_CRASH(); } else if (format[2] == 'd') { // 'Crd: rd register. int reg = instr->RvcRdValue(); if (format[1] == 'r') { PrintRegister(reg); } else if (format[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* format) { // TODO(riscv): add other rvc imm format MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm")); if (format[4] == '6') { if (format[5] == 'U') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6U")); PrintRvcImm6U(instr); return 6; } else if (format[5] == 'A') { if (format[9] == '1' && format[10] == '6') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6Addi16sp")); PrintRvcImm6Addi16sp(instr); return 13; } MOZ_CRASH(); } else if (format[5] == 'L') { if (format[6] == 'd') { if (format[7] == 's') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6Ldsp")); PrintRvcImm6Ldsp(instr); return 9; } } else if (format[6] == 'w') { if (format[7] == 's') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6Lwsp")); PrintRvcImm6Lwsp(instr); return 9; } } MOZ_CRASH(); } else if (format[5] == 'S') { if (format[6] == 'w') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6Swsp")); PrintRvcImm6Swsp(instr); return 9; } else if (format[6] == 'd') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm6Sdsp")); PrintRvcImm6Sdsp(instr); return 9; } MOZ_CRASH(); } PrintRvcImm6(instr); return 5; } else if (format[4] == '5') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm5")); if (format[5] == 'W') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm5W")); PrintRvcImm5W(instr); return 6; } else if (format[5] == 'D') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm5D")); PrintRvcImm5D(instr); return 6; } MOZ_CRASH(); } else if (format[4] == '8') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm8")); if (format[5] == 'A') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm8Addi4spn")); PrintRvcImm8Addi4spn(instr); return 13; } else if (format[5] == 'B') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm8B")); PrintRvcImm8B(instr); return 6; } MOZ_CRASH(); } else if (format[4] == '1') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cimm1")); if (format[5] == '1') { MOZ_ASSERT(STRING_STARTS_WITH(format, "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* format) { switch (format[0]) { case 'C': { // `C extension if (format[1] == 'r' || format[1] == 'f') { return FormatRvcRegister(instr, format); } else if (format[1] == 'i') { return FormatRvcImm(instr, format); } else if (format[1] == 's') { MOZ_ASSERT(STRING_STARTS_WITH(format, "Cshamt")); PrintRvcShamt(instr); return 6; } MOZ_CRASH(); } case 'c': { // `csr: CSR registers if (format[1] == 's') { if (format[2] == 'r') { PrintCSRReg(instr); return 3; } } MOZ_CRASH(); } case 'i': { // 'imm12, 'imm12x, 'imm20U, or 'imm20J: Immediates. if (format[3] == '1') { if (format[4] == '2') { MOZ_ASSERT(STRING_STARTS_WITH(format, "imm12")); if (format[5] == 'x') { PrintImm12X(instr); return 6; } PrintImm12(instr); return 5; } } else if (format[3] == '2' && format[4] == '0') { MOZ_ASSERT(STRING_STARTS_WITH(format, "imm20")); switch (format[5]) { case 'U': MOZ_ASSERT(STRING_STARTS_WITH(format, "imm20U")); PrintImm20U(instr); break; case 'J': MOZ_ASSERT(STRING_STARTS_WITH(format, "imm20J")); PrintImm20J(instr); break; } return 6; } MOZ_CRASH(); } case 'o': { // 'offB or 'offS: Offsets. if (format[3] == 'B') { MOZ_ASSERT(STRING_STARTS_WITH(format, "offB")); PrintBranchOffset(instr); return 4; } else if (format[3] == 'S') { MOZ_ASSERT(STRING_STARTS_WITH(format, "offS")); PrintStoreOffset(instr); return 4; } MOZ_CRASH(); } case 'r': { // 'r: registers. return FormatRegister(instr, format); } case 'f': { // 'f: FPUregisters or `frm return FormatFPURegisterOrRoundMode(instr, format); } case 'a': { // 'a: Atomic acquire and release. PrintAcquireRelease(instr); return 1; } case 'p': { // `pre MOZ_ASSERT(STRING_STARTS_WITH(format, "pre")); PrintMemoryOrder(instr, true); return 3; } case 's': { // 's32 or 's64: Shift amount. if (format[1] == '3') { MOZ_ASSERT(STRING_STARTS_WITH(format, "s32")); PrintShamt32(instr); return 3; } else if (format[1] == '6') { MOZ_ASSERT(STRING_STARTS_WITH(format, "s64")); PrintShamt(instr); return 3; } else if (format[1] == 'u') { MOZ_ASSERT(STRING_STARTS_WITH(format, "suc")); PrintMemoryOrder(instr, false); return 3; } else if (format[1] == 'e') { MOZ_ASSERT(STRING_STARTS_WITH(format, "sew")); PrintRvvSEW(instr); return 3; } else if (format[1] == 'i') { MOZ_ASSERT(STRING_STARTS_WITH(format, "simm5")); PrintRvvSimm5(instr); return 5; } MOZ_CRASH(); } case 'v': { if (format[1] == 'd') { MOZ_ASSERT(STRING_STARTS_WITH(format, "vd")); PrintVd(instr); return 2; } else if (format[2] == '1') { MOZ_ASSERT(STRING_STARTS_WITH(format, "vs1")); PrintVs1(instr); return 3; } else if (format[2] == '2') { MOZ_ASSERT(STRING_STARTS_WITH(format, "vs2")); PrintVs2(instr); return 3; } else { MOZ_ASSERT(STRING_STARTS_WITH(format, "vm")); PrintRvvVm(instr); return 2; } } case 'l': { MOZ_ASSERT(STRING_STARTS_WITH(format, "lmul")); PrintRvvLMUL(instr); return 4; } case 'u': { if (STRING_STARTS_WITH(format, "uimm5")) { PrintRvvUimm5(instr); return 5; } else { MOZ_ASSERT(STRING_STARTS_WITH(format, "uimm")); PrintUimm(instr); return 4; } } case 't': { // 'target: target of branch instructions' MOZ_ASSERT(STRING_STARTS_WITH(format, "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 (Assembler::IsJalr(instr->InstructionBits())) { // if (Assembler::IsAuipc((instr - 4)->InstructionBits()) && // (instr - 4)->RdValue() == instr->Rs1Value()) { // int32_t imm = Assembler::BrachlongOffset((instr - // 4)->InstructionBits(), // instr->InstructionBits()); // const char* target = // converter_.NameOfAddress(reinterpret_cast(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(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(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 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.c_str()); } void Decoder::PrintRoundingMode(Instruction* instr) { int frm = instr->RoundMode(); std::string 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.c_str()); } void Decoder::PrintMemoryOrder(Instruction* instr, bool is_pred) { int memOrder = instr->MemoryOrder(is_pred); std::string s; if ((memOrder & PSI) == PSI) { s += "i"; } if ((memOrder & PSO) == PSO) { s += "o"; } if ((memOrder & PSR) == PSR) { s += "r"; } if ((memOrder & PSW) == PSW) { s += "w"; } out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%s", s.c_str()); } // 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; #ifdef JS_CODEGEN_RISCV64 case RO_ADDW: Format(instr, "addw '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; #endif /* JS_CODEGEN_RISCV64 */ // 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; #ifdef JS_CODEGEN_RISCV64 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; #endif /*JS_CODEGEN_RISCV64*/ // 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; #ifdef JS_CODEGEN_RISCV64 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, "amoswap.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; #endif /*JS_CODEGEN_RISCV64*/ // 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 0b001: // RO_FMAX_S Format(instr, "fmax.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; #ifdef JS_CODEGEN_RISCV64 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; #endif /* JS_CODEGEN_RISCV64 */ default: UNSUPPORTED_RISCV(); } break; } case RO_FMV: { // 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; 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; #ifdef JS_CODEGEN_RISCV64 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; #endif /* JS_CODEGEN_RISCV64 */ default: { UNSUPPORTED_RISCV(); } } break; } case RO_FMV_W_X: { if (instr->Funct3Value() == 0b000) { Format(instr, "fmv.w.x 'fd, 'rs1"); } 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 0b001: // RO_FMAX_D Format(instr, "fmax.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 { UNSUPPORTED_RISCV(); } break; } case RO_FCVT_D_S: { if (instr->Rs2Value() == 0b00000) { Format(instr, "fcvt.d.s '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; 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; #ifdef JS_CODEGEN_RISCV64 case 0b000: // RO_FMV_X_D Format(instr, "fmv.x.d 'rd, 'fs1"); break; #endif /* JS_CODEGEN_RISCV64 */ 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; #ifdef JS_CODEGEN_RISCV64 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; #endif /* JS_CODEGEN_RISCV64 */ 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; #ifdef JS_CODEGEN_RISCV64 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; #endif /* JS_CODEGEN_RISCV64 */ default: UNSUPPORTED_RISCV(); } break; } #ifdef JS_CODEGEN_RISCV64 case RO_FMV_D_X: { if (instr->Funct3Value() == 0b000 && instr->Rs2Value() == 0b00000) { Format(instr, "fmv.d.x 'fd, 'rs1"); } else { UNSUPPORTED_RISCV(); } break; } #endif /* JS_CODEGEN_RISCV64 */ 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; 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; #ifdef JS_CODEGEN_RISCV64 case RO_LWU: Format(instr, "lwu 'rd, 'imm12('rs1)"); break; case RO_LD: Format(instr, "ld 'rd, 'imm12('rs1)"); break; #endif /*JS_CODEGEN_RISCV64*/ 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 RO_SLLI: Format(instr, "slli 'rd, 'rs1, 's64"); break; case RO_SRLI: { // RO_SRAI if (!instr->IsArithShift()) { Format(instr, "srli 'rd, 'rs1, 's64"); } else { Format(instr, "srai 'rd, 'rs1, 's64"); } break; } #ifdef JS_CODEGEN_RISCV64 case RO_ADDIW: if (instr->Imm12Value() == 0) Format(instr, "sext.w 'rd, 'rs1"); else Format(instr, "addiw 'rd, 'rs1, 'imm12"); break; case RO_SLLIW: Format(instr, "slliw 'rd, 'rs1, 's32"); break; case RO_SRLIW: { // RO_SRAIW if (!instr->IsArithShift()) { Format(instr, "srliw 'rd, 'rs1, 's32"); } else { Format(instr, "sraiw 'rd, 'rs1, 's32"); } break; } #endif /*JS_CODEGEN_RISCV64*/ 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; // 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; #ifdef JS_CODEGEN_RISCV64 case RO_SD: Format(instr, "sd 'rs2, 'offS('rs1)"); break; #endif /*JS_CODEGEN_RISCV64*/ // TODO(riscv): use F Extension macro block case RO_FSW: Format(instr, "fsw '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; #ifdef JS_CODEGEN_RISCV64 case RO_C_SUBW: Format(instr, "subw 'Crs1s, 'Crs1s, 'Crs2s"); break; case RO_C_ADDW: Format(instr, "addw 'Crs1s, 'Crs1s, 'Crs2s"); break; #endif 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; #ifdef JS_CODEGEN_RISCV64 case RO_C_ADDIW: Format(instr, "addiw 'Crd, 'Crd, 'Cimm6"); break; #endif 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; #ifdef JS_CODEGEN_RISCV64 case RO_C_LDSP: Format(instr, "ld 'Crd, 'Cimm6Ldsp(sp)"); break; #elif defined(JS_CODEGEN_RISCV32) case RO_C_FLWSP: Format(instr, "flw 'Cfd, 'Cimm6Ldsp(sp)"); break; #endif 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; #ifdef JS_CODEGEN_RISCV64 case RO_C_SDSP: Format(instr, "sd 'Crs2, 'Cimm6Sdsp(sp)"); break; #elif defined(JS_CODEGEN_RISCV32) case RO_C_FSWSP: Format(instr, "fsw 'Cfs2, 'Cimm6Sdsp(sp)"); break; #endif 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; #ifdef JS_CODEGEN_RISCV64 case RO_C_LD: Format(instr, "ld 'Crs2s, 'Cimm5D('Crs1s)"); break; #elif defined(JS_CODEGEN_RISCV32) case RO_C_FLW: Format(instr, "fld 'Cfs2s, 'Cimm5D('Crs1s)"); break; #endif 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; #ifdef JS_CODEGEN_RISCV64 case RO_C_SD: Format(instr, "sd 'Crs2s, 'Cimm5D('Crs1s)"); break; #elif defined(JS_CODEGEN_RISCV32) case RO_C_FSW: Format(instr, "fsw 'Cfs2s, 'Cimm5D('Crs1s)"); break; #endif 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(byte* instr_ptr) { Instruction* instr = Instruction::At(instr_ptr); // 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 buffer, uint8_t* instruction) { Decoder d(converter_, buffer); return d.InstructionDecode(instruction); } 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 buffer; buffer[0] = '\0'; uint8_t* prev_pc = pc; pc += d.InstructionDecode(buffer, pc); fprintf(f, "%p %08x %s\n", prev_pc, *reinterpret_cast(prev_pc), buffer.start()); } } } // namespace disasm } // namespace jit } // namespace js