diff options
Diffstat (limited to 'deps/v8/src/mips/disasm-mips.cc')
-rw-r--r-- | deps/v8/src/mips/disasm-mips.cc | 784 |
1 files changed, 784 insertions, 0 deletions
diff --git a/deps/v8/src/mips/disasm-mips.cc b/deps/v8/src/mips/disasm-mips.cc new file mode 100644 index 000000000..cab72d1db --- /dev/null +++ b/deps/v8/src/mips/disasm-mips.cc @@ -0,0 +1,784 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// 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. +// +// NameConverter converter; +// Disassembler d(converter); +// for (byte_* pc = begin; pc < end;) { +// char buffer[128]; +// buffer[0] = '\0'; +// byte_* prev_pc = pc; +// pc += d.InstructionDecode(buffer, sizeof 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 <assert.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#ifndef WIN32 +#include <stdint.h> +#endif + +#include "v8.h" + +#include "constants-mips.h" +#include "disasm.h" +#include "macro-assembler.h" +#include "platform.h" + +namespace assembler { +namespace mips { + + +namespace v8i = v8::internal; + + +//------------------------------------------------------------------------------ + +// 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, + v8::internal::Vector<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(byte_* instruction); + + 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 PrintCRegister(int creg); + void PrintRs(Instruction* instr); + void PrintRt(Instruction* instr); + void PrintRd(Instruction* instr); + void PrintFs(Instruction* instr); + void PrintFt(Instruction* instr); + void PrintFd(Instruction* instr); + void PrintSa(Instruction* instr); + void PrintFunction(Instruction* instr); + void PrintSecondaryField(Instruction* instr); + void PrintUImm16(Instruction* instr); + void PrintSImm16(Instruction* instr); + void PrintXImm16(Instruction* instr); + void PrintImm26(Instruction* instr); + void PrintCode(Instruction* instr); // For break and trap instructions. + // Printing of instruction name. + void PrintInstructionName(Instruction* instr); + + // Handle formatting of instructions and their options. + int FormatRegister(Instruction* instr, const char* option); + int FormatCRegister(Instruction* instr, const char* option); + int FormatOption(Instruction* instr, const char* option); + void Format(Instruction* instr, const char* format); + void Unknown(Instruction* instr); + + // Each of these functions decodes one particular instruction type. + void DecodeTypeRegister(Instruction* instr); + void DecodeTypeImmediate(Instruction* instr); + void DecodeTypeJump(Instruction* instr); + + const disasm::NameConverter& converter_; + v8::internal::Vector<char> out_buffer_; + int out_buffer_pos_; + + DISALLOW_COPY_AND_ASSIGN(Decoder); +}; + + +// 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_ < (out_buffer_.length() - 1))) { + PrintChar(cur); + cur = *str++; + } + out_buffer_[out_buffer_pos_] = 0; +} + + +// Print the register name according to the active name converter. +void Decoder::PrintRegister(int reg) { + Print(converter_.NameOfCPURegister(reg)); +} + + +void Decoder::PrintRs(Instruction* instr) { + int reg = instr->RsField(); + PrintRegister(reg); +} + + +void Decoder::PrintRt(Instruction* instr) { + int reg = instr->RtField(); + PrintRegister(reg); +} + + +void Decoder::PrintRd(Instruction* instr) { + int reg = instr->RdField(); + PrintRegister(reg); +} + + +// Print the Cregister name according to the active name converter. +void Decoder::PrintCRegister(int creg) { + Print(converter_.NameOfXMMRegister(creg)); +} + + +void Decoder::PrintFs(Instruction* instr) { + int creg = instr->RsField(); + PrintCRegister(creg); +} + + +void Decoder::PrintFt(Instruction* instr) { + int creg = instr->RtField(); + PrintCRegister(creg); +} + + +void Decoder::PrintFd(Instruction* instr) { + int creg = instr->RdField(); + PrintCRegister(creg); +} + + +// Print the integer value of the sa field. +void Decoder::PrintSa(Instruction* instr) { + int sa = instr->SaField(); + out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, + "%d", sa); +} + + +// Print 16-bit unsigned immediate value. +void Decoder::PrintUImm16(Instruction* instr) { + int32_t imm = instr->Imm16Field(); + out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, + "%u", imm); +} + + +// Print 16-bit signed immediate value. +void Decoder::PrintSImm16(Instruction* instr) { + int32_t imm = ((instr->Imm16Field())<<16)>>16; + out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, + "%d", imm); +} + + +// Print 16-bit hexa immediate value. +void Decoder::PrintXImm16(Instruction* instr) { + int32_t imm = instr->Imm16Field(); + out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, + "0x%x", imm); +} + + +// Print 26-bit immediate value. +void Decoder::PrintImm26(Instruction* instr) { + int32_t imm = instr->Imm26Field(); + out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, + "%d", imm); +} + + +// Print 26-bit immediate value. +void Decoder::PrintCode(Instruction* instr) { + if (instr->OpcodeFieldRaw() != SPECIAL) + return; // Not a break or trap instruction. + switch (instr->FunctionFieldRaw()) { + case BREAK: { + int32_t code = instr->Bits(25, 6); + out_buffer_pos_ += + v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%05x", code); + break; + } + case TGE: + case TGEU: + case TLT: + case TLTU: + case TEQ: + case TNE: { + int32_t code = instr->Bits(15, 6); + out_buffer_pos_ += + v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%03x", code); + break; + } + default: // Not a break or trap instruction. + break; + }; +} + + +// Printing of instruction name. +void Decoder::PrintInstructionName(Instruction* instr) { +} + + +// Handle all register based formatting in this function to reduce the +// complexity of FormatOption. +int Decoder::FormatRegister(Instruction* instr, const char* format) { + ASSERT(format[0] == 'r'); + if (format[1] == 's') { // 'rs: Rs register + int reg = instr->RsField(); + PrintRegister(reg); + return 2; + } else if (format[1] == 't') { // 'rt: rt register + int reg = instr->RtField(); + PrintRegister(reg); + return 2; + } else if (format[1] == 'd') { // 'rd: rd register + int reg = instr->RdField(); + PrintRegister(reg); + return 2; + } + UNREACHABLE(); + return -1; +} + + +// Handle all Cregister based formatting in this function to reduce the +// complexity of FormatOption. +int Decoder::FormatCRegister(Instruction* instr, const char* format) { + ASSERT(format[0] == 'f'); + if (format[1] == 's') { // 'fs: fs register + int reg = instr->RsField(); + PrintCRegister(reg); + return 2; + } else if (format[1] == 't') { // 'ft: ft register + int reg = instr->RtField(); + PrintCRegister(reg); + return 2; + } else if (format[1] == 'd') { // 'fd: fd register + int reg = instr->RdField(); + PrintCRegister(reg); + return 2; + } + UNREACHABLE(); + return -1; +} + + +// 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': { // 'code for break or trap instructions + ASSERT(STRING_STARTS_WITH(format, "code")); + PrintCode(instr); + return 4; + } + case 'i': { // 'imm16u or 'imm26 + if (format[3] == '1') { + ASSERT(STRING_STARTS_WITH(format, "imm16")); + if (format[5] == 's') { + ASSERT(STRING_STARTS_WITH(format, "imm16s")); + PrintSImm16(instr); + } else if (format[5] == 'u') { + ASSERT(STRING_STARTS_WITH(format, "imm16u")); + PrintSImm16(instr); + } else { + ASSERT(STRING_STARTS_WITH(format, "imm16x")); + PrintXImm16(instr); + } + return 6; + } else { + ASSERT(STRING_STARTS_WITH(format, "imm26")); + PrintImm26(instr); + return 5; + } + } + case 'r': { // 'r: registers + return FormatRegister(instr, format); + } + case 'f': { // 'f: Cregisters + return FormatCRegister(instr, format); + } + case 's': { // 'sa + ASSERT(STRING_STARTS_WITH(format, "sa")); + PrintSa(instr); + return 2; + } + }; + UNREACHABLE(); + return -1; +} + + +// 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'; +} + + +// 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"); +} + + +void Decoder::DecodeTypeRegister(Instruction* instr) { + switch (instr->OpcodeFieldRaw()) { + case COP1: // Coprocessor instructions + switch (instr->RsFieldRaw()) { + case BC1: // branch on coprocessor condition + UNREACHABLE(); + break; + case MFC1: + Format(instr, "mfc1 'rt, 'fs"); + break; + case MFHC1: + Format(instr, "mfhc1 rt, 'fs"); + break; + case MTC1: + Format(instr, "mtc1 'rt, 'fs"); + break; + case MTHC1: + Format(instr, "mthc1 rt, 'fs"); + break; + case S: + case D: + UNIMPLEMENTED_MIPS(); + break; + case W: + switch (instr->FunctionFieldRaw()) { + case CVT_S_W: + UNIMPLEMENTED_MIPS(); + break; + case CVT_D_W: // Convert word to double. + Format(instr, "cvt.d.w 'fd, 'fs"); + break; + default: + UNREACHABLE(); + }; + break; + case L: + case PS: + UNIMPLEMENTED_MIPS(); + break; + break; + default: + UNREACHABLE(); + }; + break; + case SPECIAL: + switch (instr->FunctionFieldRaw()) { + case JR: + Format(instr, "jr 'rs"); + break; + case JALR: + Format(instr, "jalr 'rs"); + break; + case SLL: + if ( 0x0 == static_cast<int>(instr->InstructionBits())) + Format(instr, "nop"); + else + Format(instr, "sll 'rd, 'rt, 'sa"); + break; + case SRL: + Format(instr, "srl 'rd, 'rt, 'sa"); + break; + case SRA: + Format(instr, "sra 'rd, 'rt, 'sa"); + break; + case SLLV: + Format(instr, "sllv 'rd, 'rt, 'rs"); + break; + case SRLV: + Format(instr, "srlv 'rd, 'rt, 'rs"); + break; + case SRAV: + Format(instr, "srav 'rd, 'rt, 'rs"); + break; + case MFHI: + Format(instr, "mfhi 'rd"); + break; + case MFLO: + Format(instr, "mflo 'rd"); + break; + case MULT: + Format(instr, "mult 'rs, 'rt"); + break; + case MULTU: + Format(instr, "multu 'rs, 'rt"); + break; + case DIV: + Format(instr, "div 'rs, 'rt"); + break; + case DIVU: + Format(instr, "divu 'rs, 'rt"); + break; + case ADD: + Format(instr, "add 'rd, 'rs, 'rt"); + break; + case ADDU: + Format(instr, "addu 'rd, 'rs, 'rt"); + break; + case SUB: + Format(instr, "sub 'rd, 'rs, 'rt"); + break; + case SUBU: + Format(instr, "sub 'rd, 'rs, 'rt"); + break; + case AND: + Format(instr, "and 'rd, 'rs, 'rt"); + break; + case OR: + if (0 == instr->RsField()) { + Format(instr, "mov 'rd, 'rt"); + } else if (0 == instr->RtField()) { + Format(instr, "mov 'rd, 'rs"); + } else { + Format(instr, "or 'rd, 'rs, 'rt"); + } + break; + case XOR: + Format(instr, "xor 'rd, 'rs, 'rt"); + break; + case NOR: + Format(instr, "nor 'rd, 'rs, 'rt"); + break; + case SLT: + Format(instr, "slt 'rd, 'rs, 'rt"); + break; + case SLTU: + Format(instr, "sltu 'rd, 'rs, 'rt"); + break; + case BREAK: + Format(instr, "break, code: 'code"); + break; + case TGE: + Format(instr, "tge 'rs, 'rt, code: 'code"); + break; + case TGEU: + Format(instr, "tgeu 'rs, 'rt, code: 'code"); + break; + case TLT: + Format(instr, "tlt 'rs, 'rt, code: 'code"); + break; + case TLTU: + Format(instr, "tltu 'rs, 'rt, code: 'code"); + break; + case TEQ: + Format(instr, "teq 'rs, 'rt, code: 'code"); + break; + case TNE: + Format(instr, "tne 'rs, 'rt, code: 'code"); + break; + default: + UNREACHABLE(); + }; + break; + case SPECIAL2: + switch (instr->FunctionFieldRaw()) { + case MUL: + break; + default: + UNREACHABLE(); + }; + break; + default: + UNREACHABLE(); + }; +} + + +void Decoder::DecodeTypeImmediate(Instruction* instr) { + switch (instr->OpcodeFieldRaw()) { + // ------------- REGIMM class. + case REGIMM: + switch (instr->RtFieldRaw()) { + case BLTZ: + Format(instr, "bltz 'rs, 'imm16u"); + break; + case BLTZAL: + Format(instr, "bltzal 'rs, 'imm16u"); + break; + case BGEZ: + Format(instr, "bgez 'rs, 'imm16u"); + break; + case BGEZAL: + Format(instr, "bgezal 'rs, 'imm16u"); + break; + default: + UNREACHABLE(); + }; + break; // case REGIMM + // ------------- Branch instructions. + case BEQ: + Format(instr, "beq 'rs, 'rt, 'imm16u"); + break; + case BNE: + Format(instr, "bne 'rs, 'rt, 'imm16u"); + break; + case BLEZ: + Format(instr, "blez 'rs, 'imm16u"); + break; + case BGTZ: + Format(instr, "bgtz 'rs, 'imm16u"); + break; + // ------------- Arithmetic instructions. + case ADDI: + Format(instr, "addi 'rt, 'rs, 'imm16s"); + break; + case ADDIU: + Format(instr, "addiu 'rt, 'rs, 'imm16s"); + break; + case SLTI: + Format(instr, "slti 'rt, 'rs, 'imm16s"); + break; + case SLTIU: + Format(instr, "sltiu 'rt, 'rs, 'imm16u"); + break; + case ANDI: + Format(instr, "andi 'rt, 'rs, 'imm16x"); + break; + case ORI: + Format(instr, "ori 'rt, 'rs, 'imm16x"); + break; + case XORI: + Format(instr, "xori 'rt, 'rs, 'imm16x"); + break; + case LUI: + Format(instr, "lui 'rt, 'imm16x"); + break; + // ------------- Memory instructions. + case LB: + Format(instr, "lb 'rt, 'imm16s('rs)"); + break; + case LW: + Format(instr, "lw 'rt, 'imm16s('rs)"); + break; + case LBU: + Format(instr, "lbu 'rt, 'imm16s('rs)"); + break; + case SB: + Format(instr, "sb 'rt, 'imm16s('rs)"); + break; + case SW: + Format(instr, "sw 'rt, 'imm16s('rs)"); + break; + case LWC1: + Format(instr, "lwc1 'ft, 'imm16s('rs)"); + break; + case LDC1: + Format(instr, "ldc1 'ft, 'imm16s('rs)"); + break; + case SWC1: + Format(instr, "swc1 'rt, 'imm16s('fs)"); + break; + case SDC1: + Format(instr, "sdc1 'rt, 'imm16s('fs)"); + break; + default: + UNREACHABLE(); + break; + }; +} + + +void Decoder::DecodeTypeJump(Instruction* instr) { + switch (instr->OpcodeFieldRaw()) { + case J: + Format(instr, "j 'imm26"); + break; + case JAL: + Format(instr, "jal 'imm26"); + break; + default: + UNREACHABLE(); + } +} + + +// 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_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, + "%08x ", + instr->InstructionBits()); + switch (instr->InstructionType()) { + case Instruction::kRegisterType: { + DecodeTypeRegister(instr); + break; + } + case Instruction::kImmediateType: { + DecodeTypeImmediate(instr); + break; + } + case Instruction::kJumpType: { + DecodeTypeJump(instr); + break; + } + default: { + UNSUPPORTED_MIPS(); + } + } + return Instruction::kInstructionSize; +} + + +} } // namespace assembler::mips + + + +//------------------------------------------------------------------------------ + +namespace disasm { + +namespace v8i = v8::internal; + + +const char* NameConverter::NameOfAddress(byte_* addr) const { + static v8::internal::EmbeddedVector<char, 32> tmp_buffer; + v8::internal::OS::SNPrintF(tmp_buffer, "%p", addr); + return tmp_buffer.start(); +} + + +const char* NameConverter::NameOfConstant(byte_* addr) const { + return NameOfAddress(addr); +} + + +const char* NameConverter::NameOfCPURegister(int reg) const { + return assembler::mips::Registers::Name(reg); +} + + +const char* NameConverter::NameOfXMMRegister(int reg) const { + return assembler::mips::FPURegister::Name(reg); +} + + +const char* NameConverter::NameOfByteCPURegister(int reg) const { + UNREACHABLE(); // MIPS does not have the concept of a byte register + return "nobytereg"; +} + + +const char* NameConverter::NameInCode(byte_* 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(v8::internal::Vector<char> buffer, + byte_* instruction) { + assembler::mips::Decoder d(converter_, buffer); + return d.InstructionDecode(instruction); +} + + +int Disassembler::ConstantPoolSizeAt(byte_* instruction) { + UNIMPLEMENTED_MIPS(); + return -1; +} + + +void Disassembler::Disassemble(FILE* f, byte_* begin, byte_* end) { + NameConverter converter; + Disassembler d(converter); + for (byte_* pc = begin; pc < end;) { + v8::internal::EmbeddedVector<char, 128> buffer; + buffer[0] = '\0'; + byte_* prev_pc = pc; + pc += d.InstructionDecode(buffer, pc); + fprintf(f, "%p %08x %s\n", + prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer.start()); + } +} + +#undef UNSUPPORTED + +} // namespace disasm + |