// 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_RISCV64_REGISTER_RISCV64_H_
#define V8_CODEGEN_RISCV64_REGISTER_RISCV64_H_

#include "src/codegen/register-base.h"
#include "src/codegen/reglist.h"
#include "src/codegen/riscv64/constants-riscv64.h"

namespace v8 {
namespace internal {

// clang-format off

#define GENERAL_REGISTERS(V)                                            \
  V(zero_reg)  V(ra)  V(sp)  V(gp)  V(tp)  V(t0)  V(t1)  V(t2)          \
  V(fp)  V(s1)  V(a0)  V(a1)  V(a2)  V(a3)  V(a4)  V(a5)                \
  V(a6)  V(a7)  V(s2)  V(s3)  V(s4)  V(s5)  V(s6)  V(s7)  V(s8)  V(s9)  \
  V(s10)  V(s11)  V(t3)  V(t4)  V(t5)  V(t6)

// s3: scratch register s4: scratch register 2  used in code-generator-riscv64
// s6: roots in Javascript code s7: context register
// s11: PtrComprCageBaseRegister
// t3 t5 : scratch register used in scratch_register_list
// t6 : call reg.
// t0 t1 t2 t4:caller saved scratch register can be used in macroassembler and
// builtin-riscv64
#define ALWAYS_ALLOCATABLE_GENERAL_REGISTERS(V)  \
             V(a0)  V(a1)  V(a2)  V(a3) \
             V(a4)  V(a5)  V(a6)  V(a7)  V(t0)  \
             V(t1)  V(t2)  V(t4)  V(s7)  V(s8) V(s9) V(s10)

#ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE
#define MAYBE_ALLOCATABLE_GENERAL_REGISTERS(V)
#else
#define MAYBE_ALLOCATABLE_GENERAL_REGISTERS(V) V(s11)
#endif

#define ALLOCATABLE_GENERAL_REGISTERS(V)  \
  ALWAYS_ALLOCATABLE_GENERAL_REGISTERS(V) \
  MAYBE_ALLOCATABLE_GENERAL_REGISTERS(V)

#define DOUBLE_REGISTERS(V)                                       \
  V(ft0)  V(ft1)  V(ft2)  V(ft3)  V(ft4)  V(ft5)  V(ft6)  V(ft7)  \
  V(fs0)  V(fs1)  V(fa0) V(fa1) V(fa2) V(fa3) V(fa4) V(fa5)       \
  V(fa6) V(fa7) V(fs2) V(fs3) V(fs4) V(fs5) V(fs6) V(fs7)         \
  V(fs8) V(fs9) V(fs10) V(fs11) V(ft8) V(ft9) V(ft10) V(ft11)

#define FLOAT_REGISTERS DOUBLE_REGISTERS
#define VECTOR_REGISTERS(V)                               \
  V(v0)  V(v1)  V(v2)  V(v3)  V(v4)  V(v5)  V(v6)  V(v7)  \
  V(v8)  V(v9)  V(v10) V(v11) V(v12) V(v13) V(v14) V(v15) \
  V(v16) V(v17) V(v18) V(v19) V(v20) V(v21) V(v22) V(v23) \
  V(v24) V(v25) V(v26) V(v27) V(v28) V(v29) V(v30) V(v31)

#define UNALLOACTABLE_VECTOR_REGISTERS(V)                 \
  V(v9)  V(v10) V(v11) V(v12) V(v13) V(v14) V(v15)        \
  V(v18) V(v19) V(v20) V(v21) V(v22) V(v23)               \
  V(v24) V(v25)

#define ALLOCATABLE_DOUBLE_REGISTERS(V)                              \
  V(ft1)  V(ft2) V(ft3) V(ft4)  V(ft5) V(ft6) V(ft7) V(ft8)          \
  V(ft9)  V(ft10) V(ft11) V(fa0) V(fa1) V(fa2) V(fa3) V(fa4) V(fa5)  \
  V(fa6)  V(fa7)

// Returns the number of padding slots needed for stack pointer alignment.
constexpr int ArgumentPaddingSlots(int argument_count) {
  // No argument padding required.
  return 0;
}

// clang-format on

// Note that the bit values must match those used in actual instruction
// encoding.
const int kNumRegs = 32;

const RegList kJSCallerSaved = 1 << 5 |   // t0
                               1 << 6 |   // t1
                               1 << 7 |   // t2
                               1 << 10 |  // a0
                               1 << 11 |  // a1
                               1 << 12 |  // a2
                               1 << 13 |  // a3
                               1 << 14 |  // a4
                               1 << 15 |  // a5
                               1 << 16 |  // a6
                               1 << 17 |  // a7
                               1 << 29;   // t4

const int kNumJSCallerSaved = 12;

// Callee-saved registers preserved when switching from C to JavaScript.
const RegList kCalleeSaved = 1 << 8 |   // fp/s0
                             1 << 9 |   // s1
                             1 << 18 |  // s2
                             1 << 19 |  // s3 scratch register
                             1 << 20 |  // s4 scratch register 2
                             1 << 21 |  // s5
                             1 << 22 |  // s6 (roots in Javascript code)
                             1 << 23 |  // s7 (cp in Javascript code)
                             1 << 24 |  // s8
                             1 << 25 |  // s9
                             1 << 26 |  // s10
                             1 << 27;   // s11

const int kNumCalleeSaved = 12;

const RegList kCalleeSavedFPU = 1 << 8 |   // fs0
                                1 << 9 |   // fs1
                                1 << 18 |  // fs2
                                1 << 19 |  // fs3
                                1 << 20 |  // fs4
                                1 << 21 |  // fs5
                                1 << 22 |  // fs6
                                1 << 23 |  // fs7
                                1 << 24 |  // fs8
                                1 << 25 |  // fs9
                                1 << 26 |  // fs10
                                1 << 27;   // fs11

const int kNumCalleeSavedFPU = NumRegs(kCalleeSavedFPU);

const RegList kCallerSavedFPU = 1 << 0 |   // ft0
                                1 << 1 |   // ft1
                                1 << 2 |   // ft2
                                1 << 3 |   // ft3
                                1 << 4 |   // ft4
                                1 << 5 |   // ft5
                                1 << 6 |   // ft6
                                1 << 7 |   // ft7
                                1 << 10 |  // fa0
                                1 << 11 |  // fa1
                                1 << 12 |  // fa2
                                1 << 13 |  // fa3
                                1 << 14 |  // fa4
                                1 << 15 |  // fa5
                                1 << 16 |  // fa6
                                1 << 17 |  // fa7
                                1 << 28 |  // ft8
                                1 << 29 |  // ft9
                                1 << 30 |  // ft10
                                1 << 31;   // ft11

const int kNumCallerSavedFPU = NumRegs(kCallerSavedFPU);

// Number of registers for which space is reserved in safepoints. Must be a
// multiple of 8.
const int kNumSafepointRegisters = 32;

// Define the list of registers actually saved at safepoints.
// Note that the number of saved registers may be smaller than the reserved
// space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters.
const RegList kSafepointSavedRegisters = kJSCallerSaved | kCalleeSaved;
const int kNumSafepointSavedRegisters = kNumJSCallerSaved + kNumCalleeSaved;

const int kUndefIndex = -1;
// Map with indexes on stack that corresponds to codes of saved registers.
const int kSafepointRegisterStackIndexMap[kNumRegs] = {kUndefIndex,  // zero_reg
                                                       kUndefIndex,  // ra
                                                       kUndefIndex,  // sp
                                                       kUndefIndex,  // gp
                                                       kUndefIndex,  // tp
                                                       0,            // t0
                                                       1,            // t1
                                                       2,            // t2
                                                       3,            // s0/fp
                                                       4,            // s1
                                                       5,            // a0
                                                       6,            // a1
                                                       7,            // a2
                                                       8,            // a3
                                                       9,            // a4
                                                       10,           // a5
                                                       11,           // a6
                                                       12,           // a7
                                                       13,           // s2
                                                       14,           // s3
                                                       15,           // s4
                                                       16,           // s5
                                                       17,           // s6
                                                       18,           // s7
                                                       19,           // s8
                                                       10,           // s9
                                                       21,           // s10
                                                       22,           // s11
                                                       kUndefIndex,  // t3
                                                       23,           // t4
                                                       kUndefIndex,  // t5
                                                       kUndefIndex};  // t6
// CPU Registers.
//
// 1) We would prefer to use an enum, but enum values are assignment-
// compatible with int, which has caused code-generation bugs.
//
// 2) We would prefer to use a class instead of a struct but we don't like
// the register initialization to depend on the particular initialization
// order (which appears to be different on OS X, Linux, and Windows for the
// installed versions of C++ we tried). Using a struct permits C-style
// "initialization". Also, the Register objects cannot be const as this
// forces initialization stubs in MSVC, making us dependent on initialization
// order.
//
// 3) By not using an enum, we are possibly preventing the compiler from
// doing certain constant folds, which may significantly reduce the
// code generated for some assembly instructions (because they boil down
// to a few constants). If this is a problem, we could change the code
// such that we use an enum in optimized mode, and the struct in debug
// mode. This way we get the compile-time error checking in debug mode
// and best performance in optimized code.

// -----------------------------------------------------------------------------
// Implementation of Register and FPURegister.

enum RegisterCode {
#define REGISTER_CODE(R) kRegCode_##R,
  GENERAL_REGISTERS(REGISTER_CODE)
#undef REGISTER_CODE
      kRegAfterLast
};

class Register : public RegisterBase<Register, kRegAfterLast> {
 public:
#if defined(V8_TARGET_LITTLE_ENDIAN)
  static constexpr int kMantissaOffset = 0;
  static constexpr int kExponentOffset = 4;
#elif defined(V8_TARGET_BIG_ENDIAN)
  static constexpr int kMantissaOffset = 4;
  static constexpr int kExponentOffset = 0;
#else
#error Unknown endianness
#endif

 private:
  friend class RegisterBase;
  explicit constexpr Register(int code) : RegisterBase(code) {}
};

// s7: context register
// s3: scratch register
// s4: scratch register 2
#define DECLARE_REGISTER(R) \
  constexpr Register R = Register::from_code(kRegCode_##R);
GENERAL_REGISTERS(DECLARE_REGISTER)
#undef DECLARE_REGISTER

constexpr Register no_reg = Register::no_reg();

int ToNumber(Register reg);

Register ToRegister(int num);

constexpr bool kPadArguments = false;
constexpr bool kSimpleFPAliasing = true;
constexpr bool kSimdMaskRegisters = false;

enum DoubleRegisterCode {
#define REGISTER_CODE(R) kDoubleCode_##R,
  DOUBLE_REGISTERS(REGISTER_CODE)
#undef REGISTER_CODE
      kDoubleAfterLast
};

enum VRegisterCode {
#define REGISTER_CODE(R) kVRCode_##R,
  VECTOR_REGISTERS(REGISTER_CODE)
#undef REGISTER_CODE
      kVRAfterLast
};
class VRegister : public RegisterBase<VRegister, kVRAfterLast> {
  friend class RegisterBase;

 public:
  explicit constexpr VRegister(int code) : RegisterBase(code) {}
};

// Coprocessor register.
class FPURegister : public RegisterBase<FPURegister, kDoubleAfterLast> {
 public:
  // TODO(plind): Warning, inconsistent numbering here. kNumFPURegisters refers
  // to number of 32-bit FPU regs, but kNumAllocatableRegisters refers to
  // number of Double regs (64-bit regs, or FPU-reg-pairs).

  FPURegister low() const {
    // TODO(plind): Create DCHECK for FR=0 mode. This usage suspect for FR=1.
    // Find low reg of a Double-reg pair, which is the reg itself.
    return FPURegister::from_code(code());
  }
  FPURegister high() const {
    // TODO(plind): Create DCHECK for FR=0 mode. This usage illegal in FR=1.
    // Find high reg of a Doubel-reg pair, which is reg + 1.
    return FPURegister::from_code(code() + 1);
  }

  // FIXME(riscv64): In Rvv, Vector regs is different from Float Regs. But in
  // this cl, in order to facilitate modification, it is assumed that the vector
  // register and floating point register are shared.
  VRegister toV() const {
    DCHECK(base::IsInRange(code(), 0, kVRAfterLast - 1));
    // FIXME(riscv): Because V0 is a special mask reg, so can't allocate it.
    // And v8 is unallocated so we replace v0 with v8
    if (code() == 0) {
      return VRegister(8);
    }
    return VRegister(code());
  }

 private:
  friend class RegisterBase;
  explicit constexpr FPURegister(int code) : RegisterBase(code) {}
};


// A few double registers are reserved: one as a scratch register and one to
//  hold 0.0.
//  fs9: 0.0
//  fs11: scratch register.

// For O32 ABI, Floats and Doubles refer to same set of 32 32-bit registers.
using FloatRegister = FPURegister;

using DoubleRegister = FPURegister;

using Simd128Register = VRegister;

#define DECLARE_DOUBLE_REGISTER(R) \
  constexpr DoubleRegister R = DoubleRegister::from_code(kDoubleCode_##R);
DOUBLE_REGISTERS(DECLARE_DOUBLE_REGISTER)
#undef DECLARE_DOUBLE_REGISTER

constexpr DoubleRegister no_dreg = DoubleRegister::no_reg();

#define DECLARE_VECTOR_REGISTER(R) \
  constexpr VRegister R = VRegister::from_code(kVRCode_##R);
VECTOR_REGISTERS(DECLARE_VECTOR_REGISTER)
#undef DECLARE_VECTOR_REGISTER

const VRegister no_msareg = VRegister::no_reg();

// Register aliases.
// cp is assumed to be a callee saved register.
constexpr Register kRootRegister = s6;
constexpr Register cp = s7;
constexpr Register kScratchReg = s3;
constexpr Register kScratchReg2 = s4;

constexpr DoubleRegister kScratchDoubleReg = ft0;

constexpr DoubleRegister kDoubleRegZero = fs9;

// Define {RegisterName} methods for the register types.
DEFINE_REGISTER_NAMES(Register, GENERAL_REGISTERS)
DEFINE_REGISTER_NAMES(FPURegister, DOUBLE_REGISTERS)
DEFINE_REGISTER_NAMES(VRegister, VECTOR_REGISTERS)

// Give alias names to registers for calling conventions.
constexpr Register kReturnRegister0 = a0;
constexpr Register kReturnRegister1 = a1;
constexpr Register kReturnRegister2 = a2;
constexpr Register kJSFunctionRegister = a1;
constexpr Register kContextRegister = s7;
constexpr Register kAllocateSizeRegister = a1;
constexpr Register kInterpreterAccumulatorRegister = a0;
constexpr Register kInterpreterBytecodeOffsetRegister = t0;
constexpr Register kInterpreterBytecodeArrayRegister = t1;
constexpr Register kInterpreterDispatchTableRegister = t2;

constexpr Register kJavaScriptCallArgCountRegister = a0;
constexpr Register kJavaScriptCallCodeStartRegister = a2;
constexpr Register kJavaScriptCallTargetRegister = kJSFunctionRegister;
constexpr Register kJavaScriptCallNewTargetRegister = a3;
constexpr Register kJavaScriptCallExtraArg1Register = a2;

constexpr Register kOffHeapTrampolineRegister = t6;
constexpr Register kRuntimeCallFunctionRegister = a1;
constexpr Register kRuntimeCallArgCountRegister = a0;
constexpr Register kRuntimeCallArgvRegister = a2;
constexpr Register kWasmInstanceRegister = a0;
constexpr Register kWasmCompileLazyFuncIndexRegister = t0;

constexpr DoubleRegister kFPReturnRegister0 = fa0;
constexpr VRegister kSimd128ScratchReg = v24;
constexpr VRegister kSimd128ScratchReg2 = v23;
constexpr VRegister kSimd128ScratchReg3 = v8;
constexpr VRegister kSimd128RegZero = v25;

#ifdef V8_COMPRESS_POINTERS_IN_SHARED_CAGE
constexpr Register kPtrComprCageBaseRegister = s11;  // callee save
#else
constexpr Register kPtrComprCageBaseRegister = kRootRegister;
#endif

}  // namespace internal
}  // namespace v8

#endif  // V8_CODEGEN_RISCV64_REGISTER_RISCV64_H_
