1// Copyright 2018 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef V8_CODEGEN_MIPS64_REGISTER_MIPS64_H_ 6#define V8_CODEGEN_MIPS64_REGISTER_MIPS64_H_ 7 8#include "src/codegen/mips64/constants-mips64.h" 9#include "src/codegen/register-base.h" 10 11namespace v8 { 12namespace internal { 13 14// clang-format off 15#define GENERAL_REGISTERS(V) \ 16 V(zero_reg) V(at) V(v0) V(v1) V(a0) V(a1) V(a2) V(a3) \ 17 V(a4) V(a5) V(a6) V(a7) V(t0) V(t1) V(t2) V(t3) \ 18 V(s0) V(s1) V(s2) V(s3) V(s4) V(s5) V(s6) V(s7) V(t8) V(t9) \ 19 V(k0) V(k1) V(gp) V(sp) V(fp) V(ra) 20 21#define ALLOCATABLE_GENERAL_REGISTERS(V) \ 22 V(a0) V(a1) V(a2) V(a3) \ 23 V(a4) V(a5) V(a6) V(a7) V(t0) V(t1) V(t2) V(t3) V(s7) \ 24 V(v0) V(v1) 25 26#define DOUBLE_REGISTERS(V) \ 27 V(f0) V(f1) V(f2) V(f3) V(f4) V(f5) V(f6) V(f7) \ 28 V(f8) V(f9) V(f10) V(f11) V(f12) V(f13) V(f14) V(f15) \ 29 V(f16) V(f17) V(f18) V(f19) V(f20) V(f21) V(f22) V(f23) \ 30 V(f24) V(f25) V(f26) V(f27) V(f28) V(f29) V(f30) V(f31) 31 32// Currently, MIPS64 just use even float point register, except 33// for C function param registers. 34#define DOUBLE_USE_REGISTERS(V) \ 35 V(f0) V(f2) V(f4) V(f6) V(f8) V(f10) V(f12) V(f13) \ 36 V(f14) V(f15) V(f16) V(f17) V(f18) V(f19) V(f20) V(f22) \ 37 V(f24) V(f26) V(f28) V(f30) 38 39#define FLOAT_REGISTERS DOUBLE_REGISTERS 40#define SIMD128_REGISTERS(V) \ 41 V(w0) V(w1) V(w2) V(w3) V(w4) V(w5) V(w6) V(w7) \ 42 V(w8) V(w9) V(w10) V(w11) V(w12) V(w13) V(w14) V(w15) \ 43 V(w16) V(w17) V(w18) V(w19) V(w20) V(w21) V(w22) V(w23) \ 44 V(w24) V(w25) V(w26) V(w27) V(w28) V(w29) V(w30) V(w31) 45 46#define ALLOCATABLE_DOUBLE_REGISTERS(V) \ 47 V(f0) V(f2) V(f4) V(f6) V(f8) V(f10) V(f12) V(f14) \ 48 V(f16) V(f18) V(f20) V(f22) V(f24) V(f26) 49// clang-format on 50 51// Note that the bit values must match those used in actual instruction 52// encoding. 53const int kNumRegs = 32; 54 55// CPU Registers. 56// 57// 1) We would prefer to use an enum, but enum values are assignment- 58// compatible with int, which has caused code-generation bugs. 59// 60// 2) We would prefer to use a class instead of a struct but we don't like 61// the register initialization to depend on the particular initialization 62// order (which appears to be different on OS X, Linux, and Windows for the 63// installed versions of C++ we tried). Using a struct permits C-style 64// "initialization". Also, the Register objects cannot be const as this 65// forces initialization stubs in MSVC, making us dependent on initialization 66// order. 67// 68// 3) By not using an enum, we are possibly preventing the compiler from 69// doing certain constant folds, which may significantly reduce the 70// code generated for some assembly instructions (because they boil down 71// to a few constants). If this is a problem, we could change the code 72// such that we use an enum in optimized mode, and the struct in debug 73// mode. This way we get the compile-time error checking in debug mode 74// and best performance in optimized code. 75 76// ----------------------------------------------------------------------------- 77// Implementation of Register and FPURegister. 78 79enum RegisterCode { 80#define REGISTER_CODE(R) kRegCode_##R, 81 GENERAL_REGISTERS(REGISTER_CODE) 82#undef REGISTER_CODE 83 kRegAfterLast 84}; 85 86class Register : public RegisterBase<Register, kRegAfterLast> { 87 public: 88#if defined(V8_TARGET_LITTLE_ENDIAN) 89 static constexpr int kMantissaOffset = 0; 90 static constexpr int kExponentOffset = 4; 91#elif defined(V8_TARGET_BIG_ENDIAN) 92 static constexpr int kMantissaOffset = 4; 93 static constexpr int kExponentOffset = 0; 94#else 95#error Unknown endianness 96#endif 97 98 private: 99 friend class RegisterBase; 100 explicit constexpr Register(int code) : RegisterBase(code) {} 101}; 102 103// s7: context register 104// s3: scratch register 105// s4: scratch register 2 106#define DECLARE_REGISTER(R) \ 107 constexpr Register R = Register::from_code(kRegCode_##R); 108GENERAL_REGISTERS(DECLARE_REGISTER) 109#undef DECLARE_REGISTER 110 111constexpr Register no_reg = Register::no_reg(); 112 113int ToNumber(Register reg); 114 115Register ToRegister(int num); 116 117// Returns the number of padding slots needed for stack pointer alignment. 118constexpr int ArgumentPaddingSlots(int argument_count) { 119 // No argument padding required. 120 return 0; 121} 122 123constexpr AliasingKind kFPAliasing = AliasingKind::kOverlap; 124constexpr bool kSimdMaskRegisters = false; 125 126enum MSARegisterCode { 127#define REGISTER_CODE(R) kMsaCode_##R, 128 SIMD128_REGISTERS(REGISTER_CODE) 129#undef REGISTER_CODE 130 kMsaAfterLast 131}; 132 133// MIPS SIMD (MSA) register 134class MSARegister : public RegisterBase<MSARegister, kMsaAfterLast> { 135 friend class RegisterBase; 136 explicit constexpr MSARegister(int code) : RegisterBase(code) {} 137}; 138 139enum DoubleRegisterCode { 140#define REGISTER_CODE(R) kDoubleCode_##R, 141 DOUBLE_REGISTERS(REGISTER_CODE) 142#undef REGISTER_CODE 143 kDoubleAfterLast 144}; 145 146// Coprocessor register. 147class FPURegister : public RegisterBase<FPURegister, kDoubleAfterLast> { 148 public: 149 // TODO(plind): Warning, inconsistent numbering here. kNumFPURegisters refers 150 // to number of 32-bit FPU regs, but kNumAllocatableRegisters refers to 151 // number of Double regs (64-bit regs, or FPU-reg-pairs). 152 153 FPURegister low() const { 154 // TODO(plind): Create DCHECK for FR=0 mode. This usage suspect for FR=1. 155 // Find low reg of a Double-reg pair, which is the reg itself. 156 DCHECK_EQ(code() % 2, 0); // Specified Double reg must be even. 157 return FPURegister::from_code(code()); 158 } 159 FPURegister high() const { 160 // TODO(plind): Create DCHECK for FR=0 mode. This usage illegal in FR=1. 161 // Find high reg of a Doubel-reg pair, which is reg + 1. 162 DCHECK_EQ(code() % 2, 0); // Specified Double reg must be even. 163 return FPURegister::from_code(code() + 1); 164 } 165 166 MSARegister toW() const { return MSARegister::from_code(code()); } 167 168 private: 169 friend class RegisterBase; 170 explicit constexpr FPURegister(int code) : RegisterBase(code) {} 171}; 172 173// A few double registers are reserved: one as a scratch register and one to 174// hold 0.0. 175// f28: 0.0 176// f30: scratch register. 177 178// V8 now supports the O32 ABI, and the FPU Registers are organized as 32 179// 32-bit registers, f0 through f31. When used as 'double' they are used 180// in pairs, starting with the even numbered register. So a double operation 181// on f0 really uses f0 and f1. 182// (Modern mips hardware also supports 32 64-bit registers, via setting 183// (privileged) Status Register FR bit to 1. This is used by the N32 ABI, 184// but it is not in common use. Someday we will want to support this in v8.) 185 186// For O32 ABI, Floats and Doubles refer to same set of 32 32-bit registers. 187using FloatRegister = FPURegister; 188 189using DoubleRegister = FPURegister; 190 191#define DECLARE_DOUBLE_REGISTER(R) \ 192 constexpr DoubleRegister R = DoubleRegister::from_code(kDoubleCode_##R); 193DOUBLE_REGISTERS(DECLARE_DOUBLE_REGISTER) 194#undef DECLARE_DOUBLE_REGISTER 195 196constexpr DoubleRegister no_dreg = DoubleRegister::no_reg(); 197 198// SIMD registers. 199using Simd128Register = MSARegister; 200 201#define DECLARE_SIMD128_REGISTER(R) \ 202 constexpr Simd128Register R = Simd128Register::from_code(kMsaCode_##R); 203SIMD128_REGISTERS(DECLARE_SIMD128_REGISTER) 204#undef DECLARE_SIMD128_REGISTER 205 206const Simd128Register no_msareg = Simd128Register::no_reg(); 207 208// Register aliases. 209// cp is assumed to be a callee saved register. 210constexpr Register kRootRegister = s6; 211constexpr Register cp = s7; 212constexpr Register kScratchReg = s3; 213constexpr Register kScratchReg2 = s4; 214constexpr DoubleRegister kScratchDoubleReg = f30; 215// FPU zero reg is often used to hold 0.0, but it's not hardwired to 0.0. 216constexpr DoubleRegister kDoubleRegZero = f28; 217// Used on mips64r6 for compare operations. 218// We use the last non-callee saved odd register for N64 ABI 219constexpr DoubleRegister kDoubleCompareReg = f23; 220// MSA zero and scratch regs must have the same numbers as FPU zero and scratch 221// MSA zero reg is often used to hold 0, but it's not hardwired to 0. 222constexpr Simd128Register kSimd128RegZero = w28; 223constexpr Simd128Register kSimd128ScratchReg = w30; 224 225// FPU (coprocessor 1) control registers. 226// Currently only FCSR (#31) is implemented. 227struct FPUControlRegister { 228 bool is_valid() const { return reg_code == kFCSRRegister; } 229 bool is(FPUControlRegister creg) const { return reg_code == creg.reg_code; } 230 int code() const { 231 DCHECK(is_valid()); 232 return reg_code; 233 } 234 int bit() const { 235 DCHECK(is_valid()); 236 return 1 << reg_code; 237 } 238 void setcode(int f) { 239 reg_code = f; 240 DCHECK(is_valid()); 241 } 242 // Unfortunately we can't make this private in a struct. 243 int reg_code; 244}; 245 246constexpr FPUControlRegister no_fpucreg = {kInvalidFPUControlRegister}; 247constexpr FPUControlRegister FCSR = {kFCSRRegister}; 248 249// MSA control registers 250struct MSAControlRegister { 251 bool is_valid() const { 252 return (reg_code == kMSAIRRegister) || (reg_code == kMSACSRRegister); 253 } 254 bool is(MSAControlRegister creg) const { return reg_code == creg.reg_code; } 255 int code() const { 256 DCHECK(is_valid()); 257 return reg_code; 258 } 259 int bit() const { 260 DCHECK(is_valid()); 261 return 1 << reg_code; 262 } 263 void setcode(int f) { 264 reg_code = f; 265 DCHECK(is_valid()); 266 } 267 // Unfortunately we can't make this private in a struct. 268 int reg_code; 269}; 270 271constexpr MSAControlRegister no_msacreg = {kInvalidMSAControlRegister}; 272constexpr MSAControlRegister MSAIR = {kMSAIRRegister}; 273constexpr MSAControlRegister MSACSR = {kMSACSRRegister}; 274 275// Define {RegisterName} methods for the register types. 276DEFINE_REGISTER_NAMES(Register, GENERAL_REGISTERS) 277DEFINE_REGISTER_NAMES(FPURegister, DOUBLE_REGISTERS) 278DEFINE_REGISTER_NAMES(MSARegister, SIMD128_REGISTERS) 279 280// Give alias names to registers for calling conventions. 281constexpr Register kReturnRegister0 = v0; 282constexpr Register kReturnRegister1 = v1; 283constexpr Register kReturnRegister2 = a0; 284constexpr Register kJSFunctionRegister = a1; 285constexpr Register kContextRegister = s7; 286constexpr Register kAllocateSizeRegister = a0; 287constexpr Register kInterpreterAccumulatorRegister = v0; 288constexpr Register kInterpreterBytecodeOffsetRegister = t0; 289constexpr Register kInterpreterBytecodeArrayRegister = t1; 290constexpr Register kInterpreterDispatchTableRegister = t2; 291 292constexpr Register kJavaScriptCallArgCountRegister = a0; 293constexpr Register kJavaScriptCallCodeStartRegister = a2; 294constexpr Register kJavaScriptCallTargetRegister = kJSFunctionRegister; 295constexpr Register kJavaScriptCallNewTargetRegister = a3; 296constexpr Register kJavaScriptCallExtraArg1Register = a2; 297 298constexpr Register kOffHeapTrampolineRegister = at; 299constexpr Register kRuntimeCallFunctionRegister = a1; 300constexpr Register kRuntimeCallArgCountRegister = a0; 301constexpr Register kRuntimeCallArgvRegister = a2; 302constexpr Register kWasmInstanceRegister = a0; 303constexpr Register kWasmCompileLazyFuncIndexRegister = t0; 304 305constexpr DoubleRegister kFPReturnRegister0 = f0; 306 307} // namespace internal 308} // namespace v8 309 310#endif // V8_CODEGEN_MIPS64_REGISTER_MIPS64_H_ 311