普通文本  |  989行  |  33.91 KB

// 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/disasm.h"
#include "src/macro-assembler.h"
#include "src/s390/constants-s390.h"

namespace v8 {
namespace internal {

const auto GetRegConfig = RegisterConfiguration::Default;

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

// 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(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_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;
  }
  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();
      break;
    }
  }

  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;
  }

  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_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

#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_.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();
}

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.start());
  }
}

}  // namespace disasm

#endif  // V8_TARGET_ARCH_S390