/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_x86_shared_Constants_x86_shared_h
#define jit_x86_shared_Constants_x86_shared_h

#include "mozilla/Assertions.h"

#include <iterator>
#include <stddef.h>
#include <stdint.h>

namespace js {
namespace jit {

namespace X86Encoding {

enum RegisterID : uint8_t {
  rax,
  rcx,
  rdx,
  rbx,
  rsp,
  rbp,
  rsi,
  rdi
#ifdef JS_CODEGEN_X64
  ,
  r8,
  r9,
  r10,
  r11,
  r12,
  r13,
  r14,
  r15
#endif
  ,
  invalid_reg
};

enum HRegisterID { ah = rsp, ch = rbp, dh = rsi, bh = rdi };

enum XMMRegisterID
// GCC < 8.0 has a bug with bitfields of enums with an underlying type.
#if defined(__clang__) || __GNUC__ > 7
    : uint8_t
#endif
{
  xmm0 = 0,
  xmm1,
  xmm2,
  xmm3,
  xmm4,
  xmm5,
  xmm6,
  xmm7
#ifdef JS_CODEGEN_X64
  ,
  xmm8,
  xmm9,
  xmm10,
  xmm11,
  xmm12,
  xmm13,
  xmm14,
  xmm15
#endif
  ,
  invalid_xmm
};

inline const char* XMMRegName(XMMRegisterID reg) {
  static const char* const names[] = {"%xmm0",
                                      "%xmm1",
                                      "%xmm2",
                                      "%xmm3",
                                      "%xmm4",
                                      "%xmm5",
                                      "%xmm6",
                                      "%xmm7"
#ifdef JS_CODEGEN_X64
                                      ,
                                      "%xmm8",
                                      "%xmm9",
                                      "%xmm10",
                                      "%xmm11",
                                      "%xmm12",
                                      "%xmm13",
                                      "%xmm14",
                                      "%xmm15"
#endif
  };
  MOZ_ASSERT(size_t(reg) < std::size(names));
  return names[reg];
}

#ifdef JS_CODEGEN_X64
inline const char* GPReg64Name(RegisterID reg) {
  static const char* const names[] = {"%rax",
                                      "%rcx",
                                      "%rdx",
                                      "%rbx",
                                      "%rsp",
                                      "%rbp",
                                      "%rsi",
                                      "%rdi"
#  ifdef JS_CODEGEN_X64
                                      ,
                                      "%r8",
                                      "%r9",
                                      "%r10",
                                      "%r11",
                                      "%r12",
                                      "%r13",
                                      "%r14",
                                      "%r15"
#  endif
  };
  MOZ_ASSERT(size_t(reg) < std::size(names));
  return names[reg];
}
#endif

inline const char* GPReg32Name(RegisterID reg) {
  static const char* const names[] = {"%eax",
                                      "%ecx",
                                      "%edx",
                                      "%ebx",
                                      "%esp",
                                      "%ebp",
                                      "%esi",
                                      "%edi"
#ifdef JS_CODEGEN_X64
                                      ,
                                      "%r8d",
                                      "%r9d",
                                      "%r10d",
                                      "%r11d",
                                      "%r12d",
                                      "%r13d",
                                      "%r14d",
                                      "%r15d"
#endif
  };
  MOZ_ASSERT(size_t(reg) < std::size(names));
  return names[reg];
}

inline const char* GPReg16Name(RegisterID reg) {
  static const char* const names[] = {"%ax",
                                      "%cx",
                                      "%dx",
                                      "%bx",
                                      "%sp",
                                      "%bp",
                                      "%si",
                                      "%di"
#ifdef JS_CODEGEN_X64
                                      ,
                                      "%r8w",
                                      "%r9w",
                                      "%r10w",
                                      "%r11w",
                                      "%r12w",
                                      "%r13w",
                                      "%r14w",
                                      "%r15w"
#endif
  };
  MOZ_ASSERT(size_t(reg) < std::size(names));
  return names[reg];
}

inline const char* GPReg8Name(RegisterID reg) {
  static const char* const names[] = {"%al",
                                      "%cl",
                                      "%dl",
                                      "%bl"
#ifdef JS_CODEGEN_X64
                                      ,
                                      "%spl",
                                      "%bpl",
                                      "%sil",
                                      "%dil",
                                      "%r8b",
                                      "%r9b",
                                      "%r10b",
                                      "%r11b",
                                      "%r12b",
                                      "%r13b",
                                      "%r14b",
                                      "%r15b"
#endif
  };
  MOZ_ASSERT(size_t(reg) < std::size(names));
  return names[reg];
}

inline const char* GPRegName(RegisterID reg) {
#ifdef JS_CODEGEN_X64
  return GPReg64Name(reg);
#else
  return GPReg32Name(reg);
#endif
}

inline bool HasSubregL(RegisterID reg) {
#ifdef JS_CODEGEN_X64
  // In 64-bit mode, all registers have an 8-bit lo subreg.
  return true;
#else
  // In 32-bit mode, only the first four registers do.
  return reg <= rbx;
#endif
}

inline bool HasSubregH(RegisterID reg) {
  // The first four registers always have h registers. However, note that
  // on x64, h registers may not be used in instructions using REX
  // prefixes. Also note that this may depend on what other registers are
  // used!
  return reg <= rbx;
}

inline HRegisterID GetSubregH(RegisterID reg) {
  MOZ_ASSERT(HasSubregH(reg));
  return HRegisterID(reg + 4);
}

inline const char* HRegName8(HRegisterID reg) {
  static const char* const names[] = {"%ah", "%ch", "%dh", "%bh"};
  size_t index = reg - GetSubregH(rax);
  MOZ_ASSERT(index < std::size(names));
  return names[index];
}

enum Condition {
  ConditionO,
  ConditionNO,
  ConditionB,
  ConditionAE,
  ConditionE,
  ConditionNE,
  ConditionBE,
  ConditionA,
  ConditionS,
  ConditionNS,
  ConditionP,
  ConditionNP,
  ConditionL,
  ConditionGE,
  ConditionLE,
  ConditionG,

  ConditionC = ConditionB,
  ConditionNC = ConditionAE
};

inline const char* CCName(Condition cc) {
  static const char* const names[] = {"o ", "no", "b ", "ae", "e ", "ne",
                                      "be", "a ", "s ", "ns", "p ", "np",
                                      "l ", "ge", "le", "g "};
  MOZ_ASSERT(size_t(cc) < std::size(names));
  return names[cc];
}

// Conditions for CMP instructions (CMPSS, CMPSD, CMPPS, CMPPD, etc).
enum ConditionCmp {
  ConditionCmp_EQ = 0x0,
  ConditionCmp_LT = 0x1,
  ConditionCmp_LE = 0x2,
  ConditionCmp_UNORD = 0x3,
  ConditionCmp_NEQ = 0x4,
  ConditionCmp_NLT = 0x5,
  ConditionCmp_NLE = 0x6,
  ConditionCmp_ORD = 0x7,
  ConditionCmp_AVX_Enabled = 0x8,
  ConditionCmp_GE = 0xD,
};

// Rounding modes for ROUNDSS / ROUNDSD / VCVTPS2PH.
enum RoundingMode {
  RoundToNearest = 0x0,
  RoundDown = 0x1,
  RoundUp = 0x2,
  RoundToZero = 0x3
};

// Rounding modes for ROUNDPS / ROUNDPD.  Note these are the same as for
// RoundingMode above but incorporate the 'inexact' bit which says not to signal
// exceptions for lost precision.  It's not obvious that this bit is needed; it
// was however suggested in the wasm SIMD proposal that led to these encodings.
enum class SSERoundingMode {
  RoundToNearest = 0x08,
  RoundDown = 0x09,
  RoundUp = 0x0A,
  RoundToZero = 0x0B
};

// Test whether the given address will fit in an address immediate field.
// This is always true on x86, but on x64 it's only true for addreses which
// fit in the 32-bit immediate field.
inline bool IsAddressImmediate(const void* address) {
  intptr_t value = reinterpret_cast<intptr_t>(address);
  int32_t immediate = static_cast<int32_t>(value);
  return value == immediate;
}

// Convert the given address to a 32-bit immediate field value. This is a
// no-op on x86, but on x64 it asserts that the address is actually a valid
// address immediate.
inline int32_t AddressImmediate(const void* address) {
  MOZ_ASSERT(IsAddressImmediate(address));
  return static_cast<int32_t>(reinterpret_cast<intptr_t>(address));
}

}  // namespace X86Encoding

}  // namespace jit
}  // namespace js

#endif /* jit_x86_shared_Constants_x86_shared_h */
