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

#ifndef V8_CODEGEN_LOONG64_CONSTANTS_LOONG64_H_
#define V8_CODEGEN_LOONG64_CONSTANTS_LOONG64_H_

#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/common/globals.h"

// Get the standard printf format macros for C99 stdint types.
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>

// Defines constants and accessor classes to assemble, disassemble and
// simulate LOONG64 instructions.

namespace v8 {
namespace internal {

constexpr size_t kMaxPCRelativeCodeRangeInMB = 128;

// -----------------------------------------------------------------------------
// Registers and FPURegisters.

// Number of general purpose registers.
const int kNumRegisters = 32;
const int kInvalidRegister = -1;

// Number of registers with pc.
const int kNumSimuRegisters = 33;

// In the simulator, the PC register is simulated as the 33th register.
const int kPCRegister = 32;

// Number of floating point registers.
const int kNumFPURegisters = 32;
const int kInvalidFPURegister = -1;

// FPU control registers.
const int kFCSRRegister = 0;
const int kInvalidFPUControlRegister = -1;
const uint32_t kFPUInvalidResult = static_cast<uint32_t>(1u << 31) - 1;
const int32_t kFPUInvalidResultNegative = static_cast<int32_t>(1u << 31);
const uint64_t kFPU64InvalidResult =
    static_cast<uint64_t>(static_cast<uint64_t>(1) << 63) - 1;
const int64_t kFPU64InvalidResultNegative =
    static_cast<int64_t>(static_cast<uint64_t>(1) << 63);

// FCSR constants.
const uint32_t kFCSRInexactCauseBit = 24;
const uint32_t kFCSRUnderflowCauseBit = 25;
const uint32_t kFCSROverflowCauseBit = 26;
const uint32_t kFCSRDivideByZeroCauseBit = 27;
const uint32_t kFCSRInvalidOpCauseBit = 28;

const uint32_t kFCSRInexactCauseMask = 1 << kFCSRInexactCauseBit;
const uint32_t kFCSRUnderflowCauseMask = 1 << kFCSRUnderflowCauseBit;
const uint32_t kFCSROverflowCauseMask = 1 << kFCSROverflowCauseBit;
const uint32_t kFCSRDivideByZeroCauseMask = 1 << kFCSRDivideByZeroCauseBit;
const uint32_t kFCSRInvalidOpCauseMask = 1 << kFCSRInvalidOpCauseBit;

const uint32_t kFCSRCauseMask =
    kFCSRInexactCauseMask | kFCSRUnderflowCauseMask | kFCSROverflowCauseMask |
    kFCSRDivideByZeroCauseMask | kFCSRInvalidOpCauseMask;

const uint32_t kFCSRExceptionCauseMask = kFCSRCauseMask ^ kFCSRInexactCauseMask;

// Actual value of root register is offset from the root array's start
// to take advantage of negative displacement values.
// TODO(sigurds): Choose best value.
constexpr int kRootRegisterBias = 256;

// Helper functions for converting between register numbers and names.
class Registers {
 public:
  // Return the name of the register.
  static const char* Name(int reg);

  // Lookup the register number for the name provided.
  static int Number(const char* name);

  struct RegisterAlias {
    int reg;
    const char* name;
  };

  static const int64_t kMaxValue = 0x7fffffffffffffffl;
  static const int64_t kMinValue = 0x8000000000000000l;

 private:
  static const char* names_[kNumSimuRegisters];
  static const RegisterAlias aliases_[];
};

// Helper functions for converting between register numbers and names.
class FPURegisters {
 public:
  // Return the name of the register.
  static const char* Name(int reg);

  // Lookup the register number for the name provided.
  static int Number(const char* name);

  struct RegisterAlias {
    int creg;
    const char* name;
  };

 private:
  static const char* names_[kNumFPURegisters];
  static const RegisterAlias aliases_[];
};

// -----------------------------------------------------------------------------
// Instructions encoding constants.

// On LoongArch all instructions are 32 bits.
using Instr = int32_t;

// Special Software Interrupt codes when used in the presence of the LOONG64
// simulator.
enum SoftwareInterruptCodes {
  // Transition to C code.
  call_rt_redirected = 0x7fff
};

// On LOONG64 Simulator breakpoints can have different codes:
// - Breaks between 0 and kMaxWatchpointCode are treated as simple watchpoints,
//   the simulator will run through them and print the registers.
// - Breaks between kMaxWatchpointCode and kMaxStopCode are treated as stop()
//   instructions (see Assembler::stop()).
// - Breaks larger than kMaxStopCode are simple breaks, dropping you into the
//   debugger.
const uint32_t kMaxWatchpointCode = 31;
const uint32_t kMaxStopCode = 127;
STATIC_ASSERT(kMaxWatchpointCode < kMaxStopCode);

// ----- Fields offset and length.
const int kRjShift = 5;
const int kRjBits = 5;
const int kRkShift = 10;
const int kRkBits = 5;
const int kRdShift = 0;
const int kRdBits = 5;
const int kSaShift = 15;
const int kSa2Bits = 2;
const int kSa3Bits = 3;
const int kCdShift = 0;
const int kCdBits = 3;
const int kCjShift = 5;
const int kCjBits = 3;
const int kCodeShift = 0;
const int kCodeBits = 15;
const int kCondShift = 15;
const int kCondBits = 5;
const int kUi5Shift = 10;
const int kUi5Bits = 5;
const int kUi6Shift = 10;
const int kUi6Bits = 6;
const int kUi12Shift = 10;
const int kUi12Bits = 12;
const int kSi12Shift = 10;
const int kSi12Bits = 12;
const int kSi14Shift = 10;
const int kSi14Bits = 14;
const int kSi16Shift = 10;
const int kSi16Bits = 16;
const int kSi20Shift = 5;
const int kSi20Bits = 20;
const int kMsbwShift = 16;
const int kMsbwBits = 5;
const int kLsbwShift = 10;
const int kLsbwBits = 5;
const int kMsbdShift = 16;
const int kMsbdBits = 6;
const int kLsbdShift = 10;
const int kLsbdBits = 6;
const int kFdShift = 0;
const int kFdBits = 5;
const int kFjShift = 5;
const int kFjBits = 5;
const int kFkShift = 10;
const int kFkBits = 5;
const int kFaShift = 15;
const int kFaBits = 5;
const int kCaShift = 15;
const int kCaBits = 3;
const int kHint15Shift = 0;
const int kHint15Bits = 15;
const int kHint5Shift = 0;
const int kHint5Bits = 5;
const int kOffsLowShift = 10;
const int kOffsLowBits = 16;
const int kOffs26HighShift = 0;
const int kOffs26HighBits = 10;
const int kOffs21HighShift = 0;
const int kOffs21HighBits = 5;
const int kImm12Shift = 0;
const int kImm12Bits = 12;
const int kImm16Shift = 0;
const int kImm16Bits = 16;
const int kImm26Shift = 0;
const int kImm26Bits = 26;
const int kImm28Shift = 0;
const int kImm28Bits = 28;
const int kImm32Shift = 0;
const int kImm32Bits = 32;

// ----- Miscellaneous useful masks.
// Instruction bit masks.
const int kRjFieldMask = ((1 << kRjBits) - 1) << kRjShift;
const int kRkFieldMask = ((1 << kRkBits) - 1) << kRkShift;
const int kRdFieldMask = ((1 << kRdBits) - 1) << kRdShift;
const int kSa2FieldMask = ((1 << kSa2Bits) - 1) << kSaShift;
const int kSa3FieldMask = ((1 << kSa3Bits) - 1) << kSaShift;
// Misc masks.
const int kHiMaskOf32 = 0xffff << 16;  // Only to be used with 32-bit values
const int kLoMaskOf32 = 0xffff;
const int kSignMaskOf32 = 0x80000000;  // Only to be used with 32-bit values
const int64_t kTop16MaskOf64 = (int64_t)0xffff << 48;
const int64_t kHigher16MaskOf64 = (int64_t)0xffff << 32;
const int64_t kUpper16MaskOf64 = (int64_t)0xffff << 16;

const int kImm12Mask = ((1 << kImm12Bits) - 1) << kImm12Shift;
const int kImm16Mask = ((1 << kImm16Bits) - 1) << kImm16Shift;
const int kImm26Mask = ((1 << kImm26Bits) - 1) << kImm26Shift;
const int kImm28Mask = ((1 << kImm28Bits) - 1) << kImm28Shift;

// ----- LOONG64 Opcodes and Function Fields.
enum Opcode : uint32_t {
  BEQZ = 0x10U << 26,
  BNEZ = 0x11U << 26,
  BCZ = 0x12U << 26,  // BCEQZ & BCNEZ
  JIRL = 0x13U << 26,
  B = 0x14U << 26,
  BL = 0x15U << 26,
  BEQ = 0x16U << 26,
  BNE = 0x17U << 26,
  BLT = 0x18U << 26,
  BGE = 0x19U << 26,
  BLTU = 0x1aU << 26,
  BGEU = 0x1bU << 26,

  ADDU16I_D = 0x4U << 26,

  LU12I_W = 0xaU << 25,
  LU32I_D = 0xbU << 25,
  PCADDI = 0xcU << 25,
  PCALAU12I = 0xdU << 25,
  PCADDU12I = 0xeU << 25,
  PCADDU18I = 0xfU << 25,

  LL_W = 0x20U << 24,
  SC_W = 0x21U << 24,
  LL_D = 0x22U << 24,
  SC_D = 0x23U << 24,
  LDPTR_W = 0x24U << 24,
  STPTR_W = 0x25U << 24,
  LDPTR_D = 0x26U << 24,
  STPTR_D = 0x27U << 24,

  BSTR_W = 0x1U << 22,  // BSTRINS_W & BSTRPICK_W
  BSTRINS_W = BSTR_W,
  BSTRPICK_W = BSTR_W,
  BSTRINS_D = 0x2U << 22,
  BSTRPICK_D = 0x3U << 22,

  SLTI = 0x8U << 22,
  SLTUI = 0x9U << 22,
  ADDI_W = 0xaU << 22,
  ADDI_D = 0xbU << 22,
  LU52I_D = 0xcU << 22,
  ANDI = 0xdU << 22,
  ORI = 0xeU << 22,
  XORI = 0xfU << 22,

  LD_B = 0xa0U << 22,
  LD_H = 0xa1U << 22,
  LD_W = 0xa2U << 22,
  LD_D = 0xa3U << 22,
  ST_B = 0xa4U << 22,
  ST_H = 0xa5U << 22,
  ST_W = 0xa6U << 22,
  ST_D = 0xa7U << 22,
  LD_BU = 0xa8U << 22,
  LD_HU = 0xa9U << 22,
  LD_WU = 0xaaU << 22,
  FLD_S = 0xacU << 22,
  FST_S = 0xadU << 22,
  FLD_D = 0xaeU << 22,
  FST_D = 0xafU << 22,

  FMADD_S = 0x81U << 20,
  FMADD_D = 0x82U << 20,
  FMSUB_S = 0x85U << 20,
  FMSUB_D = 0x86U << 20,
  FNMADD_S = 0x89U << 20,
  FNMADD_D = 0x8aU << 20,
  FNMSUB_S = 0x8dU << 20,
  FNMSUB_D = 0x8eU << 20,
  FCMP_COND_S = 0xc1U << 20,
  FCMP_COND_D = 0xc2U << 20,

  BYTEPICK_D = 0x3U << 18,
  BYTEPICK_W = 0x2U << 18,

  FSEL = 0x340U << 18,

  ALSL = 0x1U << 18,
  ALSL_W = ALSL,
  ALSL_WU = ALSL,

  ALSL_D = 0xbU << 18,

  SLLI_W = 0x40U << 16,
  SRLI_W = 0x44U << 16,
  SRAI_W = 0x48U << 16,
  ROTRI_W = 0x4cU << 16,

  SLLI_D = 0x41U << 16,
  SRLI_D = 0x45U << 16,
  SRAI_D = 0x49U << 16,
  ROTRI_D = 0x4dU << 16,

  SLLI = 0x10U << 18,
  SRLI = 0x11U << 18,
  SRAI = 0x12U << 18,
  ROTRI = 0x13U << 18,

  ADD_W = 0x20U << 15,
  ADD_D = 0x21U << 15,
  SUB_W = 0x22U << 15,
  SUB_D = 0x23U << 15,
  SLT = 0x24U << 15,
  SLTU = 0x25U << 15,
  MASKNEZ = 0x26U << 15,
  MASKEQZ = 0x27U << 15,
  NOR = 0x28U << 15,
  AND = 0x29U << 15,
  OR = 0x2aU << 15,
  XOR = 0x2bU << 15,
  ORN = 0x2cU << 15,
  ANDN = 0x2dU << 15,
  SLL_W = 0x2eU << 15,
  SRL_W = 0x2fU << 15,
  SRA_W = 0x30U << 15,
  SLL_D = 0x31U << 15,
  SRL_D = 0x32U << 15,
  SRA_D = 0x33U << 15,
  ROTR_W = 0x36U << 15,
  ROTR_D = 0x37U << 15,
  MUL_W = 0x38U << 15,
  MULH_W = 0x39U << 15,
  MULH_WU = 0x3aU << 15,
  MUL_D = 0x3bU << 15,
  MULH_D = 0x3cU << 15,
  MULH_DU = 0x3dU << 15,
  MULW_D_W = 0x3eU << 15,
  MULW_D_WU = 0x3fU << 15,

  DIV_W = 0x40U << 15,
  MOD_W = 0x41U << 15,
  DIV_WU = 0x42U << 15,
  MOD_WU = 0x43U << 15,
  DIV_D = 0x44U << 15,
  MOD_D = 0x45U << 15,
  DIV_DU = 0x46U << 15,
  MOD_DU = 0x47U << 15,

  BREAK = 0x54U << 15,

  FADD_S = 0x201U << 15,
  FADD_D = 0x202U << 15,
  FSUB_S = 0x205U << 15,
  FSUB_D = 0x206U << 15,
  FMUL_S = 0x209U << 15,
  FMUL_D = 0x20aU << 15,
  FDIV_S = 0x20dU << 15,
  FDIV_D = 0x20eU << 15,
  FMAX_S = 0x211U << 15,
  FMAX_D = 0x212U << 15,
  FMIN_S = 0x215U << 15,
  FMIN_D = 0x216U << 15,
  FMAXA_S = 0x219U << 15,
  FMAXA_D = 0x21aU << 15,
  FMINA_S = 0x21dU << 15,
  FMINA_D = 0x21eU << 15,
  FSCALEB_S = 0x221U << 15,
  FSCALEB_D = 0x222U << 15,
  FCOPYSIGN_S = 0x225U << 15,
  FCOPYSIGN_D = 0x226U << 15,

  LDX_B = 0x7000U << 15,
  LDX_H = 0x7008U << 15,
  LDX_W = 0x7010U << 15,
  LDX_D = 0x7018U << 15,
  STX_B = 0x7020U << 15,
  STX_H = 0x7028U << 15,
  STX_W = 0x7030U << 15,
  STX_D = 0x7038U << 15,
  LDX_BU = 0x7040U << 15,
  LDX_HU = 0x7048U << 15,
  LDX_WU = 0x7050U << 15,
  FLDX_S = 0x7060U << 15,
  FLDX_D = 0x7068U << 15,
  FSTX_S = 0x7070U << 15,
  FSTX_D = 0x7078U << 15,

  AMSWAP_W = 0x70c0U << 15,
  AMSWAP_D = 0x70c1U << 15,
  AMADD_W = 0x70c2U << 15,
  AMADD_D = 0x70c3U << 15,
  AMAND_W = 0x70c4U << 15,
  AMAND_D = 0x70c5U << 15,
  AMOR_W = 0x70c6U << 15,
  AMOR_D = 0x70c7U << 15,
  AMXOR_W = 0x70c8U << 15,
  AMXOR_D = 0x70c9U << 15,
  AMMAX_W = 0x70caU << 15,
  AMMAX_D = 0x70cbU << 15,
  AMMIN_W = 0x70ccU << 15,
  AMMIN_D = 0x70cdU << 15,
  AMMAX_WU = 0x70ceU << 15,
  AMMAX_DU = 0x70cfU << 15,
  AMMIN_WU = 0x70d0U << 15,
  AMMIN_DU = 0x70d1U << 15,
  AMSWAP_DB_W = 0x70d2U << 15,
  AMSWAP_DB_D = 0x70d3U << 15,
  AMADD_DB_W = 0x70d4U << 15,
  AMADD_DB_D = 0x70d5U << 15,
  AMAND_DB_W = 0x70d6U << 15,
  AMAND_DB_D = 0x70d7U << 15,
  AMOR_DB_W = 0x70d8U << 15,
  AMOR_DB_D = 0x70d9U << 15,
  AMXOR_DB_W = 0x70daU << 15,
  AMXOR_DB_D = 0x70dbU << 15,
  AMMAX_DB_W = 0x70dcU << 15,
  AMMAX_DB_D = 0x70ddU << 15,
  AMMIN_DB_W = 0x70deU << 15,
  AMMIN_DB_D = 0x70dfU << 15,
  AMMAX_DB_WU = 0x70e0U << 15,
  AMMAX_DB_DU = 0x70e1U << 15,
  AMMIN_DB_WU = 0x70e2U << 15,
  AMMIN_DB_DU = 0x70e3U << 15,

  DBAR = 0x70e4U << 15,
  IBAR = 0x70e5U << 15,

  CLO_W = 0X4U << 10,
  CLZ_W = 0X5U << 10,
  CTO_W = 0X6U << 10,
  CTZ_W = 0X7U << 10,
  CLO_D = 0X8U << 10,
  CLZ_D = 0X9U << 10,
  CTO_D = 0XaU << 10,
  CTZ_D = 0XbU << 10,
  REVB_2H = 0XcU << 10,
  REVB_4H = 0XdU << 10,
  REVB_2W = 0XeU << 10,
  REVB_D = 0XfU << 10,
  REVH_2W = 0X10U << 10,
  REVH_D = 0X11U << 10,
  BITREV_4B = 0X12U << 10,
  BITREV_8B = 0X13U << 10,
  BITREV_W = 0X14U << 10,
  BITREV_D = 0X15U << 10,
  EXT_W_H = 0X16U << 10,
  EXT_W_B = 0X17U << 10,

  FABS_S = 0X4501U << 10,
  FABS_D = 0X4502U << 10,
  FNEG_S = 0X4505U << 10,
  FNEG_D = 0X4506U << 10,
  FLOGB_S = 0X4509U << 10,
  FLOGB_D = 0X450aU << 10,
  FCLASS_S = 0X450dU << 10,
  FCLASS_D = 0X450eU << 10,
  FSQRT_S = 0X4511U << 10,
  FSQRT_D = 0X4512U << 10,
  FRECIP_S = 0X4515U << 10,
  FRECIP_D = 0X4516U << 10,
  FRSQRT_S = 0X4519U << 10,
  FRSQRT_D = 0X451aU << 10,
  FMOV_S = 0X4525U << 10,
  FMOV_D = 0X4526U << 10,
  MOVGR2FR_W = 0X4529U << 10,
  MOVGR2FR_D = 0X452aU << 10,
  MOVGR2FRH_W = 0X452bU << 10,
  MOVFR2GR_S = 0X452dU << 10,
  MOVFR2GR_D = 0X452eU << 10,
  MOVFRH2GR_S = 0X452fU << 10,
  MOVGR2FCSR = 0X4530U << 10,
  MOVFCSR2GR = 0X4532U << 10,
  MOVFR2CF = 0X4534U << 10,
  MOVGR2CF = 0X4536U << 10,

  FCVT_S_D = 0x4646U << 10,
  FCVT_D_S = 0x4649U << 10,
  FTINTRM_W_S = 0x4681U << 10,
  FTINTRM_W_D = 0x4682U << 10,
  FTINTRM_L_S = 0x4689U << 10,
  FTINTRM_L_D = 0x468aU << 10,
  FTINTRP_W_S = 0x4691U << 10,
  FTINTRP_W_D = 0x4692U << 10,
  FTINTRP_L_S = 0x4699U << 10,
  FTINTRP_L_D = 0x469aU << 10,
  FTINTRZ_W_S = 0x46a1U << 10,
  FTINTRZ_W_D = 0x46a2U << 10,
  FTINTRZ_L_S = 0x46a9U << 10,
  FTINTRZ_L_D = 0x46aaU << 10,
  FTINTRNE_W_S = 0x46b1U << 10,
  FTINTRNE_W_D = 0x46b2U << 10,
  FTINTRNE_L_S = 0x46b9U << 10,
  FTINTRNE_L_D = 0x46baU << 10,
  FTINT_W_S = 0x46c1U << 10,
  FTINT_W_D = 0x46c2U << 10,
  FTINT_L_S = 0x46c9U << 10,
  FTINT_L_D = 0x46caU << 10,
  FFINT_S_W = 0x4744U << 10,
  FFINT_S_L = 0x4746U << 10,
  FFINT_D_W = 0x4748U << 10,
  FFINT_D_L = 0x474aU << 10,
  FRINT_S = 0x4791U << 10,
  FRINT_D = 0x4792U << 10,

  MOVCF2FR = 0x4535U << 10,
  MOVCF2GR = 0x4537U << 10
};

// ----- Emulated conditions.
// On LOONG64 we use this enum to abstract from conditional branch instructions.
// The 'U' prefix is used to specify unsigned comparisons.
enum Condition {
  // Any value < 0 is considered no_condition.
  kNoCondition = -1,
  overflow = 0,
  no_overflow = 1,
  Uless = 2,
  Ugreater_equal = 3,
  Uless_equal = 4,
  Ugreater = 5,
  equal = 6,
  not_equal = 7,  // Unordered or Not Equal.
  negative = 8,
  positive = 9,
  parity_even = 10,
  parity_odd = 11,
  less = 12,
  greater_equal = 13,
  less_equal = 14,
  greater = 15,
  ueq = 16,  // Unordered or Equal.
  ogl = 17,  // Ordered and Not Equal.
  cc_always = 18,

  // Aliases.
  carry = Uless,
  not_carry = Ugreater_equal,
  zero = equal,
  eq = equal,
  not_zero = not_equal,
  ne = not_equal,
  nz = not_equal,
  sign = negative,
  not_sign = positive,
  mi = negative,
  pl = positive,
  hi = Ugreater,
  ls = Uless_equal,
  ge = greater_equal,
  lt = less,
  gt = greater,
  le = less_equal,
  hs = Ugreater_equal,
  lo = Uless,
  al = cc_always,
  ult = Uless,
  uge = Ugreater_equal,
  ule = Uless_equal,
  ugt = Ugreater,
  cc_default = kNoCondition
};

// Returns the equivalent of !cc.
// Negation of the default kNoCondition (-1) results in a non-default
// no_condition value (-2). As long as tests for no_condition check
// for condition < 0, this will work as expected.
inline Condition NegateCondition(Condition cc) {
  DCHECK(cc != cc_always);
  return static_cast<Condition>(cc ^ 1);
}

inline Condition NegateFpuCondition(Condition cc) {
  DCHECK(cc != cc_always);
  switch (cc) {
    case ult:
      return ge;
    case ugt:
      return le;
    case uge:
      return lt;
    case ule:
      return gt;
    case lt:
      return uge;
    case gt:
      return ule;
    case ge:
      return ult;
    case le:
      return ugt;
    case eq:
      return ne;
    case ne:
      return eq;
    case ueq:
      return ogl;
    case ogl:
      return ueq;
    default:
      return cc;
  }
}

// ----- Coprocessor conditions.
enum FPUCondition {
  kNoFPUCondition = -1,

  CAF = 0x00,  // False.
  SAF = 0x01,  // False.
  CLT = 0x02,  // Less Than quiet
               //  SLT  = 0x03,    // Less Than signaling
  CEQ = 0x04,
  SEQ = 0x05,
  CLE = 0x06,
  SLE = 0x07,
  CUN = 0x08,
  SUN = 0x09,
  CULT = 0x0a,
  SULT = 0x0b,
  CUEQ = 0x0c,
  SUEQ = 0x0d,
  CULE = 0x0e,
  SULE = 0x0f,
  CNE = 0x10,
  SNE = 0x11,
  COR = 0x14,
  SOR = 0x15,
  CUNE = 0x18,
  SUNE = 0x19,
};

const uint32_t kFPURoundingModeShift = 8;
const uint32_t kFPURoundingModeMask = 0b11 << kFPURoundingModeShift;

// FPU rounding modes.
enum FPURoundingMode {
  RN = 0b00 << kFPURoundingModeShift,  // Round to Nearest.
  RZ = 0b01 << kFPURoundingModeShift,  // Round towards zero.
  RP = 0b10 << kFPURoundingModeShift,  // Round towards Plus Infinity.
  RM = 0b11 << kFPURoundingModeShift,  // Round towards Minus Infinity.

  // Aliases.
  kRoundToNearest = RN,
  kRoundToZero = RZ,
  kRoundToPlusInf = RP,
  kRoundToMinusInf = RM,

  mode_round = RN,
  mode_ceil = RP,
  mode_floor = RM,
  mode_trunc = RZ
};

enum CheckForInexactConversion {
  kCheckForInexactConversion,
  kDontCheckForInexactConversion
};

enum class MaxMinKind : int { kMin = 0, kMax = 1 };

// -----------------------------------------------------------------------------
// Hints.

// Branch hints are not used on the LOONG64.  They are defined so that they can
// appear in shared function signatures, but will be ignored in LOONG64
// implementations.
enum Hint { no_hint = 0 };

inline Hint NegateHint(Hint hint) { return no_hint; }

// -----------------------------------------------------------------------------
// Specific instructions, constants, and masks.
// These constants are declared in assembler-loong64.cc, as they use named
// registers and other constants.

// Break 0xfffff, reserved for redirected real time call.
const Instr rtCallRedirInstr = BREAK | call_rt_redirected;
// A nop instruction. (Encoding of addi_w 0 0 0).
const Instr nopInstr = ADDI_W;

constexpr uint8_t kInstrSize = 4;
constexpr uint8_t kInstrSizeLog2 = 2;

class InstructionBase {
 public:
  enum Type {
    kOp6Type,
    kOp7Type,
    kOp8Type,
    kOp10Type,
    kOp12Type,
    kOp14Type,
    kOp17Type,
    kOp22Type,
    kUnsupported = -1
  };

  // Get the raw instruction bits.
  inline Instr InstructionBits() const {
    return *reinterpret_cast<const Instr*>(this);
  }

  // Set the raw instruction bits to value.
  inline void SetInstructionBits(Instr value) {
    *reinterpret_cast<Instr*>(this) = value;
  }

  // Read one particular bit out of the instruction bits.
  inline int Bit(int nr) const { return (InstructionBits() >> nr) & 1; }

  // Read a bit field out of the instruction bits.
  inline int Bits(int hi, int lo) const {
    return (InstructionBits() >> lo) & ((2U << (hi - lo)) - 1);
  }

  // Safe to call within InstructionType().
  inline int RjFieldRawNoAssert() const {
    return InstructionBits() & kRjFieldMask;
  }

  // Get the encoding type of the instruction.
  inline Type InstructionType() const;

 protected:
  InstructionBase() {}
};

template <class T>
class InstructionGetters : public T {
 public:
  inline int RjValue() const {
    return this->Bits(kRjShift + kRjBits - 1, kRjShift);
  }

  inline int RkValue() const {
    return this->Bits(kRkShift + kRkBits - 1, kRkShift);
  }

  inline int RdValue() const {
    return this->Bits(kRdShift + kRdBits - 1, kRdShift);
  }

  inline int Sa2Value() const {
    return this->Bits(kSaShift + kSa2Bits - 1, kSaShift);
  }

  inline int Sa3Value() const {
    return this->Bits(kSaShift + kSa3Bits - 1, kSaShift);
  }

  inline int Ui5Value() const {
    return this->Bits(kUi5Shift + kUi5Bits - 1, kUi5Shift);
  }

  inline int Ui6Value() const {
    return this->Bits(kUi6Shift + kUi6Bits - 1, kUi6Shift);
  }

  inline int Ui12Value() const {
    return this->Bits(kUi12Shift + kUi12Bits - 1, kUi12Shift);
  }

  inline int LsbwValue() const {
    return this->Bits(kLsbwShift + kLsbwBits - 1, kLsbwShift);
  }

  inline int MsbwValue() const {
    return this->Bits(kMsbwShift + kMsbwBits - 1, kMsbwShift);
  }

  inline int LsbdValue() const {
    return this->Bits(kLsbdShift + kLsbdBits - 1, kLsbdShift);
  }

  inline int MsbdValue() const {
    return this->Bits(kMsbdShift + kMsbdBits - 1, kMsbdShift);
  }

  inline int CondValue() const {
    return this->Bits(kCondShift + kCondBits - 1, kCondShift);
  }

  inline int Si12Value() const {
    return this->Bits(kSi12Shift + kSi12Bits - 1, kSi12Shift);
  }

  inline int Si14Value() const {
    return this->Bits(kSi14Shift + kSi14Bits - 1, kSi14Shift);
  }

  inline int Si16Value() const {
    return this->Bits(kSi16Shift + kSi16Bits - 1, kSi16Shift);
  }

  inline int Si20Value() const {
    return this->Bits(kSi20Shift + kSi20Bits - 1, kSi20Shift);
  }

  inline int FdValue() const {
    return this->Bits(kFdShift + kFdBits - 1, kFdShift);
  }

  inline int FaValue() const {
    return this->Bits(kFaShift + kFaBits - 1, kFaShift);
  }

  inline int FjValue() const {
    return this->Bits(kFjShift + kFjBits - 1, kFjShift);
  }

  inline int FkValue() const {
    return this->Bits(kFkShift + kFkBits - 1, kFkShift);
  }

  inline int CjValue() const {
    return this->Bits(kCjShift + kCjBits - 1, kCjShift);
  }

  inline int CdValue() const {
    return this->Bits(kCdShift + kCdBits - 1, kCdShift);
  }

  inline int CaValue() const {
    return this->Bits(kCaShift + kCaBits - 1, kCaShift);
  }

  inline int CodeValue() const {
    return this->Bits(kCodeShift + kCodeBits - 1, kCodeShift);
  }

  inline int Hint5Value() const {
    return this->Bits(kHint5Shift + kHint5Bits - 1, kHint5Shift);
  }

  inline int Hint15Value() const {
    return this->Bits(kHint15Shift + kHint15Bits - 1, kHint15Shift);
  }

  inline int Offs16Value() const {
    return this->Bits(kOffsLowShift + kOffsLowBits - 1, kOffsLowShift);
  }

  inline int Offs21Value() const {
    int low = this->Bits(kOffsLowShift + kOffsLowBits - 1, kOffsLowShift);
    int high =
        this->Bits(kOffs21HighShift + kOffs21HighBits - 1, kOffs21HighShift);
    return ((high << kOffsLowBits) + low);
  }

  inline int Offs26Value() const {
    int low = this->Bits(kOffsLowShift + kOffsLowBits - 1, kOffsLowShift);
    int high =
        this->Bits(kOffs26HighShift + kOffs26HighBits - 1, kOffs26HighShift);
    return ((high << kOffsLowBits) + low);
  }

  inline int RjFieldRaw() const {
    return this->InstructionBits() & kRjFieldMask;
  }

  inline int RkFieldRaw() const {
    return this->InstructionBits() & kRkFieldMask;
  }

  inline int RdFieldRaw() const {
    return this->InstructionBits() & kRdFieldMask;
  }

  inline int32_t ImmValue(int bits) const { return this->Bits(bits - 1, 0); }

  /*TODO*/
  inline int32_t Imm12Value() const { abort(); }

  inline int32_t Imm14Value() const { abort(); }

  inline int32_t Imm16Value() const { abort(); }

  // Say if the instruction is a break.
  bool IsTrap() const;
};

class Instruction : public InstructionGetters<InstructionBase> {
 public:
  // Instructions are read of out a code stream. The only way to get a
  // reference to an instruction is to convert a pointer. There is no way
  // to allocate or create instances of class Instruction.
  // Use the At(pc) function to create references to Instruction.
  static Instruction* At(byte* pc) {
    return reinterpret_cast<Instruction*>(pc);
  }

 private:
  // We need to prevent the creation of instances of class Instruction.
  DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction);
};

// -----------------------------------------------------------------------------
// LOONG64 assembly various constants.

const int kInvalidStackOffset = -1;

static const int kNegOffset = 0x00008000;

InstructionBase::Type InstructionBase::InstructionType() const {
  InstructionBase::Type kType = kUnsupported;

  // Check for kOp6Type
  switch (Bits(31, 26) << 26) {
    case ADDU16I_D:
    case BEQZ:
    case BNEZ:
    case BCZ:
    case JIRL:
    case B:
    case BL:
    case BEQ:
    case BNE:
    case BLT:
    case BGE:
    case BLTU:
    case BGEU:
      kType = kOp6Type;
      break;
    default:
      kType = kUnsupported;
  }

  if (kType == kUnsupported) {
    // Check for kOp7Type
    switch (Bits(31, 25) << 25) {
      case LU12I_W:
      case LU32I_D:
      case PCADDI:
      case PCALAU12I:
      case PCADDU12I:
      case PCADDU18I:
        kType = kOp7Type;
        break;
      default:
        kType = kUnsupported;
    }
  }

  if (kType == kUnsupported) {
    // Check for kOp8Type
    switch (Bits(31, 24) << 24) {
      case LDPTR_W:
      case STPTR_W:
      case LDPTR_D:
      case STPTR_D:
      case LL_W:
      case SC_W:
      case LL_D:
      case SC_D:
        kType = kOp8Type;
        break;
      default:
        kType = kUnsupported;
    }
  }

  if (kType == kUnsupported) {
    // Check for kOp10Type
    switch (Bits(31, 22) << 22) {
      case BSTR_W: {
        // If Bit(21) = 0, then the Opcode is not BSTR_W.
        if (Bit(21) == 0)
          kType = kUnsupported;
        else
          kType = kOp10Type;
        break;
      }
      case BSTRINS_D:
      case BSTRPICK_D:
      case SLTI:
      case SLTUI:
      case ADDI_W:
      case ADDI_D:
      case LU52I_D:
      case ANDI:
      case ORI:
      case XORI:
      case LD_B:
      case LD_H:
      case LD_W:
      case LD_D:
      case ST_B:
      case ST_H:
      case ST_W:
      case ST_D:
      case LD_BU:
      case LD_HU:
      case LD_WU:
      case FLD_S:
      case FST_S:
      case FLD_D:
      case FST_D:
        kType = kOp10Type;
        break;
      default:
        kType = kUnsupported;
    }
  }

  if (kType == kUnsupported) {
    // Check for kOp12Type
    switch (Bits(31, 20) << 20) {
      case FMADD_S:
      case FMADD_D:
      case FMSUB_S:
      case FMSUB_D:
      case FNMADD_S:
      case FNMADD_D:
      case FNMSUB_S:
      case FNMSUB_D:
      case FCMP_COND_S:
      case FCMP_COND_D:
      case FSEL:
        kType = kOp12Type;
        break;
      default:
        kType = kUnsupported;
    }
  }

  if (kType == kUnsupported) {
    // Check for kOp14Type
    switch (Bits(31, 18) << 18) {
      case ALSL:
      case BYTEPICK_W:
      case BYTEPICK_D:
      case ALSL_D:
      case SLLI:
      case SRLI:
      case SRAI:
      case ROTRI:
        kType = kOp14Type;
        break;
      default:
        kType = kUnsupported;
    }
  }

  if (kType == kUnsupported) {
    // Check for kOp17Type
    switch (Bits(31, 15) << 15) {
      case ADD_W:
      case ADD_D:
      case SUB_W:
      case SUB_D:
      case SLT:
      case SLTU:
      case MASKEQZ:
      case MASKNEZ:
      case NOR:
      case AND:
      case OR:
      case XOR:
      case ORN:
      case ANDN:
      case SLL_W:
      case SRL_W:
      case SRA_W:
      case SLL_D:
      case SRL_D:
      case SRA_D:
      case ROTR_D:
      case ROTR_W:
      case MUL_W:
      case MULH_W:
      case MULH_WU:
      case MUL_D:
      case MULH_D:
      case MULH_DU:
      case MULW_D_W:
      case MULW_D_WU:
      case DIV_W:
      case MOD_W:
      case DIV_WU:
      case MOD_WU:
      case DIV_D:
      case MOD_D:
      case DIV_DU:
      case MOD_DU:
      case BREAK:
      case FADD_S:
      case FADD_D:
      case FSUB_S:
      case FSUB_D:
      case FMUL_S:
      case FMUL_D:
      case FDIV_S:
      case FDIV_D:
      case FMAX_S:
      case FMAX_D:
      case FMIN_S:
      case FMIN_D:
      case FMAXA_S:
      case FMAXA_D:
      case FMINA_S:
      case FMINA_D:
      case LDX_B:
      case LDX_H:
      case LDX_W:
      case LDX_D:
      case STX_B:
      case STX_H:
      case STX_W:
      case STX_D:
      case LDX_BU:
      case LDX_HU:
      case LDX_WU:
      case FLDX_S:
      case FLDX_D:
      case FSTX_S:
      case FSTX_D:
      case AMSWAP_W:
      case AMSWAP_D:
      case AMADD_W:
      case AMADD_D:
      case AMAND_W:
      case AMAND_D:
      case AMOR_W:
      case AMOR_D:
      case AMXOR_W:
      case AMXOR_D:
      case AMMAX_W:
      case AMMAX_D:
      case AMMIN_W:
      case AMMIN_D:
      case AMMAX_WU:
      case AMMAX_DU:
      case AMMIN_WU:
      case AMMIN_DU:
      case AMSWAP_DB_W:
      case AMSWAP_DB_D:
      case AMADD_DB_W:
      case AMADD_DB_D:
      case AMAND_DB_W:
      case AMAND_DB_D:
      case AMOR_DB_W:
      case AMOR_DB_D:
      case AMXOR_DB_W:
      case AMXOR_DB_D:
      case AMMAX_DB_W:
      case AMMAX_DB_D:
      case AMMIN_DB_W:
      case AMMIN_DB_D:
      case AMMAX_DB_WU:
      case AMMAX_DB_DU:
      case AMMIN_DB_WU:
      case AMMIN_DB_DU:
      case DBAR:
      case IBAR:
      case FSCALEB_S:
      case FSCALEB_D:
      case FCOPYSIGN_S:
      case FCOPYSIGN_D:
        kType = kOp17Type;
        break;
      default:
        kType = kUnsupported;
    }
  }

  if (kType == kUnsupported) {
    // Check for kOp22Type
    switch (Bits(31, 10) << 10) {
      case CLZ_W:
      case CTZ_W:
      case CLZ_D:
      case CTZ_D:
      case REVB_2H:
      case REVB_4H:
      case REVB_2W:
      case REVB_D:
      case REVH_2W:
      case REVH_D:
      case BITREV_4B:
      case BITREV_8B:
      case BITREV_W:
      case BITREV_D:
      case EXT_W_B:
      case EXT_W_H:
      case FABS_S:
      case FABS_D:
      case FNEG_S:
      case FNEG_D:
      case FSQRT_S:
      case FSQRT_D:
      case FMOV_S:
      case FMOV_D:
      case MOVGR2FR_W:
      case MOVGR2FR_D:
      case MOVGR2FRH_W:
      case MOVFR2GR_S:
      case MOVFR2GR_D:
      case MOVFRH2GR_S:
      case MOVGR2FCSR:
      case MOVFCSR2GR:
      case FCVT_S_D:
      case FCVT_D_S:
      case FTINTRM_W_S:
      case FTINTRM_W_D:
      case FTINTRM_L_S:
      case FTINTRM_L_D:
      case FTINTRP_W_S:
      case FTINTRP_W_D:
      case FTINTRP_L_S:
      case FTINTRP_L_D:
      case FTINTRZ_W_S:
      case FTINTRZ_W_D:
      case FTINTRZ_L_S:
      case FTINTRZ_L_D:
      case FTINTRNE_W_S:
      case FTINTRNE_W_D:
      case FTINTRNE_L_S:
      case FTINTRNE_L_D:
      case FTINT_W_S:
      case FTINT_W_D:
      case FTINT_L_S:
      case FTINT_L_D:
      case FFINT_S_W:
      case FFINT_S_L:
      case FFINT_D_W:
      case FFINT_D_L:
      case FRINT_S:
      case FRINT_D:
      case MOVFR2CF:
      case MOVCF2FR:
      case MOVGR2CF:
      case MOVCF2GR:
      case FRECIP_S:
      case FRECIP_D:
      case FRSQRT_S:
      case FRSQRT_D:
      case FCLASS_S:
      case FCLASS_D:
      case FLOGB_S:
      case FLOGB_D:
      case CLO_W:
      case CTO_W:
      case CLO_D:
      case CTO_D:
        kType = kOp22Type;
        break;
      default:
        kType = kUnsupported;
    }
  }

  return kType;
}

// -----------------------------------------------------------------------------
// Instructions.

template <class P>
bool InstructionGetters<P>::IsTrap() const {
  return true;
}

}  // namespace internal
}  // namespace v8

#endif  // V8_CODEGEN_LOONG64_CONSTANTS_LOONG64_H_
