RegisterSets.h (45036B)
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_RegisterSets_h 8 #define jit_RegisterSets_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/Attributes.h" 12 13 #include <new> 14 #include <stddef.h> 15 #include <stdint.h> 16 17 #include "jit/IonTypes.h" 18 #include "jit/Registers.h" 19 #include "js/Value.h" 20 21 /* 22 * [SMDOC] Allocating registers by hand 23 * 24 * To reduce our maintenance burden, much of our codegen is written using an 25 * architecture-independent macroassembler layer. All abstractions are leaky; 26 * one of the main ways that the masm abstraction leaks is when it comes to 27 * finding registers. 28 * 29 * The main constraint is 32-bit x86. There are eight general purpose registers 30 * on 32-bit x86, but two of those (esp and ebp) are permanently claimed for the 31 * stack pointer and the frame pointer, leaving six useful registers. JS::Value 32 * is eight bytes, so 32-bit architectures require two registers to store Values 33 * (one for the tag and one for the payload). Therefore, the most that 34 * architecture-independent codegen can keep alive at one time is: 35 * 36 * +------+---------+ 37 * | GPRs | Values | 38 * +------+---------+ 39 * | 6 | 0 | 40 * | 4 | 1 | 41 * | 2 | 2 | 42 * | 0 | 3 | 43 * +------+---------+ 44 * 45 * Hand-written masm (eg trampolines, stubs) often requires agreement between 46 * the caller and the code itself about which values will be passed in which 47 * registers. In such cases, we usually define a named register in a header: 48 * either an arch-specific header, like RegExpMatcherRegExpReg in 49 * jit/<arch>/Assembler-<arch>.h, or an arch-independent header like the 50 * registers in jit/IonGenericCallStub.h. For scratch registers in such code, 51 * AllocatableGeneralRegisterSet can be used. 52 * 53 * Baseline deals primarily with boxed values, so we define three 54 * architecture-independent ValueOperands (R0, R1, and R2). When individual 55 * registers are needed, R<N>.scratchReg() can be used. If the sum of live 56 * Values and GPRs is greater than three, then AllocatableGeneralRegisterSet 57 * may be required. 58 * 59 * In baseline IC code, one additional register is dedicated to ICStubReg, so at 60 * most five registers (or 3 registers + 1 Value, ...) are available. Temps can 61 * be allocated using AutoScratchRegister. It's common for IC stubs to return a 62 * Value; to free up the register pair used for that output on 32-bit platforms, 63 * AutoScratchRegisterMaybeOutput/AutoScratchRegisterMaybeOutputType can be used 64 * for values that are dead before writing to the output. 65 * 66 * If more registers are necessary, there are a variety of workarounds. In some 67 * cases, the simplest answer is simply to disable an optimization on x86. We 68 * still support it, but it's not a performance priority. For example, we don't 69 * attach some specialized stubs for Map.get/has/set on x86. In other cases, it 70 * may be possible to manually spill a register to the stack to temporarily free 71 * it up. One useful pattern is to pass InvalidReg in cases where a register is 72 * not available, and decide whether to spill depending on whether a real 73 * register is free. See, for example, CacheIRCompiler::emitDataViewBoundsCheck. 74 * 75 * 76 * ### CallTempReg, ABINonArgReg, ABINonArgReturnReg, et al 77 * 78 * There are other more-or-less architecture-independent register definitions 79 * that can be useful. These include: 80 * 81 * - CallTempReg<0-5>: Six registers that are not part of the call machinery 82 * itself (not the stack pointer, frame pointer, or the link register). They 83 * are useful when setting up a call that does not use the system ABI. In Ion, 84 * JS uses these to allocate temporary registers for LIR ops that will 85 * generate a call. No more CallTempRegs can be added for use in architecture- 86 * independent code, because x86 doesn't have enough registers. 87 * 88 * - ABINonArgReg<0-3>: Four registers that are not part of the call machinery, 89 * and are also not used for passing arguments according to the system/Wasm 90 * ABI. These are primarily used for Wasm. ABINonArgReg4 could be added if 91 * needed. After that, we run out of registers on x86, because Wasm also pins 92 * the InstanceReg. See the "Wasm ABIs" SMDOC for more information. 93 * 94 * - ABINonArgReturnReg<0-1> / ABINonVolatileReg: Three registers that are not 95 * part of the call machinery, and not used by the system ABI for passing 96 * arguments or return values. ABINonVolatileReg is also required to have its 97 * value preserved over a call. This set is (again) constrained by x86: esp 98 * and ebp are claimed by the call machinery, eax/edx are return registers, 99 * and esi is the instance register. 100 * ABINonArgReturnVolatileReg is a register that is not used for calls but is 101 * *not* preserved over a call; it may or may not be the same as 102 * ABINonArgReturn<0-1>. 103 */ 104 105 namespace js { 106 namespace jit { 107 108 struct AnyRegister { 109 using Code = uint8_t; 110 111 static const uint8_t Total = Registers::Total + FloatRegisters::Total; 112 static const uint8_t FirstFloatReg = Registers::Total; 113 static const uint8_t Invalid = UINT8_MAX; 114 115 static_assert(size_t(Registers::Total) + FloatRegisters::Total <= UINT8_MAX, 116 "Number of registers must fit in uint8_t"); 117 118 private: 119 Code code_; 120 121 public: 122 AnyRegister() : code_(Invalid) {} 123 124 explicit AnyRegister(Register gpr) { code_ = gpr.code(); } 125 explicit AnyRegister(FloatRegister fpu) { 126 code_ = fpu.code() + Registers::Total; 127 } 128 static AnyRegister FromCode(uint8_t i) { 129 MOZ_ASSERT(i < Total); 130 AnyRegister r; 131 r.code_ = i; 132 return r; 133 } 134 bool isFloat() const { 135 MOZ_ASSERT(isValid()); 136 return code_ >= Registers::Total; 137 } 138 Register gpr() const { 139 MOZ_ASSERT(!isFloat()); 140 return Register::FromCode(code_); 141 } 142 FloatRegister fpu() const { 143 MOZ_ASSERT(isFloat()); 144 return FloatRegister::FromCode(code_ - Registers::Total); 145 } 146 bool operator==(AnyRegister other) const { 147 // We don't need the operands to be valid to test for equality. 148 return code_ == other.code_; 149 } 150 bool operator!=(AnyRegister other) const { 151 // We don't need the operands to be valid to test for equality. 152 return code_ != other.code_; 153 } 154 const char* name() const { return isFloat() ? fpu().name() : gpr().name(); } 155 Code code() const { 156 MOZ_ASSERT(isValid()); 157 return code_; 158 } 159 bool volatile_() const { 160 return isFloat() ? fpu().volatile_() : gpr().volatile_(); 161 } 162 AnyRegister aliased(uint8_t aliasIdx) const { 163 AnyRegister ret; 164 if (isFloat()) { 165 ret = AnyRegister(fpu().aliased(aliasIdx)); 166 } else { 167 ret = AnyRegister(gpr().aliased(aliasIdx)); 168 } 169 MOZ_ASSERT_IF(aliasIdx == 0, ret == *this); 170 return ret; 171 } 172 uint32_t numAliased() const { 173 if (isFloat()) { 174 return fpu().numAliased(); 175 } 176 return gpr().numAliased(); 177 } 178 bool aliases(const AnyRegister& other) const { 179 if (isFloat() && other.isFloat()) { 180 return fpu().aliases(other.fpu()); 181 } 182 if (!isFloat() && !other.isFloat()) { 183 return gpr().aliases(other.gpr()); 184 } 185 return false; 186 } 187 // do the two registers hold the same type of data (e.g. both float32, both 188 // gpr) 189 bool isCompatibleReg(const AnyRegister other) const { 190 if (isFloat() && other.isFloat()) { 191 return fpu().equiv(other.fpu()); 192 } 193 if (!isFloat() && !other.isFloat()) { 194 return true; 195 } 196 return false; 197 } 198 bool isValid() const { return code_ != Invalid; } 199 }; 200 201 // Registers to hold a boxed value. Uses one register on 64 bit 202 // platforms, two registers on 32 bit platforms. 203 class ValueOperand { 204 #if defined(JS_NUNBOX32) 205 Register type_; 206 Register payload_; 207 208 public: 209 constexpr ValueOperand(Register type, Register payload) 210 : type_(type), payload_(payload) {} 211 212 constexpr Register typeReg() const { return type_; } 213 constexpr Register payloadReg() const { return payload_; } 214 constexpr Register64 toRegister64() const { 215 return Register64(typeReg(), payloadReg()); 216 } 217 constexpr bool aliases(Register reg) const { 218 return type_ == reg || payload_ == reg; 219 } 220 constexpr Register payloadOrValueReg() const { return payloadReg(); } 221 bool hasVolatileReg() const { 222 return type_.volatile_() || payload_.volatile_(); 223 } 224 constexpr bool operator==(const ValueOperand& o) const { 225 return type_ == o.type_ && payload_ == o.payload_; 226 } 227 constexpr bool operator!=(const ValueOperand& o) const { 228 return !(*this == o); 229 } 230 231 #elif defined(JS_PUNBOX64) 232 Register value_; 233 234 public: 235 explicit constexpr ValueOperand(Register value) : value_(value) {} 236 237 constexpr Register valueReg() const { return value_; } 238 constexpr Register64 toRegister64() const { return Register64(valueReg()); } 239 constexpr bool aliases(Register reg) const { return value_ == reg; } 240 constexpr Register payloadOrValueReg() const { return valueReg(); } 241 bool hasVolatileReg() const { return value_.volatile_(); } 242 constexpr bool operator==(const ValueOperand& o) const { 243 return value_ == o.value_; 244 } 245 constexpr bool operator!=(const ValueOperand& o) const { 246 return !(*this == o); 247 } 248 #endif 249 250 constexpr Register scratchReg() const { return payloadOrValueReg(); } 251 252 ValueOperand() = default; 253 }; 254 255 // Registers to hold either either a typed or untyped value. 256 class TypedOrValueRegister { 257 // Type of value being stored. 258 MIRType type_; 259 260 union U { 261 AnyRegister::Code typed; 262 #if defined(JS_PUNBOX64) 263 Register::Code value; 264 #elif defined(JS_NUNBOX32) 265 struct { 266 Register::Code valueType; 267 Register::Code valuePayload; 268 } s; 269 #else 270 # error "Bad architecture" 271 #endif 272 } data; 273 274 public: 275 TypedOrValueRegister() = default; 276 277 TypedOrValueRegister(MIRType type, AnyRegister reg) : type_(type) { 278 data.typed = reg.code(); 279 } 280 281 MOZ_IMPLICIT TypedOrValueRegister(ValueOperand value) 282 : type_(MIRType::Value) { 283 #if defined(JS_PUNBOX64) 284 data.value = value.valueReg().code(); 285 #elif defined(JS_NUNBOX32) 286 data.s.valueType = value.typeReg().code(); 287 data.s.valuePayload = value.payloadReg().code(); 288 #else 289 # error "Bad architecture" 290 #endif 291 } 292 293 MIRType type() const { return type_; } 294 295 bool hasTyped() const { 296 return type() != MIRType::None && type() != MIRType::Value; 297 } 298 299 bool hasValue() const { return type() == MIRType::Value; } 300 301 AnyRegister typedReg() const { 302 MOZ_ASSERT(hasTyped()); 303 return AnyRegister::FromCode(data.typed); 304 } 305 306 ValueOperand valueReg() const { 307 MOZ_ASSERT(hasValue()); 308 #if defined(JS_PUNBOX64) 309 return ValueOperand(Register::FromCode(data.value)); 310 #elif defined(JS_NUNBOX32) 311 return ValueOperand(Register::FromCode(data.s.valueType), 312 Register::FromCode(data.s.valuePayload)); 313 #else 314 # error "Bad architecture" 315 #endif 316 } 317 318 AnyRegister scratchReg() { 319 if (hasValue()) { 320 return AnyRegister(valueReg().scratchReg()); 321 } 322 return typedReg(); 323 } 324 }; 325 326 // A constant value, or registers to hold a typed/untyped value. 327 class ConstantOrRegister { 328 // Whether a constant value is being stored. 329 bool constant_; 330 331 // Space to hold either a Value or a TypedOrValueRegister. 332 union U { 333 JS::Value constant; 334 TypedOrValueRegister reg; 335 336 // |constant| has a non-trivial constructor and therefore MUST be 337 // placement-new'd into existence. 338 MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS 339 U() {} 340 MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS 341 } data; 342 343 public: 344 ConstantOrRegister() = delete; 345 346 MOZ_IMPLICIT ConstantOrRegister(const JS::Value& value) : constant_(true) { 347 MOZ_ASSERT(constant()); 348 new (&data.constant) JS::Value(value); 349 } 350 351 MOZ_IMPLICIT ConstantOrRegister(TypedOrValueRegister reg) : constant_(false) { 352 MOZ_ASSERT(!constant()); 353 new (&data.reg) TypedOrValueRegister(reg); 354 } 355 356 bool constant() const { return constant_; } 357 358 JS::Value value() const { 359 MOZ_ASSERT(constant()); 360 return data.constant; 361 } 362 363 const TypedOrValueRegister& reg() const { 364 MOZ_ASSERT(!constant()); 365 return data.reg; 366 } 367 }; 368 369 template <typename T> 370 class TypedRegisterSet { 371 public: 372 using RegType = T; 373 using SetType = typename T::SetType; 374 375 private: 376 SetType bits_; 377 378 public: 379 explicit constexpr TypedRegisterSet(SetType bits) : bits_(bits) {} 380 381 constexpr TypedRegisterSet() : bits_(0) {} 382 constexpr TypedRegisterSet(const TypedRegisterSet<T>& set) 383 : bits_(set.bits_) {} 384 385 static inline TypedRegisterSet All() { 386 return TypedRegisterSet(T::Codes::AllocatableMask); 387 } 388 static inline TypedRegisterSet Intersect(const TypedRegisterSet& lhs, 389 const TypedRegisterSet& rhs) { 390 return TypedRegisterSet(lhs.bits_ & rhs.bits_); 391 } 392 static inline TypedRegisterSet Union(const TypedRegisterSet& lhs, 393 const TypedRegisterSet& rhs) { 394 return TypedRegisterSet(lhs.bits_ | rhs.bits_); 395 } 396 static inline TypedRegisterSet Not(const TypedRegisterSet& in) { 397 return TypedRegisterSet(~in.bits_ & T::Codes::AllocatableMask); 398 } 399 static inline TypedRegisterSet Subtract(const TypedRegisterSet& lhs, 400 const TypedRegisterSet& rhs) { 401 return TypedRegisterSet(lhs.bits_ & ~rhs.bits_); 402 } 403 static inline TypedRegisterSet VolatileNot(const TypedRegisterSet& in) { 404 const SetType allocatableVolatile = 405 T::Codes::AllocatableMask & T::Codes::VolatileMask; 406 return TypedRegisterSet(~in.bits_ & allocatableVolatile); 407 } 408 static inline TypedRegisterSet Volatile() { 409 return TypedRegisterSet(T::Codes::AllocatableMask & T::Codes::VolatileMask); 410 } 411 static inline TypedRegisterSet NonVolatile() { 412 return TypedRegisterSet(T::Codes::AllocatableMask & 413 T::Codes::NonVolatileMask); 414 } 415 416 bool empty() const { return !bits_; } 417 void clear() { bits_ = 0; } 418 419 bool hasRegisterIndex(T reg) const { 420 return !!(bits_ & (SetType(1) << reg.code())); 421 } 422 bool hasAllocatable(T reg) const { 423 return !(~bits_ & reg.alignedOrDominatedAliasedSet()); 424 } 425 426 void addRegisterIndex(T reg) { bits_ |= (SetType(1) << reg.code()); } 427 void addAllocatable(T reg) { bits_ |= reg.alignedOrDominatedAliasedSet(); } 428 429 void takeRegisterIndex(T reg) { bits_ &= ~(SetType(1) << reg.code()); } 430 void takeAllocatable(T reg) { bits_ &= ~reg.alignedOrDominatedAliasedSet(); } 431 432 static constexpr RegTypeName DefaultType = RegType::DefaultType; 433 434 template <RegTypeName Name> 435 SetType allLive() const { 436 return T::template LiveAsIndexableSet<Name>(bits_); 437 } 438 template <RegTypeName Name> 439 SetType allAllocatable() const { 440 return T::template AllocatableAsIndexableSet<Name>(bits_); 441 } 442 443 static RegType FirstRegister(SetType set) { 444 return RegType::FromCode(RegType::FirstBit(set)); 445 } 446 static RegType LastRegister(SetType set) { 447 return RegType::FromCode(RegType::LastBit(set)); 448 } 449 450 SetType bits() const { return bits_; } 451 uint32_t size() const { return T::SetSize(bits_); } 452 bool operator==(const TypedRegisterSet<T>& other) const { 453 return other.bits_ == bits_; 454 } 455 TypedRegisterSet<T> reduceSetForPush() const { 456 return T::ReduceSetForPush(*this); 457 } 458 uint32_t getPushSizeInBytes() const { return T::GetPushSizeInBytes(*this); } 459 460 size_t offsetOfPushedRegister(RegType reg) const { 461 MOZ_ASSERT(hasRegisterIndex(reg)); 462 return T::OffsetOfPushedRegister(bits(), reg); 463 } 464 }; 465 466 using GeneralRegisterSet = TypedRegisterSet<Register>; 467 using FloatRegisterSet = TypedRegisterSet<FloatRegister>; 468 469 class AnyRegisterIterator; 470 471 class RegisterSet { 472 GeneralRegisterSet gpr_; 473 FloatRegisterSet fpu_; 474 475 friend class AnyRegisterIterator; 476 477 public: 478 RegisterSet() = default; 479 constexpr RegisterSet(const GeneralRegisterSet& gpr, 480 const FloatRegisterSet& fpu) 481 : gpr_(gpr), fpu_(fpu) {} 482 static inline RegisterSet All() { 483 return RegisterSet(GeneralRegisterSet::All(), FloatRegisterSet::All()); 484 } 485 static inline RegisterSet Intersect(const RegisterSet& lhs, 486 const RegisterSet& rhs) { 487 return RegisterSet(GeneralRegisterSet::Intersect(lhs.gpr_, rhs.gpr_), 488 FloatRegisterSet::Intersect(lhs.fpu_, rhs.fpu_)); 489 } 490 static inline RegisterSet Union(const RegisterSet& lhs, 491 const RegisterSet& rhs) { 492 return RegisterSet(GeneralRegisterSet::Union(lhs.gpr_, rhs.gpr_), 493 FloatRegisterSet::Union(lhs.fpu_, rhs.fpu_)); 494 } 495 static inline RegisterSet Not(const RegisterSet& in) { 496 return RegisterSet(GeneralRegisterSet::Not(in.gpr_), 497 FloatRegisterSet::Not(in.fpu_)); 498 } 499 static inline RegisterSet Subtract(const RegisterSet& lhs, 500 const RegisterSet& rhs) { 501 return RegisterSet(GeneralRegisterSet::Subtract(lhs.gpr_, rhs.gpr_), 502 FloatRegisterSet::Subtract(lhs.fpu_, rhs.fpu_)); 503 } 504 static inline RegisterSet VolatileNot(const RegisterSet& in) { 505 return RegisterSet(GeneralRegisterSet::VolatileNot(in.gpr_), 506 FloatRegisterSet::VolatileNot(in.fpu_)); 507 } 508 static inline RegisterSet Volatile() { 509 return RegisterSet(GeneralRegisterSet::Volatile(), 510 FloatRegisterSet::Volatile()); 511 } 512 513 bool empty() const { return fpu_.empty() && gpr_.empty(); } 514 void clear() { 515 fpu_.clear(); 516 gpr_.clear(); 517 } 518 bool emptyGeneral() const { return gpr_.empty(); } 519 bool emptyFloat() const { return fpu_.empty(); } 520 521 static constexpr RegTypeName DefaultType = RegTypeName::GPR; 522 523 constexpr GeneralRegisterSet gprs() const { return gpr_; } 524 GeneralRegisterSet& gprs() { return gpr_; } 525 constexpr FloatRegisterSet fpus() const { return fpu_; } 526 FloatRegisterSet& fpus() { return fpu_; } 527 bool operator==(const RegisterSet& other) const { 528 return other.gpr_ == gpr_ && other.fpu_ == fpu_; 529 } 530 }; 531 532 // [SMDOC] JIT Register-Set overview 533 // 534 // There are 2 use cases for register sets: 535 // 536 // 1. To serve as a pool of allocatable register. This is useful for working 537 // on the code produced by some stub where free registers are available, or 538 // when we can release some registers. 539 // 540 // 2. To serve as a list of typed registers. This is useful for working with 541 // live registers and to manipulate them with the proper instructions. This 542 // is used by the register allocator to fill the Safepoints. 543 // 544 // These 2 uses cases can be used on top of 3 different backend representation 545 // of register sets, which are either GeneralRegisterSet, FloatRegisterSet, or 546 // RegisterSet (for both). These classes are used to store the bit sets to 547 // represent each register. 548 // 549 // Each use case defines an Accessor class, such as AllocatableSetAccessor or 550 // LiveSetAccessor, which is parameterized with the type of the register 551 // set. These accessors are in charge of manipulating the register set in a 552 // consistent way. 553 // 554 // The RegSetCommonInterface class is used to wrap the accessors with convenient 555 // shortcuts which are based on the accessors. 556 // 557 // Then, to avoid to many levels of complexity while using these interfaces, 558 // shortcut templates are created to make it easy to distinguish between a 559 // register set used for allocating registers, or a register set used for making 560 // a collection of allocated (live) registers. 561 // 562 // This separation exists to prevent mixing LiveSet and AllocatableSet 563 // manipulations of the same register set, and ensure safety while avoiding 564 // false positive. 565 566 template <typename RegisterSet> 567 class AllocatableSet; 568 569 template <typename RegisterSet> 570 class LiveSet; 571 572 // [SMDOC] JIT Register-Set (Allocatable) 573 // 574 // Base accessors classes have the minimal set of raw methods to manipulate the 575 // register set given as parameter in a consistent manner. These methods are: 576 // 577 // - all<Type>: Returns a bit-set of all the register of a specific type 578 // which are present. 579 // 580 // - has: Returns if all the bits needed to take a register are present. 581 // 582 // - takeUnchecked: Subtracts the bits used to represent the register in the 583 // register set. 584 // 585 // - addUnchecked: Adds the bits used to represent the register in the 586 // register set. 587 588 // The AllocatableSet accessors are used to make a pool of unused 589 // registers. Taking or adding registers should consider the aliasing rules of 590 // the architecture. For example, on ARM, the following piece of code should 591 // work fine, knowing that the double register |d0| is composed of float 592 // registers |s0| and |s1|: 593 // 594 // AllocatableFloatRegisterSet regs; 595 // regs.add(s0); 596 // regs.add(s1); 597 // // d0 is now available. 598 // regs.take(d0); 599 // 600 // These accessors are useful for allocating registers within the functions used 601 // to generate stubs, trampolines, and inline caches (BaselineIC, IonCache). 602 template <typename Set> 603 class AllocatableSetAccessors { 604 public: 605 using RegSet = Set; 606 using RegType = typename RegSet::RegType; 607 using SetType = typename RegSet::SetType; 608 609 protected: 610 RegSet set_; 611 612 template <RegTypeName Name> 613 SetType all() const { 614 return set_.template allAllocatable<Name>(); 615 } 616 617 public: 618 AllocatableSetAccessors() : set_() {} 619 explicit constexpr AllocatableSetAccessors(SetType set) : set_(set) {} 620 explicit constexpr AllocatableSetAccessors(RegSet set) : set_(set) {} 621 622 bool has(RegType reg) const { return set_.hasAllocatable(reg); } 623 624 template <RegTypeName Name> 625 bool hasAny(RegType reg) const { 626 return all<Name>() != 0; 627 } 628 629 void addUnchecked(RegType reg) { set_.addAllocatable(reg); } 630 631 void takeUnchecked(RegType reg) { set_.takeAllocatable(reg); } 632 }; 633 634 // Specialization of the AllocatableSet accessors for the RegisterSet aggregate. 635 template <> 636 class AllocatableSetAccessors<RegisterSet> { 637 public: 638 using RegSet = RegisterSet; 639 using RegType = AnyRegister; 640 using SetType = char; 641 642 protected: 643 RegisterSet set_; 644 645 template <RegTypeName Name> 646 GeneralRegisterSet::SetType allGpr() const { 647 return set_.gprs().allAllocatable<Name>(); 648 } 649 template <RegTypeName Name> 650 FloatRegisterSet::SetType allFpu() const { 651 return set_.fpus().allAllocatable<Name>(); 652 } 653 654 public: 655 AllocatableSetAccessors() = default; 656 explicit constexpr AllocatableSetAccessors(SetType) = delete; 657 explicit constexpr AllocatableSetAccessors(RegisterSet set) : set_(set) {} 658 659 bool has(Register reg) const { return set_.gprs().hasAllocatable(reg); } 660 bool has(FloatRegister reg) const { return set_.fpus().hasAllocatable(reg); } 661 662 void addUnchecked(Register reg) { set_.gprs().addAllocatable(reg); } 663 void addUnchecked(FloatRegister reg) { set_.fpus().addAllocatable(reg); } 664 665 void takeUnchecked(Register reg) { set_.gprs().takeAllocatable(reg); } 666 void takeUnchecked(FloatRegister reg) { set_.fpus().takeAllocatable(reg); } 667 }; 668 669 // [SMDOC] JIT Register-Set (Live) 670 // 671 // The LiveSet accessors are used to collect a list of allocated 672 // registers. Taking or adding a register should *not* consider the aliases, as 673 // we care about interpreting the registers with the correct type. For example, 674 // on x64, where one float registers can be interpreted as an Simd128, a Double, 675 // or a Float, adding xmm0 as an Simd128, does not make the register available 676 // as a Double. 677 // 678 // LiveFloatRegisterSet regs; 679 // regs.add(xmm0.asSimd128()); 680 // regs.take(xmm0); // Assert! 681 // 682 // These accessors are useful for recording the result of a register allocator, 683 // such as what the Backtracking allocator do on the Safepoints. 684 template <typename Set> 685 class LiveSetAccessors { 686 public: 687 using RegSet = Set; 688 using RegType = typename RegSet::RegType; 689 using SetType = typename RegSet::SetType; 690 691 protected: 692 RegSet set_; 693 694 template <RegTypeName Name> 695 SetType all() const { 696 return set_.template allLive<Name>(); 697 } 698 699 public: 700 LiveSetAccessors() : set_() {} 701 explicit constexpr LiveSetAccessors(SetType set) : set_(set) {} 702 explicit constexpr LiveSetAccessors(RegSet set) : set_(set) {} 703 704 bool has(RegType reg) const { return set_.hasRegisterIndex(reg); } 705 706 void addUnchecked(RegType reg) { set_.addRegisterIndex(reg); } 707 708 void takeUnchecked(RegType reg) { set_.takeRegisterIndex(reg); } 709 }; 710 711 // Specialization of the LiveSet accessors for the RegisterSet aggregate. 712 template <> 713 class LiveSetAccessors<RegisterSet> { 714 public: 715 using RegSet = RegisterSet; 716 using RegType = AnyRegister; 717 using SetType = char; 718 719 protected: 720 RegisterSet set_; 721 722 template <RegTypeName Name> 723 GeneralRegisterSet::SetType allGpr() const { 724 return set_.gprs().allLive<Name>(); 725 } 726 template <RegTypeName Name> 727 FloatRegisterSet::SetType allFpu() const { 728 return set_.fpus().allLive<Name>(); 729 } 730 731 public: 732 LiveSetAccessors() = default; 733 explicit constexpr LiveSetAccessors(SetType) = delete; 734 explicit constexpr LiveSetAccessors(RegisterSet set) : set_(set) {} 735 736 bool has(Register reg) const { return set_.gprs().hasRegisterIndex(reg); } 737 bool has(FloatRegister reg) const { 738 return set_.fpus().hasRegisterIndex(reg); 739 } 740 741 void addUnchecked(Register reg) { set_.gprs().addRegisterIndex(reg); } 742 void addUnchecked(FloatRegister reg) { set_.fpus().addRegisterIndex(reg); } 743 744 void takeUnchecked(Register reg) { set_.gprs().takeRegisterIndex(reg); } 745 void takeUnchecked(FloatRegister reg) { set_.fpus().takeRegisterIndex(reg); } 746 }; 747 748 #define DEFINE_ACCESSOR_CONSTRUCTORS_(REGSET) \ 749 using typename Parent::RegSet; \ 750 using typename Parent::RegType; \ 751 using typename Parent::SetType; \ 752 \ 753 constexpr REGSET() : Parent() {} \ 754 explicit constexpr REGSET(SetType set) : Parent(set) {} \ 755 explicit constexpr REGSET(RegSet set) : Parent(set) {} 756 757 // This class adds checked accessors on top of the unchecked variants defined by 758 // AllocatableSet and LiveSet accessors. Also it defines interface which are 759 // specialized to the register set implementation, such as |getAny| and 760 // |takeAny| variants. 761 template <class Accessors, typename Set> 762 class SpecializedRegSet : public Accessors { 763 using Parent = Accessors; 764 765 public: 766 DEFINE_ACCESSOR_CONSTRUCTORS_(SpecializedRegSet) 767 768 SetType bits() const { return this->Parent::set_.bits(); } 769 770 using Parent::has; 771 772 using Parent::addUnchecked; 773 void add(RegType reg) { 774 MOZ_ASSERT(!this->has(reg)); 775 addUnchecked(reg); 776 } 777 778 using Parent::takeUnchecked; 779 void take(RegType reg) { 780 MOZ_ASSERT(this->has(reg)); 781 takeUnchecked(reg); 782 } 783 784 template <RegTypeName Name> 785 bool hasAny() const { 786 return Parent::template all<Name>() != 0; 787 } 788 789 template <RegTypeName Name = RegSet::DefaultType> 790 RegType getFirst() const { 791 SetType set = Parent::template all<Name>(); 792 MOZ_ASSERT(set); 793 return RegSet::FirstRegister(set); 794 } 795 template <RegTypeName Name = RegSet::DefaultType> 796 RegType getLast() const { 797 SetType set = Parent::template all<Name>(); 798 MOZ_ASSERT(set); 799 return RegSet::LastRegister(set); 800 } 801 template <RegTypeName Name = RegSet::DefaultType> 802 RegType getAny() const { 803 // The choice of first or last here is mostly arbitrary, as they are 804 // about the same speed on popular architectures. We choose first, as 805 // it has the advantage of using the "lower" registers more often. These 806 // registers are sometimes more efficient (e.g. optimized encodings for 807 // EAX on x86). 808 return getFirst<Name>(); 809 } 810 811 template <RegTypeName Name = RegSet::DefaultType> 812 RegType getAnyExcluding(RegType preclude) { 813 if (!this->has(preclude)) { 814 return getAny<Name>(); 815 } 816 817 take(preclude); 818 RegType result = getAny<Name>(); 819 add(preclude); 820 return result; 821 } 822 823 template <RegTypeName Name = RegSet::DefaultType> 824 RegType takeAny() { 825 RegType reg = getAny<Name>(); 826 take(reg); 827 return reg; 828 } 829 template <RegTypeName Name = RegSet::DefaultType> 830 RegType takeFirst() { 831 RegType reg = getFirst<Name>(); 832 take(reg); 833 return reg; 834 } 835 template <RegTypeName Name = RegSet::DefaultType> 836 RegType takeLast() { 837 RegType reg = getLast<Name>(); 838 take(reg); 839 return reg; 840 } 841 842 ValueOperand takeAnyValue() { 843 #if defined(JS_NUNBOX32) 844 return ValueOperand(takeAny<RegTypeName::GPR>(), 845 takeAny<RegTypeName::GPR>()); 846 #elif defined(JS_PUNBOX64) 847 return ValueOperand(takeAny<RegTypeName::GPR>()); 848 #else 849 # error "Bad architecture" 850 #endif 851 } 852 853 bool aliases(ValueOperand v) const { 854 #ifdef JS_NUNBOX32 855 return this->has(v.typeReg()) || this->has(v.payloadReg()); 856 #else 857 return this->has(v.valueReg()); 858 #endif 859 } 860 861 template <RegTypeName Name = RegSet::DefaultType> 862 RegType takeAnyExcluding(RegType preclude) { 863 RegType reg = getAnyExcluding<Name>(preclude); 864 take(reg); 865 return reg; 866 } 867 }; 868 869 // Specialization of the accessors for the RegisterSet aggregate. 870 template <class Accessors> 871 class SpecializedRegSet<Accessors, RegisterSet> : public Accessors { 872 using Parent = Accessors; 873 874 public: 875 DEFINE_ACCESSOR_CONSTRUCTORS_(SpecializedRegSet) 876 877 GeneralRegisterSet gprs() const { return this->Parent::set_.gprs(); } 878 GeneralRegisterSet& gprs() { return this->Parent::set_.gprs(); } 879 FloatRegisterSet fpus() const { return this->Parent::set_.fpus(); } 880 FloatRegisterSet& fpus() { return this->Parent::set_.fpus(); } 881 882 bool emptyGeneral() const { return this->Parent::set_.emptyGeneral(); } 883 bool emptyFloat() const { return this->Parent::set_.emptyFloat(); } 884 885 using Parent::has; 886 bool has(AnyRegister reg) const { 887 return reg.isFloat() ? this->has(reg.fpu()) : this->has(reg.gpr()); 888 } 889 890 template <RegTypeName Name> 891 bool hasAny() const { 892 if (Name == RegTypeName::GPR) { 893 return Parent::template allGpr<RegTypeName::GPR>() != 0; 894 } 895 return Parent::template allFpu<Name>() != 0; 896 } 897 898 using Parent::addUnchecked; 899 void addUnchecked(AnyRegister reg) { 900 if (reg.isFloat()) { 901 addUnchecked(reg.fpu()); 902 } else { 903 addUnchecked(reg.gpr()); 904 } 905 } 906 907 void add(Register reg) { 908 MOZ_ASSERT(!this->has(reg)); 909 addUnchecked(reg); 910 } 911 void add(FloatRegister reg) { 912 MOZ_ASSERT(!this->has(reg)); 913 addUnchecked(reg); 914 } 915 void add(AnyRegister reg) { 916 if (reg.isFloat()) { 917 add(reg.fpu()); 918 } else { 919 add(reg.gpr()); 920 } 921 } 922 923 using Parent::takeUnchecked; 924 void takeUnchecked(AnyRegister reg) { 925 if (reg.isFloat()) { 926 takeUnchecked(reg.fpu()); 927 } else { 928 takeUnchecked(reg.gpr()); 929 } 930 } 931 932 void take(Register reg) { 933 #ifdef DEBUG 934 bool hasReg = this->has(reg); 935 MOZ_ASSERT(hasReg); 936 #endif 937 takeUnchecked(reg); 938 } 939 void take(FloatRegister reg) { 940 MOZ_ASSERT(this->has(reg)); 941 takeUnchecked(reg); 942 } 943 void take(AnyRegister reg) { 944 if (reg.isFloat()) { 945 take(reg.fpu()); 946 } else { 947 take(reg.gpr()); 948 } 949 } 950 951 Register getAnyGeneral() const { 952 GeneralRegisterSet::SetType set = 953 Parent::template allGpr<RegTypeName::GPR>(); 954 MOZ_ASSERT(set); 955 return GeneralRegisterSet::FirstRegister(set); 956 } 957 template <RegTypeName Name = RegTypeName::Float64> 958 FloatRegister getAnyFloat() const { 959 FloatRegisterSet::SetType set = Parent::template allFpu<Name>(); 960 MOZ_ASSERT(set); 961 return FloatRegisterSet::FirstRegister(set); 962 } 963 964 Register takeAnyGeneral() { 965 Register reg = getAnyGeneral(); 966 take(reg); 967 return reg; 968 } 969 template <RegTypeName Name = RegTypeName::Float64> 970 FloatRegister takeAnyFloat() { 971 FloatRegister reg = getAnyFloat<Name>(); 972 take(reg); 973 return reg; 974 } 975 ValueOperand takeAnyValue() { 976 #if defined(JS_NUNBOX32) 977 return ValueOperand(takeAnyGeneral(), takeAnyGeneral()); 978 #elif defined(JS_PUNBOX64) 979 return ValueOperand(takeAnyGeneral()); 980 #else 981 # error "Bad architecture" 982 #endif 983 } 984 }; 985 986 // Interface which is common to all register set implementations. It overloads 987 // |add|, |take| and |takeUnchecked| methods for types such as |ValueOperand|, 988 // |TypedOrValueRegister|, and |Register64|. 989 template <class Accessors, typename Set> 990 class CommonRegSet : public SpecializedRegSet<Accessors, Set> { 991 using Parent = SpecializedRegSet<Accessors, Set>; 992 993 public: 994 DEFINE_ACCESSOR_CONSTRUCTORS_(CommonRegSet) 995 996 RegSet set() const { return this->Parent::set_; } 997 RegSet& set() { return this->Parent::set_; } 998 999 bool empty() const { return this->Parent::set_.empty(); } 1000 void clear() { this->Parent::set_.clear(); } 1001 1002 using Parent::add; 1003 void add(ValueOperand value) { 1004 #if defined(JS_NUNBOX32) 1005 add(value.payloadReg()); 1006 add(value.typeReg()); 1007 #elif defined(JS_PUNBOX64) 1008 add(value.valueReg()); 1009 #else 1010 # error "Bad architecture" 1011 #endif 1012 } 1013 void add(Register64 reg) { 1014 #if JS_BITS_PER_WORD == 32 1015 add(reg.high); 1016 add(reg.low); 1017 #else 1018 add(reg.reg); 1019 #endif 1020 } 1021 1022 using Parent::addUnchecked; 1023 void addUnchecked(ValueOperand value) { 1024 #if defined(JS_NUNBOX32) 1025 addUnchecked(value.payloadReg()); 1026 addUnchecked(value.typeReg()); 1027 #elif defined(JS_PUNBOX64) 1028 addUnchecked(value.valueReg()); 1029 #else 1030 # error "Bad architecture" 1031 #endif 1032 } 1033 void addUnchecked(Register64 reg) { 1034 #if JS_BITS_PER_WORD == 32 1035 addUnchecked(reg.high); 1036 addUnchecked(reg.low); 1037 #else 1038 addUnchecked(reg.reg); 1039 #endif 1040 } 1041 1042 void add(TypedOrValueRegister reg) { 1043 if (reg.hasValue()) { 1044 add(reg.valueReg()); 1045 } else if (reg.hasTyped()) { 1046 add(reg.typedReg()); 1047 } 1048 } 1049 1050 using Parent::take; 1051 void take(ValueOperand value) { 1052 #if defined(JS_NUNBOX32) 1053 take(value.payloadReg()); 1054 take(value.typeReg()); 1055 #elif defined(JS_PUNBOX64) 1056 take(value.valueReg()); 1057 #else 1058 # error "Bad architecture" 1059 #endif 1060 } 1061 void take(TypedOrValueRegister reg) { 1062 if (reg.hasValue()) { 1063 take(reg.valueReg()); 1064 } else if (reg.hasTyped()) { 1065 take(reg.typedReg()); 1066 } 1067 } 1068 void take(Register64 reg) { 1069 #if JS_BITS_PER_WORD == 32 1070 take(reg.high); 1071 take(reg.low); 1072 #else 1073 take(reg.reg); 1074 #endif 1075 } 1076 1077 using Parent::takeUnchecked; 1078 void takeUnchecked(ValueOperand value) { 1079 #if defined(JS_NUNBOX32) 1080 takeUnchecked(value.payloadReg()); 1081 takeUnchecked(value.typeReg()); 1082 #elif defined(JS_PUNBOX64) 1083 takeUnchecked(value.valueReg()); 1084 #else 1085 # error "Bad architecture" 1086 #endif 1087 } 1088 void takeUnchecked(TypedOrValueRegister reg) { 1089 if (reg.hasValue()) { 1090 takeUnchecked(reg.valueReg()); 1091 } else if (reg.hasTyped()) { 1092 takeUnchecked(reg.typedReg()); 1093 } 1094 } 1095 void takeUnchecked(Register64 reg) { 1096 #if JS_BITS_PER_WORD == 32 1097 takeUnchecked(reg.high); 1098 takeUnchecked(reg.low); 1099 #else 1100 takeUnchecked(reg.reg); 1101 #endif 1102 } 1103 }; 1104 1105 // These classes do not provide any additional members, they only use their 1106 // constructors to forward to the common interface for all register sets. The 1107 // only benefit of these classes is to provide user friendly names. 1108 template <typename Set> 1109 class LiveSet : public CommonRegSet<LiveSetAccessors<Set>, Set> { 1110 using Parent = CommonRegSet<LiveSetAccessors<Set>, Set>; 1111 1112 public: 1113 DEFINE_ACCESSOR_CONSTRUCTORS_(LiveSet) 1114 }; 1115 1116 template <typename Set> 1117 class AllocatableSet : public CommonRegSet<AllocatableSetAccessors<Set>, Set> { 1118 using Parent = CommonRegSet<AllocatableSetAccessors<Set>, Set>; 1119 1120 public: 1121 DEFINE_ACCESSOR_CONSTRUCTORS_(AllocatableSet) 1122 1123 LiveSet<Set> asLiveSet() const { return LiveSet<Set>(this->set()); } 1124 }; 1125 1126 #define DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_(REGSET) \ 1127 using typename Parent::RegSet; \ 1128 using typename Parent::RegType; \ 1129 using typename Parent::SetType; \ 1130 \ 1131 constexpr REGSET() : Parent() {} \ 1132 explicit constexpr REGSET(SetType) = delete; \ 1133 explicit constexpr REGSET(RegSet set) : Parent(set) {} \ 1134 constexpr REGSET(GeneralRegisterSet gpr, FloatRegisterSet fpu) \ 1135 : Parent(RegisterSet(gpr, fpu)) {} \ 1136 REGSET(REGSET<GeneralRegisterSet> gpr, REGSET<FloatRegisterSet> fpu) \ 1137 : Parent(RegisterSet(gpr.set(), fpu.set())) {} 1138 1139 template <> 1140 class LiveSet<RegisterSet> 1141 : public CommonRegSet<LiveSetAccessors<RegisterSet>, RegisterSet> { 1142 // Note: We have to provide a qualified name for LiveSetAccessors, as it is 1143 // interpreted as being the specialized class name inherited from the parent 1144 // class specialization. 1145 using Parent = CommonRegSet<jit::LiveSetAccessors<RegisterSet>, RegisterSet>; 1146 1147 public: 1148 DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_(LiveSet) 1149 }; 1150 1151 template <> 1152 class AllocatableSet<RegisterSet> 1153 : public CommonRegSet<AllocatableSetAccessors<RegisterSet>, RegisterSet> { 1154 // Note: We have to provide a qualified name for AllocatableSetAccessors, as 1155 // it is interpreted as being the specialized class name inherited from the 1156 // parent class specialization. 1157 using Parent = 1158 CommonRegSet<jit::AllocatableSetAccessors<RegisterSet>, RegisterSet>; 1159 1160 public: 1161 DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_(AllocatableSet) 1162 1163 LiveSet<RegisterSet> asLiveSet() const { 1164 return LiveSet<RegisterSet>(this->set()); 1165 } 1166 }; 1167 1168 #undef DEFINE_ACCESSOR_CONSTRUCTORS_FOR_REGISTERSET_ 1169 #undef DEFINE_ACCESSOR_CONSTRUCTORS_ 1170 1171 using AllocatableGeneralRegisterSet = AllocatableSet<GeneralRegisterSet>; 1172 using AllocatableFloatRegisterSet = AllocatableSet<FloatRegisterSet>; 1173 using AllocatableRegisterSet = AllocatableSet<RegisterSet>; 1174 1175 using LiveGeneralRegisterSet = LiveSet<GeneralRegisterSet>; 1176 using LiveFloatRegisterSet = LiveSet<FloatRegisterSet>; 1177 using LiveRegisterSet = LiveSet<RegisterSet>; 1178 1179 // iterates in whatever order happens to be convenient. 1180 // Use TypedRegisterBackwardIterator or TypedRegisterForwardIterator if a 1181 // specific order is required. 1182 template <typename T> 1183 class TypedRegisterIterator { 1184 LiveSet<TypedRegisterSet<T>> regset_; 1185 1186 public: 1187 explicit TypedRegisterIterator(TypedRegisterSet<T> regset) 1188 : regset_(regset) {} 1189 explicit TypedRegisterIterator(LiveSet<TypedRegisterSet<T>> regset) 1190 : regset_(regset) {} 1191 TypedRegisterIterator(const TypedRegisterIterator& other) 1192 : regset_(other.regset_) {} 1193 1194 bool more() const { return !regset_.empty(); } 1195 TypedRegisterIterator<T>& operator++() { 1196 regset_.template takeAny<RegTypeName::Any>(); 1197 return *this; 1198 } 1199 T operator*() const { return regset_.template getAny<RegTypeName::Any>(); } 1200 }; 1201 1202 // iterates backwards, that is, rn to r0 1203 template <typename T> 1204 class TypedRegisterBackwardIterator { 1205 LiveSet<TypedRegisterSet<T>> regset_; 1206 1207 public: 1208 explicit TypedRegisterBackwardIterator(TypedRegisterSet<T> regset) 1209 : regset_(regset) {} 1210 explicit TypedRegisterBackwardIterator(LiveSet<TypedRegisterSet<T>> regset) 1211 : regset_(regset) {} 1212 TypedRegisterBackwardIterator(const TypedRegisterBackwardIterator& other) 1213 : regset_(other.regset_) {} 1214 1215 bool more() const { return !regset_.empty(); } 1216 TypedRegisterBackwardIterator<T>& operator++() { 1217 regset_.template takeLast<RegTypeName::Any>(); 1218 return *this; 1219 } 1220 T operator*() const { return regset_.template getLast<RegTypeName::Any>(); } 1221 }; 1222 1223 // iterates forwards, that is r0 to rn 1224 template <typename T> 1225 class TypedRegisterForwardIterator { 1226 LiveSet<TypedRegisterSet<T>> regset_; 1227 1228 public: 1229 explicit TypedRegisterForwardIterator(TypedRegisterSet<T> regset) 1230 : regset_(regset) {} 1231 explicit TypedRegisterForwardIterator(LiveSet<TypedRegisterSet<T>> regset) 1232 : regset_(regset) {} 1233 TypedRegisterForwardIterator(const TypedRegisterForwardIterator& other) 1234 : regset_(other.regset_) {} 1235 1236 bool more() const { return !regset_.empty(); } 1237 TypedRegisterForwardIterator<T>& operator++() { 1238 regset_.template takeFirst<RegTypeName::Any>(); 1239 return *this; 1240 } 1241 T operator*() const { return regset_.template getFirst<RegTypeName::Any>(); } 1242 }; 1243 1244 using GeneralRegisterIterator = TypedRegisterIterator<Register>; 1245 using FloatRegisterIterator = TypedRegisterIterator<FloatRegister>; 1246 using GeneralRegisterBackwardIterator = TypedRegisterBackwardIterator<Register>; 1247 using FloatRegisterBackwardIterator = 1248 TypedRegisterBackwardIterator<FloatRegister>; 1249 using GeneralRegisterForwardIterator = TypedRegisterForwardIterator<Register>; 1250 using FloatRegisterForwardIterator = 1251 TypedRegisterForwardIterator<FloatRegister>; 1252 1253 class AnyRegisterIterator { 1254 GeneralRegisterIterator geniter_; 1255 FloatRegisterIterator floatiter_; 1256 1257 public: 1258 AnyRegisterIterator() 1259 : geniter_(GeneralRegisterSet::All()), 1260 floatiter_(FloatRegisterSet::All()) {} 1261 AnyRegisterIterator(GeneralRegisterSet genset, FloatRegisterSet floatset) 1262 : geniter_(genset), floatiter_(floatset) {} 1263 explicit AnyRegisterIterator(const RegisterSet& set) 1264 : geniter_(set.gpr_), floatiter_(set.fpu_) {} 1265 explicit AnyRegisterIterator(const LiveSet<RegisterSet>& set) 1266 : geniter_(set.gprs()), floatiter_(set.fpus()) {} 1267 AnyRegisterIterator(const AnyRegisterIterator& other) = default; 1268 bool more() const { return geniter_.more() || floatiter_.more(); } 1269 AnyRegisterIterator& operator++() { 1270 if (geniter_.more()) { 1271 ++geniter_; 1272 } else { 1273 ++floatiter_; 1274 } 1275 return *this; 1276 } 1277 AnyRegister operator*() const { 1278 if (geniter_.more()) { 1279 return AnyRegister(*geniter_); 1280 } 1281 return AnyRegister(*floatiter_); 1282 } 1283 }; 1284 1285 class ABIArg { 1286 public: 1287 enum Kind { 1288 GPR, 1289 #ifdef JS_CODEGEN_REGISTER_PAIR 1290 GPR_PAIR, 1291 #endif 1292 FPU, 1293 Stack, 1294 Uninitialized = -1 1295 }; 1296 1297 private: 1298 Kind kind_; 1299 union { 1300 Register::Code gpr_; 1301 FloatRegister::Code fpu_; 1302 uint32_t offset_; 1303 } u; 1304 1305 public: 1306 ABIArg() : kind_(Uninitialized) { u.offset_ = -1; } 1307 explicit ABIArg(Register gpr) : kind_(GPR) { u.gpr_ = gpr.code(); } 1308 explicit ABIArg(Register gprLow, Register gprHigh) { 1309 #if defined(JS_CODEGEN_REGISTER_PAIR) 1310 kind_ = GPR_PAIR; 1311 #else 1312 MOZ_CRASH("Unsupported type of ABI argument."); 1313 #endif 1314 u.gpr_ = gprLow.code(); 1315 MOZ_ASSERT(u.gpr_ % 2 == 0); 1316 MOZ_ASSERT(u.gpr_ + 1 == gprHigh.code()); 1317 } 1318 explicit ABIArg(FloatRegister fpu) : kind_(FPU) { u.fpu_ = fpu.code(); } 1319 explicit ABIArg(uint32_t offset) : kind_(Stack) { u.offset_ = offset; } 1320 1321 Kind kind() const { 1322 MOZ_ASSERT(kind_ != Uninitialized); 1323 return kind_; 1324 } 1325 #ifdef JS_CODEGEN_REGISTER_PAIR 1326 bool isGeneralRegPair() const { return kind() == GPR_PAIR; } 1327 #else 1328 bool isGeneralRegPair() const { return false; } 1329 #endif 1330 1331 Register gpr() const { 1332 MOZ_ASSERT(kind() == GPR); 1333 return Register::FromCode(u.gpr_); 1334 } 1335 Register64 gpr64() const { 1336 #ifdef JS_PUNBOX64 1337 return Register64(gpr()); 1338 #else 1339 return Register64(oddGpr(), evenGpr()); 1340 #endif 1341 } 1342 Register evenGpr() const { 1343 MOZ_ASSERT(isGeneralRegPair()); 1344 return Register::FromCode(u.gpr_); 1345 } 1346 Register oddGpr() const { 1347 MOZ_ASSERT(isGeneralRegPair()); 1348 return Register::FromCode(u.gpr_ + 1); 1349 } 1350 FloatRegister fpu() const { 1351 MOZ_ASSERT(kind() == FPU); 1352 return FloatRegister::FromCode(u.fpu_); 1353 } 1354 uint32_t offsetFromArgBase() const { 1355 MOZ_ASSERT(kind() == Stack); 1356 return u.offset_; 1357 } 1358 1359 bool argInRegister() const { return kind() != Stack; } 1360 AnyRegister reg() const { 1361 return kind() == GPR ? AnyRegister(gpr()) : AnyRegister(fpu()); 1362 } 1363 1364 bool operator==(const ABIArg& rhs) const { 1365 if (kind_ != rhs.kind_) { 1366 return false; 1367 } 1368 1369 switch (kind_) { 1370 case GPR: 1371 return u.gpr_ == rhs.u.gpr_; 1372 #if defined(JS_CODEGEN_REGISTER_PAIR) 1373 case GPR_PAIR: 1374 return u.gpr_ == rhs.u.gpr_; 1375 #endif 1376 case FPU: 1377 return u.fpu_ == rhs.u.fpu_; 1378 case Stack: 1379 return u.offset_ == rhs.u.offset_; 1380 case Uninitialized: 1381 return true; 1382 } 1383 MOZ_CRASH("Invalid value for ABIArg kind"); 1384 } 1385 1386 bool operator!=(const ABIArg& rhs) const { return !(*this == rhs); } 1387 }; 1388 1389 // Get the set of registers which should be saved by a block of code which 1390 // clobbers all registers besides |unused|, but does not clobber floating point 1391 // registers. 1392 inline LiveGeneralRegisterSet SavedNonVolatileRegisters( 1393 const AllocatableGeneralRegisterSet& unused) { 1394 LiveGeneralRegisterSet result; 1395 1396 for (GeneralRegisterIterator iter(GeneralRegisterSet::NonVolatile()); 1397 iter.more(); ++iter) { 1398 Register reg = *iter; 1399 if (!unused.has(reg)) { 1400 result.add(reg); 1401 } 1402 } 1403 1404 // Some platforms require the link register to be saved, if calls can be made. 1405 #if defined(JS_CODEGEN_ARM) 1406 result.add(Register::FromCode(Registers::lr)); 1407 #elif defined(JS_CODEGEN_ARM64) 1408 result.add(Register::FromCode(Registers::lr)); 1409 #elif defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64) || \ 1410 defined(JS_CODEGEN_RISCV64) 1411 result.add(Register::FromCode(Registers::ra)); 1412 #endif 1413 1414 return result; 1415 } 1416 1417 } // namespace jit 1418 } // namespace js 1419 1420 #endif /* jit_RegisterSets_h */