// Copyright 2014 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. // // NameConverter converter; // Disassembler d(converter); // for (byte* pc = begin; pc < end;) { // v8::internal::EmbeddedVector buffer; // byte* 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 #include #include #include #if V8_TARGET_ARCH_S390 #include "src/base/platform/platform.h" #include "src/disasm.h" #include "src/macro-assembler.h" #include "src/s390/constants-s390.h" namespace v8 { namespace internal { const auto GetRegConfig = RegisterConfiguration::Crankshaft; //------------------------------------------------------------------------------ // 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, Vector 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 PrintDRegister(int reg); void PrintSoftwareInterrupt(SoftwareInterruptCodes svc); // Handle formatting of instructions and their options. int FormatRegister(Instruction* instr, const char* option); int FormatFloatingRegister(Instruction* instr, const char* option); int FormatMask(Instruction* instr, const char* option); int FormatDisplacement(Instruction* instr, const char* option); int FormatImmediate(Instruction* instr, const char* option); int FormatOption(Instruction* instr, const char* option); void Format(Instruction* instr, const char* format); void Unknown(Instruction* instr); void UnknownFormat(Instruction* instr, const char* opcname); bool DecodeTwoByte(Instruction* instr); bool DecodeFourByte(Instruction* instr); bool DecodeSixByte(Instruction* instr); const disasm::NameConverter& converter_; Vector 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)); } // Print the double FP register name according to the active name converter. void Decoder::PrintDRegister(int reg) { Print(GetRegConfig()->GetDoubleRegisterName(reg)); } // Print SoftwareInterrupt codes. Factoring this out reduces the complexity of // the FormatOption method. void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes svc) { switch (svc) { case kCallRtRedirected: Print("call rt redirected"); return; case kBreakpoint: Print("breakpoint"); return; default: if (svc >= kStopCode) { out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d - 0x%x", svc & kStopCodeMask, svc & kStopCodeMask); } else { out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", svc); } return; } } // Handle all register based formatting in this function to reduce the // complexity of FormatOption. int Decoder::FormatRegister(Instruction* instr, const char* format) { DCHECK(format[0] == 'r'); if (format[1] == '1') { // 'r1: register resides in bit 8-11 RRInstruction* rrinstr = reinterpret_cast(instr); int reg = rrinstr->R1Value(); PrintRegister(reg); return 2; } else if (format[1] == '2') { // 'r2: register resides in bit 12-15 RRInstruction* rrinstr = reinterpret_cast(instr); int reg = rrinstr->R2Value(); // indicating it is a r0 for displacement, in which case the offset // should be 0. if (format[2] == 'd') { if (reg == 0) return 4; PrintRegister(reg); return 3; } else { PrintRegister(reg); return 2; } } else if (format[1] == '3') { // 'r3: register resides in bit 16-19 RSInstruction* rsinstr = reinterpret_cast(instr); int reg = rsinstr->B2Value(); PrintRegister(reg); return 2; } else if (format[1] == '4') { // 'r4: register resides in bit 20-23 RSInstruction* rsinstr = reinterpret_cast(instr); int reg = rsinstr->B2Value(); PrintRegister(reg); return 2; } else if (format[1] == '5') { // 'r5: register resides in bit 24-28 RREInstruction* rreinstr = reinterpret_cast(instr); int reg = rreinstr->R1Value(); PrintRegister(reg); return 2; } else if (format[1] == '6') { // 'r6: register resides in bit 29-32 RREInstruction* rreinstr = reinterpret_cast(instr); int reg = rreinstr->R2Value(); PrintRegister(reg); return 2; } else if (format[1] == '7') { // 'r6: register resides in bit 32-35 SSInstruction* ssinstr = reinterpret_cast(instr); int reg = ssinstr->B2Value(); PrintRegister(reg); return 2; } UNREACHABLE(); return -1; } int Decoder::FormatFloatingRegister(Instruction* instr, const char* format) { DCHECK(format[0] == 'f'); // reuse 1, 5 and 6 because it is coresponding if (format[1] == '1') { // 'r1: register resides in bit 8-11 RRInstruction* rrinstr = reinterpret_cast(instr); int reg = rrinstr->R1Value(); PrintDRegister(reg); return 2; } else if (format[1] == '2') { // 'f2: register resides in bit 12-15 RRInstruction* rrinstr = reinterpret_cast(instr); int reg = rrinstr->R2Value(); PrintDRegister(reg); return 2; } else if (format[1] == '3') { // 'f3: register resides in bit 16-19 RRDInstruction* rrdinstr = reinterpret_cast(instr); int reg = rrdinstr->R1Value(); PrintDRegister(reg); return 2; } else if (format[1] == '5') { // 'f5: register resides in bit 24-28 RREInstruction* rreinstr = reinterpret_cast(instr); int reg = rreinstr->R1Value(); PrintDRegister(reg); return 2; } else if (format[1] == '6') { // 'f6: register resides in bit 29-32 RREInstruction* rreinstr = reinterpret_cast(instr); int reg = rreinstr->R2Value(); PrintDRegister(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 'o': { if (instr->Bit(10) == 1) { Print("o"); } return 1; } case '.': { if (instr->Bit(0) == 1) { Print("."); } else { Print(" "); // ensure consistent spacing } return 1; } case 'r': { return FormatRegister(instr, format); } case 'f': { return FormatFloatingRegister(instr, format); } case 'i': { // int16 return FormatImmediate(instr, format); } case 'u': { // uint16 int32_t value = instr->Bits(15, 0); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 6; } case 'l': { // Link (LK) Bit 0 if (instr->Bit(0) == 1) { Print("l"); } return 1; } case 'a': { // Absolute Address Bit 1 if (instr->Bit(1) == 1) { Print("a"); } return 1; } case 't': { // 'target: target of branch instructions // target26 or target16 DCHECK(STRING_STARTS_WITH(format, "target")); if ((format[6] == '2') && (format[7] == '6')) { int off = ((instr->Bits(25, 2)) << 8) >> 6; out_buffer_pos_ += SNPrintF( out_buffer_ + out_buffer_pos_, "%+d -> %s", off, converter_.NameOfAddress(reinterpret_cast(instr) + off)); return 8; } else if ((format[6] == '1') && (format[7] == '6')) { int off = ((instr->Bits(15, 2)) << 18) >> 16; out_buffer_pos_ += SNPrintF( out_buffer_ + out_buffer_pos_, "%+d -> %s", off, converter_.NameOfAddress(reinterpret_cast(instr) + off)); return 8; } case 'm': { return FormatMask(instr, format); } } case 'd': { // ds value for offset return FormatDisplacement(instr, format); } default: { UNREACHABLE(); break; } } UNREACHABLE(); return -1; } int Decoder::FormatMask(Instruction* instr, const char* format) { DCHECK(format[0] == 'm'); int32_t value = 0; if ((format[1] == '1')) { // prints the mask format in bits 8-12 value = reinterpret_cast(instr)->R1Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", value); return 2; } else if (format[1] == '2') { // mask format in bits 16-19 value = reinterpret_cast(instr)->B2Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", value); return 2; } else if (format[1] == '3') { // mask format in bits 20-23 value = reinterpret_cast(instr)->M4Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", value); return 2; } out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } int Decoder::FormatDisplacement(Instruction* instr, const char* format) { DCHECK(format[0] == 'd'); if (format[1] == '1') { // displacement in 20-31 RSInstruction* rsinstr = reinterpret_cast(instr); uint16_t value = rsinstr->D2Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == '2') { // displacement in 20-39 RXYInstruction* rxyinstr = reinterpret_cast(instr); int32_t value = rxyinstr->D2Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == '4') { // SS displacement 2 36-47 SSInstruction* ssInstr = reinterpret_cast(instr); uint16_t value = ssInstr->D2Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == '3') { // SS displacement 1 20 - 32 SSInstruction* ssInstr = reinterpret_cast(instr); uint16_t value = ssInstr->D1Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else { // s390 specific int32_t value = SIGN_EXT_IMM16(instr->Bits(15, 0) & ~3); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 1; } } int Decoder::FormatImmediate(Instruction* instr, const char* format) { DCHECK(format[0] == 'i'); if (format[1] == '1') { // immediate in 16-31 RIInstruction* riinstr = reinterpret_cast(instr); int16_t value = riinstr->I2Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == '2') { // immediate in 16-48 RILInstruction* rilinstr = reinterpret_cast(instr); int32_t value = rilinstr->I2Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == '3') { // immediate in I format IInstruction* iinstr = reinterpret_cast(instr); int8_t value = iinstr->IValue(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == '4') { // immediate in 16-31, but outputs as offset RIInstruction* riinstr = reinterpret_cast(instr); int16_t value = riinstr->I2Value() * 2; if (value >= 0) out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*+"); else out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*"); out_buffer_pos_ += SNPrintF( out_buffer_ + out_buffer_pos_, "%d -> %s", value, converter_.NameOfAddress(reinterpret_cast(instr) + value)); return 2; } else if (format[1] == '5') { // immediate in 16-31, but outputs as offset RILInstruction* rilinstr = reinterpret_cast(instr); int32_t value = rilinstr->I2Value() * 2; if (value >= 0) out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*+"); else out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*"); out_buffer_pos_ += SNPrintF( out_buffer_ + out_buffer_pos_, "%d -> %s", value, converter_.NameOfAddress(reinterpret_cast(instr) + value)); return 2; } else if (format[1] == '6') { // unsigned immediate in 16-31 RIInstruction* riinstr = reinterpret_cast(instr); uint16_t value = riinstr->I2UnsignedValue(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == '7') { // unsigned immediate in 16-47 RILInstruction* rilinstr = reinterpret_cast(instr); uint32_t value = rilinstr->I2UnsignedValue(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == '8') { // unsigned immediate in 8-15 SSInstruction* ssinstr = reinterpret_cast(instr); uint8_t value = ssinstr->Length(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == '9') { // unsigned immediate in 16-23 RIEInstruction* rie_instr = reinterpret_cast(instr); uint8_t value = rie_instr->I3Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == 'a') { // unsigned immediate in 24-31 RIEInstruction* rie_instr = reinterpret_cast(instr); uint8_t value = rie_instr->I4Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == 'b') { // unsigned immediate in 32-39 RIEInstruction* rie_instr = reinterpret_cast(instr); uint8_t value = rie_instr->I5Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == 'c') { // signed immediate in 8-15 SSInstruction* ssinstr = reinterpret_cast(instr); int8_t value = ssinstr->Length(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == 'd') { // signed immediate in 32-47 SILInstruction* silinstr = reinterpret_cast(instr); int16_t value = silinstr->I2Value(); out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%d", value); return 2; } else if (format[1] == 'e') { // immediate in 16-47, but outputs as offset RILInstruction* rilinstr = reinterpret_cast(instr); int32_t value = rilinstr->I2Value() * 2; if (value >= 0) out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*+"); else out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "*"); out_buffer_pos_ += SNPrintF( out_buffer_ + out_buffer_pos_, "%d -> %s", value, converter_.NameOfAddress(reinterpret_cast(instr) + value)); 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'; } // 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"); } // For currently unimplemented decodings the disassembler calls // UnknownFormat(instr) which will just print opcode name of the // instruction bits. void Decoder::UnknownFormat(Instruction* instr, const char* name) { char buffer[100]; snprintf(buffer, sizeof(buffer), "%s (unknown-format)", name); Format(instr, buffer); } // Disassembles Two Byte S390 Instructions // @return true if successfully decoded bool Decoder::DecodeTwoByte(Instruction* instr) { // Print the Instruction bits. out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%04x ", instr->InstructionBits()); Opcode opcode = instr->S390OpcodeValue(); switch (opcode) { case AR: Format(instr, "ar\t'r1,'r2"); break; case SR: Format(instr, "sr\t'r1,'r2"); break; case MR: Format(instr, "mr\t'r1,'r2"); break; case DR: Format(instr, "dr\t'r1,'r2"); break; case OR: Format(instr, "or\t'r1,'r2"); break; case NR: Format(instr, "nr\t'r1,'r2"); break; case XR: Format(instr, "xr\t'r1,'r2"); break; case LR: Format(instr, "lr\t'r1,'r2"); break; case CR: Format(instr, "cr\t'r1,'r2"); break; case CLR: Format(instr, "clr\t'r1,'r2"); break; case BCR: Format(instr, "bcr\t'm1,'r2"); break; case LTR: Format(instr, "ltr\t'r1,'r2"); break; case ALR: Format(instr, "alr\t'r1,'r2"); break; case SLR: Format(instr, "slr\t'r1,'r2"); break; case LNR: Format(instr, "lnr\t'r1,'r2"); break; case LCR: Format(instr, "lcr\t'r1,'r2"); break; case BASR: Format(instr, "basr\t'r1,'r2"); break; case LDR: Format(instr, "ldr\t'f1,'f2"); break; case BKPT: Format(instr, "bkpt"); break; case LPR: Format(instr, "lpr\t'r1, 'r2"); break; default: return false; } return true; } // Disassembles Four Byte S390 Instructions // @return true if successfully decoded bool Decoder::DecodeFourByte(Instruction* instr) { // Print the Instruction bits. out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%08x ", instr->InstructionBits()); Opcode opcode = instr->S390OpcodeValue(); switch (opcode) { case AHI: Format(instr, "ahi\t'r1,'i1"); break; case AGHI: Format(instr, "aghi\t'r1,'i1"); break; case LHI: Format(instr, "lhi\t'r1,'i1"); break; case LGHI: Format(instr, "lghi\t'r1,'i1"); break; case MHI: Format(instr, "mhi\t'r1,'i1"); break; case MGHI: Format(instr, "mghi\t'r1,'i1"); break; case CHI: Format(instr, "chi\t'r1,'i1"); break; case CGHI: Format(instr, "cghi\t'r1,'i1"); break; case BRAS: Format(instr, "bras\t'r1,'i1"); break; case BRC: Format(instr, "brc\t'm1,'i4"); break; case BRCT: Format(instr, "brct\t'r1,'i4"); break; case BRCTG: Format(instr, "brctg\t'r1,'i4"); break; case IIHH: Format(instr, "iihh\t'r1,'i1"); break; case IIHL: Format(instr, "iihl\t'r1,'i1"); break; case IILH: Format(instr, "iilh\t'r1,'i1"); break; case IILL: Format(instr, "iill\t'r1,'i1"); break; case OILL: Format(instr, "oill\t'r1,'i1"); break; case TMLL: Format(instr, "tmll\t'r1,'i1"); break; case STM: Format(instr, "stm\t'r1,'r2,'d1('r3)"); break; case LM: Format(instr, "lm\t'r1,'r2,'d1('r3)"); break; case SLL: Format(instr, "sll\t'r1,'d1('r3)"); break; case SRL: Format(instr, "srl\t'r1,'d1('r3)"); break; case SLA: Format(instr, "sla\t'r1,'d1('r3)"); break; case SRA: Format(instr, "sra\t'r1,'d1('r3)"); break; case SLDL: Format(instr, "sldl\t'r1,'d1('r3)"); break; case AGR: Format(instr, "agr\t'r5,'r6"); break; case AGFR: Format(instr, "agfr\t'r5,'r6"); break; case ARK: Format(instr, "ark\t'r5,'r6,'r3"); break; case AGRK: Format(instr, "agrk\t'r5,'r6,'r3"); break; case SGR: Format(instr, "sgr\t'r5,'r6"); break; case SGFR: Format(instr, "sgfr\t'r5,'r6"); break; case SRK: Format(instr, "srk\t'r5,'r6,'r3"); break; case SGRK: Format(instr, "sgrk\t'r5,'r6,'r3"); break; case NGR: Format(instr, "ngr\t'r5,'r6"); break; case NRK: Format(instr, "nrk\t'r5,'r6,'r3"); break; case NGRK: Format(instr, "ngrk\t'r5,'r6,'r3"); break; case NILL: Format(instr, "nill\t'r1,'i1"); break; case NILH: Format(instr, "nilh\t'r1,'i1"); break; case OGR: Format(instr, "ogr\t'r5,'r6"); break; case ORK: Format(instr, "ork\t'r5,'r6,'r3"); break; case OGRK: Format(instr, "ogrk\t'r5,'r6,'r3"); break; case XGR: Format(instr, "xgr\t'r5,'r6"); break; case XRK: Format(instr, "xrk\t'r5,'r6,'r3"); break; case XGRK: Format(instr, "xgrk\t'r5,'r6,'r3"); break; case CGFR: Format(instr, "cgfr\t'r5,'r6"); break; case CGR: Format(instr, "cgr\t'r5,'r6"); break; case CLGR: Format(instr, "clgr\t'r5,'r6"); break; case LLGFR: Format(instr, "llgfr\t'r5,'r6"); break; case POPCNT_Z: Format(instr, "popcnt\t'r5,'r6"); break; case LLGCR: Format(instr, "llgcr\t'r5,'r6"); break; case LLCR: Format(instr, "llcr\t'r5,'r6"); break; case LBR: Format(instr, "lbr\t'r5,'r6"); break; case LEDBR: Format(instr, "ledbr\t'f5,'f6"); break; case LDEBR: Format(instr, "ldebr\t'f5,'f6"); break; case LTGR: Format(instr, "ltgr\t'r5,'r6"); break; case LTDBR: Format(instr, "ltdbr\t'f5,'f6"); break; case LTEBR: Format(instr, "ltebr\t'f5,'f6"); break; case LRVR: Format(instr, "lrvr\t'r5,'r6"); break; case LRVGR: Format(instr, "lrvgr\t'r5,'r6"); break; case LGR: Format(instr, "lgr\t'r5,'r6"); break; case LGDR: Format(instr, "lgdr\t'r5,'f6"); break; case LGFR: Format(instr, "lgfr\t'r5,'r6"); break; case LTGFR: Format(instr, "ltgfr\t'r5,'r6"); break; case LCGR: Format(instr, "lcgr\t'r5,'r6"); break; case MSR: Format(instr, "msr\t'r5,'r6"); break; case MSRKC: Format(instr, "msrkc\t'r5,'r6,'r3"); break; case LGBR: Format(instr, "lgbr\t'r5,'r6"); break; case LGHR: Format(instr, "lghr\t'r5,'r6"); break; case MSGR: Format(instr, "msgr\t'r5,'r6"); break; case MSGRKC: Format(instr, "msgrkc\t'r5,'r6,'r3"); break; case DSGR: Format(instr, "dsgr\t'r5,'r6"); break; case DSGFR: Format(instr, "dsgfr\t'r5,'r6"); break; case MSGFR: Format(instr, "msgfr\t'r5,'r6"); break; case LZDR: Format(instr, "lzdr\t'f5"); break; case MLR: Format(instr, "mlr\t'r5,'r6"); break; case MLGR: Format(instr, "mlgr\t'r5,'r6"); break; case ALCR: Format(instr, "alcr\t'r5,'r6"); break; case ALGR: Format(instr, "algr\t'r5,'r6"); break; case ALRK: Format(instr, "alrk\t'r5,'r6,'r3"); break; case ALGRK: Format(instr, "algrk\t'r5,'r6,'r3"); break; case SLGR: Format(instr, "slgr\t'r5,'r6"); break; case SLBR: Format(instr, "slbr\t'r5,'r6"); break; case DLR: Format(instr, "dlr\t'r5,'r6"); break; case DLGR: Format(instr, "dlgr\t'r5,'r6"); break; case SLRK: Format(instr, "slrk\t'r5,'r6,'r3"); break; case SLGRK: Format(instr, "slgrk\t'r5,'r6,'r3"); break; case LHR: Format(instr, "lhr\t'r5,'r6"); break; case LLHR: Format(instr, "llhr\t'r5,'r6"); break; case LLGHR: Format(instr, "llghr\t'r5,'r6"); break; case LOCR: Format(instr, "locr\t'm1,'r5,'r6"); break; case LOCGR: Format(instr, "locgr\t'm1,'r5,'r6"); break; case LNGR: Format(instr, "lngr\t'r5,'r6"); break; case A: Format(instr, "a\t'r1,'d1('r2d,'r3)"); break; case S: Format(instr, "s\t'r1,'d1('r2d,'r3)"); break; case M: Format(instr, "m\t'r1,'d1('r2d,'r3)"); break; case D: Format(instr, "d\t'r1,'d1('r2d,'r3)"); break; case O: Format(instr, "o\t'r1,'d1('r2d,'r3)"); break; case N: Format(instr, "n\t'r1,'d1('r2d,'r3)"); break; case L: Format(instr, "l\t'r1,'d1('r2d,'r3)"); break; case C: Format(instr, "c\t'r1,'d1('r2d,'r3)"); break; case AH: Format(instr, "ah\t'r1,'d1('r2d,'r3)"); break; case SH: Format(instr, "sh\t'r1,'d1('r2d,'r3)"); break; case MH: Format(instr, "mh\t'r1,'d1('r2d,'r3)"); break; case AL: Format(instr, "al\t'r1,'d1('r2d,'r3)"); break; case SL: Format(instr, "sl\t'r1,'d1('r2d,'r3)"); break; case LA: Format(instr, "la\t'r1,'d1('r2d,'r3)"); break; case CH: Format(instr, "ch\t'r1,'d1('r2d,'r3)"); break; case CL: Format(instr, "cl\t'r1,'d1('r2d,'r3)"); break; case CLI: Format(instr, "cli\t'd1('r3),'i8"); break; case TM: Format(instr, "tm\t'd1('r3),'i8"); break; case BC: Format(instr, "bc\t'm1,'d1('r2d,'r3)"); break; case BCT: Format(instr, "bct\t'r1,'d1('r2d,'r3)"); break; case ST: Format(instr, "st\t'r1,'d1('r2d,'r3)"); break; case STC: Format(instr, "stc\t'r1,'d1('r2d,'r3)"); break; case IC_z: Format(instr, "ic\t'r1,'d1('r2d,'r3)"); break; case LD: Format(instr, "ld\t'f1,'d1('r2d,'r3)"); break; case LE: Format(instr, "le\t'f1,'d1('r2d,'r3)"); break; case LDGR: Format(instr, "ldgr\t'f5,'r6"); break; case MS: Format(instr, "ms\t'r1,'d1('r2d,'r3)"); break; case STE: Format(instr, "ste\t'f1,'d1('r2d,'r3)"); break; case STD: Format(instr, "std\t'f1,'d1('r2d,'r3)"); break; case CFDBR: Format(instr, "cfdbr\t'r5,'m2,'f6"); break; case CDFBR: Format(instr, "cdfbr\t'f5,'m2,'r6"); break; case CFEBR: Format(instr, "cfebr\t'r5,'m2,'f6"); break; case CEFBR: Format(instr, "cefbr\t'f5,'m2,'r6"); break; case CELFBR: Format(instr, "celfbr\t'f5,'m2,'r6"); break; case CGEBR: Format(instr, "cgebr\t'r5,'m2,'f6"); break; case CGDBR: Format(instr, "cgdbr\t'r5,'m2,'f6"); break; case CEGBR: Format(instr, "cegbr\t'f5,'m2,'r6"); break; case CDGBR: Format(instr, "cdgbr\t'f5,'m2,'r6"); break; case CDLFBR: Format(instr, "cdlfbr\t'f5,'m2,'r6"); break; case CDLGBR: Format(instr, "cdlgbr\t'f5,'m2,'r6"); break; case CELGBR: Format(instr, "celgbr\t'f5,'m2,'r6"); break; case CLFDBR: Format(instr, "clfdbr\t'r5,'m2,'f6"); break; case CLFEBR: Format(instr, "clfebr\t'r5,'m2,'f6"); break; case CLGEBR: Format(instr, "clgebr\t'r5,'m2,'f6"); break; case CLGDBR: Format(instr, "clgdbr\t'r5,'m2,'f6"); break; case AEBR: Format(instr, "aebr\t'f5,'f6"); break; case SEBR: Format(instr, "sebr\t'f5,'f6"); break; case MEEBR: Format(instr, "meebr\t'f5,'f6"); break; case DEBR: Format(instr, "debr\t'f5,'f6"); break; case ADBR: Format(instr, "adbr\t'f5,'f6"); break; case SDBR: Format(instr, "sdbr\t'f5,'f6"); break; case MDBR: Format(instr, "mdbr\t'f5,'f6"); break; case DDBR: Format(instr, "ddbr\t'f5,'f6"); break; case CDBR: Format(instr, "cdbr\t'f5,'f6"); break; case CEBR: Format(instr, "cebr\t'f5,'f6"); break; case SQDBR: Format(instr, "sqdbr\t'f5,'f6"); break; case SQEBR: Format(instr, "sqebr\t'f5,'f6"); break; case LCDBR: Format(instr, "lcdbr\t'f5,'f6"); break; case LCEBR: Format(instr, "lcebr\t'f5,'f6"); break; case STH: Format(instr, "sth\t'r1,'d1('r2d,'r3)"); break; case SRDA: Format(instr, "srda\t'r1,'d1('r3)"); break; case SRDL: Format(instr, "srdl\t'r1,'d1('r3)"); break; case MADBR: Format(instr, "madbr\t'f3,'f5,'f6"); break; case MSDBR: Format(instr, "msdbr\t'f3,'f5,'f6"); break; case FLOGR: Format(instr, "flogr\t'r5,'r6"); break; case FIEBRA: Format(instr, "fiebra\t'f5,'m2,'f6,'m3"); break; case FIDBRA: Format(instr, "fidbra\t'f5,'m2,'f6,'m3"); break; // TRAP4 is used in calling to native function. it will not be generated // in native code. case TRAP4: { Format(instr, "trap4"); break; } case LPGR: Format(instr, "lpgr\t'r1, 'r2"); break; case LPGFR: Format(instr, "lpgfr\t'r1,'r2"); break; default: return false; } return true; } // Disassembles Six Byte S390 Instructions // @return true if successfully decoded bool Decoder::DecodeSixByte(Instruction* instr) { // Print the Instruction bits. out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%012" PRIx64 " ", instr->InstructionBits()); Opcode opcode = instr->S390OpcodeValue(); switch (opcode) { case DUMY: Format(instr, "dumy\t'r1, 'd2 ( 'r2d, 'r3 )"); break; #define DECODE_VRR_C_INSTRUCTIONS(name, opcode_name, opcode_value) \ case opcode_name: \ Format(instr, #name "\t'f1,'f2,'f3"); \ break; S390_VRR_C_OPCODE_LIST(DECODE_VRR_C_INSTRUCTIONS) #undef DECODE_VRR_C_INSTRUCTIONS case LLILF: Format(instr, "llilf\t'r1,'i7"); break; case LLIHF: Format(instr, "llihf\t'r1,'i7"); break; case AFI: Format(instr, "afi\t'r1,'i7"); break; case AIH: Format(instr, "aih\t'r1,'i7"); break; case ASI: Format(instr, "asi\t'd2('r3),'ic"); break; case AGSI: Format(instr, "agsi\t'd2('r3),'ic"); break; case ALFI: Format(instr, "alfi\t'r1,'i7"); break; case AHIK: Format(instr, "ahik\t'r1,'r2,'i1"); break; case AGHIK: Format(instr, "aghik\t'r1,'r2,'i1"); break; case CLGFI: Format(instr, "clgfi\t'r1,'i7"); break; case CLFI: Format(instr, "clfi\t'r1,'i7"); break; case CLIH: Format(instr, "clih\t'r1,'i7"); break; case CIH: Format(instr, "cih\t'r1,'i2"); break; case CFI: Format(instr, "cfi\t'r1,'i2"); break; case CGFI: Format(instr, "cgfi\t'r1,'i2"); break; case BRASL: Format(instr, "brasl\t'r1,'ie"); break; case BRCL: Format(instr, "brcl\t'm1,'i5"); break; case IIHF: Format(instr, "iihf\t'r1,'i7"); break; case LGFI: Format(instr, "lgfi\t'r1,'i7"); break; case IILF: Format(instr, "iilf\t'r1,'i7"); break; case XIHF: Format(instr, "xihf\t'r1,'i7"); break; case XILF: Format(instr, "xilf\t'r1,'i7"); break; case SLLK: Format(instr, "sllk\t'r1,'r2,'d2('r3)"); break; case SLLG: Format(instr, "sllg\t'r1,'r2,'d2('r3)"); break; case RLL: Format(instr, "rll\t'r1,'r2,'d2('r3)"); break; case RLLG: Format(instr, "rllg\t'r1,'r2,'d2('r3)"); break; case SRLK: Format(instr, "srlk\t'r1,'r2,'d2('r3)"); break; case SRLG: Format(instr, "srlg\t'r1,'r2,'d2('r3)"); break; case SLAK: Format(instr, "slak\t'r1,'r2,'d2('r3)"); break; case SLAG: Format(instr, "slag\t'r1,'r2,'d2('r3)"); break; case SRAK: Format(instr, "srak\t'r1,'r2,'d2('r3)"); break; case SRAG: Format(instr, "srag\t'r1,'r2,'d2('r3)"); break; case RISBG: Format(instr, "risbg\t'r1,'r2,'i9,'ia,'ib"); break; case RISBGN: Format(instr, "risbgn\t'r1,'r2,'i9,'ia,'ib"); break; case LOCG: Format(instr, "locg\t'm2,'r1,'d2('r3)"); break; case LOC: Format(instr, "loc\t'm2,'r1,'d2('r3)"); break; case LMY: Format(instr, "lmy\t'r1,'r2,'d2('r3)"); break; case LMG: Format(instr, "lmg\t'r1,'r2,'d2('r3)"); break; case STMY: Format(instr, "stmy\t'r1,'r2,'d2('r3)"); break; case STMG: Format(instr, "stmg\t'r1,'r2,'d2('r3)"); break; case LT: Format(instr, "lt\t'r1,'d2('r2d,'r3)"); break; case LTG: Format(instr, "ltg\t'r1,'d2('r2d,'r3)"); break; case ML: Format(instr, "ml\t'r1,'d2('r2d,'r3)"); break; case AY: Format(instr, "ay\t'r1,'d2('r2d,'r3)"); break; case SY: Format(instr, "sy\t'r1,'d2('r2d,'r3)"); break; case NY: Format(instr, "ny\t'r1,'d2('r2d,'r3)"); break; case OY: Format(instr, "oy\t'r1,'d2('r2d,'r3)"); break; case XY: Format(instr, "xy\t'r1,'d2('r2d,'r3)"); break; case CY: Format(instr, "cy\t'r1,'d2('r2d,'r3)"); break; case AHY: Format(instr, "ahy\t'r1,'d2('r2d,'r3)"); break; case SHY: Format(instr, "shy\t'r1,'d2('r2d,'r3)"); break; case LGH: Format(instr, "lgh\t'r1,'d2('r2d,'r3)"); break; case AG: Format(instr, "ag\t'r1,'d2('r2d,'r3)"); break; case AGF: Format(instr, "agf\t'r1,'d2('r2d,'r3)"); break; case SG: Format(instr, "sg\t'r1,'d2('r2d,'r3)"); break; case NG: Format(instr, "ng\t'r1,'d2('r2d,'r3)"); break; case OG: Format(instr, "og\t'r1,'d2('r2d,'r3)"); break; case XG: Format(instr, "xg\t'r1,'d2('r2d,'r3)"); break; case CG: Format(instr, "cg\t'r1,'d2('r2d,'r3)"); break; case LB: Format(instr, "lb\t'r1,'d2('r2d,'r3)"); break; case LRVH: Format(instr, "lrvh\t'r1,'d2('r2d,'r3)"); break; case LRV: Format(instr, "lrv\t'r1,'d2('r2d,'r3)"); break; case LRVG: Format(instr, "lrvg\t'r1,'d2('r2d,'r3)"); break; case LG: Format(instr, "lg\t'r1,'d2('r2d,'r3)"); break; case LGF: Format(instr, "lgf\t'r1,'d2('r2d,'r3)"); break; case LLGF: Format(instr, "llgf\t'r1,'d2('r2d,'r3)"); break; case LY: Format(instr, "ly\t'r1,'d2('r2d,'r3)"); break; case ALY: Format(instr, "aly\t'r1,'d2('r2d,'r3)"); break; case ALG: Format(instr, "alg\t'r1,'d2('r2d,'r3)"); break; case SLG: Format(instr, "slg\t'r1,'d2('r2d,'r3)"); break; case SGF: Format(instr, "sgf\t'r1,'d2('r2d,'r3)"); break; case SLY: Format(instr, "sly\t'r1,'d2('r2d,'r3)"); break; case LLH: Format(instr, "llh\t'r1,'d2('r2d,'r3)"); break; case LLGH: Format(instr, "llgh\t'r1,'d2('r2d,'r3)"); break; case LLC: Format(instr, "llc\t'r1,'d2('r2d,'r3)"); break; case LLGC: Format(instr, "llgc\t'r1,'d2('r2d,'r3)"); break; case LDEB: Format(instr, "ldeb\t'f1,'d2('r2d,'r3)"); break; case LAY: Format(instr, "lay\t'r1,'d2('r2d,'r3)"); break; case LARL: Format(instr, "larl\t'r1,'i5"); break; case LGB: Format(instr, "lgb\t'r1,'d2('r2d,'r3)"); break; case CHY: Format(instr, "chy\t'r1,'d2('r2d,'r3)"); break; case CLY: Format(instr, "cly\t'r1,'d2('r2d,'r3)"); break; case CLIY: Format(instr, "cliy\t'd2('r3),'i8"); break; case TMY: Format(instr, "tmy\t'd2('r3),'i8"); break; case CLG: Format(instr, "clg\t'r1,'d2('r2d,'r3)"); break; case BCTG: Format(instr, "bctg\t'r1,'d2('r2d,'r3)"); break; case STY: Format(instr, "sty\t'r1,'d2('r2d,'r3)"); break; case STRVH: Format(instr, "strvh\t'r1,'d2('r2d,'r3)"); break; case STRV: Format(instr, "strv\t'r1,'d2('r2d,'r3)"); break; case STRVG: Format(instr, "strvg\t'r1,'d2('r2d,'r3)"); break; case STG: Format(instr, "stg\t'r1,'d2('r2d,'r3)"); break; case ICY: Format(instr, "icy\t'r1,'d2('r2d,'r3)"); break; case MVC: Format(instr, "mvc\t'd3('i8,'r3),'d4('r7)"); break; case MVHI: Format(instr, "mvhi\t'd3('r3),'id"); break; case MVGHI: Format(instr, "mvghi\t'd3('r3),'id"); break; case ALGFI: Format(instr, "algfi\t'r1,'i7"); break; case SLGFI: Format(instr, "slgfi\t'r1,'i7"); break; case SLFI: Format(instr, "slfi\t'r1,'i7"); break; case NIHF: Format(instr, "nihf\t'r1,'i7"); break; case NILF: Format(instr, "nilf\t'r1,'i7"); break; case OIHF: Format(instr, "oihf\t'r1,'i7"); break; case OILF: Format(instr, "oilf\t'r1,'i7"); break; case MSFI: Format(instr, "msfi\t'r1,'i7"); break; case MSGFI: Format(instr, "msgfi\t'r1,'i7"); break; case LDY: Format(instr, "ldy\t'f1,'d2('r2d,'r3)"); break; case LEY: Format(instr, "ley\t'f1,'d2('r2d,'r3)"); break; case MSG: Format(instr, "msg\t'r1,'d2('r2d,'r3)"); break; case DSG: Format(instr, "dsg\t'r1,'d2('r2d,'r3)"); break; case DSGF: Format(instr, "dsgf\t'r1,'d2('r2d,'r3)"); break; case MSGF: Format(instr, "msgf\t'r1,'d2('r2d,'r3)"); break; case MSY: Format(instr, "msy\t'r1,'d2('r2d,'r3)"); break; case STEY: Format(instr, "stey\t'f1,'d2('r2d,'r3)"); break; case STDY: Format(instr, "stdy\t'f1,'d2('r2d,'r3)"); break; case ADB: Format(instr, "adb\t'f1,'d1('r2d, 'r3)"); break; case CDB: Format(instr, "cdb\t'f1,'d1('r2d, 'r3)"); break; case CEB: Format(instr, "ceb\t'f1,'d1('r2d, 'r3)"); break; case SDB: Format(instr, "sdb\t'r1,'d1('r2d, 'r3)"); break; case MDB: Format(instr, "mdb\t'r1,'d1('r2d, 'r3)"); break; case DDB: Format(instr, "ddb\t'r1,'d1('r2d, 'r3)"); break; case SQDB: Format(instr, "sqdb\t'r1,'d1('r2d, 'r3)"); break; case PFD: Format(instr, "pfd\t'm1,'d2('r2d,'r3)"); break; default: return false; } return true; } #undef VERIFIY // Disassemble the instruction at *instr_ptr into the output buffer. int Decoder::InstructionDecode(byte* instr_ptr) { Instruction* instr = Instruction::At(instr_ptr); int instrLength = instr->InstructionLength(); if (2 == instrLength) DecodeTwoByte(instr); else if (4 == instrLength) DecodeFourByte(instr); else DecodeSixByte(instr); return instrLength; } } // namespace internal } // namespace v8 //------------------------------------------------------------------------------ namespace disasm { const char* NameConverter::NameOfAddress(byte* addr) const { v8::internal::SNPrintF(tmp_buffer_, "%p", static_cast(addr)); return tmp_buffer_.start(); } const char* NameConverter::NameOfConstant(byte* addr) const { return NameOfAddress(addr); } const char* NameConverter::NameOfCPURegister(int reg) const { return v8::internal::GetRegConfig()->GetGeneralRegisterName(reg); } const char* NameConverter::NameOfByteCPURegister(int reg) const { UNREACHABLE(); // S390 does not have the concept of a byte register return "nobytereg"; } const char* NameConverter::NameOfXMMRegister(int reg) const { // S390 does not have XMM register // TODO(joransiu): Consider update this for Vector Regs UNREACHABLE(); return "noxmmreg"; } 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 buffer, byte* instruction) { v8::internal::Decoder d(converter_, buffer); return d.InstructionDecode(instruction); } // The S390 assembler does not currently use constant pools. int Disassembler::ConstantPoolSizeAt(byte* instruction) { 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 buffer; buffer[0] = '\0'; byte* prev_pc = pc; pc += d.InstructionDecode(buffer, pc); v8::internal::PrintF(f, "%p %08x %s\n", static_cast(prev_pc), *reinterpret_cast(prev_pc), buffer.start()); } } } // namespace disasm #endif // V8_TARGET_ARCH_S390