Registers.h (9353B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef jit_Registers_h 8 #define jit_Registers_h 9 10 #include "mozilla/Array.h" 11 12 #include "jit/IonTypes.h" 13 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) 14 # include "jit/x86-shared/Architecture-x86-shared.h" 15 #elif defined(JS_CODEGEN_ARM) 16 # include "jit/arm/Architecture-arm.h" 17 #elif defined(JS_CODEGEN_ARM64) 18 # include "jit/arm64/Architecture-arm64.h" 19 #elif defined(JS_CODEGEN_MIPS64) 20 # include "jit/mips64/Architecture-mips64.h" 21 #elif defined(JS_CODEGEN_LOONG64) 22 # include "jit/loong64/Architecture-loong64.h" 23 #elif defined(JS_CODEGEN_RISCV64) 24 # include "jit/riscv64/Architecture-riscv64.h" 25 #elif defined(JS_CODEGEN_WASM32) 26 # include "jit/wasm32/Architecture-wasm32.h" 27 #elif defined(JS_CODEGEN_NONE) 28 # include "jit/none/Architecture-none.h" 29 #else 30 # error "Unknown architecture!" 31 #endif 32 33 namespace js { 34 namespace jit { 35 36 struct Register { 37 using Codes = Registers; 38 using Encoding = Codes::Encoding; 39 using Code = Codes::Code; 40 using SetType = Codes::SetType; 41 42 Encoding reg_; 43 explicit constexpr Register(Encoding e) : reg_(e) {} 44 Register() : reg_(Encoding(Codes::Invalid)) {} 45 46 static Register FromCode(Code i) { 47 MOZ_ASSERT(i < Registers::Total); 48 Register r{Encoding(i)}; 49 return r; 50 } 51 static Register FromName(const char* name) { 52 Code code = Registers::FromName(name); 53 Register r{Encoding(code)}; 54 return r; 55 } 56 constexpr static Register Invalid() { 57 Register r{Encoding(Codes::Invalid)}; 58 return r; 59 } 60 constexpr Code code() const { return Code(reg_); } 61 Encoding encoding() const { 62 MOZ_ASSERT(Code(reg_) < Registers::Total); 63 return reg_; 64 } 65 const char* name() const { return Registers::GetName(code()); } 66 constexpr bool operator==(Register other) const { return reg_ == other.reg_; } 67 constexpr bool operator!=(Register other) const { return reg_ != other.reg_; } 68 bool volatile_() const { 69 return !!((SetType(1) << code()) & Registers::VolatileMask); 70 } 71 bool aliases(const Register& other) const { return reg_ == other.reg_; } 72 uint32_t numAliased() const { return 1; } 73 74 Register aliased(uint32_t aliasIdx) const { 75 MOZ_ASSERT(aliasIdx == 0); 76 return *this; 77 } 78 79 SetType alignedOrDominatedAliasedSet() const { return SetType(1) << code(); } 80 81 static constexpr RegTypeName DefaultType = RegTypeName::GPR; 82 83 template <RegTypeName = DefaultType> 84 static SetType LiveAsIndexableSet(SetType s) { 85 return SetType(0); 86 } 87 88 template <RegTypeName Name = DefaultType> 89 static SetType AllocatableAsIndexableSet(SetType s) { 90 static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable"); 91 return SetType(0); 92 } 93 94 static uint32_t SetSize(SetType x) { return Codes::SetSize(x); } 95 static uint32_t FirstBit(SetType x) { return Codes::FirstBit(x); } 96 static uint32_t LastBit(SetType x) { return Codes::LastBit(x); } 97 98 // Returns the offset of |reg| on the stack, assuming all registers in |set| 99 // were pushed in order (e.g. by |PushRegsInMask|). This is computed by 100 // clearing the lower bits (registers that were pushed later). 101 static size_t OffsetOfPushedRegister(SetType set, Register reg) { 102 return sizeof(Codes::RegisterContent) * Codes::SetSize(set >> reg.code()); 103 } 104 }; 105 106 // Architectures where the stack pointer is not a plain register with a standard 107 // register encoding must define JS_HAS_HIDDEN_SP and HiddenSPEncoding. 108 109 #ifdef JS_HAS_HIDDEN_SP 110 struct RegisterOrSP { 111 // The register code -- but possibly one that cannot be represented as a bit 112 // position in a 32-bit vector. 113 uint32_t code; 114 115 explicit RegisterOrSP(uint32_t code) : code(code) {} 116 explicit RegisterOrSP(Register r) : code(r.code()) {} 117 }; 118 119 static inline bool IsHiddenSP(RegisterOrSP r) { 120 return r.code == HiddenSPEncoding; 121 } 122 123 static inline Register AsRegister(RegisterOrSP r) { 124 MOZ_ASSERT(!IsHiddenSP(r)); 125 return Register::FromCode(r.code); 126 } 127 128 static inline Register AsRegister(Register r) { return r; } 129 130 inline bool operator==(Register r, RegisterOrSP e) { 131 return r.code() == e.code; 132 } 133 134 inline bool operator!=(Register r, RegisterOrSP e) { return !(r == e); } 135 136 inline bool operator==(RegisterOrSP e, Register r) { return r == e; } 137 138 inline bool operator!=(RegisterOrSP e, Register r) { return r != e; } 139 140 inline bool operator==(RegisterOrSP lhs, RegisterOrSP rhs) { 141 return lhs.code == rhs.code; 142 } 143 144 inline bool operator!=(RegisterOrSP lhs, RegisterOrSP rhs) { 145 return !(lhs == rhs); 146 } 147 #else 148 // On platforms where there's nothing special about SP, make RegisterOrSP be 149 // just Register, and return false for IsHiddenSP(r) for any r so that we use 150 // "normal" code for handling the SP. This reduces ifdeffery throughout the 151 // jit. 152 using RegisterOrSP = Register; 153 154 static inline bool IsHiddenSP(RegisterOrSP r) { return false; } 155 156 static inline Register AsRegister(RegisterOrSP r) { return r; } 157 #endif 158 159 template <> 160 inline Register::SetType Register::LiveAsIndexableSet<RegTypeName::GPR>( 161 SetType set) { 162 return set; 163 } 164 165 template <> 166 inline Register::SetType Register::LiveAsIndexableSet<RegTypeName::Any>( 167 SetType set) { 168 return set; 169 } 170 171 template <> 172 inline Register::SetType Register::AllocatableAsIndexableSet<RegTypeName::GPR>( 173 SetType set) { 174 return set; 175 } 176 177 #if JS_BITS_PER_WORD == 32 178 // Note, some platform code depends on INT64LOW_OFFSET being zero. 179 static const uint32_t INT64LOW_OFFSET = 0 * sizeof(int32_t); 180 static const uint32_t INT64HIGH_OFFSET = 1 * sizeof(int32_t); 181 #endif 182 183 struct Register64 { 184 #ifdef JS_PUNBOX64 185 Register reg; 186 #else 187 Register high; 188 Register low; 189 #endif 190 191 #ifdef JS_PUNBOX64 192 explicit constexpr Register64(Register r) : reg(r) {} 193 constexpr bool operator==(Register64 other) const { return reg == other.reg; } 194 constexpr bool operator!=(Register64 other) const { return reg != other.reg; } 195 Register scratchReg() { return reg; } 196 static constexpr Register64 Invalid() { 197 return Register64(Register::Invalid()); 198 } 199 #else 200 constexpr Register64(Register h, Register l) : high(h), low(l) {} 201 constexpr bool operator==(Register64 other) const { 202 return high == other.high && low == other.low; 203 } 204 constexpr bool operator!=(Register64 other) const { 205 return high != other.high || low != other.low; 206 } 207 Register scratchReg() { return high; } 208 Register secondScratchReg() { return low; } 209 static constexpr Register64 Invalid() { 210 return Register64(Register::Invalid(), Register::Invalid()); 211 } 212 #endif 213 }; 214 215 class RegisterDump { 216 public: 217 using GPRArray = mozilla::Array<Registers::RegisterContent, Registers::Total>; 218 using FPUArray = mozilla::Array<FloatRegisters::RegisterContent, 219 FloatRegisters::TotalPhys>; 220 221 protected: // Silence Clang warning. 222 GPRArray regs_; 223 FPUArray fpregs_; 224 225 public: 226 static size_t offsetOfRegister(Register reg) { 227 return offsetof(RegisterDump, regs_) + reg.code() * sizeof(uintptr_t); 228 } 229 static size_t offsetOfRegister(FloatRegister reg) { 230 return offsetof(RegisterDump, fpregs_) + reg.getRegisterDumpOffsetInBytes(); 231 } 232 }; 233 234 // Class for mapping each register to an offset. 235 class RegisterOffsets { 236 mozilla::Array<uint32_t, Registers::Total> offsets_; 237 238 // Sentinel value representing an uninitialized offset. 239 static constexpr uint32_t InvalidOffset = UINT32_MAX; 240 241 public: 242 RegisterOffsets() { 243 for (size_t i = 0; i < Registers::Total; i++) { 244 offsets_[i] = InvalidOffset; 245 } 246 } 247 248 RegisterOffsets(const RegisterOffsets&) = delete; 249 void operator=(const RegisterOffsets&) = delete; 250 251 bool hasOffset(Register reg) const { 252 return offsets_[reg.code()] != InvalidOffset; 253 } 254 uint32_t getOffset(Register reg) const { 255 MOZ_ASSERT(hasOffset(reg)); 256 return offsets_[reg.code()]; 257 } 258 void setOffset(Register reg, size_t offset) { 259 MOZ_ASSERT(offset < InvalidOffset); 260 offsets_[reg.code()] = uint32_t(offset); 261 } 262 }; 263 264 class MacroAssembler; 265 266 // Declares a register as owned within the scope of the object. 267 // In debug mode, owned register state is tracked within the MacroAssembler, 268 // and an assert will fire if ownership is conflicting. 269 // In contrast to ARM64's UseScratchRegisterScope, this class has no overhead 270 // in non-debug builds. 271 template <class RegisterType> 272 struct AutoGenericRegisterScope : public RegisterType { 273 // Prevent MacroAssembler templates from creating copies, 274 // which causes the destructor to fire more than once. 275 AutoGenericRegisterScope(const AutoGenericRegisterScope& other) = delete; 276 277 #ifdef DEBUG 278 MacroAssembler& masm_; 279 bool released_; 280 explicit AutoGenericRegisterScope(MacroAssembler& masm, RegisterType reg); 281 ~AutoGenericRegisterScope(); 282 void release(); 283 void reacquire(); 284 #else 285 constexpr explicit AutoGenericRegisterScope(MacroAssembler& masm, 286 RegisterType reg) 287 : RegisterType(reg) {} 288 void release() {} 289 void reacquire() {} 290 #endif 291 }; 292 293 using AutoRegisterScope = AutoGenericRegisterScope<Register>; 294 using AutoFloatRegisterScope = AutoGenericRegisterScope<FloatRegister>; 295 296 } // namespace jit 297 } // namespace js 298 299 #endif /* jit_Registers_h */