// Copyright 2015 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.

#include "src/wasm/wasm-opcodes.h"
#include "src/messages.h"
#include "src/signature.h"

namespace v8 {
namespace internal {
namespace wasm {

typedef Signature<LocalType> FunctionSig;

const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
  switch (opcode) {
#define DECLARE_NAME_CASE(name, opcode, sig) \
  case kExpr##name:                          \
    return "Expr" #name;
    FOREACH_OPCODE(DECLARE_NAME_CASE)
#undef DECLARE_NAME_CASE
    default:
      break;
  }
  return "Unknown";
}

const char* WasmOpcodes::ShortOpcodeName(WasmOpcode opcode) {
  switch (opcode) {
#define DECLARE_NAME_CASE(name, opcode, sig) \
  case kExpr##name:                          \
    return #name;
    FOREACH_OPCODE(DECLARE_NAME_CASE)
#undef DECLARE_NAME_CASE
    default:
      break;
  }
  return "Unknown";
}

bool WasmOpcodes::IsPrefixOpcode(WasmOpcode opcode) {
  switch (opcode) {
#define CHECK_PREFIX(name, opcode) \
  case k##name##Prefix:            \
    return true;
    FOREACH_PREFIX(CHECK_PREFIX)
#undef CHECK_PREFIX
    default:
      return false;
  }
}

std::ostream& operator<<(std::ostream& os, const FunctionSig& sig) {
  if (sig.return_count() == 0) os << "v";
  for (size_t i = 0; i < sig.return_count(); ++i) {
    os << WasmOpcodes::ShortNameOf(sig.GetReturn(i));
  }
  os << "_";
  if (sig.parameter_count() == 0) os << "v";
  for (size_t i = 0; i < sig.parameter_count(); ++i) {
    os << WasmOpcodes::ShortNameOf(sig.GetParam(i));
  }
  return os;
}

#define DECLARE_SIG_ENUM(name, ...) kSigEnum_##name,

enum WasmOpcodeSig { FOREACH_SIGNATURE(DECLARE_SIG_ENUM) };

// TODO(titzer): not static-initializer safe. Wrap in LazyInstance.
#define DECLARE_SIG(name, ...)                      \
  static LocalType kTypes_##name[] = {__VA_ARGS__}; \
  static const FunctionSig kSig_##name(             \
      1, static_cast<int>(arraysize(kTypes_##name)) - 1, kTypes_##name);

FOREACH_SIGNATURE(DECLARE_SIG)

#define DECLARE_SIG_ENTRY(name, ...) &kSig_##name,

static const FunctionSig* kSimpleExprSigs[] = {
    nullptr, FOREACH_SIGNATURE(DECLARE_SIG_ENTRY)};

#define DECLARE_SIMD_SIG_ENTRY(name, ...) &kSig_##name,

static const FunctionSig* kSimdExprSigs[] = {
    nullptr, FOREACH_SIMD_SIGNATURE(DECLARE_SIMD_SIG_ENTRY)};

static byte kSimpleExprSigTable[256];
static byte kSimpleAsmjsExprSigTable[256];
static byte kSimdExprSigTable[256];
static byte kAtomicExprSigTable[256];

// Initialize the signature table.
static void InitSigTables() {
#define SET_SIG_TABLE(name, opcode, sig) \
  kSimpleExprSigTable[opcode] = static_cast<int>(kSigEnum_##sig) + 1;
  FOREACH_SIMPLE_OPCODE(SET_SIG_TABLE);
#undef SET_SIG_TABLE
#define SET_ASMJS_SIG_TABLE(name, opcode, sig) \
  kSimpleAsmjsExprSigTable[opcode] = static_cast<int>(kSigEnum_##sig) + 1;
  FOREACH_ASMJS_COMPAT_OPCODE(SET_ASMJS_SIG_TABLE);
#undef SET_ASMJS_SIG_TABLE
  byte simd_index;
#define SET_SIG_TABLE(name, opcode, sig) \
  simd_index = opcode & 0xff;            \
  kSimdExprSigTable[simd_index] = static_cast<int>(kSigEnum_##sig) + 1;
  FOREACH_SIMD_0_OPERAND_OPCODE(SET_SIG_TABLE)
#undef SET_SIG_TABLE
  byte atomic_index;
#define SET_ATOMIC_SIG_TABLE(name, opcode, sig) \
  atomic_index = opcode & 0xff;                 \
  kAtomicExprSigTable[atomic_index] = static_cast<int>(kSigEnum_##sig) + 1;
  FOREACH_ATOMIC_OPCODE(SET_ATOMIC_SIG_TABLE)
#undef SET_ATOMIC_SIG_TABLE
}

class SigTable {
 public:
  SigTable() {
    // TODO(ahaas): Move {InitSigTable} into the class.
    InitSigTables();
  }
  FunctionSig* Signature(WasmOpcode opcode) const {
    return const_cast<FunctionSig*>(
        kSimpleExprSigs[kSimpleExprSigTable[static_cast<byte>(opcode)]]);
  }
  FunctionSig* AsmjsSignature(WasmOpcode opcode) const {
    return const_cast<FunctionSig*>(
        kSimpleExprSigs[kSimpleAsmjsExprSigTable[static_cast<byte>(opcode)]]);
  }
  FunctionSig* SimdSignature(WasmOpcode opcode) const {
    return const_cast<FunctionSig*>(
        kSimdExprSigs[kSimdExprSigTable[static_cast<byte>(opcode & 0xff)]]);
  }
  FunctionSig* AtomicSignature(WasmOpcode opcode) const {
    return const_cast<FunctionSig*>(
        kSimpleExprSigs[kAtomicExprSigTable[static_cast<byte>(opcode & 0xff)]]);
  }
};

static base::LazyInstance<SigTable>::type sig_table = LAZY_INSTANCE_INITIALIZER;

FunctionSig* WasmOpcodes::Signature(WasmOpcode opcode) {
  if (opcode >> 8 == kSimdPrefix) {
    return sig_table.Get().SimdSignature(opcode);
  } else {
    return sig_table.Get().Signature(opcode);
  }
}

FunctionSig* WasmOpcodes::AsmjsSignature(WasmOpcode opcode) {
  return sig_table.Get().AsmjsSignature(opcode);
}

FunctionSig* WasmOpcodes::AtomicSignature(WasmOpcode opcode) {
  return sig_table.Get().AtomicSignature(opcode);
}

// TODO(titzer): pull WASM_64 up to a common header.
#if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64
#define WASM_64 1
#else
#define WASM_64 0
#endif

int WasmOpcodes::TrapReasonToMessageId(TrapReason reason) {
  switch (reason) {
#define TRAPREASON_TO_MESSAGE(name) \
  case k##name:                     \
    return MessageTemplate::kWasm##name;
    FOREACH_WASM_TRAPREASON(TRAPREASON_TO_MESSAGE)
#undef TRAPREASON_TO_MESSAGE
    default:
      return MessageTemplate::kNone;
  }
}

const char* WasmOpcodes::TrapReasonMessage(TrapReason reason) {
  return MessageTemplate::TemplateString(TrapReasonToMessageId(reason));
}
}  // namespace wasm
}  // namespace internal
}  // namespace v8