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