Architecture-x86-shared.h (14953B)
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_x86_shared_Architecture_x86_h 8 #define jit_x86_shared_Architecture_x86_h 9 10 #if !defined(JS_CODEGEN_X86) && !defined(JS_CODEGEN_X64) 11 # error "Unsupported architecture!" 12 #endif 13 14 #include "mozilla/MathAlgorithms.h" 15 16 #include <algorithm> 17 #include <string.h> 18 19 #include "jit/shared/Architecture-shared.h" 20 21 #include "jit/x86-shared/Constants-x86-shared.h" 22 23 namespace js { 24 namespace jit { 25 26 #if defined(JS_CODEGEN_X86) 27 // These offsets are specific to nunboxing, and capture offsets into the 28 // components of a js::Value. 29 static const int32_t NUNBOX32_TYPE_OFFSET = 4; 30 static const int32_t NUNBOX32_PAYLOAD_OFFSET = 0; 31 #endif 32 33 #if defined(JS_CODEGEN_X64) && defined(_WIN64) 34 static constexpr uint32_t ShadowStackSpace = 32; 35 #else 36 static constexpr uint32_t ShadowStackSpace = 0; 37 #endif 38 39 static const uint32_t JumpImmediateRange = INT32_MAX; 40 41 class Registers { 42 public: 43 using Code = uint8_t; 44 using Encoding = X86Encoding::RegisterID; 45 46 // Content spilled during bailouts. 47 union RegisterContent { 48 uintptr_t r; 49 }; 50 51 #if defined(JS_CODEGEN_X86) 52 using SetType = uint8_t; 53 54 static const char* GetName(Code code) { 55 return X86Encoding::GPRegName(Encoding(code)); 56 } 57 58 static const uint32_t Total = 8; 59 static const uint32_t TotalPhys = 8; 60 static const uint32_t Allocatable = 7; 61 62 #elif defined(JS_CODEGEN_X64) 63 using SetType = uint16_t; 64 65 static const char* GetName(Code code) { 66 static const char* const Names[] = { 67 "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", 68 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}; 69 return Names[code]; 70 } 71 72 static const uint32_t Total = 16; 73 static const uint32_t TotalPhys = 16; 74 static const uint32_t Allocatable = 14; 75 #endif 76 77 static uint32_t SetSize(SetType x) { 78 static_assert(sizeof(SetType) <= 4, "SetType must be, at most, 32 bits"); 79 return mozilla::CountPopulation32(x); 80 } 81 static uint32_t FirstBit(SetType x) { 82 return mozilla::CountTrailingZeroes32(x); 83 } 84 static uint32_t LastBit(SetType x) { 85 return 31 - mozilla::CountLeadingZeroes32(x); 86 } 87 88 static Code FromName(const char* name) { 89 for (size_t i = 0; i < Total; i++) { 90 if (strcmp(GetName(Code(i)), name) == 0) { 91 return Code(i); 92 } 93 } 94 return Invalid; 95 } 96 97 static const Encoding StackPointer = X86Encoding::rsp; 98 static const Encoding Invalid = X86Encoding::invalid_reg; 99 100 static const SetType AllMask = (1 << Total) - 1; 101 102 #if defined(JS_CODEGEN_X86) 103 static const SetType ArgRegMask = 0; 104 105 static const SetType VolatileMask = (1 << X86Encoding::rax) | 106 (1 << X86Encoding::rcx) | 107 (1 << X86Encoding::rdx); 108 109 static const SetType WrapperMask = VolatileMask | (1 << X86Encoding::rbx); 110 111 static const SetType SingleByteRegs = 112 (1 << X86Encoding::rax) | (1 << X86Encoding::rcx) | 113 (1 << X86Encoding::rdx) | (1 << X86Encoding::rbx); 114 115 static const SetType NonAllocatableMask = 116 (1 << X86Encoding::rsp) | (1 << X86Encoding::rbp); 117 118 // Registers returned from a JS -> JS call. 119 static const SetType JSCallMask = 120 (1 << X86Encoding::rcx) | (1 << X86Encoding::rdx); 121 122 // Registers returned from a JS -> C call. 123 static const SetType CallMask = (1 << X86Encoding::rax); 124 125 #elif defined(JS_CODEGEN_X64) 126 static const SetType ArgRegMask = 127 # if !defined(_WIN64) 128 (1 << X86Encoding::rdi) | (1 << X86Encoding::rsi) | 129 # endif 130 (1 << X86Encoding::rdx) | (1 << X86Encoding::rcx) | 131 (1 << X86Encoding::r8) | (1 << X86Encoding::r9); 132 133 static const SetType VolatileMask = (1 << X86Encoding::rax) | ArgRegMask | 134 (1 << X86Encoding::r10) | 135 (1 << X86Encoding::r11); 136 137 static const SetType WrapperMask = VolatileMask; 138 139 static const SetType SingleByteRegs = AllMask & ~(1 << X86Encoding::rsp); 140 141 static const SetType NonAllocatableMask = 142 (1 << X86Encoding::rsp) | (1 << X86Encoding::rbp) | 143 (1 << X86Encoding::r11); // This is ScratchReg. 144 145 // Registers returned from a JS -> JS call. 146 static const SetType JSCallMask = (1 << X86Encoding::rcx); 147 148 // Registers returned from a JS -> C call. 149 static const SetType CallMask = (1 << X86Encoding::rax); 150 151 #endif 152 153 static const SetType NonVolatileMask = 154 AllMask & ~VolatileMask & ~(1 << X86Encoding::rsp); 155 156 static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; 157 }; 158 159 using PackedRegisterMask = Registers::SetType; 160 161 class FloatRegisters { 162 public: 163 using Encoding = X86Encoding::XMMRegisterID; 164 165 // Observe that there is a Simd128 type on both x86 and x64 whether SIMD is 166 // implemented/enabled or not, and that the RegisterContent union is large 167 // enough for a V128 datum always. Producers and consumers of a register dump 168 // must be aware of this even if they don't need to save/restore values in the 169 // high lanes of the SIMD registers. See the DumpAllRegs() implementations, 170 // for example. 171 172 enum ContentType { 173 Single, // 32-bit float. 174 Double, // 64-bit double. 175 Simd128, // 128-bit Wasm SIMD type. 176 NumTypes 177 }; 178 179 // Content spilled during bailouts. 180 union RegisterContent { 181 float s; 182 double d; 183 uint8_t v128[16]; 184 }; 185 186 static const char* GetName(Encoding code) { 187 return X86Encoding::XMMRegName(code); 188 } 189 190 static Encoding FromName(const char* name) { 191 for (size_t i = 0; i < Total; i++) { 192 if (strcmp(GetName(Encoding(i)), name) == 0) { 193 return Encoding(i); 194 } 195 } 196 return Invalid; 197 } 198 199 static const Encoding Invalid = X86Encoding::invalid_xmm; 200 201 #if defined(JS_CODEGEN_X86) 202 static const uint32_t Total = 8 * NumTypes; 203 static const uint32_t TotalPhys = 8; 204 static const uint32_t Allocatable = 7; 205 using SetType = uint32_t; 206 #elif defined(JS_CODEGEN_X64) 207 static const uint32_t Total = 16 * NumTypes; 208 static const uint32_t TotalPhys = 16; 209 static const uint32_t Allocatable = 15; 210 using SetType = uint64_t; 211 #endif 212 213 static_assert(sizeof(SetType) * 8 >= Total, 214 "SetType should be large enough to enumerate all registers."); 215 216 // Magic values which are used to duplicate a mask of physical register for 217 // a specific type of register. A multiplication is used to copy and shift 218 // the bits of the physical register mask. 219 static const SetType SpreadSingle = SetType(1) 220 << (uint32_t(Single) * TotalPhys); 221 static const SetType SpreadDouble = SetType(1) 222 << (uint32_t(Double) * TotalPhys); 223 static const SetType SpreadSimd128 = SetType(1) 224 << (uint32_t(Simd128) * TotalPhys); 225 static const SetType SpreadScalar = SpreadSingle | SpreadDouble; 226 static const SetType SpreadVector = SpreadSimd128; 227 static const SetType Spread = SpreadScalar | SpreadVector; 228 229 static const SetType AllPhysMask = ((1 << TotalPhys) - 1); 230 static const SetType AllMask = AllPhysMask * Spread; 231 static const SetType AllDoubleMask = AllPhysMask * SpreadDouble; 232 static const SetType AllSingleMask = AllPhysMask * SpreadSingle; 233 static const SetType AllVector128Mask = AllPhysMask * SpreadSimd128; 234 235 #if defined(JS_CODEGEN_X86) 236 static const SetType NonAllocatableMask = 237 Spread * (1 << X86Encoding::xmm7); // This is ScratchDoubleReg. 238 239 #elif defined(JS_CODEGEN_X64) 240 static const SetType NonAllocatableMask = 241 Spread * (1 << X86Encoding::xmm15); // This is ScratchDoubleReg. 242 #endif 243 244 #if defined(JS_CODEGEN_X64) && defined(_WIN64) 245 static const SetType VolatileMask = 246 ((1 << X86Encoding::xmm0) | (1 << X86Encoding::xmm1) | 247 (1 << X86Encoding::xmm2) | (1 << X86Encoding::xmm3) | 248 (1 << X86Encoding::xmm4) | (1 << X86Encoding::xmm5)) * 249 Spread; 250 #else 251 static const SetType VolatileMask = AllMask; 252 #endif 253 254 static const SetType NonVolatileMask = AllMask & ~VolatileMask; 255 static const SetType WrapperMask = VolatileMask; 256 static const SetType AllocatableMask = AllMask & ~NonAllocatableMask; 257 }; 258 259 static const uint32_t SpillSlotSize = 260 std::max(sizeof(Registers::RegisterContent), 261 sizeof(FloatRegisters::RegisterContent)); 262 263 template <typename T> 264 class TypedRegisterSet; 265 266 struct FloatRegister { 267 using Codes = FloatRegisters; 268 using Code = size_t; 269 using Encoding = Codes::Encoding; 270 using SetType = Codes::SetType; 271 static uint32_t SetSize(SetType x) { 272 // Count the number of non-aliased registers, for the moment. 273 // 274 // Copy the set bits of each typed register to the low part of the of 275 // the Set, and count the number of registers. This is made to avoid 276 // registers which are allocated twice with different types (such as in 277 // AllMask). 278 x |= x >> (2 * Codes::TotalPhys); 279 x |= x >> Codes::TotalPhys; 280 x &= Codes::AllPhysMask; 281 static_assert(Codes::AllPhysMask <= 0xffff, 282 "We can safely use CountPopulation32"); 283 return mozilla::CountPopulation32(x); 284 } 285 286 #if defined(JS_CODEGEN_X86) 287 static uint32_t FirstBit(SetType x) { 288 static_assert(sizeof(SetType) == 4, "SetType must be 32 bits"); 289 return mozilla::CountTrailingZeroes32(x); 290 } 291 static uint32_t LastBit(SetType x) { 292 return 31 - mozilla::CountLeadingZeroes32(x); 293 } 294 295 #elif defined(JS_CODEGEN_X64) 296 static uint32_t FirstBit(SetType x) { 297 static_assert(sizeof(SetType) == 8, "SetType must be 64 bits"); 298 return mozilla::CountTrailingZeroes64(x); 299 } 300 static uint32_t LastBit(SetType x) { 301 return 63 - mozilla::CountLeadingZeroes64(x); 302 } 303 #endif 304 305 private: 306 // Note: These fields are using one extra bit to make the invalid enumerated 307 // values fit, and thus prevent a warning. 308 Codes::Encoding reg_ : 5; 309 Codes::ContentType type_ : 3; 310 bool isInvalid_ : 1; 311 312 // Constants used for exporting/importing the float register code. 313 #if defined(JS_CODEGEN_X86) 314 static const size_t RegSize = 3; 315 #elif defined(JS_CODEGEN_X64) 316 static const size_t RegSize = 4; 317 #endif 318 static const size_t RegMask = (1 << RegSize) - 1; 319 320 public: 321 constexpr FloatRegister() 322 : reg_(Codes::Encoding(0)), type_(Codes::Single), isInvalid_(true) {} 323 constexpr FloatRegister(uint32_t r, Codes::ContentType k) 324 : reg_(Codes::Encoding(r)), type_(k), isInvalid_(false) {} 325 constexpr FloatRegister(Codes::Encoding r, Codes::ContentType k) 326 : reg_(r), type_(k), isInvalid_(false) {} 327 328 static FloatRegister FromCode(uint32_t i) { 329 MOZ_ASSERT(i < Codes::Total); 330 return FloatRegister(i & RegMask, Codes::ContentType(i >> RegSize)); 331 } 332 333 bool isSingle() const { 334 MOZ_ASSERT(!isInvalid()); 335 return type_ == Codes::Single; 336 } 337 bool isDouble() const { 338 MOZ_ASSERT(!isInvalid()); 339 return type_ == Codes::Double; 340 } 341 bool isSimd128() const { 342 MOZ_ASSERT(!isInvalid()); 343 return type_ == Codes::Simd128; 344 } 345 bool isInvalid() const { return isInvalid_; } 346 347 FloatRegister asSingle() const { 348 MOZ_ASSERT(!isInvalid()); 349 return FloatRegister(reg_, Codes::Single); 350 } 351 FloatRegister asDouble() const { 352 MOZ_ASSERT(!isInvalid()); 353 return FloatRegister(reg_, Codes::Double); 354 } 355 FloatRegister asSimd128() const { 356 MOZ_ASSERT(!isInvalid()); 357 return FloatRegister(reg_, Codes::Simd128); 358 } 359 360 uint32_t size() const { 361 MOZ_ASSERT(!isInvalid()); 362 if (isSingle()) { 363 return sizeof(float); 364 } 365 if (isDouble()) { 366 return sizeof(double); 367 } 368 MOZ_ASSERT(isSimd128()); 369 return 4 * sizeof(int32_t); 370 } 371 372 Code code() const { 373 MOZ_ASSERT(!isInvalid()); 374 MOZ_ASSERT(uint32_t(reg_) < Codes::TotalPhys); 375 // :TODO: ARM is doing the same thing, but we should avoid this, except 376 // that the RegisterSets depends on this. 377 return Code(reg_ | (type_ << RegSize)); 378 } 379 Encoding encoding() const { 380 MOZ_ASSERT(!isInvalid()); 381 MOZ_ASSERT(uint32_t(reg_) < Codes::TotalPhys); 382 return reg_; 383 } 384 // defined in Assembler-x86-shared.cpp 385 const char* name() const; 386 bool volatile_() const { 387 return !!((SetType(1) << code()) & FloatRegisters::VolatileMask); 388 } 389 bool operator!=(FloatRegister other) const { 390 return other.reg_ != reg_ || other.type_ != type_; 391 } 392 bool operator==(FloatRegister other) const { 393 return other.reg_ == reg_ && other.type_ == type_; 394 } 395 bool aliases(FloatRegister other) const { return other.reg_ == reg_; } 396 // Check if two floating point registers have the same type. 397 bool equiv(FloatRegister other) const { return other.type_ == type_; } 398 399 uint32_t numAliased() const { return Codes::NumTypes; } 400 uint32_t numAlignedAliased() const { return numAliased(); } 401 402 FloatRegister aliased(uint32_t aliasIdx) const { 403 MOZ_ASSERT(aliasIdx < Codes::NumTypes); 404 return FloatRegister( 405 reg_, Codes::ContentType((aliasIdx + type_) % Codes::NumTypes)); 406 } 407 FloatRegister alignedAliased(uint32_t aliasIdx) const { 408 return aliased(aliasIdx); 409 } 410 411 SetType alignedOrDominatedAliasedSet() const { return Codes::Spread << reg_; } 412 413 static constexpr RegTypeName DefaultType = RegTypeName::Float64; 414 415 template <RegTypeName = DefaultType> 416 static SetType LiveAsIndexableSet(SetType s) { 417 return SetType(0); 418 } 419 420 template <RegTypeName Name = DefaultType> 421 static SetType AllocatableAsIndexableSet(SetType s) { 422 static_assert(Name != RegTypeName::Any, "Allocatable set are not iterable"); 423 return LiveAsIndexableSet<Name>(s); 424 } 425 426 static TypedRegisterSet<FloatRegister> ReduceSetForPush( 427 const TypedRegisterSet<FloatRegister>& s); 428 static uint32_t GetPushSizeInBytes(const TypedRegisterSet<FloatRegister>& s); 429 uint32_t getRegisterDumpOffsetInBytes(); 430 }; 431 432 template <> 433 inline FloatRegister::SetType 434 FloatRegister::LiveAsIndexableSet<RegTypeName::Float32>(SetType set) { 435 return set & FloatRegisters::AllSingleMask; 436 } 437 438 template <> 439 inline FloatRegister::SetType 440 FloatRegister::LiveAsIndexableSet<RegTypeName::Float64>(SetType set) { 441 return set & FloatRegisters::AllDoubleMask; 442 } 443 444 template <> 445 inline FloatRegister::SetType 446 FloatRegister::LiveAsIndexableSet<RegTypeName::Vector128>(SetType set) { 447 return set & FloatRegisters::AllVector128Mask; 448 } 449 450 template <> 451 inline FloatRegister::SetType 452 FloatRegister::LiveAsIndexableSet<RegTypeName::Any>(SetType set) { 453 return set; 454 } 455 456 // Arm/D32 has double registers that can NOT be treated as float32 457 // and this requires some dances in lowering. 458 inline bool hasUnaliasedDouble() { return false; } 459 460 // On ARM, Dn aliases both S2n and S2n+1, so if you need to convert a float32 461 // to a double as a temporary, you need a temporary double register. 462 inline bool hasMultiAlias() { return false; } 463 464 } // namespace jit 465 } // namespace js 466 467 #endif /* jit_x86_shared_Architecture_x86_h */