diff options
Diffstat (limited to 'deps/v8/src/diagnostics/s390/disasm-s390.cc')
-rw-r--r-- | deps/v8/src/diagnostics/s390/disasm-s390.cc | 1064 |
1 files changed, 1064 insertions, 0 deletions
diff --git a/deps/v8/src/diagnostics/s390/disasm-s390.cc b/deps/v8/src/diagnostics/s390/disasm-s390.cc new file mode 100644 index 0000000000..9b10e236ce --- /dev/null +++ b/deps/v8/src/diagnostics/s390/disasm-s390.cc @@ -0,0 +1,1064 @@ +// 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<char, 256> buffer; +// byte* prev_pc = pc; +// pc += d.InstructionDecode(buffer, pc); +// printf("%p %08x %s\n", +// prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer); +// } +// +// The Disassembler class also has a convenience method to disassemble a block +// of code into a FILE*, meaning that the above functionality could also be +// achieved by just calling Disassembler::Disassemble(stdout, begin, end); + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#if V8_TARGET_ARCH_S390 + +#include "src/base/platform/platform.h" +#include "src/codegen/macro-assembler.h" +#include "src/codegen/register-configuration.h" +#include "src/codegen/s390/constants-s390.h" +#include "src/diagnostics/disasm.h" + +namespace v8 { +namespace 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, 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 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 DecodeSpecial(Instruction* instr); + bool DecodeGeneric(Instruction* instr); + + const disasm::NameConverter& converter_; + 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)); +} + +// Print the double FP register name according to the active name converter. +void Decoder::PrintDRegister(int reg) { + Print(RegisterName(DoubleRegister::from_code(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_EQ(format[0], 'r'); + + if (format[1] == '1') { // 'r1: register resides in bit 8-11 + int reg = instr->Bits<SixByteInstr, int>(39, 36); + PrintRegister(reg); + return 2; + } else if (format[1] == '2') { // 'r2: register resides in bit 12-15 + int reg = instr->Bits<SixByteInstr, int>(35, 32); + // 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 + int reg = instr->Bits<SixByteInstr, int>(31, 28); + PrintRegister(reg); + return 2; + } else if (format[1] == '4') { // 'r4: register resides in bit 20-23 + int reg = instr->Bits<SixByteInstr, int>(27, 24); + PrintRegister(reg); + return 2; + } else if (format[1] == '5') { // 'r5: register resides in bit 24-27 + int reg = instr->Bits<SixByteInstr, int>(23, 20); + PrintRegister(reg); + return 2; + } else if (format[1] == '6') { // 'r6: register resides in bit 28-31 + int reg = instr->Bits<SixByteInstr, int>(19, 16); + PrintRegister(reg); + return 2; + } else if (format[1] == '7') { // 'r6: register resides in bit 32-35 + int reg = instr->Bits<SixByteInstr, int>(15, 12); + PrintRegister(reg); + return 2; + } + + UNREACHABLE(); +} + +int Decoder::FormatFloatingRegister(Instruction* instr, const char* format) { + DCHECK_EQ(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<RRInstruction*>(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<RRInstruction*>(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<RRDInstruction*>(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<RREInstruction*>(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<RREInstruction*>(instr); + int reg = rreinstr->R2Value(); + PrintDRegister(reg); + return 2; + } else if (format[1] == '4') { + VRR_E_Instruction* vrreinstr = reinterpret_cast<VRR_E_Instruction*>(instr); + int reg = vrreinstr->R4Value(); + PrintDRegister(reg); + return 2; + } + UNREACHABLE(); +} + +// 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<byte*>(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<byte*>(instr) + off)); + return 8; + } + break; + case 'm': { + return FormatMask(instr, format); + } + } + case 'd': { // ds value for offset + return FormatDisplacement(instr, format); + } + default: { + UNREACHABLE(); + } + } + + UNREACHABLE(); +} + +int Decoder::FormatMask(Instruction* instr, const char* format) { + DCHECK_EQ(format[0], 'm'); + int32_t value = 0; + if ((format[1] == '1')) { // prints the mask format in bits 8-12 + value = reinterpret_cast<RRInstruction*>(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<RXInstruction*>(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<RRFInstruction*>(instr)->M4Value(); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", value); + return 2; + } else if (format[1] == '4') { // mask format in bits 32-35 + value = reinterpret_cast<VRR_C_Instruction*>(instr)->M4Value(); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", value); + return 2; + } else if (format[1] == '5') { // mask format in bits 28-31 + value = reinterpret_cast<VRR_C_Instruction*>(instr)->M5Value(); + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", value); + return 2; + } else if (format[1] == '6') { // mask format in bits 24-27 + value = reinterpret_cast<VRR_C_Instruction*>(instr)->M6Value(); + 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_EQ(format[0], 'd'); + + if (format[1] == '1') { // displacement in 20-31 + RSInstruction* rsinstr = reinterpret_cast<RSInstruction*>(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<RXYInstruction*>(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<SSInstruction*>(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<SSInstruction*>(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_EQ(format[0], 'i'); + + if (format[1] == '1') { // immediate in 16-31 + RIInstruction* riinstr = reinterpret_cast<RIInstruction*>(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<RILInstruction*>(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<IInstruction*>(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<RIInstruction*>(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<byte*>(instr) + value)); + return 2; + } else if (format[1] == '5') { // immediate in 16-31, but outputs as offset + RILInstruction* rilinstr = reinterpret_cast<RILInstruction*>(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<byte*>(instr) + value)); + return 2; + } else if (format[1] == '6') { // unsigned immediate in 16-31 + RIInstruction* riinstr = reinterpret_cast<RIInstruction*>(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<RILInstruction*>(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<SSInstruction*>(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<RIEInstruction*>(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<RIEInstruction*>(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<RIEInstruction*>(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<SSInstruction*>(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<SILInstruction*>(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<RILInstruction*>(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<byte*>(instr) + value)); + return 2; + } + + UNREACHABLE(); +} + +// 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 resemble 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); +} + +#undef VERIFY +#undef STRING_STARTS_WITH + +// Handles special cases of instructions; +// @return true if successfully decoded +bool Decoder::DecodeSpecial(Instruction* instr) { + Opcode opcode = instr->S390OpcodeValue(); + switch (opcode) { + case BKPT: + Format(instr, "bkpt"); + break; + case DUMY: + Format(instr, "dumy\t'r1, 'd2 ( 'r2d, 'r3 )"); + break; + /* RR format */ + case LDR: + Format(instr, "ldr\t'f1,'f2"); + break; + case BCR: + Format(instr, "bcr\t'm1,'r2"); + break; + case OR: + Format(instr, "or\t'r1,'r2"); + break; + case CR: + Format(instr, "cr\t'r1,'r2"); + break; + case MR: + Format(instr, "mr\t'r1,'r2"); + break; + case HER_Z: + Format(instr, "her\t'r1,'r2"); + break; + /* RI-b format */ + case BRAS: + Format(instr, "bras\t'r1,'i1"); + break; + /* RRE format */ + case MDBR: + Format(instr, "mdbr\t'f5,'f6"); + break; + case SDBR: + Format(instr, "sdbr\t'f5,'f6"); + break; + case ADBR: + Format(instr, "adbr\t'f5,'f6"); + break; + case CDBR: + Format(instr, "cdbr\t'f5,'f6"); + break; + case MEEBR: + Format(instr, "meebr\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 LTEBR: + Format(instr, "ltebr\t'f5,'f6"); + break; + case LDEBR: + Format(instr, "ldebr\t'f5,'f6"); + break; + case CEBR: + Format(instr, "cebr\t'f5,'f6"); + break; + case AEBR: + Format(instr, "aebr\t'f5,'f6"); + break; + case SEBR: + Format(instr, "sebr\t'f5,'f6"); + break; + case DEBR: + Format(instr, "debr\t'f5,'f6"); + break; + case LTDBR: + Format(instr, "ltdbr\t'f5,'f6"); + break; + case LDGR: + Format(instr, "ldgr\t'f5,'f6"); + break; + case DDBR: + Format(instr, "ddbr\t'f5,'f6"); + break; + case LZDR: + Format(instr, "lzdr\t'f5"); + break; + /* RRF-e format */ + case FIEBRA: + Format(instr, "fiebra\t'f5,'m2,'f6,'m3"); + break; + case FIDBRA: + Format(instr, "fidbra\t'f5,'m2,'f6,'m3"); + break; + /* RX-a format */ + case IC_z: + Format(instr, "ic\t'r1,'d1('r2d,'r3)"); + break; + case AL: + Format(instr, "al\t'r1,'d1('r2d,'r3)"); + break; + case LE: + Format(instr, "le\t'f1,'d1('r2d,'r3)"); + break; + case LD: + Format(instr, "ld\t'f1,'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; + /* S format */ + // TRAP4 is used in calling to native function. it will not be generated + // in native code. + case TRAP4: + Format(instr, "trap4"); + break; + /* RIL-a format */ + case CFI: + Format(instr, "cfi\t'r1,'i2"); + break; + case CGFI: + Format(instr, "cgfi\t'r1,'i2"); + break; + case AFI: + Format(instr, "afi\t'r1,'i2"); + break; + case AGFI: + Format(instr, "agfi\t'r1,'i2"); + break; + case MSFI: + Format(instr, "msfi\t'r1,'i2"); + break; + case MSGFI: + Format(instr, "msgfi\t'r1,'i2"); + break; + case ALSIH: + Format(instr, "alsih\t'r1,'i2"); + break; + case ALSIHN: + Format(instr, "alsihn\t'r1,'i2"); + break; + case CIH: + Format(instr, "cih\t'r1,'i2"); + break; + case AIH: + Format(instr, "aih\t'r1,'i2"); + break; + case LGFI: + Format(instr, "lgfi\t'r1,'i2"); + break; + /* SIY format */ + case ASI: + Format(instr, "asi\t'd2('r3),'ic"); + break; + case AGSI: + Format(instr, "agsi\t'd2('r3),'ic"); + break; + /* RXY-a format */ + case LT: + Format(instr, "lt\t'r1,'d2('r2d,'r3)"); + break; + case LDY: + Format(instr, "ldy\t'f1,'d2('r2d,'r3)"); + break; + case LEY: + Format(instr, "ley\t'f1,'d2('r2d,'r3)"); + break; + case STDY: + Format(instr, "stdy\t'f1,'d2('r2d,'r3)"); + break; + case STEY: + Format(instr, "stey\t'f1,'d2('r2d,'r3)"); + break; + /* RXE format */ + case LDEB: + Format(instr, "ldeb\t'f1,'d2('r2d,'r3)"); + break; + default: + return false; + } + return true; +} + +// Handles common cases of instructions; +// @return true if successfully decoded +bool Decoder::DecodeGeneric(Instruction* instr) { + Opcode opcode = instr->S390OpcodeValue(); + switch (opcode) { + /* 2 bytes */ +#define DECODE_RR_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'r2"); \ + break; + S390_RR_OPCODE_LIST(DECODE_RR_INSTRUCTIONS) +#undef DECODE_RR_INSTRUCTIONS + + /* 4 bytes */ +#define DECODE_RS_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'r2,'d1('r3)"); \ + break; + S390_RS_A_OPCODE_LIST(DECODE_RS_A_INSTRUCTIONS) +#undef DECODE_RS_A_INSTRUCTIONS + +#define DECODE_RSI_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'r2,'i4"); \ + break; + S390_RSI_OPCODE_LIST(DECODE_RSI_INSTRUCTIONS) +#undef DECODE_RSI_INSTRUCTIONS + +#define DECODE_RI_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'i1"); \ + break; + S390_RI_A_OPCODE_LIST(DECODE_RI_A_INSTRUCTIONS) +#undef DECODE_RI_A_INSTRUCTIONS + +#define DECODE_RI_B_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'i4"); \ + break; + S390_RI_B_OPCODE_LIST(DECODE_RI_B_INSTRUCTIONS) +#undef DECODE_RI_B_INSTRUCTIONS + +#define DECODE_RI_C_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'm1,'i4"); \ + break; + S390_RI_C_OPCODE_LIST(DECODE_RI_C_INSTRUCTIONS) +#undef DECODE_RI_C_INSTRUCTIONS + +#define DECODE_RRE_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r5,'r6"); \ + break; + S390_RRE_OPCODE_LIST(DECODE_RRE_INSTRUCTIONS) +#undef DECODE_RRE_INSTRUCTIONS + +#define DECODE_RRF_A_INSTRUCTIONS(name, opcode_name, opcode_val) \ + case opcode_name: \ + Format(instr, #name "\t'r5,'r6,'r3"); \ + break; + S390_RRF_A_OPCODE_LIST(DECODE_RRF_A_INSTRUCTIONS) +#undef DECODE_RRF_A_INSTRUCTIONS + +#define DECODE_RRF_C_INSTRUCTIONS(name, opcode_name, opcode_val) \ + case opcode_name: \ + Format(instr, #name "\t'r5,'r6,'m2"); \ + break; + S390_RRF_C_OPCODE_LIST(DECODE_RRF_C_INSTRUCTIONS) +#undef DECODE_RRF_C_INSTRUCTIONS + +#define DECODE_RRF_E_INSTRUCTIONS(name, opcode_name, opcode_val) \ + case opcode_name: \ + Format(instr, #name "\t'r5,'m2,'f6"); \ + break; + S390_RRF_E_OPCODE_LIST(DECODE_RRF_E_INSTRUCTIONS) +#undef DECODE_RRF_E_INSTRUCTIONS + +#define DECODE_RX_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'d1('r2d,'r3)"); \ + break; + S390_RX_A_OPCODE_LIST(DECODE_RX_A_INSTRUCTIONS) +#undef DECODE_RX_A_INSTRUCTIONS + +#define DECODE_RX_B_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'm1,'d1('r2d,'r3)"); \ + break; + S390_RX_B_OPCODE_LIST(DECODE_RX_B_INSTRUCTIONS) +#undef DECODE_RX_B_INSTRUCTIONS + +#define DECODE_RRD_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f3,'f5,'f6"); \ + break; + S390_RRD_OPCODE_LIST(DECODE_RRD_INSTRUCTIONS) +#undef DECODE_RRD_INSTRUCTIONS + +#define DECODE_SI_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'd1('r3),'i8"); \ + break; + S390_SI_OPCODE_LIST(DECODE_SI_INSTRUCTIONS) +#undef DECODE_SI_INSTRUCTIONS + + /* 6 bytes */ +#define DECODE_VRR_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f1,'f2,'m4,'m5,'m6"); \ + break; + S390_VRR_A_OPCODE_LIST(DECODE_VRR_A_INSTRUCTIONS) +#undef DECODE_VRR_A_INSTRUCTIONS + +#define DECODE_VRR_B_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f1,'f2,'f3,'m4,'m6"); \ + break; + S390_VRR_B_OPCODE_LIST(DECODE_VRR_B_INSTRUCTIONS) +#undef DECODE_VRR_B_INSTRUCTIONS + +#define DECODE_VRR_C_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f1,'f2,'f3,'m4"); \ + break; + S390_VRR_C_OPCODE_LIST(DECODE_VRR_C_INSTRUCTIONS) +#undef DECODE_VRR_C_INSTRUCTIONS + +#define DECODE_VRR_E_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f1,'f2,'f3,'f4,'m5,'m3"); \ + break; + S390_VRR_E_OPCODE_LIST(DECODE_VRR_E_INSTRUCTIONS) +#undef DECODE_VRR_E_INSTRUCTIONS + +#define DECODE_VRX_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f1,'d1('r2d,'r3),'m4"); \ + break; + S390_VRX_OPCODE_LIST(DECODE_VRX_INSTRUCTIONS) +#undef DECODE_VRX_INSTRUCTIONS + +#define DECODE_VRS_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f1,'f2,'d1('r3),'m4"); \ + break; + S390_VRS_A_OPCODE_LIST(DECODE_VRS_A_INSTRUCTIONS) +#undef DECODE_VRS_A_INSTRUCTIONS + +#define DECODE_VRS_B_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f1,'r2,'d1('r3),'m4"); \ + break; + S390_VRS_B_OPCODE_LIST(DECODE_VRS_B_INSTRUCTIONS) +#undef DECODE_VRS_B_INSTRUCTIONS + +#define DECODE_VRS_C_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'f2,'d1('r3),'m4"); \ + break; + S390_VRS_C_OPCODE_LIST(DECODE_VRS_C_INSTRUCTIONS) +#undef DECODE_VRS_C_INSTRUCTIONS + +#define DECODE_VRI_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f1,'i1,'m4"); \ + break; + S390_VRI_A_OPCODE_LIST(DECODE_VRI_A_INSTRUCTIONS) +#undef DECODE_VRI_A_INSTRUCTIONS + +#define DECODE_VRI_C_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f1,'f2,'i1,'m4"); \ + break; + S390_VRI_C_OPCODE_LIST(DECODE_VRI_C_INSTRUCTIONS) +#undef DECODE_VRI_C_INSTRUCTIONS + +#define DECODE_RIL_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'i7"); \ + break; + S390_RIL_A_OPCODE_LIST(DECODE_RIL_A_INSTRUCTIONS) +#undef DECODE_RIL_A_INSTRUCTIONS + +#define DECODE_RIL_B_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'ie"); \ + break; + S390_RIL_B_OPCODE_LIST(DECODE_RIL_B_INSTRUCTIONS) +#undef DECODE_RIL_B_INSTRUCTIONS + +#define DECODE_RIL_C_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'm1,'ie"); \ + break; + S390_RIL_C_OPCODE_LIST(DECODE_RIL_C_INSTRUCTIONS) +#undef DECODE_RIL_C_INSTRUCTIONS + +#define DECODE_SIY_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'd2('r3),'i8"); \ + break; + S390_SIY_OPCODE_LIST(DECODE_SIY_INSTRUCTIONS) +#undef DECODE_SIY_INSTRUCTIONS + +#define DECODE_RIE_D_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'r2,'i1"); \ + break; + S390_RIE_D_OPCODE_LIST(DECODE_RIE_D_INSTRUCTIONS) +#undef DECODE_RIE_D_INSTRUCTIONS + +#define DECODE_RIE_E_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'r2,'i4"); \ + break; + S390_RIE_E_OPCODE_LIST(DECODE_RIE_E_INSTRUCTIONS) +#undef DECODE_RIE_E_INSTRUCTIONS + +#define DECODE_RIE_F_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'r2,'i9,'ia,'ib"); \ + break; + S390_RIE_F_OPCODE_LIST(DECODE_RIE_F_INSTRUCTIONS) +#undef DECODE_RIE_F_INSTRUCTIONS + +#define DECODE_RSY_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'r2,'d2('r3)"); \ + break; + S390_RSY_A_OPCODE_LIST(DECODE_RSY_A_INSTRUCTIONS) +#undef DECODE_RSY_A_INSTRUCTIONS + +#define DECODE_RSY_B_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'm2,'r1,'d2('r3)"); \ + break; + S390_RSY_B_OPCODE_LIST(DECODE_RSY_B_INSTRUCTIONS) +#undef DECODE_RSY_B_INSTRUCTIONS + +#define DECODE_RXY_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'r1,'d2('r2d,'r3)"); \ + break; + S390_RXY_A_OPCODE_LIST(DECODE_RXY_A_INSTRUCTIONS) +#undef DECODE_RXY_A_INSTRUCTIONS + +#define DECODE_RXY_B_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'm1,'d2('r2d,'r3)"); \ + break; + S390_RXY_B_OPCODE_LIST(DECODE_RXY_B_INSTRUCTIONS) +#undef DECODE_RXY_B_INSTRUCTIONS + +#define DECODE_RXE_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'f1,'d1('r2d, 'r3)"); \ + break; + S390_RXE_OPCODE_LIST(DECODE_RXE_INSTRUCTIONS) +#undef DECODE_RXE_INSTRUCTIONS + +#define DECODE_SIL_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'd3('r3),'id"); \ + break; + S390_SIL_OPCODE_LIST(DECODE_SIL_INSTRUCTIONS) +#undef DECODE_SIL_INSTRUCTIONS + +#define DECODE_SS_A_INSTRUCTIONS(name, opcode_name, opcode_value) \ + case opcode_name: \ + Format(instr, #name "\t'd3('i8,'r3),'d4('r7)"); \ + break; + S390_SS_A_OPCODE_LIST(DECODE_SS_A_INSTRUCTIONS) +#undef DECODE_SS_A_INSTRUCTIONS + + default: + return false; + } + return true; +} + +// 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(); + + // Print the Instruction bits. + if (instrLength == 2) { + out_buffer_pos_ += + SNPrintF(out_buffer_ + out_buffer_pos_, "%04x ", + instr->InstructionBits<TwoByteInstr>()); + } else if (instrLength == 4) { + out_buffer_pos_ += SNPrintF(out_buffer_ + out_buffer_pos_, "%08x ", + instr->InstructionBits<FourByteInstr>()); + } else { + out_buffer_pos_ += + SNPrintF(out_buffer_ + out_buffer_pos_, "%012" PRIx64 " ", + instr->InstructionBits<SixByteInstr>()); + } + + bool decoded = DecodeSpecial(instr); + if (!decoded) decoded = DecodeGeneric(instr); + if (!decoded) Unknown(instr); + return instrLength; +} + +} // namespace internal +} // namespace v8 + +//------------------------------------------------------------------------------ + +namespace disasm { + +const char* NameConverter::NameOfAddress(byte* addr) const { + v8::internal::SNPrintF(tmp_buffer_, "%p", static_cast<void*>(addr)); + return tmp_buffer_.begin(); +} + +const char* NameConverter::NameOfConstant(byte* addr) const { + return NameOfAddress(addr); +} + +const char* NameConverter::NameOfCPURegister(int reg) const { + return RegisterName(i::Register::from_code(reg)); +} + +const char* NameConverter::NameOfByteCPURegister(int reg) const { + UNREACHABLE(); // S390 does not have the concept of a byte register +} + +const char* NameConverter::NameOfXMMRegister(int reg) const { + // S390 does not have XMM register + // TODO(joransiu): Consider update this for Vector Regs + UNREACHABLE(); +} + +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 ""; +} + +//------------------------------------------------------------------------------ + +int Disassembler::InstructionDecode(v8::internal::Vector<char> 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, + UnimplementedOpcodeAction unimplemented_action) { + NameConverter converter; + Disassembler d(converter, unimplemented_action); + for (byte* pc = begin; pc < end;) { + v8::internal::EmbeddedVector<char, 128> buffer; + buffer[0] = '\0'; + byte* prev_pc = pc; + pc += d.InstructionDecode(buffer, pc); + v8::internal::PrintF(f, "%p %08x %s\n", static_cast<void*>(prev_pc), + *reinterpret_cast<int32_t*>(prev_pc), buffer.begin()); + } +} + +} // namespace disasm + +#endif // V8_TARGET_ARCH_S390 |