MIR.h (325080B)
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 /* 8 * Everything needed to build actual MIR instructions: the actual opcodes and 9 * instructions, the instruction interface, and use chains. 10 */ 11 12 #ifndef jit_MIR_h 13 #define jit_MIR_h 14 15 #include "mozilla/Array.h" 16 #include "mozilla/Attributes.h" 17 #include "mozilla/EnumSet.h" 18 #include "mozilla/HashFunctions.h" 19 #ifdef JS_JITSPEW 20 # include "mozilla/Attributes.h" // MOZ_STACK_CLASS 21 #endif 22 #include "mozilla/MacroForEach.h" 23 #ifdef JS_JITSPEW 24 # include "mozilla/Sprintf.h" 25 # include "mozilla/Vector.h" 26 #endif 27 28 #include <algorithm> 29 30 #include "NamespaceImports.h" 31 32 #include "jit/AtomicOp.h" 33 #include "jit/FixedList.h" 34 #include "jit/InlineList.h" 35 #include "jit/JitAllocPolicy.h" 36 #include "jit/MacroAssembler.h" 37 #include "jit/MIROpsGenerated.h" 38 #include "jit/ShuffleAnalysis.h" 39 #include "jit/TypeData.h" 40 #include "jit/TypePolicy.h" 41 #include "js/experimental/JitInfo.h" // JSJit{Getter,Setter}Op, JSJitInfo 42 #include "js/HeapAPI.h" 43 #include "js/ScalarType.h" // js::Scalar::Type 44 #include "js/Value.h" 45 #include "js/Vector.h" 46 #include "vm/BigIntType.h" 47 #include "vm/EnvironmentObject.h" 48 #include "vm/FunctionFlags.h" // js::FunctionFlags 49 #include "vm/JSContext.h" 50 #include "vm/RegExpObject.h" 51 #include "vm/TypedArrayObject.h" 52 #include "wasm/WasmJS.h" // for WasmInstanceObject 53 #include "wasm/WasmValType.h" 54 55 namespace JS { 56 struct ExpandoAndGeneration; 57 } 58 59 namespace js { 60 61 namespace wasm { 62 class FuncExport; 63 extern uint32_t MIRTypeToABIResultSize(jit::MIRType); 64 } // namespace wasm 65 66 class JS_PUBLIC_API GenericPrinter; 67 class NativeIteratorListHead; 68 class StringObject; 69 70 enum class UnaryMathFunction : uint8_t; 71 72 bool CurrentThreadIsIonCompiling(); 73 74 namespace jit { 75 76 class CallInfo; 77 class ShapeListSnapshot; 78 class ShapeListWithOffsetsSnapshot; 79 80 #ifdef JS_JITSPEW 81 // Helper for debug printing. Avoids creating a MIR.h <--> MIRGraph.h cycle. 82 // Implementation of this needs to see inside `MBasicBlock`; that is possible 83 // in MIR.cpp since it also includes MIRGraph.h, whereas this file does not. 84 class MBasicBlock; 85 uint32_t GetMBasicBlockId(const MBasicBlock* block); 86 87 // Helper class for debug printing. This class allows `::getExtras` methods 88 // to add strings to be printed, on a per-MIR-node basis. The strings are 89 // copied into storage owned by this class when `::add` is called, so the 90 // `::getExtras` methods do not need to be concerned about storage management. 91 class MOZ_STACK_CLASS ExtrasCollector { 92 mozilla::Vector<UniqueChars, 4> strings_; 93 94 public: 95 // Add `str` to the collection. A copy, owned by this object, is made. In 96 // case of OOM the call has no effect. 97 void add(const char* str) { 98 UniqueChars dup = DuplicateString(str); 99 if (dup) { 100 (void)strings_.append(std::move(dup)); 101 } 102 } 103 size_t count() const { return strings_.length(); } 104 UniqueChars get(size_t ix) { return std::move(strings_[ix]); } 105 }; 106 #endif 107 108 // Forward declarations of MIR types. 109 #define FORWARD_DECLARE(op) class M##op; 110 MIR_OPCODE_LIST(FORWARD_DECLARE) 111 #undef FORWARD_DECLARE 112 113 // MDefinition visitor which ignores non-overloaded visit functions. 114 class MDefinitionVisitorDefaultNoop { 115 public: 116 #define VISIT_INS(op) \ 117 void visit##op(M##op*) {} 118 MIR_OPCODE_LIST(VISIT_INS) 119 #undef VISIT_INS 120 }; 121 122 class BytecodeSite; 123 class CompactBufferWriter; 124 class Range; 125 126 #define MIR_FLAG_LIST(_) \ 127 _(InWorklist) \ 128 _(EmittedAtUses) \ 129 _(Commutative) \ 130 _(Movable) /* Allow passes like LICM to move this instruction */ \ 131 _(Lowered) /* (Debug only) has a virtual register */ \ 132 _(Guard) /* Not removable if uses == 0 */ \ 133 \ 134 /* Flag an instruction to be considered as a Guard if the instructions \ 135 * bails out on some inputs. \ 136 * \ 137 * Some optimizations can replace an instruction, and leave its operands \ 138 * unused. When the type information of the operand got used as a \ 139 * predicate of the transformation, then we have to flag the operands as \ 140 * GuardRangeBailouts. \ 141 * \ 142 * This flag prevents further optimization of instructions, which \ 143 * might remove the run-time checks (bailout conditions) used as a \ 144 * predicate of the previous transformation. \ 145 */ \ 146 _(GuardRangeBailouts) \ 147 \ 148 /* Some instructions have uses that aren't directly represented in the \ 149 * graph, and need to be handled specially. As an example, this is used to \ 150 * keep the flagged instruction in resume points, not substituting with an \ 151 * UndefinedValue. This can be used by call inlining when a function \ 152 * argument is not used by the inlined instructions. It is also used \ 153 * to annotate instructions which were used in removed branches. \ 154 */ \ 155 _(ImplicitlyUsed) \ 156 \ 157 /* The instruction has been marked dead for lazy removal from resume \ 158 * points. \ 159 */ \ 160 _(Unused) \ 161 \ 162 /* Marks if the current instruction should go to the bailout paths instead \ 163 * of producing code as part of the control flow. This flag can only be set \ 164 * on instructions which are only used by ResumePoint or by other flagged \ 165 * instructions. \ 166 */ \ 167 _(RecoveredOnBailout) \ 168 \ 169 /* Some instructions might represent an object, but the memory of these \ 170 * objects might be incomplete if we have not recovered all the stores which \ 171 * were supposed to happen before. This flag is used to annotate \ 172 * instructions which might return a pointer to a memory area which is not \ 173 * yet fully initialized. This flag is used to ensure that stores are \ 174 * executed before returning the value. \ 175 */ \ 176 _(IncompleteObject) \ 177 \ 178 /* For WebAssembly, there are functions with multiple results. Instead of \ 179 * having the results defined by one call instruction, they are instead \ 180 * captured in subsequent result capture instructions, because modelling \ 181 * multi-value results in Ion is too complicated. However since they \ 182 * capture ambient live registers, it would be an error to move an unrelated \ 183 * instruction between the call and the result capture. This flag is used \ 184 * to prevent code motion from moving instructions in invalid ways. \ 185 */ \ 186 _(CallResultCapture) \ 187 \ 188 /* The current instruction got discarded from the MIR Graph. This is useful \ 189 * when we want to iterate over resume points and instructions, while \ 190 * handling instructions which are discarded without reporting to the \ 191 * iterator. \ 192 */ \ 193 _(Discarded) 194 195 class MDefinition; 196 class MInstruction; 197 class MBasicBlock; 198 class MNode; 199 class MUse; 200 class MPhi; 201 class MIRGraph; 202 class MResumePoint; 203 class MControlInstruction; 204 205 // Represents a use of a node. 206 class MUse : public TempObject, public InlineListNode<MUse> { 207 // Grant access to setProducerUnchecked. 208 friend class MDefinition; 209 friend class MPhi; 210 211 MDefinition* producer_; // MDefinition that is being used. 212 MNode* consumer_; // The node that is using this operand. 213 214 // Low-level unchecked edit method for replaceAllUsesWith and 215 // MPhi::removeOperand. This doesn't update use lists! 216 // replaceAllUsesWith and MPhi::removeOperand do that manually. 217 void setProducerUnchecked(MDefinition* producer) { 218 MOZ_ASSERT(consumer_); 219 MOZ_ASSERT(producer_); 220 MOZ_ASSERT(producer); 221 producer_ = producer; 222 } 223 224 public: 225 // Default constructor for use in vectors. 226 MUse() : producer_(nullptr), consumer_(nullptr) {} 227 228 // Move constructor for use in vectors. When an MUse is moved, it stays 229 // in its containing use list. 230 MUse(MUse&& other) 231 : InlineListNode<MUse>(std::move(other)), 232 producer_(other.producer_), 233 consumer_(other.consumer_) {} 234 235 // Construct an MUse initialized with |producer| and |consumer|. 236 MUse(MDefinition* producer, MNode* consumer) { 237 initUnchecked(producer, consumer); 238 } 239 240 // Set this use, which was previously clear. 241 inline void init(MDefinition* producer, MNode* consumer); 242 // Like init, but works even when the use contains uninitialized data. 243 inline void initUnchecked(MDefinition* producer, MNode* consumer); 244 // Like initUnchecked, but set the producer to nullptr. 245 inline void initUncheckedWithoutProducer(MNode* consumer); 246 // Set this use, which was not previously clear. 247 inline void replaceProducer(MDefinition* producer); 248 // Clear this use. 249 inline void releaseProducer(); 250 251 MDefinition* producer() const { 252 MOZ_ASSERT(producer_ != nullptr); 253 return producer_; 254 } 255 bool hasProducer() const { return producer_ != nullptr; } 256 MNode* consumer() const { 257 MOZ_ASSERT(consumer_ != nullptr); 258 return consumer_; 259 } 260 261 #ifdef DEBUG 262 // Return the operand index of this MUse in its consumer. This is DEBUG-only 263 // as normal code should instead call indexOf on the cast consumer directly, 264 // to allow it to be devirtualized and inlined. 265 size_t index() const; 266 #endif 267 }; 268 269 using MUseIterator = InlineList<MUse>::iterator; 270 271 // A node is an entry in the MIR graph. It has two kinds: 272 // MInstruction: an instruction which appears in the IR stream. 273 // MResumePoint: a list of instructions that correspond to the state of the 274 // interpreter/Baseline stack. 275 // 276 // Nodes can hold references to MDefinitions. Each MDefinition has a list of 277 // nodes holding such a reference (its use chain). 278 class MNode : public TempObject { 279 protected: 280 enum class Kind { Definition = 0, ResumePoint }; 281 282 private: 283 static const uintptr_t KindMask = 0x1; 284 uintptr_t blockAndKind_; 285 286 Kind kind() const { return Kind(blockAndKind_ & KindMask); } 287 288 protected: 289 explicit MNode(const MNode& other) : blockAndKind_(other.blockAndKind_) {} 290 291 MNode(MBasicBlock* block, Kind kind) { setBlockAndKind(block, kind); } 292 293 void setBlockAndKind(MBasicBlock* block, Kind kind) { 294 blockAndKind_ = uintptr_t(block) | uintptr_t(kind); 295 MOZ_ASSERT(this->block() == block); 296 } 297 298 MBasicBlock* definitionBlock() const { 299 MOZ_ASSERT(isDefinition()); 300 static_assert(unsigned(Kind::Definition) == 0, 301 "Code below relies on low bit being 0"); 302 return reinterpret_cast<MBasicBlock*>(blockAndKind_); 303 } 304 MBasicBlock* resumePointBlock() const { 305 MOZ_ASSERT(isResumePoint()); 306 static_assert(unsigned(Kind::ResumePoint) == 1, 307 "Code below relies on low bit being 1"); 308 // Use a subtraction: if the caller does block()->foo, the compiler 309 // will be able to fold it with the load. 310 return reinterpret_cast<MBasicBlock*>(blockAndKind_ - 1); 311 } 312 313 public: 314 // Returns the definition at a given operand. 315 virtual MDefinition* getOperand(size_t index) const = 0; 316 virtual size_t numOperands() const = 0; 317 virtual size_t indexOf(const MUse* u) const = 0; 318 319 bool isDefinition() const { return kind() == Kind::Definition; } 320 bool isResumePoint() const { return kind() == Kind::ResumePoint; } 321 MBasicBlock* block() const { 322 return reinterpret_cast<MBasicBlock*>(blockAndKind_ & ~KindMask); 323 } 324 MBasicBlock* caller() const; 325 326 // Sets an already set operand, updating use information. If you're looking 327 // for setOperand, this is probably what you want. 328 virtual void replaceOperand(size_t index, MDefinition* operand) = 0; 329 330 // Resets the operand to an uninitialized state, breaking the link 331 // with the previous operand's producer. 332 void releaseOperand(size_t index) { getUseFor(index)->releaseProducer(); } 333 bool hasOperand(size_t index) const { 334 return getUseFor(index)->hasProducer(); 335 } 336 337 inline MDefinition* toDefinition(); 338 inline MResumePoint* toResumePoint(); 339 340 [[nodiscard]] virtual bool writeRecoverData( 341 CompactBufferWriter& writer) const; 342 343 #ifdef JS_JITSPEW 344 virtual void dump(GenericPrinter& out) const = 0; 345 virtual void dump() const = 0; 346 #endif 347 348 protected: 349 // Need visibility on getUseFor to avoid O(n^2) complexity. 350 friend void AssertBasicGraphCoherency(MIRGraph& graph, bool force); 351 352 // Gets the MUse corresponding to given operand. 353 virtual MUse* getUseFor(size_t index) = 0; 354 virtual const MUse* getUseFor(size_t index) const = 0; 355 }; 356 357 class AliasSet { 358 private: 359 uint32_t flags_; 360 361 public: 362 enum Flag { 363 None_ = 0, 364 ObjectFields = 1 << 0, // shape, class, slots, length etc. 365 Element = 1 << 1, // A Value member of obj->elements or 366 // a typed object. 367 UnboxedElement = 1 << 2, // An unboxed scalar or reference member of 368 // typed object. 369 DynamicSlot = 1 << 3, // A Value member of obj->slots. 370 FixedSlot = 1 << 4, // A Value member of obj->fixedSlots(). 371 DOMProperty = 1 << 5, // A DOM property 372 WasmInstanceData = 1 << 6, // An asm.js/wasm private global var 373 WasmHeap = 1 << 7, // An asm.js/wasm heap load 374 WasmHeapMeta = 1 << 8, // The asm.js/wasm heap base pointer and 375 // bounds check limit, in Instance. 376 ArrayBufferViewLengthOrOffset = 377 1 << 9, // An array buffer view's length or byteOffset 378 WasmGlobalCell = 1 << 10, // A wasm global cell 379 WasmTableElement = 1 << 11, // An element of a wasm table 380 WasmTableMeta = 1 << 12, // A wasm table elements pointer and 381 // length field, in instance data. 382 WasmStackResult = 1 << 13, // A stack result from the current function 383 384 // JSContext's exception state. This is used on instructions like MThrow 385 // or MNewArrayDynamicLength that throw exceptions (other than OOM) but have 386 // no other side effect, to ensure that they get their own up-to-date resume 387 // point. (This resume point will be used when constructing the Baseline 388 // frame during exception bailouts.) 389 ExceptionState = 1 << 14, 390 391 // Used for instructions that load the privateSlot of DOM proxies and 392 // the ExpandoAndGeneration. 393 DOMProxyExpando = 1 << 15, 394 395 // Hash table of a Map/Set/WeakMap/WeakSet object. 396 MapOrSetHashTable = 1 << 16, 397 398 // Internal state of the random number generator 399 RNG = 1 << 17, 400 401 // The pendingException slot on the wasm instance object. 402 WasmPendingException = 1 << 18, 403 404 // The fuzzilliHash slot 405 FuzzilliHash = 1 << 19, 406 407 // The WasmStructObject::inlineData_[..] storage area 408 WasmStructInlineDataArea = 1 << 20, 409 410 // The WasmStructObject::outlineData_ pointer only 411 WasmStructOutlineDataPointer = 1 << 21, 412 413 // The malloc'd block that WasmStructObject::outlineData_ points at 414 WasmStructOutlineDataArea = 1 << 22, 415 416 // The WasmArrayObject::numElements_ field 417 WasmArrayNumElements = 1 << 23, 418 419 // The WasmArrayObject::data_ pointer only 420 WasmArrayDataPointer = 1 << 24, 421 422 // The malloc'd block that WasmArrayObject::data_ points at 423 WasmArrayDataArea = 1 << 25, 424 425 // The generation counter associated with the global object 426 GlobalGenerationCounter = 1 << 26, 427 428 // The SharedArrayRawBuffer::length field. 429 SharedArrayRawBufferLength = 1 << 27, 430 431 Last = SharedArrayRawBufferLength, 432 433 Any = Last | (Last - 1), 434 NumCategories = 28, 435 436 // Indicates load or store. 437 Store_ = 1 << 31 438 }; 439 440 static_assert((1 << NumCategories) - 1 == Any, 441 "NumCategories must include all flags present in Any"); 442 443 explicit constexpr AliasSet(uint32_t flags) : flags_(flags) {} 444 445 public: 446 inline constexpr bool isNone() const { return flags_ == None_; } 447 constexpr uint32_t flags() const { return flags_ & Any; } 448 inline constexpr bool isStore() const { return !!(flags_ & Store_); } 449 inline constexpr bool isLoad() const { return !isStore() && !isNone(); } 450 inline constexpr AliasSet operator|(const AliasSet& other) const { 451 return AliasSet(flags_ | other.flags_); 452 } 453 inline constexpr AliasSet operator&(const AliasSet& other) const { 454 return AliasSet(flags_ & other.flags_); 455 } 456 inline constexpr AliasSet operator~() const { return AliasSet(~flags_); } 457 static constexpr AliasSet None() { return AliasSet(None_); } 458 static constexpr AliasSet Load(uint32_t flags) { 459 MOZ_ASSERT(flags && !(flags & Store_)); 460 return AliasSet(flags); 461 } 462 static constexpr AliasSet Store(uint32_t flags) { 463 MOZ_ASSERT(flags && !(flags & Store_)); 464 return AliasSet(flags | Store_); 465 } 466 }; 467 468 using MDefinitionVector = Vector<MDefinition*, 6, JitAllocPolicy>; 469 using MInstructionVector = Vector<MInstruction*, 6, JitAllocPolicy>; 470 471 // When a floating-point value is used by nodes which would prefer to 472 // receive integer inputs, we may be able to help by computing our result 473 // into an integer directly. 474 // 475 // A value can be truncated in 4 differents ways: 476 // 1. Ignore Infinities (x / 0 --> 0). 477 // 2. Ignore overflow (INT_MIN / -1 == (INT_MAX + 1) --> INT_MIN) 478 // 3. Ignore negative zeros. (-0 --> 0) 479 // 4. Ignore remainder. (3 / 4 --> 0) 480 // 481 // Indirect truncation is used to represent that we are interested in the 482 // truncated result, but only if it can safely flow into operations which 483 // are computed modulo 2^32, such as (2) and (3). Infinities are not safe, 484 // as they would have absorbed other math operations. Remainders are not 485 // safe, as fractions can be scaled up by multiplication. 486 // 487 // Division is a particularly interesting node here because it covers all 4 488 // cases even when its own operands are integers. 489 // 490 // Note that these enum values are ordered from least value-modifying to 491 // most value-modifying, and code relies on this ordering. 492 enum class TruncateKind { 493 // No correction. 494 NoTruncate = 0, 495 // An integer is desired, but we can't skip bailout checks. 496 TruncateAfterBailouts = 1, 497 // The value will be truncated after some arithmetic (see above). 498 IndirectTruncate = 2, 499 // Direct and infallible truncation to int32. 500 Truncate = 3 501 }; 502 503 // An MDefinition is an SSA name. 504 class MDefinition : public MNode { 505 friend class MBasicBlock; 506 507 public: 508 enum class Opcode : uint16_t { 509 #define DEFINE_OPCODES(op) op, 510 MIR_OPCODE_LIST(DEFINE_OPCODES) 511 #undef DEFINE_OPCODES 512 }; 513 514 private: 515 InlineList<MUse> uses_; // Use chain. 516 uint32_t id_; // Instruction ID, which after block re-ordering 517 // is sorted within a basic block. 518 Opcode op_; // Opcode. 519 uint16_t flags_; // Bit flags. 520 Range* range_; // Any computed range for this def. 521 union { 522 MDefinition* 523 loadDependency_; // Implicit dependency (store, call, etc.) of this 524 // instruction. Used by alias analysis, GVN and LICM. 525 uint32_t virtualRegister_; // Used by lowering to map definitions to 526 // virtual registers. 527 }; 528 529 // Track bailouts by storing the current pc in MIR instruction. Also used 530 // for profiling and keeping track of what the last known pc was. 531 const BytecodeSite* trackedSite_; 532 533 // For nodes of MIRType::WasmAnyRef, a type precisely describing the value of 534 // the node. It is set by the "Track wasm ref types" pass in Ion, and enables 535 // GVN and LICM to perform more advanced optimizations (such as allowing 536 // instructions to move if the source values are non-null, or omitting casts 537 // that are statically known to succeed or fail). 538 wasm::MaybeRefType wasmRefType_; 539 540 // If we generate a bailout path for this instruction, this is the 541 // bailout kind that will be encoded in the snapshot. When we bail out, 542 // FinishBailoutToBaseline may take action based on the bailout kind to 543 // prevent bailout loops. (For example, if an instruction bails out after 544 // being hoisted by LICM, we will disable LICM when recompiling the script.) 545 BailoutKind bailoutKind_; 546 547 MIRType resultType_; // Representation of result type. 548 549 private: 550 enum Flag { 551 None = 0, 552 #define DEFINE_FLAG(flag) flag, 553 MIR_FLAG_LIST(DEFINE_FLAG) 554 #undef DEFINE_FLAG 555 Total 556 }; 557 558 bool hasFlags(uint32_t flags) const { return (flags_ & flags) == flags; } 559 void removeFlags(uint32_t flags) { flags_ &= ~flags; } 560 void setFlags(uint32_t flags) { flags_ |= flags; } 561 562 // Calling isDefinition or isResumePoint on MDefinition is unnecessary. 563 bool isDefinition() const = delete; 564 bool isResumePoint() const = delete; 565 566 protected: 567 void setInstructionBlock(MBasicBlock* block, const BytecodeSite* site) { 568 MOZ_ASSERT(isInstruction()); 569 setBlockAndKind(block, Kind::Definition); 570 setTrackedSite(site); 571 } 572 573 void setPhiBlock(MBasicBlock* block) { 574 MOZ_ASSERT(isPhi()); 575 setBlockAndKind(block, Kind::Definition); 576 } 577 578 static HashNumber addU32ToHash(HashNumber hash, uint32_t data) { 579 return data + (hash << 6) + (hash << 16) - hash; 580 } 581 582 static HashNumber addU64ToHash(HashNumber hash, uint64_t data) { 583 hash = addU32ToHash(hash, uint32_t(data)); 584 hash = addU32ToHash(hash, uint32_t(data >> 32)); 585 return hash; 586 } 587 588 public: 589 explicit MDefinition(Opcode op) 590 : MNode(nullptr, Kind::Definition), 591 id_(0), 592 op_(op), 593 flags_(0), 594 range_(nullptr), 595 loadDependency_(nullptr), 596 trackedSite_(nullptr), 597 bailoutKind_(BailoutKind::Unknown), 598 resultType_(MIRType::None) {} 599 600 // Copying a definition leaves the list of uses empty. 601 explicit MDefinition(const MDefinition& other) 602 : MNode(other), 603 id_(0), 604 op_(other.op_), 605 flags_(other.flags_), 606 range_(other.range_), 607 loadDependency_(other.loadDependency_), 608 trackedSite_(other.trackedSite_), 609 bailoutKind_(other.bailoutKind_), 610 resultType_(other.resultType_) {} 611 612 Opcode op() const { return op_; } 613 614 #ifdef JS_JITSPEW 615 const char* opName() const; 616 void printName(GenericPrinter& out) const; 617 static void PrintOpcodeName(GenericPrinter& out, Opcode op); 618 virtual void printOpcode(GenericPrinter& out) const; 619 void dump(GenericPrinter& out) const override; 620 void dump() const override; 621 void dumpLocation(GenericPrinter& out) const; 622 void dumpLocation() const; 623 // Dump any other stuff the node wants to have printed in `extras`. The 624 // added strings are copied, with the `ExtrasCollector` taking ownership of 625 // the copies. 626 virtual void getExtras(ExtrasCollector* extras) const {} 627 #endif 628 629 // Also for LICM. Test whether this definition is likely to be a call, which 630 // would clobber all or many of the floating-point registers, such that 631 // hoisting floating-point constants out of containing loops isn't likely to 632 // be worthwhile. 633 virtual bool possiblyCalls() const { return false; } 634 635 MBasicBlock* block() const { return definitionBlock(); } 636 637 private: 638 void setTrackedSite(const BytecodeSite* site) { 639 MOZ_ASSERT(site); 640 trackedSite_ = site; 641 } 642 643 public: 644 const BytecodeSite* trackedSite() const { 645 MOZ_ASSERT(trackedSite_, 646 "missing tracked bytecode site; node not assigned to a block?"); 647 return trackedSite_; 648 } 649 650 BailoutKind bailoutKind() const { return bailoutKind_; } 651 void setBailoutKind(BailoutKind kind) { bailoutKind_ = kind; } 652 653 // Return the range of this value, *before* any bailout checks. Contrast 654 // this with the type() method, and the Range constructor which takes an 655 // MDefinition*, which describe the value *after* any bailout checks. 656 // 657 // Warning: Range analysis is removing the bit-operations such as '| 0' at 658 // the end of the transformations. Using this function to analyse any 659 // operands after the truncate phase of the range analysis will lead to 660 // errors. Instead, one should define the collectRangeInfoPreTrunc() to set 661 // the right set of flags which are dependent on the range of the inputs. 662 Range* range() const { 663 MOZ_ASSERT(type() != MIRType::None); 664 return range_; 665 } 666 void setRange(Range* range) { 667 MOZ_ASSERT(type() != MIRType::None); 668 range_ = range; 669 } 670 671 virtual HashNumber valueHash() const; 672 virtual bool congruentTo(const MDefinition* ins) const { return false; } 673 const MDefinition* skipObjectGuards() const; 674 675 // Note that, for a call `congruentIfOperandsEqual(ins)` inside some class 676 // MFoo, if `true` is returned then we are ensured that `ins` is also an 677 // MFoo, so it is safe to do `ins->toMFoo()` without first checking whether 678 // `ins->isMFoo()`. 679 bool congruentIfOperandsEqual(const MDefinition* ins) const; 680 681 virtual MDefinition* foldsTo(TempAllocator& alloc); 682 virtual void analyzeEdgeCasesForward(); 683 virtual void analyzeEdgeCasesBackward(); 684 685 // |canTruncate| reports if this instruction supports truncation. If 686 // |canTruncate| function returns true, then the |truncate| function is 687 // called on the same instruction to mutate the instruction, such as updating 688 // the return type, the range and the specialization of the instruction. 689 virtual bool canTruncate() const; 690 virtual void truncate(TruncateKind kind); 691 692 // Determine what kind of truncate this node prefers for the operand at the 693 // given index. 694 virtual TruncateKind operandTruncateKind(size_t index) const; 695 696 // Compute an absolute or symbolic range for the value of this node. 697 virtual void computeRange(TempAllocator& alloc) {} 698 699 // Collect information from the pre-truncated ranges. 700 virtual void collectRangeInfoPreTrunc() {} 701 702 uint32_t id() const { 703 MOZ_ASSERT(block()); 704 return id_; 705 } 706 void setId(uint32_t id) { id_ = id; } 707 708 #define FLAG_ACCESSOR(flag) \ 709 bool is##flag() const { \ 710 static_assert(Flag::Total <= sizeof(flags_) * 8, \ 711 "Flags should fit in flags_ field"); \ 712 return hasFlags(1 << flag); \ 713 } \ 714 void set##flag() { \ 715 MOZ_ASSERT(!hasFlags(1 << flag)); \ 716 setFlags(1 << flag); \ 717 } \ 718 void setNot##flag() { \ 719 MOZ_ASSERT(hasFlags(1 << flag)); \ 720 removeFlags(1 << flag); \ 721 } \ 722 void set##flag##Unchecked() { setFlags(1 << flag); } \ 723 void setNot##flag##Unchecked() { removeFlags(1 << flag); } 724 725 MIR_FLAG_LIST(FLAG_ACCESSOR) 726 #undef FLAG_ACCESSOR 727 728 bool hasAnyFlags() const { return flags_ != 0; } 729 730 // Return the type of this value. This may be speculative, and enforced 731 // dynamically with the use of bailout checks. If all the bailout checks 732 // pass, the value will have this type. 733 // 734 // Unless this is an MUrsh that has bailouts disabled, which, as a special 735 // case, may return a value in (INT32_MAX,UINT32_MAX] even when its type() 736 // is MIRType::Int32. 737 MIRType type() const { return resultType_; } 738 739 // Default EnumSet serialization is based on the enum's underlying type, which 740 // means uint8_t for MIRType. To store all possible MIRType values we need at 741 // least uint32_t. 742 using MIRTypeEnumSet = mozilla::EnumSet<MIRType, uint32_t>; 743 static_assert(static_cast<size_t>(MIRType::Last) < 744 sizeof(MIRTypeEnumSet::serializedType) * CHAR_BIT); 745 746 // Get the wasm reference type stored on the node. Do NOT use in congruentTo, 747 // as this value can change throughout the optimization process. See 748 // ReplaceAllUsesWith in ValueNumbering.cpp. 749 wasm::MaybeRefType wasmRefType() const { return wasmRefType_; } 750 751 // Sets the wasm reference type stored on the node. Does not check if there 752 // was already a type on the node, which may lead to bugs; consider using 753 // `initWasmRefType` instead if it applies. 754 void setWasmRefType(wasm::MaybeRefType refType) { wasmRefType_ = refType; } 755 756 // Sets the wasm reference type stored on the node. To be used for nodes that 757 // have a fixed ref type that is set up front, which is a common case. Must be 758 // called only during the node constructor and never again afterward. 759 void initWasmRefType(wasm::MaybeRefType refType) { 760 MOZ_ASSERT(!wasmRefType_); 761 setWasmRefType(refType); 762 } 763 764 // Compute the wasm reference type for this node. This method is called by 765 // updateWasmRefType. By default it returns the ref type stored on the node, 766 // which means it will return either Nothing or a value set by 767 // initWasmRefType. 768 virtual wasm::MaybeRefType computeWasmRefType() const { return wasmRefType_; } 769 770 // Return true if the result type is a member of the given types. 771 bool typeIsOneOf(MIRTypeEnumSet types) const { 772 MOZ_ASSERT(!types.isEmpty()); 773 return types.contains(type()); 774 } 775 776 // Float32 specialization operations (see big comment in IonAnalysis before 777 // the Float32 specialization algorithm). 778 virtual bool isFloat32Commutative() const { return false; } 779 virtual bool canProduceFloat32() const { return false; } 780 virtual bool canConsumeFloat32(MUse* use) const { return false; } 781 virtual void trySpecializeFloat32(TempAllocator& alloc) {} 782 #ifdef DEBUG 783 // Used during the pass that checks that Float32 flow into valid MDefinitions 784 virtual bool isConsistentFloat32Use(MUse* use) const { 785 return type() == MIRType::Float32 || canConsumeFloat32(use); 786 } 787 #endif 788 789 // Returns the beginning of this definition's use chain. 790 MUseIterator usesBegin() const { return uses_.begin(); } 791 792 // Returns the end of this definition's use chain. 793 MUseIterator usesEnd() const { return uses_.end(); } 794 795 bool canEmitAtUses() const { return !isEmittedAtUses(); } 796 797 // Removes a use at the given position 798 void removeUse(MUse* use) { uses_.remove(use); } 799 800 #if defined(DEBUG) || defined(JS_JITSPEW) 801 // Number of uses of this instruction. This function is only available 802 // in DEBUG mode since it requires traversing the list. Most users should 803 // use hasUses() or hasOneUse() instead. 804 size_t useCount() const; 805 806 // Number of uses of this instruction (only counting MDefinitions, ignoring 807 // MResumePoints). This function is only available in DEBUG mode since it 808 // requires traversing the list. Most users should use hasUses() or 809 // hasOneUse() instead. 810 size_t defUseCount() const; 811 #endif 812 813 // Test whether this MDefinition has exactly one use. 814 bool hasOneUse() const; 815 816 // Test whether this MDefinition has exactly one use. 817 // (only counting MDefinitions, ignoring MResumePoints) 818 bool hasOneDefUse() const; 819 820 // Test whether this MDefinition has exactly one live use. (only counting 821 // MDefinitions which are not recovered on bailout and ignoring MResumePoints) 822 bool hasOneLiveDefUse() const; 823 824 // Test whether this MDefinition has at least one use. 825 // (only counting MDefinitions, ignoring MResumePoints) 826 bool hasDefUses() const; 827 828 // Test whether this MDefinition has at least one non-recovered use. 829 // (only counting MDefinitions, ignoring MResumePoints) 830 bool hasLiveDefUses() const; 831 832 bool hasUses() const { return !uses_.empty(); } 833 834 // If this MDefinition has a single use (ignoring MResumePoints), returns that 835 // use's definition. Else returns nullptr. 836 MDefinition* maybeSingleDefUse() const; 837 838 // Returns the most recently added use (ignoring MResumePoints) for this 839 // MDefinition. Returns nullptr if there are no uses. Note that this relies on 840 // addUse adding new uses to the front of the list, and should only be called 841 // during MIR building (before optimization passes make changes to the uses). 842 MDefinition* maybeMostRecentlyAddedDefUse() const; 843 844 void addUse(MUse* use) { 845 MOZ_ASSERT(use->producer() == this); 846 uses_.pushFront(use); 847 } 848 void addUseUnchecked(MUse* use) { 849 MOZ_ASSERT(use->producer() == this); 850 uses_.pushFrontUnchecked(use); 851 } 852 void replaceUse(MUse* old, MUse* now) { 853 MOZ_ASSERT(now->producer() == this); 854 uses_.replace(old, now); 855 } 856 857 // Replace the current instruction by a dominating instruction |dom| in all 858 // uses of the current instruction. 859 void replaceAllUsesWith(MDefinition* dom); 860 861 // Like replaceAllUsesWith, but doesn't set ImplicitlyUsed on |this|'s 862 // operands. 863 void justReplaceAllUsesWith(MDefinition* dom); 864 865 // Replace the current instruction by an optimized-out constant in all uses 866 // of the current instruction. Note, that optimized-out constant should not 867 // be observed, and thus they should not flow in any computation. 868 [[nodiscard]] bool optimizeOutAllUses(TempAllocator& alloc); 869 870 // Replace the current instruction by a dominating instruction |dom| in all 871 // instruction, but keep the current instruction for resume point and 872 // instruction which are recovered on bailouts. 873 void replaceAllLiveUsesWith(MDefinition* dom); 874 875 void setVirtualRegister(uint32_t vreg) { 876 virtualRegister_ = vreg; 877 setLoweredUnchecked(); 878 } 879 uint32_t virtualRegister() const { 880 MOZ_ASSERT(isLowered()); 881 return virtualRegister_; 882 } 883 884 public: 885 // Opcode testing and casts. 886 template <typename MIRType> 887 bool is() const { 888 return op() == MIRType::classOpcode; 889 } 890 template <typename MIRType> 891 MIRType* to() { 892 MOZ_ASSERT(this->is<MIRType>()); 893 return static_cast<MIRType*>(this); 894 } 895 template <typename MIRType> 896 const MIRType* to() const { 897 MOZ_ASSERT(this->is<MIRType>()); 898 return static_cast<const MIRType*>(this); 899 } 900 #define OPCODE_CASTS(opcode) \ 901 bool is##opcode() const { return this->is<M##opcode>(); } \ 902 M##opcode* to##opcode() { return this->to<M##opcode>(); } \ 903 const M##opcode* to##opcode() const { return this->to<M##opcode>(); } 904 MIR_OPCODE_LIST(OPCODE_CASTS) 905 #undef OPCODE_CASTS 906 907 inline MConstant* maybeConstantValue(); 908 909 inline MInstruction* toInstruction(); 910 inline const MInstruction* toInstruction() const; 911 bool isInstruction() const { return !isPhi(); } 912 913 virtual bool isControlInstruction() const { return false; } 914 inline MControlInstruction* toControlInstruction(); 915 916 void setResultType(MIRType type) { resultType_ = type; } 917 virtual AliasSet getAliasSet() const { 918 // Instructions are effectful by default. 919 return AliasSet::Store(AliasSet::Any); 920 } 921 922 #ifdef DEBUG 923 bool hasDefaultAliasSet() const { 924 AliasSet set = getAliasSet(); 925 return set.isStore() && set.flags() == AliasSet::Flag::Any; 926 } 927 #endif 928 929 MDefinition* dependency() const { 930 MOZ_ASSERT_IF(getAliasSet().isStore(), !loadDependency_); 931 return loadDependency_; 932 } 933 void setDependency(MDefinition* dependency) { 934 MOZ_ASSERT(!getAliasSet().isStore()); 935 loadDependency_ = dependency; 936 } 937 bool isEffectful() const { return getAliasSet().isStore(); } 938 939 #ifdef DEBUG 940 bool needsResumePoint() const { 941 // Return whether this instruction should have its own resume point. 942 return isEffectful(); 943 } 944 #endif 945 946 enum class AliasType : uint32_t { NoAlias = 0, MayAlias = 1, MustAlias = 2 }; 947 virtual AliasType mightAlias(const MDefinition* store) const { 948 // Return whether this load may depend on the specified store, given 949 // that the alias sets intersect. This may be refined to exclude 950 // possible aliasing in cases where alias set flags are too imprecise. 951 if (!(getAliasSet().flags() & store->getAliasSet().flags())) { 952 return AliasType::NoAlias; 953 } 954 MOZ_ASSERT(!isEffectful() && store->isEffectful()); 955 return AliasType::MayAlias; 956 } 957 958 virtual bool canRecoverOnBailout() const { return false; } 959 }; 960 961 // An MUseDefIterator walks over uses in a definition, skipping any use that is 962 // not a definition. Items from the use list must not be deleted during 963 // iteration. 964 class MUseDefIterator { 965 const MDefinition* def_; 966 MUseIterator current_; 967 968 MUseIterator search(MUseIterator start) { 969 MUseIterator i(start); 970 for (; i != def_->usesEnd(); i++) { 971 if (i->consumer()->isDefinition()) { 972 return i; 973 } 974 } 975 return def_->usesEnd(); 976 } 977 978 public: 979 explicit MUseDefIterator(const MDefinition* def) 980 : def_(def), current_(search(def->usesBegin())) {} 981 982 explicit operator bool() const { return current_ != def_->usesEnd(); } 983 MUseDefIterator operator++() { 984 MOZ_ASSERT(current_ != def_->usesEnd()); 985 ++current_; 986 current_ = search(current_); 987 return *this; 988 } 989 MUseDefIterator operator++(int) { 990 MUseDefIterator old(*this); 991 operator++(); 992 return old; 993 } 994 MUse* use() const { return *current_; } 995 MDefinition* def() const { return current_->consumer()->toDefinition(); } 996 }; 997 998 // Helper class to check that GC pointers embedded in MIR instructions are not 999 // in the nursery. Off-thread compilation and nursery GCs can happen in 1000 // parallel. Nursery pointers are handled with MNurseryObject and the 1001 // nurseryObjects lists in WarpSnapshot and IonScript. 1002 // 1003 // These GC things are rooted through the WarpSnapshot. Compacting GCs cancel 1004 // off-thread compilations. 1005 template <typename T> 1006 class CompilerGCPointer { 1007 js::gc::Cell* ptr_; 1008 1009 public: 1010 explicit CompilerGCPointer(T ptr) : ptr_(ptr) { 1011 MOZ_ASSERT_IF(ptr, !IsInsideNursery(ptr)); 1012 MOZ_ASSERT_IF(!CurrentThreadIsIonCompiling(), TlsContext.get()->suppressGC); 1013 } 1014 1015 operator T() const { return static_cast<T>(ptr_); } 1016 T operator->() const { return static_cast<T>(ptr_); } 1017 1018 private: 1019 CompilerGCPointer() = delete; 1020 CompilerGCPointer(const CompilerGCPointer<T>&) = delete; 1021 CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete; 1022 }; 1023 1024 using CompilerObject = CompilerGCPointer<JSObject*>; 1025 using CompilerNativeObject = CompilerGCPointer<NativeObject*>; 1026 using CompilerFunction = CompilerGCPointer<JSFunction*>; 1027 using CompilerBaseScript = CompilerGCPointer<BaseScript*>; 1028 using CompilerPropertyName = CompilerGCPointer<PropertyName*>; 1029 using CompilerShape = CompilerGCPointer<Shape*>; 1030 using CompilerGetterSetter = CompilerGCPointer<GetterSetter*>; 1031 1032 // An instruction is an SSA name that is inserted into a basic block's IR 1033 // stream. 1034 class MInstruction : public MDefinition, public InlineListNode<MInstruction> { 1035 MResumePoint* resumePoint_; 1036 1037 protected: 1038 // All MInstructions are using the "MFoo::New(alloc)" notation instead of 1039 // the TempObject new operator. This code redefines the new operator as 1040 // protected, and delegates to the TempObject new operator. Thus, the 1041 // following code prevents calls to "new(alloc) MFoo" outside the MFoo 1042 // members. 1043 inline void* operator new(size_t nbytes, 1044 TempAllocator::Fallible view) noexcept(true) { 1045 return TempObject::operator new(nbytes, view); 1046 } 1047 inline void* operator new(size_t nbytes, TempAllocator& alloc) { 1048 return TempObject::operator new(nbytes, alloc); 1049 } 1050 template <class T> 1051 inline void* operator new(size_t nbytes, T* pos) { 1052 return TempObject::operator new(nbytes, pos); 1053 } 1054 1055 public: 1056 explicit MInstruction(Opcode op) : MDefinition(op), resumePoint_(nullptr) {} 1057 1058 // Copying an instruction leaves the resume point as empty. 1059 explicit MInstruction(const MInstruction& other) 1060 : MDefinition(other), resumePoint_(nullptr) {} 1061 1062 // Convenient function used for replacing a load by the value of the store 1063 // if the types are match, and boxing the value if they do not match. 1064 MDefinition* foldsToStore(TempAllocator& alloc); 1065 1066 void setResumePoint(MResumePoint* resumePoint); 1067 void stealResumePoint(MInstruction* other); 1068 1069 void moveResumePointAsEntry(); 1070 void clearResumePoint(); 1071 MResumePoint* resumePoint() const { return resumePoint_; } 1072 1073 // For instructions which can be cloned with new inputs, with all other 1074 // information being the same. clone() implementations do not need to worry 1075 // about cloning generic MInstruction/MDefinition state like flags and 1076 // resume points. 1077 virtual bool canClone() const { return false; } 1078 virtual MInstruction* clone(TempAllocator& alloc, 1079 const MDefinitionVector& inputs) const { 1080 MOZ_CRASH(); 1081 } 1082 1083 // Instructions needing to hook into type analysis should return a 1084 // TypePolicy. 1085 virtual const TypePolicy* typePolicy() = 0; 1086 virtual MIRType typePolicySpecialization() = 0; 1087 }; 1088 1089 // Note: GenerateOpcodeFiles.py generates MOpcodesGenerated.h based on the 1090 // INSTRUCTION_HEADER* macros. 1091 #define INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode) \ 1092 static const Opcode classOpcode = Opcode::opcode; \ 1093 using MThisOpcode = M##opcode; 1094 1095 #define INSTRUCTION_HEADER(opcode) \ 1096 INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode) \ 1097 virtual const TypePolicy* typePolicy() override; \ 1098 virtual MIRType typePolicySpecialization() override; 1099 1100 #define ALLOW_CLONE(typename) \ 1101 bool canClone() const override { return true; } \ 1102 MInstruction* clone(TempAllocator& alloc, const MDefinitionVector& inputs) \ 1103 const override { \ 1104 MOZ_ASSERT(numOperands() == inputs.length()); \ 1105 MInstruction* res = new (alloc) typename(*this); \ 1106 if (!res) { \ 1107 return nullptr; \ 1108 } \ 1109 for (size_t i = 0; i < numOperands(); i++) { \ 1110 res->replaceOperand(i, inputs[i]); \ 1111 } \ 1112 return res; \ 1113 } 1114 1115 // Adds MFoo::New functions which are mirroring the arguments of the 1116 // constructors. Opcodes which are using this macro can be called with a 1117 // TempAllocator, or the fallible version of the TempAllocator. 1118 #define TRIVIAL_NEW_WRAPPERS \ 1119 template <typename... Args> \ 1120 static MThisOpcode* New(TempAllocator& alloc, Args&&... args) { \ 1121 return new (alloc) MThisOpcode(std::forward<Args>(args)...); \ 1122 } \ 1123 template <typename... Args> \ 1124 static MThisOpcode* New(TempAllocator::Fallible alloc, Args&&... args) { \ 1125 return new (alloc) MThisOpcode(std::forward<Args>(args)...); \ 1126 } 1127 1128 // These macros are used as a syntactic sugar for writting getOperand 1129 // accessors. They are meant to be used in the body of MIR Instructions as 1130 // follows: 1131 // 1132 // public: 1133 // INSTRUCTION_HEADER(Foo) 1134 // NAMED_OPERANDS((0, lhs), (1, rhs)) 1135 // 1136 // The above example defines 2 accessors, one named "lhs" accessing the first 1137 // operand, and a one named "rhs" accessing the second operand. 1138 #define NAMED_OPERAND_ACCESSOR(Index, Name) \ 1139 MDefinition* Name() const { return getOperand(Index); } 1140 #define NAMED_OPERAND_ACCESSOR_APPLY(Args) NAMED_OPERAND_ACCESSOR Args 1141 #define NAMED_OPERANDS(...) \ 1142 MOZ_FOR_EACH(NAMED_OPERAND_ACCESSOR_APPLY, (), (__VA_ARGS__)) 1143 1144 template <size_t Arity> 1145 class MAryInstruction : public MInstruction { 1146 MOZ_NO_UNIQUE_ADDRESS mozilla::Array<MUse, Arity> operands_; 1147 1148 protected: 1149 MUse* getUseFor(size_t index) final { return &operands_[index]; } 1150 const MUse* getUseFor(size_t index) const final { return &operands_[index]; } 1151 void initOperand(size_t index, MDefinition* operand) { 1152 operands_[index].init(operand, this); 1153 } 1154 1155 public: 1156 MDefinition* getOperand(size_t index) const final { 1157 return operands_[index].producer(); 1158 } 1159 size_t numOperands() const final { return Arity; } 1160 #ifdef DEBUG 1161 static const size_t staticNumOperands = Arity; 1162 #endif 1163 size_t indexOf(const MUse* u) const final { 1164 MOZ_ASSERT(u >= &operands_[0]); 1165 MOZ_ASSERT(u <= &operands_[numOperands() - 1]); 1166 return u - &operands_[0]; 1167 } 1168 void replaceOperand(size_t index, MDefinition* operand) final { 1169 operands_[index].replaceProducer(operand); 1170 } 1171 1172 explicit MAryInstruction(Opcode op) : MInstruction(op) {} 1173 1174 explicit MAryInstruction(const MAryInstruction<Arity>& other) 1175 : MInstruction(other) { 1176 for (int i = 0; i < (int)Arity; 1177 i++) { // N.B. use |int| to avoid warnings when Arity == 0 1178 operands_[i].init(other.operands_[i].producer(), this); 1179 } 1180 } 1181 }; 1182 1183 class MNullaryInstruction : public MAryInstruction<0>, 1184 public NoTypePolicy::Data { 1185 protected: 1186 explicit MNullaryInstruction(Opcode op) : MAryInstruction(op) {} 1187 1188 HashNumber valueHash() const override; 1189 }; 1190 1191 class MUnaryInstruction : public MAryInstruction<1> { 1192 protected: 1193 MUnaryInstruction(Opcode op, MDefinition* ins) : MAryInstruction(op) { 1194 initOperand(0, ins); 1195 } 1196 1197 HashNumber valueHash() const override; 1198 1199 public: 1200 NAMED_OPERANDS((0, input)) 1201 }; 1202 1203 class MBinaryInstruction : public MAryInstruction<2> { 1204 protected: 1205 MBinaryInstruction(Opcode op, MDefinition* left, MDefinition* right) 1206 : MAryInstruction(op) { 1207 initOperand(0, left); 1208 initOperand(1, right); 1209 } 1210 1211 public: 1212 NAMED_OPERANDS((0, lhs), (1, rhs)) 1213 1214 protected: 1215 HashNumber valueHash() const override; 1216 1217 bool binaryCongruentTo(const MDefinition* ins) const { 1218 if (op() != ins->op()) { 1219 return false; 1220 } 1221 1222 if (type() != ins->type()) { 1223 return false; 1224 } 1225 1226 if (isEffectful() || ins->isEffectful()) { 1227 return false; 1228 } 1229 1230 const MDefinition* left = getOperand(0); 1231 const MDefinition* right = getOperand(1); 1232 if (isCommutative() && left->id() > right->id()) { 1233 std::swap(left, right); 1234 } 1235 1236 const MBinaryInstruction* bi = static_cast<const MBinaryInstruction*>(ins); 1237 const MDefinition* insLeft = bi->getOperand(0); 1238 const MDefinition* insRight = bi->getOperand(1); 1239 if (bi->isCommutative() && insLeft->id() > insRight->id()) { 1240 std::swap(insLeft, insRight); 1241 } 1242 1243 return left == insLeft && right == insRight; 1244 } 1245 1246 public: 1247 // Return if the operands to this instruction are both unsigned. 1248 static bool unsignedOperands(MDefinition* left, MDefinition* right); 1249 bool unsignedOperands(); 1250 1251 // Replace any wrapping operands with the underlying int32 operands 1252 // in case of unsigned operands. 1253 void replaceWithUnsignedOperands(); 1254 }; 1255 1256 class MTernaryInstruction : public MAryInstruction<3> { 1257 protected: 1258 MTernaryInstruction(Opcode op, MDefinition* first, MDefinition* second, 1259 MDefinition* third) 1260 : MAryInstruction(op) { 1261 initOperand(0, first); 1262 initOperand(1, second); 1263 initOperand(2, third); 1264 } 1265 1266 HashNumber valueHash() const override; 1267 }; 1268 1269 class MQuaternaryInstruction : public MAryInstruction<4> { 1270 protected: 1271 MQuaternaryInstruction(Opcode op, MDefinition* first, MDefinition* second, 1272 MDefinition* third, MDefinition* fourth) 1273 : MAryInstruction(op) { 1274 initOperand(0, first); 1275 initOperand(1, second); 1276 initOperand(2, third); 1277 initOperand(3, fourth); 1278 } 1279 1280 HashNumber valueHash() const override; 1281 }; 1282 1283 class MQuinaryInstruction : public MAryInstruction<5> { 1284 protected: 1285 MQuinaryInstruction(Opcode op, MDefinition* first, MDefinition* second, 1286 MDefinition* third, MDefinition* fourth, 1287 MDefinition* fifth) 1288 : MAryInstruction(op) { 1289 initOperand(0, first); 1290 initOperand(1, second); 1291 initOperand(2, third); 1292 initOperand(3, fourth); 1293 initOperand(4, fifth); 1294 } 1295 1296 HashNumber valueHash() const override; 1297 }; 1298 1299 template <class T> 1300 class MVariadicT : public T { 1301 FixedList<MUse> operands_; 1302 1303 protected: 1304 explicit MVariadicT(typename T::Opcode op) : T(op) {} 1305 [[nodiscard]] bool init(TempAllocator& alloc, size_t length) { 1306 return operands_.init(alloc, length); 1307 } 1308 void initOperand(size_t index, MDefinition* operand) { 1309 // FixedList doesn't initialize its elements, so do an unchecked init. 1310 operands_[index].initUnchecked(operand, this); 1311 } 1312 MUse* getUseFor(size_t index) final { return &operands_[index]; } 1313 const MUse* getUseFor(size_t index) const final { return &operands_[index]; } 1314 1315 // The MWasmCallBase mixin performs initialization for it's subclasses. 1316 friend class MWasmCallBase; 1317 1318 public: 1319 // Will assert if called before initialization. 1320 MDefinition* getOperand(size_t index) const final { 1321 return operands_[index].producer(); 1322 } 1323 size_t numOperands() const final { return operands_.length(); } 1324 size_t indexOf(const MUse* u) const final { 1325 MOZ_ASSERT(u >= &operands_[0]); 1326 MOZ_ASSERT(u <= &operands_[numOperands() - 1]); 1327 return u - &operands_[0]; 1328 } 1329 void replaceOperand(size_t index, MDefinition* operand) final { 1330 operands_[index].replaceProducer(operand); 1331 } 1332 }; 1333 1334 // An instruction with a variable number of operands. Note that the 1335 // MFoo::New constructor for variadic instructions fallibly 1336 // initializes the operands_ array and must be checked for OOM. 1337 using MVariadicInstruction = MVariadicT<MInstruction>; 1338 1339 // All barriered operations: 1340 // - MCompareExchangeTypedArrayElement 1341 // - MExchangeTypedArrayElement 1342 // - MAtomicTypedArrayElementBinop 1343 // - MGrowableSharedArrayBufferByteLength 1344 // 1345 // And operations which are optionally barriered: 1346 // - MLoadUnboxedScalar 1347 // - MStoreUnboxedScalar 1348 // - MResizableTypedArrayLength 1349 // - MResizableDataViewByteLength 1350 // 1351 // Must have the following attributes: 1352 // 1353 // - Not movable 1354 // - Not removable 1355 // - Not congruent with any other instruction 1356 // - Effectful (they alias every TypedArray store) 1357 // 1358 // The intended effect of those constraints is to prevent all loads and stores 1359 // preceding the barriered operation from being moved to after the barriered 1360 // operation, and vice versa, and to prevent the barriered operation from being 1361 // removed or hoisted. 1362 1363 enum class MemoryBarrierRequirement : bool { 1364 NotRequired, 1365 Required, 1366 }; 1367 1368 inline Synchronization SynchronizeLoad( 1369 MemoryBarrierRequirement requiresBarrier) { 1370 if (requiresBarrier == MemoryBarrierRequirement::Required) { 1371 return Synchronization::Load(); 1372 } 1373 return Synchronization::None(); 1374 } 1375 1376 inline Synchronization SynchronizeStore( 1377 MemoryBarrierRequirement requiresBarrier) { 1378 if (requiresBarrier == MemoryBarrierRequirement::Required) { 1379 return Synchronization::Store(); 1380 } 1381 return Synchronization::None(); 1382 } 1383 1384 MIR_OPCODE_CLASS_GENERATED 1385 1386 // Truncation barrier. This is intended for protecting its input against 1387 // follow-up truncation optimizations. 1388 class MLimitedTruncate : public MUnaryInstruction, 1389 public ConvertToInt32Policy<0>::Data { 1390 TruncateKind truncate_; 1391 TruncateKind truncateLimit_; 1392 1393 MLimitedTruncate(MDefinition* input, TruncateKind limit) 1394 : MUnaryInstruction(classOpcode, input), 1395 truncate_(TruncateKind::NoTruncate), 1396 truncateLimit_(limit) { 1397 setResultType(MIRType::Int32); 1398 setMovable(); 1399 } 1400 1401 public: 1402 INSTRUCTION_HEADER(LimitedTruncate) 1403 TRIVIAL_NEW_WRAPPERS 1404 1405 AliasSet getAliasSet() const override { return AliasSet::None(); } 1406 1407 void computeRange(TempAllocator& alloc) override; 1408 bool canTruncate() const override; 1409 void truncate(TruncateKind kind) override; 1410 TruncateKind operandTruncateKind(size_t index) const override; 1411 TruncateKind truncateKind() const { return truncate_; } 1412 void setTruncateKind(TruncateKind kind) { truncate_ = kind; } 1413 }; 1414 1415 // Truncation barrier. This is intended for protecting its input against 1416 // follow-up truncation optimizations. 1417 class MIntPtrLimitedTruncate : public MUnaryInstruction, 1418 public NoTypePolicy::Data { 1419 explicit MIntPtrLimitedTruncate(MDefinition* input) 1420 : MUnaryInstruction(classOpcode, input) { 1421 MOZ_ASSERT(input->type() == MIRType::IntPtr); 1422 setResultType(MIRType::IntPtr); 1423 setMovable(); 1424 } 1425 1426 public: 1427 INSTRUCTION_HEADER(IntPtrLimitedTruncate) 1428 TRIVIAL_NEW_WRAPPERS 1429 1430 AliasSet getAliasSet() const override { return AliasSet::None(); } 1431 }; 1432 1433 // Truncation barrier. This is intended for protecting its input against 1434 // follow-up truncation optimizations. 1435 class MInt64LimitedTruncate : public MUnaryInstruction, 1436 public NoTypePolicy::Data { 1437 explicit MInt64LimitedTruncate(MDefinition* input) 1438 : MUnaryInstruction(classOpcode, input) { 1439 MOZ_ASSERT(input->type() == MIRType::Int64); 1440 setResultType(MIRType::Int64); 1441 setMovable(); 1442 } 1443 1444 public: 1445 INSTRUCTION_HEADER(Int64LimitedTruncate) 1446 TRIVIAL_NEW_WRAPPERS 1447 1448 AliasSet getAliasSet() const override { return AliasSet::None(); } 1449 }; 1450 1451 // A constant js::Value. 1452 class MConstant : public MNullaryInstruction { 1453 struct Payload { 1454 union { 1455 bool b; 1456 int32_t i32; 1457 int64_t i64; 1458 intptr_t iptr; 1459 float f; 1460 double d; 1461 JSOffThreadAtom* str; 1462 JS::Symbol* sym; 1463 BigInt* bi; 1464 JSObject* obj; 1465 Shape* shape; 1466 uint64_t asBits; 1467 }; 1468 Payload() : asBits(0) {} 1469 }; 1470 1471 Payload payload_; 1472 1473 static_assert(sizeof(Payload) == sizeof(uint64_t), 1474 "asBits must be big enough for all payload bits"); 1475 1476 #ifdef DEBUG 1477 void assertInitializedPayload() const; 1478 #else 1479 void assertInitializedPayload() const {} 1480 #endif 1481 1482 explicit MConstant(MIRType type) : MNullaryInstruction(classOpcode) { 1483 setResultType(type); 1484 setMovable(); 1485 } 1486 explicit MConstant(bool b) : MConstant(MIRType::Boolean) { payload_.b = b; } 1487 explicit MConstant(double d) : MConstant(MIRType::Double) { payload_.d = d; } 1488 explicit MConstant(float f) : MConstant(MIRType::Float32) { payload_.f = f; } 1489 explicit MConstant(int32_t i) : MConstant(MIRType::Int32) { 1490 payload_.i32 = i; 1491 } 1492 MConstant(MIRType type, int64_t i) : MConstant(type) { 1493 MOZ_ASSERT(type == MIRType::Int64 || type == MIRType::IntPtr); 1494 if (type == MIRType::Int64) { 1495 payload_.i64 = i; 1496 } else { 1497 payload_.iptr = i; 1498 } 1499 } 1500 1501 MConstant(TempAllocator& alloc, const Value& v); 1502 explicit MConstant(JSObject* obj); 1503 explicit MConstant(Shape* shape); 1504 1505 public: 1506 INSTRUCTION_HEADER(Constant) 1507 static MConstant* New(TempAllocator& alloc, const Value& v); 1508 static MConstant* New(TempAllocator::Fallible alloc, const Value& v); 1509 static MConstant* NewBoolean(TempAllocator& alloc, bool b); 1510 static MConstant* NewDouble(TempAllocator& alloc, double d); 1511 static MConstant* NewFloat32(TempAllocator& alloc, double d); 1512 static MConstant* NewInt32(TempAllocator& alloc, int32_t i); 1513 static MConstant* NewInt64(TempAllocator& alloc, int64_t i); 1514 static MConstant* NewIntPtr(TempAllocator& alloc, intptr_t i); 1515 static MConstant* NewMagic(TempAllocator& alloc, JSWhyMagic m); 1516 static MConstant* NewNull(TempAllocator& alloc); 1517 static MConstant* NewObject(TempAllocator& alloc, JSObject* v); 1518 static MConstant* NewShape(TempAllocator& alloc, Shape* s); 1519 static MConstant* NewString(TempAllocator& alloc, JSString* s); 1520 static MConstant* NewUndefined(TempAllocator& alloc); 1521 1522 // Try to convert this constant to boolean, similar to js::ToBoolean. 1523 // Returns false if the type is MIRType::Magic* or MIRType::Object. 1524 [[nodiscard]] bool valueToBoolean(bool* res) const; 1525 1526 #ifdef JS_JITSPEW 1527 void printOpcode(GenericPrinter& out) const override; 1528 #endif 1529 1530 HashNumber valueHash() const override; 1531 bool congruentTo(const MDefinition* ins) const override; 1532 1533 AliasSet getAliasSet() const override { return AliasSet::None(); } 1534 1535 void computeRange(TempAllocator& alloc) override; 1536 bool canTruncate() const override; 1537 void truncate(TruncateKind kind) override; 1538 1539 bool canProduceFloat32() const override; 1540 1541 ALLOW_CLONE(MConstant) 1542 1543 bool equals(const MConstant* other) const { 1544 assertInitializedPayload(); 1545 return type() == other->type() && payload_.asBits == other->payload_.asBits; 1546 } 1547 1548 bool toBoolean() const { 1549 MOZ_ASSERT(type() == MIRType::Boolean); 1550 return payload_.b; 1551 } 1552 int32_t toInt32() const { 1553 MOZ_ASSERT(type() == MIRType::Int32); 1554 return payload_.i32; 1555 } 1556 int64_t toInt64() const { 1557 MOZ_ASSERT(type() == MIRType::Int64); 1558 return payload_.i64; 1559 } 1560 intptr_t toIntPtr() const { 1561 MOZ_ASSERT(type() == MIRType::IntPtr); 1562 return payload_.iptr; 1563 } 1564 bool isInt32(int32_t i) const { 1565 return type() == MIRType::Int32 && payload_.i32 == i; 1566 } 1567 bool isInt64(int64_t i) const { 1568 return type() == MIRType::Int64 && payload_.i64 == i; 1569 } 1570 const double& toDouble() const { 1571 MOZ_ASSERT(type() == MIRType::Double); 1572 return payload_.d; 1573 } 1574 const float& toFloat32() const { 1575 MOZ_ASSERT(type() == MIRType::Float32); 1576 return payload_.f; 1577 } 1578 JSOffThreadAtom* toString() const { 1579 MOZ_ASSERT(type() == MIRType::String); 1580 return payload_.str; 1581 } 1582 JS::Symbol* toSymbol() const { 1583 MOZ_ASSERT(type() == MIRType::Symbol); 1584 return payload_.sym; 1585 } 1586 BigInt* toBigInt() const { 1587 MOZ_ASSERT(type() == MIRType::BigInt); 1588 return payload_.bi; 1589 } 1590 JSObject& toObject() const { 1591 MOZ_ASSERT(type() == MIRType::Object); 1592 return *payload_.obj; 1593 } 1594 JSObject* toObjectOrNull() const { 1595 if (type() == MIRType::Object) { 1596 return payload_.obj; 1597 } 1598 MOZ_ASSERT(type() == MIRType::Null); 1599 return nullptr; 1600 } 1601 Shape* toShape() const { 1602 MOZ_ASSERT(type() == MIRType::Shape); 1603 return payload_.shape; 1604 } 1605 1606 bool isTypeRepresentableAsDouble() const { 1607 return IsTypeRepresentableAsDouble(type()); 1608 } 1609 double numberToDouble() const { 1610 MOZ_ASSERT(isTypeRepresentableAsDouble()); 1611 if (type() == MIRType::Int32) { 1612 return toInt32(); 1613 } 1614 if (type() == MIRType::Double) { 1615 return toDouble(); 1616 } 1617 return toFloat32(); 1618 } 1619 1620 // Convert this constant to a js::Value. Float32 constants will be stored 1621 // as DoubleValue and NaNs are canonicalized. Callers must be careful: not 1622 // all constants can be represented by js::Value (wasm supports int64). 1623 Value toJSValue() const; 1624 }; 1625 1626 inline HashNumber ConstantValueHash(MIRType type, uint64_t payload) { 1627 // Build a 64-bit value holding both the payload and the type. 1628 static const size_t TypeBits = 8; 1629 static const size_t TypeShift = 64 - TypeBits; 1630 MOZ_ASSERT(uintptr_t(type) <= (1 << TypeBits) - 1); 1631 uint64_t bits = (uint64_t(type) << TypeShift) ^ payload; 1632 1633 // Fold all 64 bits into the 32-bit result. It's tempting to just discard 1634 // half of the bits, as this is just a hash, however there are many common 1635 // patterns of values where only the low or the high bits vary, so 1636 // discarding either side would lead to excessive hash collisions. 1637 return (HashNumber)bits ^ (HashNumber)(bits >> 32); 1638 } 1639 1640 class MParameter : public MNullaryInstruction { 1641 int32_t index_; 1642 1643 explicit MParameter(int32_t index) 1644 : MNullaryInstruction(classOpcode), index_(index) { 1645 setResultType(MIRType::Value); 1646 } 1647 1648 public: 1649 INSTRUCTION_HEADER(Parameter) 1650 TRIVIAL_NEW_WRAPPERS 1651 1652 static const int32_t THIS_SLOT = -1; 1653 int32_t index() const { return index_; } 1654 #ifdef JS_JITSPEW 1655 void printOpcode(GenericPrinter& out) const override; 1656 #endif 1657 HashNumber valueHash() const override; 1658 bool congruentTo(const MDefinition* ins) const override; 1659 }; 1660 1661 class MControlInstruction : public MInstruction { 1662 protected: 1663 explicit MControlInstruction(Opcode op) : MInstruction(op) {} 1664 1665 public: 1666 virtual size_t numSuccessors() const = 0; 1667 virtual MBasicBlock* getSuccessor(size_t i) const = 0; 1668 virtual void replaceSuccessor(size_t i, MBasicBlock* successor) = 0; 1669 1670 void initSuccessor(size_t i, MBasicBlock* successor) { 1671 MOZ_ASSERT(!getSuccessor(i)); 1672 replaceSuccessor(i, successor); 1673 } 1674 1675 bool isControlInstruction() const override { return true; } 1676 1677 #ifdef JS_JITSPEW 1678 void printOpcode(GenericPrinter& out) const override; 1679 #endif 1680 }; 1681 1682 class MTableSwitch final : public MControlInstruction, 1683 public NoFloatPolicy<0>::Data { 1684 // The successors of the tableswitch 1685 // - First successor = the default case 1686 // - Successors 2 and higher = the cases 1687 Vector<MBasicBlock*, 0, JitAllocPolicy> successors_; 1688 // Index into successors_ sorted on case index 1689 Vector<size_t, 0, JitAllocPolicy> cases_; 1690 1691 MUse operand_; 1692 int32_t low_; 1693 int32_t high_; 1694 1695 void initOperand(size_t index, MDefinition* operand) { 1696 MOZ_ASSERT(index == 0); 1697 operand_.init(operand, this); 1698 } 1699 1700 MTableSwitch(TempAllocator& alloc, MDefinition* ins, int32_t low, 1701 int32_t high) 1702 : MControlInstruction(classOpcode), 1703 successors_(alloc), 1704 cases_(alloc), 1705 low_(low), 1706 high_(high) { 1707 initOperand(0, ins); 1708 } 1709 1710 protected: 1711 MUse* getUseFor(size_t index) override { 1712 MOZ_ASSERT(index == 0); 1713 return &operand_; 1714 } 1715 1716 const MUse* getUseFor(size_t index) const override { 1717 MOZ_ASSERT(index == 0); 1718 return &operand_; 1719 } 1720 1721 public: 1722 INSTRUCTION_HEADER(TableSwitch) 1723 1724 static MTableSwitch* New(TempAllocator& alloc, MDefinition* ins, int32_t low, 1725 int32_t high) { 1726 return new (alloc) MTableSwitch(alloc, ins, low, high); 1727 } 1728 1729 size_t numSuccessors() const override { return successors_.length(); } 1730 1731 [[nodiscard]] bool addSuccessor(MBasicBlock* successor, size_t* index) { 1732 MOZ_ASSERT(successors_.length() < (size_t)(high_ - low_ + 2)); 1733 MOZ_ASSERT(!successors_.empty()); 1734 *index = successors_.length(); 1735 return successors_.append(successor); 1736 } 1737 1738 MBasicBlock* getSuccessor(size_t i) const override { 1739 MOZ_ASSERT(i < numSuccessors()); 1740 return successors_[i]; 1741 } 1742 1743 void replaceSuccessor(size_t i, MBasicBlock* successor) override { 1744 MOZ_ASSERT(i < numSuccessors()); 1745 successors_[i] = successor; 1746 } 1747 1748 int32_t low() const { return low_; } 1749 1750 int32_t high() const { return high_; } 1751 1752 MBasicBlock* getDefault() const { return getSuccessor(0); } 1753 1754 MBasicBlock* getCase(size_t i) const { return getSuccessor(cases_[i]); } 1755 1756 [[nodiscard]] bool addDefault(MBasicBlock* block, size_t* index = nullptr) { 1757 MOZ_ASSERT(successors_.empty()); 1758 if (index) { 1759 *index = 0; 1760 } 1761 return successors_.append(block); 1762 } 1763 1764 [[nodiscard]] bool addCase(size_t successorIndex) { 1765 return cases_.append(successorIndex); 1766 } 1767 1768 size_t numCases() const { return high() - low() + 1; } 1769 1770 MDefinition* getOperand(size_t index) const override { 1771 MOZ_ASSERT(index == 0); 1772 return operand_.producer(); 1773 } 1774 1775 size_t numOperands() const override { return 1; } 1776 1777 size_t indexOf(const MUse* u) const final { 1778 MOZ_ASSERT(u == getUseFor(0)); 1779 return 0; 1780 } 1781 1782 void replaceOperand(size_t index, MDefinition* operand) final { 1783 MOZ_ASSERT(index == 0); 1784 operand_.replaceProducer(operand); 1785 } 1786 1787 MDefinition* foldsTo(TempAllocator& alloc) override; 1788 1789 // It does read memory in that it must read an entry from the jump table, 1790 // but that's effectively data that is private to this MIR. And it should 1791 // certainly never be modified by any other MIR. Hence it is effect-free 1792 // from an alias-analysis standpoint. 1793 AliasSet getAliasSet() const override { return AliasSet::None(); } 1794 }; 1795 1796 template <size_t Arity, size_t Successors> 1797 class MAryControlInstruction : public MControlInstruction { 1798 MOZ_NO_UNIQUE_ADDRESS mozilla::Array<MUse, Arity> operands_; 1799 MOZ_NO_UNIQUE_ADDRESS mozilla::Array<MBasicBlock*, Successors> successors_; 1800 1801 protected: 1802 explicit MAryControlInstruction(Opcode op) : MControlInstruction(op) {} 1803 void setSuccessor(size_t index, MBasicBlock* successor) { 1804 successors_[index] = successor; 1805 } 1806 1807 MUse* getUseFor(size_t index) final { return &operands_[index]; } 1808 const MUse* getUseFor(size_t index) const final { return &operands_[index]; } 1809 void initOperand(size_t index, MDefinition* operand) { 1810 operands_[index].init(operand, this); 1811 } 1812 1813 public: 1814 MDefinition* getOperand(size_t index) const final { 1815 return operands_[index].producer(); 1816 } 1817 size_t numOperands() const final { return Arity; } 1818 size_t indexOf(const MUse* u) const final { 1819 MOZ_ASSERT(u >= &operands_[0]); 1820 MOZ_ASSERT(u <= &operands_[numOperands() - 1]); 1821 return u - &operands_[0]; 1822 } 1823 void replaceOperand(size_t index, MDefinition* operand) final { 1824 operands_[index].replaceProducer(operand); 1825 } 1826 size_t numSuccessors() const final { return Successors; } 1827 MBasicBlock* getSuccessor(size_t i) const final { return successors_[i]; } 1828 void replaceSuccessor(size_t i, MBasicBlock* succ) final { 1829 successors_[i] = succ; 1830 } 1831 }; 1832 1833 template <size_t Successors> 1834 class MVariadicControlInstruction : public MVariadicT<MControlInstruction> { 1835 MOZ_NO_UNIQUE_ADDRESS mozilla::Array<MBasicBlock*, Successors> successors_; 1836 1837 protected: 1838 explicit MVariadicControlInstruction(Opcode op) 1839 : MVariadicT<MControlInstruction>(op) {} 1840 void setSuccessor(size_t index, MBasicBlock* successor) { 1841 successors_[index] = successor; 1842 } 1843 1844 public: 1845 size_t numSuccessors() const final { return Successors; } 1846 MBasicBlock* getSuccessor(size_t i) const final { return successors_[i]; } 1847 void replaceSuccessor(size_t i, MBasicBlock* succ) final { 1848 successors_[i] = succ; 1849 } 1850 }; 1851 1852 // Jump to the start of another basic block. 1853 class MGoto : public MAryControlInstruction<0, 1>, public NoTypePolicy::Data { 1854 explicit MGoto(MBasicBlock* target) : MAryControlInstruction(classOpcode) { 1855 setSuccessor(TargetIndex, target); 1856 } 1857 1858 public: 1859 INSTRUCTION_HEADER(Goto) 1860 static MGoto* New(TempAllocator& alloc, MBasicBlock* target); 1861 static MGoto* New(TempAllocator::Fallible alloc, MBasicBlock* target); 1862 1863 // Variant that may patch the target later. 1864 static MGoto* New(TempAllocator& alloc); 1865 1866 static constexpr size_t TargetIndex = 0; 1867 1868 MBasicBlock* target() const { return getSuccessor(TargetIndex); } 1869 void setTarget(MBasicBlock* target) { setSuccessor(TargetIndex, target); } 1870 1871 AliasSet getAliasSet() const override { return AliasSet::None(); } 1872 1873 #ifdef JS_JITSPEW 1874 void getExtras(ExtrasCollector* extras) const override { 1875 char buf[64]; 1876 SprintfLiteral(buf, "Block%u", GetMBasicBlockId(target())); 1877 extras->add(buf); 1878 } 1879 #endif 1880 1881 ALLOW_CLONE(MGoto) 1882 }; 1883 1884 // Tests if the input instruction evaluates to true or false, and jumps to the 1885 // start of a corresponding basic block. 1886 class MTest : public MAryControlInstruction<1, 2>, public TestPolicy::Data { 1887 // It is allowable to specify `trueBranch` or `falseBranch` as nullptr and 1888 // patch it in later. 1889 MTest(MDefinition* ins, MBasicBlock* trueBranch, MBasicBlock* falseBranch) 1890 : MAryControlInstruction(classOpcode) { 1891 initOperand(0, ins); 1892 setSuccessor(TrueBranchIndex, trueBranch); 1893 setSuccessor(FalseBranchIndex, falseBranch); 1894 } 1895 1896 TypeDataList observedTypes_; 1897 1898 public: 1899 INSTRUCTION_HEADER(Test) 1900 TRIVIAL_NEW_WRAPPERS 1901 NAMED_OPERANDS((0, input)) 1902 1903 const TypeDataList& observedTypes() const { return observedTypes_; } 1904 void setObservedTypes(const TypeDataList& observed) { 1905 observedTypes_ = observed; 1906 } 1907 1908 static constexpr size_t TrueBranchIndex = 0; 1909 static constexpr size_t FalseBranchIndex = 1; 1910 1911 MBasicBlock* ifTrue() const { return getSuccessor(TrueBranchIndex); } 1912 MBasicBlock* ifFalse() const { return getSuccessor(FalseBranchIndex); } 1913 void setIfTrue(MBasicBlock* target) { setSuccessor(TrueBranchIndex, target); } 1914 void setIfFalse(MBasicBlock* target) { 1915 setSuccessor(FalseBranchIndex, target); 1916 } 1917 1918 MBasicBlock* branchSuccessor(BranchDirection dir) const { 1919 return (dir == TRUE_BRANCH) ? ifTrue() : ifFalse(); 1920 } 1921 1922 AliasSet getAliasSet() const override { return AliasSet::None(); } 1923 1924 MDefinition* foldsDoubleNegation(TempAllocator& alloc); 1925 MDefinition* foldsConstant(TempAllocator& alloc); 1926 MDefinition* foldsTypes(TempAllocator& alloc); 1927 MDefinition* foldsNeedlessControlFlow(TempAllocator& alloc); 1928 MDefinition* foldsRedundantTest(TempAllocator& alloc); 1929 MDefinition* foldsTo(TempAllocator& alloc) override; 1930 1931 #ifdef DEBUG 1932 bool isConsistentFloat32Use(MUse* use) const override { return true; } 1933 #endif 1934 1935 #ifdef JS_JITSPEW 1936 void getExtras(ExtrasCollector* extras) const override { 1937 char buf[64]; 1938 SprintfLiteral(buf, "true->Block%u false->Block%u", 1939 GetMBasicBlockId(ifTrue()), GetMBasicBlockId(ifFalse())); 1940 extras->add(buf); 1941 } 1942 #endif 1943 1944 bool canClone() const override { return true; } 1945 MInstruction* clone(TempAllocator& alloc, 1946 const MDefinitionVector& inputs) const override { 1947 MInstruction* res = new (alloc) MTest(input(), ifTrue(), ifFalse()); 1948 if (!res) { 1949 return nullptr; 1950 } 1951 for (size_t i = 0; i < numOperands(); i++) { 1952 res->replaceOperand(i, inputs[i]); 1953 } 1954 return res; 1955 } 1956 }; 1957 1958 // Returns from this function to the previous caller. 1959 class MReturn : public MAryControlInstruction<1, 0>, 1960 public BoxInputsPolicy::Data { 1961 explicit MReturn(MDefinition* ins) : MAryControlInstruction(classOpcode) { 1962 initOperand(0, ins); 1963 } 1964 1965 public: 1966 INSTRUCTION_HEADER(Return) 1967 TRIVIAL_NEW_WRAPPERS 1968 NAMED_OPERANDS((0, input)) 1969 1970 AliasSet getAliasSet() const override { return AliasSet::None(); } 1971 }; 1972 1973 class MNewArray : public MUnaryInstruction, public NoTypePolicy::Data { 1974 private: 1975 // Number of elements to allocate for the array. 1976 uint32_t length_; 1977 1978 // Heap where the array should be allocated. 1979 gc::Heap initialHeap_; 1980 1981 bool vmCall_; 1982 1983 MNewArray(uint32_t length, MConstant* templateConst, gc::Heap initialHeap, 1984 bool vmCall = false); 1985 1986 public: 1987 INSTRUCTION_HEADER(NewArray) 1988 TRIVIAL_NEW_WRAPPERS 1989 1990 static MNewArray* NewVM(TempAllocator& alloc, uint32_t length, 1991 MConstant* templateConst, gc::Heap initialHeap) { 1992 return new (alloc) MNewArray(length, templateConst, initialHeap, true); 1993 } 1994 1995 uint32_t length() const { return length_; } 1996 1997 JSObject* templateObject() const { 1998 return getOperand(0)->toConstant()->toObjectOrNull(); 1999 } 2000 2001 gc::Heap initialHeap() const { return initialHeap_; } 2002 2003 bool isVMCall() const { return vmCall_; } 2004 2005 // NewArray is marked as non-effectful because all our allocations are 2006 // either lazy when we are using "new Array(length)" or bounded by the 2007 // script or the stack size when we are using "new Array(...)" or "[...]" 2008 // notations. So we might have to allocate the array twice if we bail 2009 // during the computation of the first element of the square braket 2010 // notation. 2011 virtual AliasSet getAliasSet() const override { return AliasSet::None(); } 2012 2013 [[nodiscard]] bool writeRecoverData( 2014 CompactBufferWriter& writer) const override; 2015 bool canRecoverOnBailout() const override { 2016 // The template object can safely be used in the recover instruction 2017 // because it can never be mutated by any other function execution. 2018 return templateObject() != nullptr; 2019 } 2020 }; 2021 2022 class MNewTypedArray : public MUnaryInstruction, public NoTypePolicy::Data { 2023 gc::Heap initialHeap_; 2024 2025 MNewTypedArray(MConstant* templateConst, gc::Heap initialHeap) 2026 : MUnaryInstruction(classOpcode, templateConst), 2027 initialHeap_(initialHeap) { 2028 setResultType(MIRType::Object); 2029 } 2030 2031 public: 2032 INSTRUCTION_HEADER(NewTypedArray) 2033 TRIVIAL_NEW_WRAPPERS 2034 2035 auto* templateObject() const { 2036 auto* cst = getOperand(0)->toConstant(); 2037 return &cst->toObject().as<FixedLengthTypedArrayObject>(); 2038 } 2039 2040 gc::Heap initialHeap() const { return initialHeap_; } 2041 2042 virtual AliasSet getAliasSet() const override { return AliasSet::None(); } 2043 2044 [[nodiscard]] bool writeRecoverData( 2045 CompactBufferWriter& writer) const override; 2046 bool canRecoverOnBailout() const override { return true; } 2047 }; 2048 2049 class MNewObject : public MUnaryInstruction, public NoTypePolicy::Data { 2050 public: 2051 enum Mode { ObjectLiteral, ObjectCreate }; 2052 2053 private: 2054 gc::Heap initialHeap_; 2055 Mode mode_; 2056 bool vmCall_; 2057 2058 MNewObject(MConstant* templateConst, gc::Heap initialHeap, Mode mode, 2059 bool vmCall = false) 2060 : MUnaryInstruction(classOpcode, templateConst), 2061 initialHeap_(initialHeap), 2062 mode_(mode), 2063 vmCall_(vmCall) { 2064 if (mode == ObjectLiteral) { 2065 MOZ_ASSERT(!templateObject()); 2066 } else { 2067 MOZ_ASSERT(templateObject()); 2068 } 2069 setResultType(MIRType::Object); 2070 2071 // The constant is kept separated in a MConstant, this way we can safely 2072 // mark it during GC if we recover the object allocation. Otherwise, by 2073 // making it emittedAtUses, we do not produce register allocations for 2074 // it and inline its content inside the code produced by the 2075 // CodeGenerator. 2076 if (templateConst->toConstant()->type() == MIRType::Object) { 2077 templateConst->setEmittedAtUses(); 2078 } 2079 } 2080 2081 public: 2082 INSTRUCTION_HEADER(NewObject) 2083 TRIVIAL_NEW_WRAPPERS 2084 2085 static MNewObject* NewVM(TempAllocator& alloc, MConstant* templateConst, 2086 gc::Heap initialHeap, Mode mode) { 2087 return new (alloc) MNewObject(templateConst, initialHeap, mode, true); 2088 } 2089 2090 Mode mode() const { return mode_; } 2091 2092 JSObject* templateObject() const { 2093 return getOperand(0)->toConstant()->toObjectOrNull(); 2094 } 2095 2096 gc::Heap initialHeap() const { return initialHeap_; } 2097 2098 bool isVMCall() const { return vmCall_; } 2099 2100 [[nodiscard]] bool writeRecoverData( 2101 CompactBufferWriter& writer) const override; 2102 bool canRecoverOnBailout() const override { 2103 // The template object can safely be used in the recover instruction 2104 // because it can never be mutated by any other function execution. 2105 return templateObject() != nullptr; 2106 } 2107 }; 2108 2109 class MNewPlainObject : public MUnaryInstruction, public NoTypePolicy::Data { 2110 private: 2111 uint32_t numFixedSlots_; 2112 uint32_t numDynamicSlots_; 2113 gc::AllocKind allocKind_; 2114 gc::Heap initialHeap_; 2115 2116 MNewPlainObject(MConstant* shapeConst, uint32_t numFixedSlots, 2117 uint32_t numDynamicSlots, gc::AllocKind allocKind, 2118 gc::Heap initialHeap) 2119 : MUnaryInstruction(classOpcode, shapeConst), 2120 numFixedSlots_(numFixedSlots), 2121 numDynamicSlots_(numDynamicSlots), 2122 allocKind_(allocKind), 2123 initialHeap_(initialHeap) { 2124 setResultType(MIRType::Object); 2125 2126 // The shape constant is kept separated in a MConstant. This way we can 2127 // safely mark it during GC if we recover the object allocation. Otherwise, 2128 // by making it emittedAtUses, we do not produce register allocations for it 2129 // and inline its content inside the code produced by the CodeGenerator. 2130 MOZ_ASSERT(shapeConst->toConstant()->type() == MIRType::Shape); 2131 shapeConst->setEmittedAtUses(); 2132 } 2133 2134 public: 2135 INSTRUCTION_HEADER(NewPlainObject) 2136 TRIVIAL_NEW_WRAPPERS 2137 2138 const Shape* shape() const { return getOperand(0)->toConstant()->toShape(); } 2139 2140 uint32_t numFixedSlots() const { return numFixedSlots_; } 2141 uint32_t numDynamicSlots() const { return numDynamicSlots_; } 2142 gc::AllocKind allocKind() const { return allocKind_; } 2143 gc::Heap initialHeap() const { return initialHeap_; } 2144 2145 [[nodiscard]] bool writeRecoverData( 2146 CompactBufferWriter& writer) const override; 2147 bool canRecoverOnBailout() const override { return true; } 2148 2149 AliasSet getAliasSet() const override { return AliasSet::None(); } 2150 }; 2151 2152 class MNewArrayObject : public MUnaryInstruction, public NoTypePolicy::Data { 2153 private: 2154 uint32_t length_; 2155 gc::Heap initialHeap_; 2156 2157 MNewArrayObject(TempAllocator& alloc, MConstant* shapeConst, uint32_t length, 2158 gc::Heap initialHeap) 2159 : MUnaryInstruction(classOpcode, shapeConst), 2160 length_(length), 2161 initialHeap_(initialHeap) { 2162 setResultType(MIRType::Object); 2163 MOZ_ASSERT(shapeConst->toConstant()->type() == MIRType::Shape); 2164 shapeConst->setEmittedAtUses(); 2165 } 2166 2167 public: 2168 INSTRUCTION_HEADER(NewArrayObject) 2169 TRIVIAL_NEW_WRAPPERS 2170 2171 static MNewArrayObject* New(TempAllocator& alloc, MConstant* shapeConst, 2172 uint32_t length, gc::Heap initialHeap) { 2173 return new (alloc) MNewArrayObject(alloc, shapeConst, length, initialHeap); 2174 } 2175 2176 const Shape* shape() const { return getOperand(0)->toConstant()->toShape(); } 2177 2178 // See MNewArray::getAliasSet comment. 2179 AliasSet getAliasSet() const override { return AliasSet::None(); } 2180 2181 uint32_t length() const { return length_; } 2182 gc::Heap initialHeap() const { return initialHeap_; } 2183 2184 [[nodiscard]] bool writeRecoverData( 2185 CompactBufferWriter& writer) const override; 2186 bool canRecoverOnBailout() const override { return true; } 2187 }; 2188 2189 class MNewIterator : public MUnaryInstruction, public NoTypePolicy::Data { 2190 public: 2191 enum Type { 2192 ArrayIterator, 2193 StringIterator, 2194 RegExpStringIterator, 2195 }; 2196 2197 private: 2198 Type type_; 2199 2200 MNewIterator(MConstant* templateConst, Type type) 2201 : MUnaryInstruction(classOpcode, templateConst), type_(type) { 2202 setResultType(MIRType::Object); 2203 templateConst->setEmittedAtUses(); 2204 } 2205 2206 public: 2207 INSTRUCTION_HEADER(NewIterator) 2208 TRIVIAL_NEW_WRAPPERS 2209 2210 Type type() const { return type_; } 2211 2212 JSObject* templateObject() { 2213 return getOperand(0)->toConstant()->toObjectOrNull(); 2214 } 2215 2216 AliasSet getAliasSet() const override { return AliasSet::None(); } 2217 2218 [[nodiscard]] bool writeRecoverData( 2219 CompactBufferWriter& writer) const override; 2220 bool canRecoverOnBailout() const override { return true; } 2221 }; 2222 2223 // Represent the content of all slots of an object. This instruction is not 2224 // lowered and is not used to generate code. 2225 class MObjectState : public MVariadicInstruction, 2226 public NoFloatPolicyAfter<1>::Data { 2227 private: 2228 uint32_t numSlots_; 2229 uint32_t numFixedSlots_; 2230 2231 explicit MObjectState(JSObject* templateObject); 2232 explicit MObjectState(const Shape* shape); 2233 explicit MObjectState(MObjectState* state); 2234 2235 [[nodiscard]] bool init(TempAllocator& alloc, MDefinition* obj); 2236 2237 void initSlot(uint32_t slot, MDefinition* def) { initOperand(slot + 1, def); } 2238 2239 public: 2240 INSTRUCTION_HEADER(ObjectState) 2241 NAMED_OPERANDS((0, object)) 2242 2243 // Return the template object of any object creation which can be recovered 2244 // on bailout. 2245 static JSObject* templateObjectOf(MDefinition* obj); 2246 2247 static MObjectState* New(TempAllocator& alloc, MDefinition* obj); 2248 static MObjectState* Copy(TempAllocator& alloc, MObjectState* state); 2249 2250 // As we might do read of uninitialized properties, we have to copy the 2251 // initial values from the template object. 2252 void initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal); 2253 2254 size_t numFixedSlots() const { return numFixedSlots_; } 2255 size_t numSlots() const { return numSlots_; } 2256 2257 MDefinition* getSlot(uint32_t slot) const { return getOperand(slot + 1); } 2258 void setSlot(uint32_t slot, MDefinition* def) { 2259 replaceOperand(slot + 1, def); 2260 } 2261 2262 bool hasFixedSlot(uint32_t slot) const { 2263 return slot < numSlots() && slot < numFixedSlots(); 2264 } 2265 MDefinition* getFixedSlot(uint32_t slot) const { 2266 MOZ_ASSERT(slot < numFixedSlots()); 2267 return getSlot(slot); 2268 } 2269 void setFixedSlot(uint32_t slot, MDefinition* def) { 2270 MOZ_ASSERT(slot < numFixedSlots()); 2271 setSlot(slot, def); 2272 } 2273 2274 bool hasDynamicSlot(uint32_t slot) const { 2275 return numFixedSlots() < numSlots() && slot < numSlots() - numFixedSlots(); 2276 } 2277 MDefinition* getDynamicSlot(uint32_t slot) const { 2278 return getSlot(slot + numFixedSlots()); 2279 } 2280 void setDynamicSlot(uint32_t slot, MDefinition* def) { 2281 setSlot(slot + numFixedSlots(), def); 2282 } 2283 2284 [[nodiscard]] bool writeRecoverData( 2285 CompactBufferWriter& writer) const override; 2286 bool canRecoverOnBailout() const override { return true; } 2287 }; 2288 2289 // Represent the contents of all elements of an array. This instruction is not 2290 // lowered and is not used to generate code. 2291 class MArrayState : public MVariadicInstruction, 2292 public NoFloatPolicyAfter<2>::Data { 2293 private: 2294 uint32_t numElements_; 2295 2296 explicit MArrayState(MDefinition* arr); 2297 2298 [[nodiscard]] bool init(TempAllocator& alloc, MDefinition* obj, 2299 MDefinition* len); 2300 2301 void initElement(uint32_t index, MDefinition* def) { 2302 initOperand(index + 2, def); 2303 } 2304 2305 public: 2306 INSTRUCTION_HEADER(ArrayState) 2307 NAMED_OPERANDS((0, array), (1, initializedLength)) 2308 2309 static MArrayState* New(TempAllocator& alloc, MDefinition* arr, 2310 MDefinition* initLength); 2311 static MArrayState* Copy(TempAllocator& alloc, MArrayState* state); 2312 2313 void initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal); 2314 2315 void setInitializedLength(MDefinition* def) { replaceOperand(1, def); } 2316 2317 size_t numElements() const { return numElements_; } 2318 2319 MDefinition* getElement(uint32_t index) const { 2320 return getOperand(index + 2); 2321 } 2322 void setElement(uint32_t index, MDefinition* def) { 2323 replaceOperand(index + 2, def); 2324 } 2325 2326 [[nodiscard]] bool writeRecoverData( 2327 CompactBufferWriter& writer) const override; 2328 bool canRecoverOnBailout() const override { return true; } 2329 }; 2330 2331 // WrappedFunction stores information about a function that can safely be used 2332 // off-thread. In particular, a function's flags can be modified on the main 2333 // thread as functions are relazified and delazified, so we must be careful not 2334 // to access these flags off-thread. 2335 class WrappedFunction : public TempObject { 2336 // If this is a native function without a JitEntry, the JSFunction*. 2337 CompilerFunction nativeFun_; 2338 uint16_t nargs_; 2339 js::FunctionFlags flags_; 2340 2341 public: 2342 WrappedFunction(JSFunction* nativeFun, uint16_t nargs, FunctionFlags flags); 2343 2344 // Note: When adding new accessors be sure to add consistency asserts 2345 // to the constructor. 2346 2347 size_t nargs() const { return nargs_; } 2348 2349 bool isNativeWithoutJitEntry() const { 2350 return flags_.isNativeWithoutJitEntry(); 2351 } 2352 bool hasJitEntry() const { return flags_.hasJitEntry(); } 2353 bool isConstructor() const { return flags_.isConstructor(); } 2354 bool isClassConstructor() const { return flags_.isClassConstructor(); } 2355 2356 // These fields never change, they can be accessed off-main thread. 2357 JSNative native() const { 2358 MOZ_ASSERT(isNativeWithoutJitEntry()); 2359 return nativeFun_->nativeUnchecked(); 2360 } 2361 bool hasJitInfo() const { 2362 return flags_.canHaveJitInfo() && nativeFun_->jitInfoUnchecked(); 2363 } 2364 const JSJitInfo* jitInfo() const { 2365 MOZ_ASSERT(hasJitInfo()); 2366 return nativeFun_->jitInfoUnchecked(); 2367 } 2368 2369 JSFunction* rawNativeJSFunction() const { return nativeFun_; } 2370 }; 2371 2372 enum class DOMObjectKind : uint8_t { Proxy, Native }; 2373 2374 class MCallBase : public MVariadicInstruction, public CallPolicy::Data { 2375 protected: 2376 // The callee, this, and the actual arguments are all operands of MCall. 2377 static const size_t CalleeOperandIndex = 0; 2378 static const size_t NumNonArgumentOperands = 1; 2379 2380 explicit MCallBase(Opcode op) : MVariadicInstruction(op) {} 2381 2382 public: 2383 void initCallee(MDefinition* func) { initOperand(CalleeOperandIndex, func); } 2384 MDefinition* getCallee() const { return getOperand(CalleeOperandIndex); } 2385 2386 void replaceCallee(MInstruction* newfunc) { 2387 replaceOperand(CalleeOperandIndex, newfunc); 2388 } 2389 2390 void addArg(size_t argnum, MDefinition* arg); 2391 2392 MDefinition* getArg(uint32_t index) const { 2393 return getOperand(NumNonArgumentOperands + index); 2394 } 2395 2396 // The number of stack arguments is the max between the number of formal 2397 // arguments and the number of actual arguments. The number of stack 2398 // argument includes the |undefined| padding added in case of underflow. 2399 // Includes |this|. 2400 uint32_t numStackArgs() const { 2401 return numOperands() - NumNonArgumentOperands; 2402 } 2403 uint32_t paddedNumStackArgs() const { 2404 if (JitStackValueAlignment > 1) { 2405 return AlignBytes(numStackArgs(), JitStackValueAlignment); 2406 } 2407 return numStackArgs(); 2408 } 2409 2410 static size_t IndexOfThis() { return NumNonArgumentOperands; } 2411 static size_t IndexOfArgument(size_t index) { 2412 return NumNonArgumentOperands + index + 1; // +1 to skip |this|. 2413 } 2414 static size_t IndexOfStackArg(size_t index) { 2415 return NumNonArgumentOperands + index; 2416 } 2417 }; 2418 2419 class MCall : public MCallBase { 2420 protected: 2421 // Monomorphic cache for MCalls with a single JSFunction target. 2422 WrappedFunction* target_; 2423 2424 // Original value of argc from the bytecode. 2425 uint32_t numActualArgs_; 2426 2427 // True if the call is for JSOp::New or JSOp::SuperCall. 2428 bool construct_ : 1; 2429 2430 // True if the caller does not use the return value. 2431 bool ignoresReturnValue_ : 1; 2432 2433 bool needsClassCheck_ : 1; 2434 bool maybeCrossRealm_ : 1; 2435 bool needsThisCheck_ : 1; 2436 2437 MCall(WrappedFunction* target, uint32_t numActualArgs, bool construct, 2438 bool ignoresReturnValue) 2439 : MCallBase(classOpcode), 2440 target_(target), 2441 numActualArgs_(numActualArgs), 2442 construct_(construct), 2443 ignoresReturnValue_(ignoresReturnValue), 2444 needsClassCheck_(true), 2445 maybeCrossRealm_(true), 2446 needsThisCheck_(false) { 2447 setResultType(MIRType::Value); 2448 } 2449 2450 public: 2451 INSTRUCTION_HEADER(Call) 2452 static MCall* New(TempAllocator& alloc, WrappedFunction* target, 2453 size_t maxArgc, size_t numActualArgs, bool construct, 2454 bool ignoresReturnValue, bool isDOMCall, 2455 mozilla::Maybe<DOMObjectKind> objectKind, 2456 mozilla::Maybe<gc::Heap> initialHeap); 2457 2458 bool needsClassCheck() const { return needsClassCheck_; } 2459 void disableClassCheck() { needsClassCheck_ = false; } 2460 2461 bool maybeCrossRealm() const { return maybeCrossRealm_; } 2462 void setNotCrossRealm() { maybeCrossRealm_ = false; } 2463 2464 bool needsThisCheck() const { return needsThisCheck_; } 2465 void setNeedsThisCheck() { 2466 MOZ_ASSERT(construct_); 2467 needsThisCheck_ = true; 2468 } 2469 2470 // For monomorphic callsites. 2471 WrappedFunction* getSingleTarget() const { return target_; } 2472 2473 bool isConstructing() const { return construct_; } 2474 2475 bool ignoresReturnValue() const { return ignoresReturnValue_; } 2476 2477 // Does not include |this|. 2478 uint32_t numActualArgs() const { return numActualArgs_; } 2479 2480 bool possiblyCalls() const override { return true; } 2481 2482 virtual bool isCallDOMNative() const { return false; } 2483 2484 // A method that can be called to tell the MCall to figure out whether it's 2485 // movable or not. This can't be done in the constructor, because it 2486 // depends on the arguments to the call, and those aren't passed to the 2487 // constructor but are set up later via addArg. 2488 virtual void computeMovable() {} 2489 }; 2490 2491 class MCallDOMNative : public MCall { 2492 // A helper class for MCalls for DOM natives. Note that this is NOT 2493 // actually a separate MIR op from MCall, because all sorts of places use 2494 // isCall() to check for calls and all we really want is to overload a few 2495 // virtual things from MCall. 2496 2497 DOMObjectKind objectKind_; 2498 2499 // Allow wrapper pre-tenuring 2500 gc::Heap initialHeap_ = gc::Heap::Default; 2501 2502 MCallDOMNative(WrappedFunction* target, uint32_t numActualArgs, 2503 DOMObjectKind objectKind, gc::Heap initialHeap) 2504 : MCall(target, numActualArgs, false, false), 2505 objectKind_(objectKind), 2506 initialHeap_(initialHeap) { 2507 MOZ_ASSERT(getJitInfo()->type() != JSJitInfo::InlinableNative); 2508 2509 // If our jitinfo is not marked eliminatable, that means that our C++ 2510 // implementation is fallible or that it never wants to be eliminated or 2511 // that we have no hope of ever doing the sort of argument analysis that 2512 // would allow us to detemine that we're side-effect-free. In the 2513 // latter case we wouldn't get DCEd no matter what, but for the former 2514 // two cases we have to explicitly say that we can't be DCEd. 2515 if (!getJitInfo()->isEliminatable) { 2516 setGuard(); 2517 } 2518 } 2519 2520 friend MCall* MCall::New(TempAllocator& alloc, WrappedFunction* target, 2521 size_t maxArgc, size_t numActualArgs, bool construct, 2522 bool ignoresReturnValue, bool isDOMCall, 2523 mozilla::Maybe<DOMObjectKind> objectKind, 2524 mozilla::Maybe<gc::Heap> initalHeap); 2525 2526 const JSJitInfo* getJitInfo() const; 2527 2528 public: 2529 DOMObjectKind objectKind() const { return objectKind_; } 2530 2531 virtual AliasSet getAliasSet() const override; 2532 2533 virtual bool congruentTo(const MDefinition* ins) const override; 2534 2535 virtual bool isCallDOMNative() const override { return true; } 2536 2537 virtual void computeMovable() override; 2538 2539 gc::Heap initialHeap() { return initialHeap_; } 2540 }; 2541 2542 // Used to invoke a JSClass call/construct hook. 2543 class MCallClassHook : public MCallBase { 2544 const JSNative target_; 2545 bool constructing_ : 1; 2546 bool ignoresReturnValue_ : 1; 2547 2548 MCallClassHook(JSNative target, bool constructing) 2549 : MCallBase(classOpcode), 2550 target_(target), 2551 constructing_(constructing), 2552 ignoresReturnValue_(false) { 2553 setResultType(MIRType::Value); 2554 } 2555 2556 public: 2557 INSTRUCTION_HEADER(CallClassHook) 2558 static MCallClassHook* New(TempAllocator& alloc, JSNative target, 2559 uint32_t argc, bool constructing); 2560 2561 JSNative target() const { return target_; } 2562 bool isConstructing() const { return constructing_; } 2563 2564 uint32_t numActualArgs() const { 2565 uint32_t thisAndNewTarget = 1 + constructing_; 2566 MOZ_ASSERT(numStackArgs() >= thisAndNewTarget); 2567 return numStackArgs() - thisAndNewTarget; 2568 } 2569 2570 bool maybeCrossRealm() const { return true; } 2571 2572 bool ignoresReturnValue() const { return ignoresReturnValue_; } 2573 void setIgnoresReturnValue() { ignoresReturnValue_ = true; } 2574 2575 bool possiblyCalls() const override { return true; } 2576 }; 2577 2578 // fun.apply(self, arguments) 2579 class MApplyArgs : public MTernaryInstruction, 2580 public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, 2581 BoxPolicy<2>>::Data { 2582 // Single target from CacheIR, or nullptr 2583 WrappedFunction* target_; 2584 // Number of extra initial formals to skip. 2585 uint32_t numExtraFormals_; 2586 bool maybeCrossRealm_ = true; 2587 bool ignoresReturnValue_ = false; 2588 2589 MApplyArgs(WrappedFunction* target, MDefinition* fun, MDefinition* argc, 2590 MDefinition* self, uint32_t numExtraFormals = 0) 2591 : MTernaryInstruction(classOpcode, fun, argc, self), 2592 target_(target), 2593 numExtraFormals_(numExtraFormals) { 2594 MOZ_ASSERT(argc->type() == MIRType::Int32); 2595 setResultType(MIRType::Value); 2596 } 2597 2598 public: 2599 INSTRUCTION_HEADER(ApplyArgs) 2600 TRIVIAL_NEW_WRAPPERS 2601 NAMED_OPERANDS((0, getFunction), (1, getArgc), (2, getThis)) 2602 2603 WrappedFunction* getSingleTarget() const { return target_; } 2604 2605 uint32_t numExtraFormals() const { return numExtraFormals_; } 2606 2607 bool maybeCrossRealm() const { return maybeCrossRealm_; } 2608 void setNotCrossRealm() { maybeCrossRealm_ = false; } 2609 2610 bool ignoresReturnValue() const { return ignoresReturnValue_; } 2611 void setIgnoresReturnValue() { ignoresReturnValue_ = true; } 2612 2613 bool isConstructing() const { return false; } 2614 2615 bool possiblyCalls() const override { return true; } 2616 }; 2617 2618 class MApplyArgsObj 2619 : public MTernaryInstruction, 2620 public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2>>::Data { 2621 WrappedFunction* target_; 2622 bool maybeCrossRealm_ = true; 2623 bool ignoresReturnValue_ = false; 2624 2625 MApplyArgsObj(WrappedFunction* target, MDefinition* fun, MDefinition* argsObj, 2626 MDefinition* thisArg) 2627 : MTernaryInstruction(classOpcode, fun, argsObj, thisArg), 2628 target_(target) { 2629 MOZ_ASSERT(argsObj->type() == MIRType::Object); 2630 setResultType(MIRType::Value); 2631 } 2632 2633 public: 2634 INSTRUCTION_HEADER(ApplyArgsObj) 2635 TRIVIAL_NEW_WRAPPERS 2636 NAMED_OPERANDS((0, getFunction), (1, getArgsObj), (2, getThis)) 2637 2638 WrappedFunction* getSingleTarget() const { return target_; } 2639 2640 bool maybeCrossRealm() const { return maybeCrossRealm_; } 2641 void setNotCrossRealm() { maybeCrossRealm_ = false; } 2642 2643 bool ignoresReturnValue() const { return ignoresReturnValue_; } 2644 void setIgnoresReturnValue() { ignoresReturnValue_ = true; } 2645 2646 bool isConstructing() const { return false; } 2647 2648 bool possiblyCalls() const override { return true; } 2649 }; 2650 2651 // fun.apply(fn, array) 2652 class MApplyArray : public MTernaryInstruction, 2653 public MixPolicy<ObjectPolicy<0>, BoxPolicy<2>>::Data { 2654 // Single target from CacheIR, or nullptr 2655 WrappedFunction* target_; 2656 bool maybeCrossRealm_ = true; 2657 bool ignoresReturnValue_ = false; 2658 2659 MApplyArray(WrappedFunction* target, MDefinition* fun, MDefinition* elements, 2660 MDefinition* self) 2661 : MTernaryInstruction(classOpcode, fun, elements, self), target_(target) { 2662 MOZ_ASSERT(elements->type() == MIRType::Elements); 2663 setResultType(MIRType::Value); 2664 } 2665 2666 public: 2667 INSTRUCTION_HEADER(ApplyArray) 2668 TRIVIAL_NEW_WRAPPERS 2669 NAMED_OPERANDS((0, getFunction), (1, getElements), (2, getThis)) 2670 2671 WrappedFunction* getSingleTarget() const { return target_; } 2672 2673 bool maybeCrossRealm() const { return maybeCrossRealm_; } 2674 void setNotCrossRealm() { maybeCrossRealm_ = false; } 2675 2676 bool ignoresReturnValue() const { return ignoresReturnValue_; } 2677 void setIgnoresReturnValue() { ignoresReturnValue_ = true; } 2678 2679 bool isConstructing() const { return false; } 2680 2681 bool possiblyCalls() const override { return true; } 2682 }; 2683 2684 // |new F(...arguments)| and |super(...arguments)|. 2685 class MConstructArgs : public MQuaternaryInstruction, 2686 public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, 2687 BoxPolicy<2>, ObjectPolicy<3>>::Data { 2688 // Single target from CacheIR, or nullptr 2689 WrappedFunction* target_; 2690 // Number of extra initial formals to skip. 2691 uint32_t numExtraFormals_; 2692 bool maybeCrossRealm_ = true; 2693 2694 MConstructArgs(WrappedFunction* target, MDefinition* fun, MDefinition* argc, 2695 MDefinition* thisValue, MDefinition* newTarget, 2696 uint32_t numExtraFormals = 0) 2697 : MQuaternaryInstruction(classOpcode, fun, argc, thisValue, newTarget), 2698 target_(target), 2699 numExtraFormals_(numExtraFormals) { 2700 MOZ_ASSERT(argc->type() == MIRType::Int32); 2701 setResultType(MIRType::Value); 2702 } 2703 2704 public: 2705 INSTRUCTION_HEADER(ConstructArgs) 2706 TRIVIAL_NEW_WRAPPERS 2707 NAMED_OPERANDS((0, getFunction), (1, getArgc), (2, getThis), 2708 (3, getNewTarget)) 2709 2710 WrappedFunction* getSingleTarget() const { return target_; } 2711 2712 uint32_t numExtraFormals() const { return numExtraFormals_; } 2713 2714 bool maybeCrossRealm() const { return maybeCrossRealm_; } 2715 void setNotCrossRealm() { maybeCrossRealm_ = false; } 2716 2717 bool ignoresReturnValue() const { return false; } 2718 bool isConstructing() const { return true; } 2719 2720 bool possiblyCalls() const override { return true; } 2721 }; 2722 2723 // |new F(...args)| and |super(...args)|. 2724 class MConstructArray 2725 : public MQuaternaryInstruction, 2726 public MixPolicy<ObjectPolicy<0>, BoxPolicy<2>, ObjectPolicy<3>>::Data { 2727 // Single target from CacheIR, or nullptr 2728 WrappedFunction* target_; 2729 bool maybeCrossRealm_ = true; 2730 bool needsThisCheck_ = false; 2731 2732 MConstructArray(WrappedFunction* target, MDefinition* fun, 2733 MDefinition* elements, MDefinition* thisValue, 2734 MDefinition* newTarget) 2735 : MQuaternaryInstruction(classOpcode, fun, elements, thisValue, 2736 newTarget), 2737 target_(target) { 2738 MOZ_ASSERT(elements->type() == MIRType::Elements); 2739 setResultType(MIRType::Value); 2740 } 2741 2742 public: 2743 INSTRUCTION_HEADER(ConstructArray) 2744 TRIVIAL_NEW_WRAPPERS 2745 NAMED_OPERANDS((0, getFunction), (1, getElements), (2, getThis), 2746 (3, getNewTarget)) 2747 2748 WrappedFunction* getSingleTarget() const { return target_; } 2749 2750 bool maybeCrossRealm() const { return maybeCrossRealm_; } 2751 void setNotCrossRealm() { maybeCrossRealm_ = false; } 2752 2753 bool needsThisCheck() const { return needsThisCheck_; } 2754 void setNeedsThisCheck() { needsThisCheck_ = true; } 2755 2756 bool ignoresReturnValue() const { return false; } 2757 bool isConstructing() const { return true; } 2758 2759 bool possiblyCalls() const override { return true; } 2760 }; 2761 2762 class MBail : public MNullaryInstruction { 2763 explicit MBail(BailoutKind kind) : MNullaryInstruction(classOpcode) { 2764 setBailoutKind(kind); 2765 setGuard(); 2766 } 2767 2768 public: 2769 INSTRUCTION_HEADER(Bail) 2770 2771 static MBail* New(TempAllocator& alloc, BailoutKind kind) { 2772 return new (alloc) MBail(kind); 2773 } 2774 static MBail* New(TempAllocator& alloc) { 2775 return new (alloc) MBail(BailoutKind::Inevitable); 2776 } 2777 2778 AliasSet getAliasSet() const override { return AliasSet::None(); } 2779 }; 2780 2781 class MUnreachable : public MAryControlInstruction<0, 0>, 2782 public NoTypePolicy::Data { 2783 MUnreachable() : MAryControlInstruction(classOpcode) {} 2784 2785 public: 2786 INSTRUCTION_HEADER(Unreachable) 2787 TRIVIAL_NEW_WRAPPERS 2788 2789 AliasSet getAliasSet() const override { return AliasSet::None(); } 2790 }; 2791 2792 class MAssertRecoveredOnBailout : public MUnaryInstruction, 2793 public NoTypePolicy::Data { 2794 bool mustBeRecovered_; 2795 2796 MAssertRecoveredOnBailout(MDefinition* ins, bool mustBeRecovered) 2797 : MUnaryInstruction(classOpcode, ins), mustBeRecovered_(mustBeRecovered) { 2798 setResultType(MIRType::Value); 2799 setRecoveredOnBailout(); 2800 setGuard(); 2801 } 2802 2803 public: 2804 INSTRUCTION_HEADER(AssertRecoveredOnBailout) 2805 TRIVIAL_NEW_WRAPPERS 2806 2807 // Needed to assert that float32 instructions are correctly recovered. 2808 bool canConsumeFloat32(MUse* use) const override { return true; } 2809 2810 [[nodiscard]] bool writeRecoverData( 2811 CompactBufferWriter& writer) const override; 2812 bool canRecoverOnBailout() const override { return true; } 2813 }; 2814 2815 class MAssertFloat32 : public MUnaryInstruction, public NoTypePolicy::Data { 2816 bool mustBeFloat32_; 2817 2818 MAssertFloat32(MDefinition* value, bool mustBeFloat32) 2819 : MUnaryInstruction(classOpcode, value), mustBeFloat32_(mustBeFloat32) {} 2820 2821 public: 2822 INSTRUCTION_HEADER(AssertFloat32) 2823 TRIVIAL_NEW_WRAPPERS 2824 2825 bool canConsumeFloat32(MUse* use) const override { return true; } 2826 2827 bool mustBeFloat32() const { return mustBeFloat32_; } 2828 }; 2829 2830 class MCompare : public MBinaryInstruction, public ComparePolicy::Data { 2831 public: 2832 enum CompareType { 2833 2834 // Anything compared to Undefined 2835 Compare_Undefined, 2836 2837 // Anything compared to Null 2838 Compare_Null, 2839 2840 // Int32 compared to Int32 2841 // Boolean compared to Boolean 2842 Compare_Int32, 2843 2844 // Int32 compared as unsigneds 2845 Compare_UInt32, 2846 2847 // Int64 compared to Int64. 2848 Compare_Int64, 2849 2850 // Int64 compared as unsigneds. 2851 Compare_UInt64, 2852 2853 // IntPtr compared to IntPtr. 2854 Compare_IntPtr, 2855 2856 // IntPtr compared as unsigneds. 2857 Compare_UIntPtr, 2858 2859 // Double compared to Double 2860 Compare_Double, 2861 2862 // Float compared to Float 2863 Compare_Float32, 2864 2865 // String compared to String 2866 Compare_String, 2867 2868 // Symbol compared to Symbol 2869 Compare_Symbol, 2870 2871 // Object compared to Object 2872 Compare_Object, 2873 2874 // BigInt compared to BigInt 2875 Compare_BigInt, 2876 2877 // BigInt compared to Int32 2878 Compare_BigInt_Int32, 2879 2880 // BigInt compared to Double 2881 Compare_BigInt_Double, 2882 2883 // BigInt compared to String 2884 Compare_BigInt_String, 2885 2886 // Wasm Ref/AnyRef/NullRef compared to Ref/AnyRef/NullRef 2887 Compare_WasmAnyRef, 2888 }; 2889 2890 private: 2891 CompareType compareType_; 2892 JSOp jsop_; 2893 bool operandsAreNeverNaN_; 2894 2895 // When a floating-point comparison is converted to an integer comparison 2896 // (when range analysis proves it safe), we need to convert the operands 2897 // to integer as well. 2898 bool truncateOperands_; 2899 2900 MCompare(MDefinition* left, MDefinition* right, JSOp jsop, 2901 CompareType compareType) 2902 : MBinaryInstruction(classOpcode, left, right), 2903 compareType_(compareType), 2904 jsop_(jsop), 2905 operandsAreNeverNaN_(false), 2906 truncateOperands_(false) { 2907 setResultType(MIRType::Boolean); 2908 setMovable(); 2909 } 2910 2911 public: 2912 INSTRUCTION_HEADER(Compare) 2913 TRIVIAL_NEW_WRAPPERS 2914 2915 static MCompare* NewWasm(TempAllocator& alloc, MDefinition* left, 2916 MDefinition* right, JSOp jsop, 2917 CompareType compareType) { 2918 MOZ_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 || 2919 compareType == Compare_Int64 || compareType == Compare_UInt64 || 2920 compareType == Compare_Double || 2921 compareType == Compare_Float32 || 2922 compareType == Compare_WasmAnyRef); 2923 auto* ins = MCompare::New(alloc, left, right, jsop, compareType); 2924 ins->setResultType(MIRType::Int32); 2925 return ins; 2926 } 2927 2928 [[nodiscard]] bool tryFold(bool* result); 2929 [[nodiscard]] bool evaluateConstantOperands(TempAllocator& alloc, 2930 bool* result); 2931 MDefinition* foldsTo(TempAllocator& alloc) override; 2932 2933 CompareType compareType() const { return compareType_; } 2934 bool isInt32Comparison() const { return compareType() == Compare_Int32; } 2935 bool isDoubleComparison() const { return compareType() == Compare_Double; } 2936 bool isFloat32Comparison() const { return compareType() == Compare_Float32; } 2937 bool isNumericComparison() const { 2938 return isInt32Comparison() || isDoubleComparison() || isFloat32Comparison(); 2939 } 2940 2941 JSOp jsop() const { return jsop_; } 2942 bool operandsAreNeverNaN() const { return operandsAreNeverNaN_; } 2943 AliasSet getAliasSet() const override { return AliasSet::None(); } 2944 2945 #ifdef JS_JITSPEW 2946 void printOpcode(GenericPrinter& out) const override; 2947 #endif 2948 void collectRangeInfoPreTrunc() override; 2949 2950 void trySpecializeFloat32(TempAllocator& alloc) override; 2951 bool isFloat32Commutative() const override { return true; } 2952 bool canTruncate() const override; 2953 void truncate(TruncateKind kind) override; 2954 TruncateKind operandTruncateKind(size_t index) const override; 2955 2956 #ifdef DEBUG 2957 bool isConsistentFloat32Use(MUse* use) const override { 2958 // Both sides of the compare can be Float32 2959 return compareType_ == Compare_Float32; 2960 } 2961 #endif 2962 2963 ALLOW_CLONE(MCompare) 2964 2965 private: 2966 [[nodiscard]] bool tryFoldEqualOperands(bool* result); 2967 [[nodiscard]] bool tryFoldTypeOf(bool* result); 2968 [[nodiscard]] MDefinition* tryFoldTypeOf(TempAllocator& alloc); 2969 [[nodiscard]] MDefinition* tryFoldCharCompare(TempAllocator& alloc); 2970 [[nodiscard]] MDefinition* tryFoldStringCompare(TempAllocator& alloc); 2971 [[nodiscard]] MDefinition* tryFoldStringSubstring(TempAllocator& alloc); 2972 [[nodiscard]] MDefinition* tryFoldStringIndexOf(TempAllocator& alloc); 2973 [[nodiscard]] MDefinition* tryFoldBigInt64(TempAllocator& alloc); 2974 [[nodiscard]] MDefinition* tryFoldBigIntPtr(TempAllocator& alloc); 2975 [[nodiscard]] MDefinition* tryFoldBigInt(TempAllocator& alloc); 2976 2977 public: 2978 bool congruentTo(const MDefinition* ins) const override { 2979 if (!binaryCongruentTo(ins)) { 2980 return false; 2981 } 2982 return compareType() == ins->toCompare()->compareType() && 2983 jsop() == ins->toCompare()->jsop(); 2984 } 2985 2986 [[nodiscard]] bool writeRecoverData( 2987 CompactBufferWriter& writer) const override; 2988 bool canRecoverOnBailout() const override { 2989 switch (compareType_) { 2990 case Compare_Undefined: 2991 case Compare_Null: 2992 case Compare_Int32: 2993 case Compare_UInt32: 2994 case Compare_Double: 2995 case Compare_Float32: 2996 case Compare_String: 2997 case Compare_Symbol: 2998 case Compare_Object: 2999 case Compare_BigInt: 3000 case Compare_BigInt_Int32: 3001 case Compare_BigInt_Double: 3002 case Compare_BigInt_String: 3003 return true; 3004 3005 case Compare_Int64: 3006 case Compare_UInt64: 3007 case Compare_IntPtr: 3008 case Compare_UIntPtr: 3009 case Compare_WasmAnyRef: 3010 return false; 3011 } 3012 MOZ_CRASH("unexpected compare type"); 3013 } 3014 3015 #ifdef JS_JITSPEW 3016 void getExtras(ExtrasCollector* extras) const override { 3017 const char* ty = nullptr; 3018 switch (compareType_) { 3019 case Compare_Undefined: 3020 ty = "Undefined"; 3021 break; 3022 case Compare_Null: 3023 ty = "Null"; 3024 break; 3025 case Compare_Int32: 3026 ty = "Int32"; 3027 break; 3028 case Compare_UInt32: 3029 ty = "UInt32"; 3030 break; 3031 case Compare_Int64: 3032 ty = "Int64"; 3033 break; 3034 case Compare_UInt64: 3035 ty = "UInt64"; 3036 break; 3037 case Compare_IntPtr: 3038 ty = "IntPtr"; 3039 break; 3040 case Compare_UIntPtr: 3041 ty = "UIntPtr"; 3042 break; 3043 case Compare_Double: 3044 ty = "Double"; 3045 break; 3046 case Compare_Float32: 3047 ty = "Float32"; 3048 break; 3049 case Compare_String: 3050 ty = "String"; 3051 break; 3052 case Compare_Symbol: 3053 ty = "Symbol"; 3054 break; 3055 case Compare_Object: 3056 ty = "Object"; 3057 break; 3058 case Compare_BigInt: 3059 ty = "BigInt"; 3060 break; 3061 case Compare_BigInt_Int32: 3062 ty = "BigInt_Int32"; 3063 break; 3064 case Compare_BigInt_Double: 3065 ty = "BigInt_Double"; 3066 break; 3067 case Compare_BigInt_String: 3068 ty = "BigInt_String"; 3069 break; 3070 case Compare_WasmAnyRef: 3071 ty = "WasmAnyRef"; 3072 break; 3073 default: 3074 ty = "!!unknown!!"; 3075 break; 3076 }; 3077 char buf[64]; 3078 SprintfLiteral(buf, "ty=%s jsop=%s", ty, CodeName(jsop())); 3079 extras->add(buf); 3080 } 3081 #endif 3082 }; 3083 3084 // Takes a typed value and returns an untyped value. 3085 class MBox : public MUnaryInstruction, public NoTypePolicy::Data { 3086 explicit MBox(MDefinition* ins) : MUnaryInstruction(classOpcode, ins) { 3087 // Cannot box a box. 3088 MOZ_ASSERT(ins->type() != MIRType::Value); 3089 3090 setResultType(MIRType::Value); 3091 setMovable(); 3092 } 3093 3094 public: 3095 INSTRUCTION_HEADER(Box) 3096 TRIVIAL_NEW_WRAPPERS 3097 3098 bool congruentTo(const MDefinition* ins) const override { 3099 return congruentIfOperandsEqual(ins); 3100 } 3101 3102 MDefinition* foldsTo(TempAllocator& alloc) override; 3103 3104 AliasSet getAliasSet() const override { return AliasSet::None(); } 3105 3106 ALLOW_CLONE(MBox) 3107 }; 3108 3109 // Note: the op may have been inverted during lowering (to put constants in a 3110 // position where they can be immediates), so it is important to use the 3111 // lir->jsop() instead of the mir->jsop() when it is present. 3112 static inline Assembler::Condition JSOpToCondition( 3113 MCompare::CompareType compareType, JSOp op) { 3114 bool isSigned = (compareType != MCompare::Compare_UInt32 && 3115 compareType != MCompare::Compare_UInt64 && 3116 compareType != MCompare::Compare_UIntPtr); 3117 return JSOpToCondition(op, isSigned); 3118 } 3119 3120 // Takes a typed value and checks if it is a certain type. If so, the payload 3121 // is unpacked and returned as that type. Otherwise, it is considered a 3122 // deoptimization. 3123 class MUnbox final : public MUnaryInstruction, public BoxInputsPolicy::Data { 3124 public: 3125 enum Mode { 3126 Fallible, // Check the type, and deoptimize if unexpected. 3127 Infallible, // Type guard is not necessary. 3128 }; 3129 3130 private: 3131 Mode mode_; 3132 3133 MUnbox(MDefinition* ins, MIRType type, Mode mode) 3134 : MUnaryInstruction(classOpcode, ins), mode_(mode) { 3135 // Only allow unboxing a non MIRType::Value when input and output types 3136 // don't match. This is often used to force a bailout. Boxing happens 3137 // during type analysis. 3138 MOZ_ASSERT_IF(ins->type() != MIRType::Value, type != ins->type()); 3139 3140 MOZ_ASSERT(type == MIRType::Boolean || type == MIRType::Int32 || 3141 type == MIRType::Double || type == MIRType::String || 3142 type == MIRType::Symbol || type == MIRType::BigInt || 3143 type == MIRType::Object); 3144 3145 setResultType(type); 3146 setMovable(); 3147 3148 if (mode_ == Fallible) { 3149 setGuard(); 3150 } 3151 } 3152 3153 public: 3154 INSTRUCTION_HEADER(Unbox) 3155 TRIVIAL_NEW_WRAPPERS 3156 3157 Mode mode() const { return mode_; } 3158 bool fallible() const { return mode() != Infallible; } 3159 bool congruentTo(const MDefinition* ins) const override { 3160 if (!ins->isUnbox() || ins->toUnbox()->mode() != mode()) { 3161 return false; 3162 } 3163 return congruentIfOperandsEqual(ins); 3164 } 3165 3166 MDefinition* foldsTo(TempAllocator& alloc) override; 3167 3168 AliasSet getAliasSet() const override { return AliasSet::None(); } 3169 #ifdef JS_JITSPEW 3170 void printOpcode(GenericPrinter& out) const override; 3171 #endif 3172 3173 ALLOW_CLONE(MUnbox) 3174 }; 3175 3176 class MAssertRange : public MUnaryInstruction, public NoTypePolicy::Data { 3177 // This is the range checked by the assertion. Don't confuse this with the 3178 // range_ member or the range() accessor. Since MAssertRange doesn't return 3179 // a value, it doesn't use those. 3180 const Range* assertedRange_; 3181 3182 MAssertRange(MDefinition* ins, const Range* assertedRange) 3183 : MUnaryInstruction(classOpcode, ins), assertedRange_(assertedRange) { 3184 setGuard(); 3185 setResultType(MIRType::None); 3186 } 3187 3188 public: 3189 INSTRUCTION_HEADER(AssertRange) 3190 TRIVIAL_NEW_WRAPPERS 3191 3192 const Range* assertedRange() const { return assertedRange_; } 3193 3194 AliasSet getAliasSet() const override { return AliasSet::None(); } 3195 3196 #ifdef JS_JITSPEW 3197 void printOpcode(GenericPrinter& out) const override; 3198 #endif 3199 }; 3200 3201 class MAssertClass : public MUnaryInstruction, public NoTypePolicy::Data { 3202 const JSClass* class_; 3203 3204 MAssertClass(MDefinition* obj, const JSClass* clasp) 3205 : MUnaryInstruction(classOpcode, obj), class_(clasp) { 3206 MOZ_ASSERT(obj->type() == MIRType::Object); 3207 3208 setGuard(); 3209 setResultType(MIRType::None); 3210 } 3211 3212 public: 3213 INSTRUCTION_HEADER(AssertClass) 3214 TRIVIAL_NEW_WRAPPERS 3215 3216 const JSClass* getClass() const { return class_; } 3217 AliasSet getAliasSet() const override { return AliasSet::None(); } 3218 }; 3219 3220 class MAssertShape : public MUnaryInstruction, public NoTypePolicy::Data { 3221 CompilerShape shape_; 3222 3223 MAssertShape(MDefinition* obj, Shape* shape) 3224 : MUnaryInstruction(classOpcode, obj), shape_(shape) { 3225 MOZ_ASSERT(obj->type() == MIRType::Object); 3226 3227 setGuard(); 3228 setResultType(MIRType::None); 3229 } 3230 3231 public: 3232 INSTRUCTION_HEADER(AssertShape) 3233 TRIVIAL_NEW_WRAPPERS 3234 3235 const Shape* shape() const { return shape_; } 3236 AliasSet getAliasSet() const override { return AliasSet::None(); } 3237 }; 3238 3239 // Eager initialization of arguments object. 3240 class MCreateArgumentsObject : public MUnaryInstruction, 3241 public ObjectPolicy<0>::Data { 3242 CompilerGCPointer<ArgumentsObject*> templateObj_; 3243 3244 MCreateArgumentsObject(MDefinition* callObj, ArgumentsObject* templateObj) 3245 : MUnaryInstruction(classOpcode, callObj), templateObj_(templateObj) { 3246 setResultType(MIRType::Object); 3247 } 3248 3249 public: 3250 INSTRUCTION_HEADER(CreateArgumentsObject) 3251 TRIVIAL_NEW_WRAPPERS 3252 NAMED_OPERANDS((0, getCallObject)) 3253 3254 ArgumentsObject* templateObject() const { return templateObj_; } 3255 3256 AliasSet getAliasSet() const override { return AliasSet::None(); } 3257 3258 bool possiblyCalls() const override { return true; } 3259 3260 [[nodiscard]] bool writeRecoverData( 3261 CompactBufferWriter& writer) const override; 3262 bool canRecoverOnBailout() const override { return true; } 3263 }; 3264 3265 // Eager initialization of arguments object for inlined function 3266 class MCreateInlinedArgumentsObject : public MVariadicInstruction, 3267 public NoFloatPolicyAfter<0>::Data { 3268 CompilerGCPointer<ArgumentsObject*> templateObj_; 3269 3270 explicit MCreateInlinedArgumentsObject(ArgumentsObject* templateObj) 3271 : MVariadicInstruction(classOpcode), templateObj_(templateObj) { 3272 setResultType(MIRType::Object); 3273 } 3274 3275 static const size_t NumNonArgumentOperands = 2; 3276 3277 public: 3278 INSTRUCTION_HEADER(CreateInlinedArgumentsObject) 3279 static MCreateInlinedArgumentsObject* New(TempAllocator& alloc, 3280 MDefinition* callObj, 3281 MDefinition* callee, 3282 MDefinitionVector& args, 3283 ArgumentsObject* templateObj); 3284 NAMED_OPERANDS((0, getCallObject), (1, getCallee)) 3285 3286 ArgumentsObject* templateObject() const { return templateObj_; } 3287 3288 MDefinition* getArg(uint32_t idx) const { 3289 return getOperand(idx + NumNonArgumentOperands); 3290 } 3291 uint32_t numActuals() const { return numOperands() - NumNonArgumentOperands; } 3292 3293 AliasSet getAliasSet() const override { return AliasSet::None(); } 3294 3295 bool possiblyCalls() const override { return true; } 3296 3297 [[nodiscard]] bool writeRecoverData( 3298 CompactBufferWriter& writer) const override; 3299 bool canRecoverOnBailout() const override { return true; } 3300 }; 3301 3302 class MGetInlinedArgument 3303 : public MVariadicInstruction, 3304 public MixPolicy<UnboxedInt32Policy<0>, NoFloatPolicyAfter<1>>::Data { 3305 MGetInlinedArgument() : MVariadicInstruction(classOpcode) { 3306 setResultType(MIRType::Value); 3307 } 3308 3309 static const size_t NumNonArgumentOperands = 1; 3310 3311 public: 3312 INSTRUCTION_HEADER(GetInlinedArgument) 3313 static MGetInlinedArgument* New(TempAllocator& alloc, MDefinition* index, 3314 MCreateInlinedArgumentsObject* args); 3315 static MGetInlinedArgument* New(TempAllocator& alloc, MDefinition* index, 3316 const CallInfo& callInfo); 3317 NAMED_OPERANDS((0, index)) 3318 3319 MDefinition* getArg(uint32_t idx) const { 3320 return getOperand(idx + NumNonArgumentOperands); 3321 } 3322 uint32_t numActuals() const { return numOperands() - NumNonArgumentOperands; } 3323 3324 bool congruentTo(const MDefinition* ins) const override { 3325 return congruentIfOperandsEqual(ins); 3326 } 3327 AliasSet getAliasSet() const override { return AliasSet::None(); } 3328 3329 MDefinition* foldsTo(TempAllocator& alloc) override; 3330 }; 3331 3332 class MGetInlinedArgumentHole 3333 : public MVariadicInstruction, 3334 public MixPolicy<UnboxedInt32Policy<0>, NoFloatPolicyAfter<1>>::Data { 3335 MGetInlinedArgumentHole() : MVariadicInstruction(classOpcode) { 3336 setGuard(); 3337 setResultType(MIRType::Value); 3338 } 3339 3340 static const size_t NumNonArgumentOperands = 1; 3341 3342 public: 3343 INSTRUCTION_HEADER(GetInlinedArgumentHole) 3344 static MGetInlinedArgumentHole* New(TempAllocator& alloc, MDefinition* index, 3345 MCreateInlinedArgumentsObject* args); 3346 NAMED_OPERANDS((0, index)) 3347 3348 MDefinition* getArg(uint32_t idx) const { 3349 return getOperand(idx + NumNonArgumentOperands); 3350 } 3351 uint32_t numActuals() const { return numOperands() - NumNonArgumentOperands; } 3352 3353 bool congruentTo(const MDefinition* ins) const override { 3354 return congruentIfOperandsEqual(ins); 3355 } 3356 AliasSet getAliasSet() const override { return AliasSet::None(); } 3357 3358 MDefinition* foldsTo(TempAllocator& alloc) override; 3359 }; 3360 3361 class MInlineArgumentsSlice 3362 : public MVariadicInstruction, 3363 public MixPolicy<UnboxedInt32Policy<0>, UnboxedInt32Policy<1>, 3364 NoFloatPolicyAfter<2>>::Data { 3365 JSObject* templateObj_; 3366 gc::Heap initialHeap_; 3367 3368 MInlineArgumentsSlice(JSObject* templateObj, gc::Heap initialHeap) 3369 : MVariadicInstruction(classOpcode), 3370 templateObj_(templateObj), 3371 initialHeap_(initialHeap) { 3372 setResultType(MIRType::Object); 3373 } 3374 3375 static const size_t NumNonArgumentOperands = 2; 3376 3377 public: 3378 INSTRUCTION_HEADER(InlineArgumentsSlice) 3379 static MInlineArgumentsSlice* New(TempAllocator& alloc, MDefinition* begin, 3380 MDefinition* count, 3381 MCreateInlinedArgumentsObject* args, 3382 JSObject* templateObj, 3383 gc::Heap initialHeap); 3384 NAMED_OPERANDS((0, begin), (1, count)) 3385 3386 JSObject* templateObj() const { return templateObj_; } 3387 gc::Heap initialHeap() const { return initialHeap_; } 3388 3389 MDefinition* getArg(uint32_t idx) const { 3390 return getOperand(idx + NumNonArgumentOperands); 3391 } 3392 uint32_t numActuals() const { return numOperands() - NumNonArgumentOperands; } 3393 3394 AliasSet getAliasSet() const override { return AliasSet::None(); } 3395 3396 bool possiblyCalls() const override { return true; } 3397 }; 3398 3399 // Allocates a new BoundFunctionObject and calls 3400 // BoundFunctionObject::functionBindImpl. This instruction can have arbitrary 3401 // side-effects because the GetProperty calls for length/name can call into JS. 3402 class MBindFunction 3403 : public MVariadicInstruction, 3404 public MixPolicy<ObjectPolicy<0>, NoFloatPolicyAfter<1>>::Data { 3405 CompilerGCPointer<JSObject*> templateObj_; 3406 3407 explicit MBindFunction(JSObject* templateObj) 3408 : MVariadicInstruction(classOpcode), templateObj_(templateObj) { 3409 setResultType(MIRType::Object); 3410 } 3411 3412 // The target object is operand 0. 3413 static const size_t NumNonArgumentOperands = 1; 3414 3415 public: 3416 INSTRUCTION_HEADER(BindFunction) 3417 static MBindFunction* New(TempAllocator& alloc, MDefinition* target, 3418 uint32_t argc, JSObject* templateObj); 3419 NAMED_OPERANDS((0, target)) 3420 3421 JSObject* templateObject() const { return templateObj_; } 3422 3423 MDefinition* getArg(uint32_t idx) const { 3424 return getOperand(idx + NumNonArgumentOperands); 3425 } 3426 void initArg(size_t i, MDefinition* arg) { 3427 initOperand(NumNonArgumentOperands + i, arg); 3428 } 3429 uint32_t numStackArgs() const { 3430 return numOperands() - NumNonArgumentOperands; 3431 } 3432 3433 bool possiblyCalls() const override { return true; } 3434 }; 3435 3436 class MToFPInstruction : public MUnaryInstruction, public ToDoublePolicy::Data { 3437 protected: 3438 MToFPInstruction(Opcode op, MDefinition* def, MIRType resultType) 3439 : MUnaryInstruction(op, def) { 3440 setResultType(resultType); 3441 setMovable(); 3442 3443 // Guard unless the conversion is known to be non-effectful & non-throwing. 3444 if (!def->typeIsOneOf({MIRType::Undefined, MIRType::Null, MIRType::Boolean, 3445 MIRType::Int32, MIRType::Double, MIRType::Float32, 3446 MIRType::String})) { 3447 setGuard(); 3448 } 3449 } 3450 }; 3451 3452 // Converts a primitive (either typed or untyped) to a double. If the input is 3453 // not primitive at runtime, a bailout occurs. 3454 class MToDouble : public MToFPInstruction { 3455 private: 3456 TruncateKind implicitTruncate_ = TruncateKind::NoTruncate; 3457 3458 explicit MToDouble(MDefinition* def) 3459 : MToFPInstruction(classOpcode, def, MIRType::Double) {} 3460 3461 public: 3462 INSTRUCTION_HEADER(ToDouble) 3463 TRIVIAL_NEW_WRAPPERS 3464 3465 MDefinition* foldsTo(TempAllocator& alloc) override; 3466 bool congruentTo(const MDefinition* ins) const override { 3467 return congruentIfOperandsEqual(ins); 3468 } 3469 AliasSet getAliasSet() const override { return AliasSet::None(); } 3470 3471 void computeRange(TempAllocator& alloc) override; 3472 bool canTruncate() const override; 3473 void truncate(TruncateKind kind) override; 3474 TruncateKind operandTruncateKind(size_t index) const override; 3475 3476 #ifdef DEBUG 3477 bool isConsistentFloat32Use(MUse* use) const override { return true; } 3478 #endif 3479 3480 TruncateKind truncateKind() const { return implicitTruncate_; } 3481 void setTruncateKind(TruncateKind kind) { 3482 implicitTruncate_ = std::max(implicitTruncate_, kind); 3483 } 3484 3485 [[nodiscard]] bool writeRecoverData( 3486 CompactBufferWriter& writer) const override; 3487 bool canRecoverOnBailout() const override { 3488 if (input()->type() == MIRType::Value) { 3489 return false; 3490 } 3491 if (input()->type() == MIRType::Symbol) { 3492 return false; 3493 } 3494 if (input()->type() == MIRType::BigInt) { 3495 return false; 3496 } 3497 3498 return true; 3499 } 3500 3501 ALLOW_CLONE(MToDouble) 3502 }; 3503 3504 // Converts a primitive (either typed or untyped) to a float32. If the input is 3505 // not primitive at runtime, a bailout occurs. 3506 class MToFloat32 : public MToFPInstruction { 3507 bool mustPreserveNaN_ = false; 3508 3509 explicit MToFloat32(MDefinition* def) 3510 : MToFPInstruction(classOpcode, def, MIRType::Float32) {} 3511 3512 explicit MToFloat32(MDefinition* def, bool mustPreserveNaN) 3513 : MToFloat32(def) { 3514 mustPreserveNaN_ = mustPreserveNaN; 3515 } 3516 3517 public: 3518 INSTRUCTION_HEADER(ToFloat32) 3519 TRIVIAL_NEW_WRAPPERS 3520 3521 virtual MDefinition* foldsTo(TempAllocator& alloc) override; 3522 bool congruentTo(const MDefinition* ins) const override { 3523 if (!congruentIfOperandsEqual(ins)) { 3524 return false; 3525 } 3526 return ins->toToFloat32()->mustPreserveNaN_ == mustPreserveNaN_; 3527 } 3528 AliasSet getAliasSet() const override { return AliasSet::None(); } 3529 3530 void computeRange(TempAllocator& alloc) override; 3531 3532 bool canConsumeFloat32(MUse* use) const override { return true; } 3533 bool canProduceFloat32() const override { return true; } 3534 3535 [[nodiscard]] bool writeRecoverData( 3536 CompactBufferWriter& writer) const override; 3537 bool canRecoverOnBailout() const override { return true; } 3538 3539 ALLOW_CLONE(MToFloat32) 3540 }; 3541 3542 // Converts a primitive (either typed or untyped) to a float16. If the input is 3543 // not primitive at runtime, a bailout occurs. 3544 class MToFloat16 : public MToFPInstruction { 3545 explicit MToFloat16(MDefinition* def) 3546 : MToFPInstruction(classOpcode, def, MIRType::Float32) {} 3547 3548 public: 3549 INSTRUCTION_HEADER(ToFloat16) 3550 TRIVIAL_NEW_WRAPPERS 3551 3552 virtual MDefinition* foldsTo(TempAllocator& alloc) override; 3553 bool congruentTo(const MDefinition* ins) const override { 3554 return congruentIfOperandsEqual(ins); 3555 } 3556 AliasSet getAliasSet() const override { return AliasSet::None(); } 3557 3558 // This instruction can produce but NOT consume float32. 3559 bool canProduceFloat32() const override { return true; } 3560 3561 #ifdef DEBUG 3562 // Float16 inputs are typed as float32, but this instruction can NOT consume 3563 // float32. 3564 bool isConsistentFloat32Use(MUse* use) const override { return true; } 3565 #endif 3566 3567 [[nodiscard]] bool writeRecoverData( 3568 CompactBufferWriter& writer) const override; 3569 bool canRecoverOnBailout() const override { return true; } 3570 3571 ALLOW_CLONE(MToFloat16) 3572 }; 3573 3574 class MWrapInt64ToInt32 : public MUnaryInstruction, public NoTypePolicy::Data { 3575 bool bottomHalf_; 3576 3577 explicit MWrapInt64ToInt32(MDefinition* def, bool bottomHalf = true) 3578 : MUnaryInstruction(classOpcode, def), bottomHalf_(bottomHalf) { 3579 setResultType(MIRType::Int32); 3580 setMovable(); 3581 } 3582 3583 public: 3584 INSTRUCTION_HEADER(WrapInt64ToInt32) 3585 TRIVIAL_NEW_WRAPPERS 3586 3587 MDefinition* foldsTo(TempAllocator& alloc) override; 3588 bool congruentTo(const MDefinition* ins) const override { 3589 if (!ins->isWrapInt64ToInt32()) { 3590 return false; 3591 } 3592 if (ins->toWrapInt64ToInt32()->bottomHalf() != bottomHalf()) { 3593 return false; 3594 } 3595 return congruentIfOperandsEqual(ins); 3596 } 3597 AliasSet getAliasSet() const override { return AliasSet::None(); } 3598 3599 bool bottomHalf() const { return bottomHalf_; } 3600 3601 ALLOW_CLONE(MWrapInt64ToInt32) 3602 }; 3603 3604 class MExtendInt32ToInt64 : public MUnaryInstruction, 3605 public NoTypePolicy::Data { 3606 bool isUnsigned_; 3607 3608 MExtendInt32ToInt64(MDefinition* def, bool isUnsigned) 3609 : MUnaryInstruction(classOpcode, def), isUnsigned_(isUnsigned) { 3610 setResultType(MIRType::Int64); 3611 setMovable(); 3612 } 3613 3614 public: 3615 INSTRUCTION_HEADER(ExtendInt32ToInt64) 3616 TRIVIAL_NEW_WRAPPERS 3617 3618 bool isUnsigned() const { return isUnsigned_; } 3619 3620 MDefinition* foldsTo(TempAllocator& alloc) override; 3621 bool congruentTo(const MDefinition* ins) const override { 3622 if (!ins->isExtendInt32ToInt64()) { 3623 return false; 3624 } 3625 if (ins->toExtendInt32ToInt64()->isUnsigned_ != isUnsigned_) { 3626 return false; 3627 } 3628 return congruentIfOperandsEqual(ins); 3629 } 3630 AliasSet getAliasSet() const override { return AliasSet::None(); } 3631 3632 ALLOW_CLONE(MExtendInt32ToInt64) 3633 }; 3634 3635 // Converts an int32 value to intptr by sign-extending it. 3636 class MInt32ToIntPtr : public MUnaryInstruction, 3637 public UnboxedInt32Policy<0>::Data { 3638 bool canBeNegative_ = true; 3639 3640 explicit MInt32ToIntPtr(MDefinition* def) 3641 : MUnaryInstruction(classOpcode, def) { 3642 setResultType(MIRType::IntPtr); 3643 setMovable(); 3644 } 3645 3646 public: 3647 INSTRUCTION_HEADER(Int32ToIntPtr) 3648 TRIVIAL_NEW_WRAPPERS 3649 3650 bool canBeNegative() const { return canBeNegative_; } 3651 void setCanNotBeNegative() { canBeNegative_ = false; } 3652 3653 void computeRange(TempAllocator& alloc) override; 3654 void collectRangeInfoPreTrunc() override; 3655 3656 MDefinition* foldsTo(TempAllocator& alloc) override; 3657 3658 bool congruentTo(const MDefinition* ins) const override { 3659 return congruentIfOperandsEqual(ins); 3660 } 3661 AliasSet getAliasSet() const override { return AliasSet::None(); } 3662 }; 3663 3664 // Converts an IntPtr value >= 0 to Int32. Bails out if the value > INT32_MAX. 3665 class MNonNegativeIntPtrToInt32 : public MUnaryInstruction, 3666 public NoTypePolicy::Data { 3667 explicit MNonNegativeIntPtrToInt32(MDefinition* def) 3668 : MUnaryInstruction(classOpcode, def) { 3669 MOZ_ASSERT(def->type() == MIRType::IntPtr); 3670 setResultType(MIRType::Int32); 3671 setMovable(); 3672 } 3673 3674 public: 3675 INSTRUCTION_HEADER(NonNegativeIntPtrToInt32) 3676 TRIVIAL_NEW_WRAPPERS 3677 3678 void computeRange(TempAllocator& alloc) override; 3679 3680 bool congruentTo(const MDefinition* ins) const override { 3681 return congruentIfOperandsEqual(ins); 3682 } 3683 AliasSet getAliasSet() const override { return AliasSet::None(); } 3684 }; 3685 3686 // Converts an IntPtr value to Double. 3687 class MIntPtrToDouble : public MUnaryInstruction, public NoTypePolicy::Data { 3688 explicit MIntPtrToDouble(MDefinition* def) 3689 : MUnaryInstruction(classOpcode, def) { 3690 MOZ_ASSERT(def->type() == MIRType::IntPtr); 3691 setResultType(MIRType::Double); 3692 setMovable(); 3693 } 3694 3695 public: 3696 INSTRUCTION_HEADER(IntPtrToDouble) 3697 TRIVIAL_NEW_WRAPPERS 3698 3699 bool congruentTo(const MDefinition* ins) const override { 3700 return congruentIfOperandsEqual(ins); 3701 } 3702 AliasSet getAliasSet() const override { return AliasSet::None(); } 3703 }; 3704 3705 // Subtracts (byteSize - 1) from the input value. Bails out if the result is 3706 // negative. This is used to implement bounds checks for DataView accesses. 3707 class MAdjustDataViewLength : public MUnaryInstruction, 3708 public NoTypePolicy::Data { 3709 const uint32_t byteSize_; 3710 3711 MAdjustDataViewLength(MDefinition* input, uint32_t byteSize) 3712 : MUnaryInstruction(classOpcode, input), byteSize_(byteSize) { 3713 MOZ_ASSERT(input->type() == MIRType::IntPtr); 3714 MOZ_ASSERT(byteSize > 1); 3715 setResultType(MIRType::IntPtr); 3716 setMovable(); 3717 setGuard(); 3718 } 3719 3720 public: 3721 INSTRUCTION_HEADER(AdjustDataViewLength) 3722 TRIVIAL_NEW_WRAPPERS 3723 3724 uint32_t byteSize() const { return byteSize_; } 3725 3726 bool congruentTo(const MDefinition* ins) const override { 3727 if (!ins->isAdjustDataViewLength()) { 3728 return false; 3729 } 3730 if (ins->toAdjustDataViewLength()->byteSize() != byteSize()) { 3731 return false; 3732 } 3733 return congruentIfOperandsEqual(ins); 3734 } 3735 AliasSet getAliasSet() const override { return AliasSet::None(); } 3736 }; 3737 3738 class MInt64ToFloatingPoint : public MUnaryInstruction, 3739 public NoTypePolicy::Data { 3740 bool isUnsigned_; 3741 wasm::BytecodeOffset bytecodeOffset_; 3742 3743 MInt64ToFloatingPoint(MDefinition* def, MIRType type, 3744 wasm::BytecodeOffset bytecodeOffset, bool isUnsigned) 3745 : MUnaryInstruction(classOpcode, def), 3746 isUnsigned_(isUnsigned), 3747 bytecodeOffset_(bytecodeOffset) { 3748 MOZ_ASSERT(IsFloatingPointType(type)); 3749 setResultType(type); 3750 setMovable(); 3751 } 3752 3753 public: 3754 INSTRUCTION_HEADER(Int64ToFloatingPoint) 3755 TRIVIAL_NEW_WRAPPERS 3756 3757 bool isUnsigned() const { return isUnsigned_; } 3758 wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; } 3759 3760 bool congruentTo(const MDefinition* ins) const override { 3761 if (!ins->isInt64ToFloatingPoint()) { 3762 return false; 3763 } 3764 if (ins->toInt64ToFloatingPoint()->isUnsigned_ != isUnsigned_) { 3765 return false; 3766 } 3767 return congruentIfOperandsEqual(ins); 3768 } 3769 AliasSet getAliasSet() const override { return AliasSet::None(); } 3770 }; 3771 3772 // It used only for arm now because on arm we need to call builtin to convert 3773 // i64 to float. 3774 class MBuiltinInt64ToFloatingPoint : public MAryInstruction<2>, 3775 public NoTypePolicy::Data { 3776 bool isUnsigned_; 3777 wasm::BytecodeOffset bytecodeOffset_; 3778 3779 MBuiltinInt64ToFloatingPoint(MDefinition* def, MDefinition* instance, 3780 MIRType type, 3781 wasm::BytecodeOffset bytecodeOffset, 3782 bool isUnsigned) 3783 : MAryInstruction(classOpcode), 3784 isUnsigned_(isUnsigned), 3785 bytecodeOffset_(bytecodeOffset) { 3786 MOZ_ASSERT(IsFloatingPointType(type)); 3787 initOperand(0, def); 3788 initOperand(1, instance); 3789 setResultType(type); 3790 setMovable(); 3791 } 3792 3793 public: 3794 INSTRUCTION_HEADER(BuiltinInt64ToFloatingPoint) 3795 NAMED_OPERANDS((0, input), (1, instance)); 3796 TRIVIAL_NEW_WRAPPERS 3797 3798 bool isUnsigned() const { return isUnsigned_; } 3799 wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; } 3800 3801 bool congruentTo(const MDefinition* ins) const override { 3802 if (!ins->isBuiltinInt64ToFloatingPoint()) { 3803 return false; 3804 } 3805 if (ins->toBuiltinInt64ToFloatingPoint()->isUnsigned_ != isUnsigned_) { 3806 return false; 3807 } 3808 return congruentIfOperandsEqual(ins); 3809 } 3810 AliasSet getAliasSet() const override { return AliasSet::None(); } 3811 }; 3812 3813 // Applies ECMA's ToNumber on a primitive (either typed or untyped) and expects 3814 // the result to be precisely representable as an Int32, otherwise bails. 3815 // 3816 // If the input is not primitive at runtime, a bailout occurs. If the input 3817 // cannot be converted to an int32 without loss (i.e. 5.5 or undefined) then a 3818 // bailout occurs. 3819 class MToNumberInt32 : public MUnaryInstruction, public ToInt32Policy::Data { 3820 bool needsNegativeZeroCheck_; 3821 IntConversionInputKind conversion_; 3822 3823 explicit MToNumberInt32(MDefinition* def, IntConversionInputKind conversion = 3824 IntConversionInputKind::Any) 3825 : MUnaryInstruction(classOpcode, def), 3826 needsNegativeZeroCheck_(true), 3827 conversion_(conversion) { 3828 setResultType(MIRType::Int32); 3829 setMovable(); 3830 3831 // Guard unless the conversion is known to be non-effectful & non-throwing. 3832 if (!def->typeIsOneOf({MIRType::Undefined, MIRType::Null, MIRType::Boolean, 3833 MIRType::Int32, MIRType::Double, MIRType::Float32, 3834 MIRType::String})) { 3835 setGuard(); 3836 } 3837 } 3838 3839 public: 3840 INSTRUCTION_HEADER(ToNumberInt32) 3841 TRIVIAL_NEW_WRAPPERS 3842 3843 MDefinition* foldsTo(TempAllocator& alloc) override; 3844 3845 // this only has backwards information flow. 3846 void analyzeEdgeCasesBackward() override; 3847 3848 bool needsNegativeZeroCheck() const { return needsNegativeZeroCheck_; } 3849 void setNeedsNegativeZeroCheck(bool needsCheck) { 3850 needsNegativeZeroCheck_ = needsCheck; 3851 } 3852 3853 IntConversionInputKind conversion() const { return conversion_; } 3854 3855 bool congruentTo(const MDefinition* ins) const override { 3856 if (!ins->isToNumberInt32() || 3857 ins->toToNumberInt32()->conversion() != conversion()) { 3858 return false; 3859 } 3860 return congruentIfOperandsEqual(ins); 3861 } 3862 3863 AliasSet getAliasSet() const override { return AliasSet::None(); } 3864 void computeRange(TempAllocator& alloc) override; 3865 void collectRangeInfoPreTrunc() override; 3866 3867 #ifdef DEBUG 3868 bool isConsistentFloat32Use(MUse* use) const override { return true; } 3869 #endif 3870 3871 ALLOW_CLONE(MToNumberInt32) 3872 }; 3873 3874 // Converts a value or typed input to a truncated int32, for use with bitwise 3875 // operations. This is an infallible ValueToECMAInt32. 3876 class MTruncateToInt32 : public MUnaryInstruction, public ToInt32Policy::Data { 3877 wasm::TrapSiteDesc trapSiteDesc_; 3878 3879 explicit MTruncateToInt32( 3880 MDefinition* def, wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc()) 3881 : MUnaryInstruction(classOpcode, def), trapSiteDesc_(trapSiteDesc) { 3882 setResultType(MIRType::Int32); 3883 setMovable(); 3884 3885 // Guard unless the conversion is known to be non-effectful & non-throwing. 3886 if (mightHaveSideEffects(def)) { 3887 setGuard(); 3888 } 3889 } 3890 3891 public: 3892 INSTRUCTION_HEADER(TruncateToInt32) 3893 TRIVIAL_NEW_WRAPPERS 3894 3895 static bool mightHaveSideEffects(MDefinition* def) { 3896 return !def->typeIsOneOf({MIRType::Undefined, MIRType::Null, 3897 MIRType::Boolean, MIRType::Int32, MIRType::Double, 3898 MIRType::Float32, MIRType::String}); 3899 } 3900 3901 MDefinition* foldsTo(TempAllocator& alloc) override; 3902 3903 bool congruentTo(const MDefinition* ins) const override { 3904 return congruentIfOperandsEqual(ins); 3905 } 3906 AliasSet getAliasSet() const override { return AliasSet::None(); } 3907 3908 void computeRange(TempAllocator& alloc) override; 3909 TruncateKind operandTruncateKind(size_t index) const override; 3910 #ifdef DEBUG 3911 bool isConsistentFloat32Use(MUse* use) const override { return true; } 3912 #endif 3913 3914 [[nodiscard]] bool writeRecoverData( 3915 CompactBufferWriter& writer) const override; 3916 bool canRecoverOnBailout() const override { 3917 return input()->type() < MIRType::Symbol; 3918 } 3919 3920 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } 3921 3922 ALLOW_CLONE(MTruncateToInt32) 3923 }; 3924 3925 // Converts a primitive (either typed or untyped) to a BigInt. If the input is 3926 // not primitive at runtime, a bailout occurs. 3927 class MToBigInt : public MUnaryInstruction, public ToBigIntPolicy::Data { 3928 private: 3929 explicit MToBigInt(MDefinition* def) : MUnaryInstruction(classOpcode, def) { 3930 setResultType(MIRType::BigInt); 3931 setMovable(); 3932 3933 // Guard unless the conversion is known to be non-effectful & non-throwing. 3934 if (!def->typeIsOneOf({MIRType::Boolean, MIRType::BigInt})) { 3935 setGuard(); 3936 } 3937 } 3938 3939 public: 3940 INSTRUCTION_HEADER(ToBigInt) 3941 TRIVIAL_NEW_WRAPPERS 3942 3943 bool congruentTo(const MDefinition* ins) const override { 3944 return congruentIfOperandsEqual(ins); 3945 } 3946 AliasSet getAliasSet() const override { return AliasSet::None(); } 3947 3948 ALLOW_CLONE(MToBigInt) 3949 }; 3950 3951 // Takes a Value or typed input and returns a suitable Int64 using the 3952 // ToBigInt algorithm, possibly calling out to the VM for string, etc inputs. 3953 class MToInt64 : public MUnaryInstruction, public ToInt64Policy::Data { 3954 explicit MToInt64(MDefinition* def) : MUnaryInstruction(classOpcode, def) { 3955 setResultType(MIRType::Int64); 3956 setMovable(); 3957 3958 // Guard unless the conversion is known to be non-effectful & non-throwing. 3959 if (!def->typeIsOneOf( 3960 {MIRType::Boolean, MIRType::BigInt, MIRType::Int64})) { 3961 setGuard(); 3962 } 3963 } 3964 3965 public: 3966 INSTRUCTION_HEADER(ToInt64) 3967 TRIVIAL_NEW_WRAPPERS 3968 3969 bool congruentTo(const MDefinition* ins) const override { 3970 return congruentIfOperandsEqual(ins); 3971 } 3972 3973 AliasSet getAliasSet() const override { return AliasSet::None(); } 3974 3975 MDefinition* foldsTo(TempAllocator& alloc) override; 3976 3977 ALLOW_CLONE(MToInt64) 3978 }; 3979 3980 // Takes a BigInt pointer and returns its toInt64 value. 3981 class MTruncateBigIntToInt64 : public MUnaryInstruction, 3982 public NoTypePolicy::Data { 3983 explicit MTruncateBigIntToInt64(MDefinition* def) 3984 : MUnaryInstruction(classOpcode, def) { 3985 MOZ_ASSERT(def->type() == MIRType::BigInt); 3986 setResultType(MIRType::Int64); 3987 setMovable(); 3988 } 3989 3990 public: 3991 INSTRUCTION_HEADER(TruncateBigIntToInt64) 3992 TRIVIAL_NEW_WRAPPERS 3993 3994 bool congruentTo(const MDefinition* ins) const override { 3995 return congruentIfOperandsEqual(ins); 3996 } 3997 3998 AliasSet getAliasSet() const override { return AliasSet::None(); } 3999 4000 MDefinition* foldsTo(TempAllocator& alloc) override; 4001 4002 ALLOW_CLONE(MTruncateBigIntToInt64) 4003 }; 4004 4005 // Takes an Int64 and returns a fresh BigInt pointer. 4006 class MInt64ToBigInt : public MUnaryInstruction, public NoTypePolicy::Data { 4007 bool isSigned_; 4008 4009 MInt64ToBigInt(MDefinition* def, bool isSigned) 4010 : MUnaryInstruction(classOpcode, def), isSigned_(isSigned) { 4011 MOZ_ASSERT(def->type() == MIRType::Int64); 4012 setResultType(MIRType::BigInt); 4013 setMovable(); 4014 } 4015 4016 public: 4017 INSTRUCTION_HEADER(Int64ToBigInt) 4018 TRIVIAL_NEW_WRAPPERS 4019 4020 bool congruentTo(const MDefinition* ins) const override { 4021 return congruentIfOperandsEqual(ins) && 4022 ins->toInt64ToBigInt()->isSigned() == isSigned(); 4023 } 4024 4025 AliasSet getAliasSet() const override { return AliasSet::None(); } 4026 4027 bool isSigned() const { return isSigned_; } 4028 4029 [[nodiscard]] bool writeRecoverData( 4030 CompactBufferWriter& writer) const override; 4031 bool canRecoverOnBailout() const override { return true; } 4032 4033 ALLOW_CLONE(MInt64ToBigInt) 4034 }; 4035 4036 // Takes an Int64 and returns a IntPtr. 4037 class MInt64ToIntPtr : public MUnaryInstruction, public NoTypePolicy::Data { 4038 bool isSigned_; 4039 4040 MInt64ToIntPtr(MDefinition* def, bool isSigned) 4041 : MUnaryInstruction(classOpcode, def), isSigned_(isSigned) { 4042 MOZ_ASSERT(def->type() == MIRType::Int64); 4043 setResultType(MIRType::IntPtr); 4044 setMovable(); 4045 } 4046 4047 public: 4048 INSTRUCTION_HEADER(Int64ToIntPtr) 4049 TRIVIAL_NEW_WRAPPERS 4050 4051 bool congruentTo(const MDefinition* ins) const override { 4052 return congruentIfOperandsEqual(ins) && 4053 ins->toInt64ToIntPtr()->isSigned() == isSigned(); 4054 } 4055 4056 AliasSet getAliasSet() const override { return AliasSet::None(); } 4057 4058 bool isSigned() const { return isSigned_; } 4059 4060 ALLOW_CLONE(MInt64ToIntPtr) 4061 }; 4062 4063 // Takes a IntPtr and returns an Int64. 4064 class MIntPtrToInt64 : public MUnaryInstruction, public NoTypePolicy::Data { 4065 explicit MIntPtrToInt64(MDefinition* def) 4066 : MUnaryInstruction(classOpcode, def) { 4067 MOZ_ASSERT(def->type() == MIRType::IntPtr); 4068 setResultType(MIRType::Int64); 4069 setMovable(); 4070 } 4071 4072 public: 4073 INSTRUCTION_HEADER(IntPtrToInt64) 4074 TRIVIAL_NEW_WRAPPERS 4075 4076 bool congruentTo(const MDefinition* ins) const override { 4077 return congruentIfOperandsEqual(ins); 4078 } 4079 4080 AliasSet getAliasSet() const override { return AliasSet::None(); } 4081 4082 ALLOW_CLONE(MIntPtrToInt64) 4083 }; 4084 4085 // Converts any type to a string 4086 class MToString : public MUnaryInstruction, public ToStringPolicy::Data { 4087 public: 4088 // MToString has two modes for handling of object/symbol arguments: if the 4089 // to-string conversion happens as part of another opcode, we have to bail out 4090 // to Baseline. If the conversion is for a stand-alone JSOp we can support 4091 // side-effects. 4092 enum class SideEffectHandling { Bailout, Supported }; 4093 4094 private: 4095 SideEffectHandling sideEffects_; 4096 bool mightHaveSideEffects_ = false; 4097 4098 MToString(MDefinition* def, SideEffectHandling sideEffects) 4099 : MUnaryInstruction(classOpcode, def), sideEffects_(sideEffects) { 4100 setResultType(MIRType::String); 4101 4102 if (!def->typeIsOneOf({MIRType::Undefined, MIRType::Null, MIRType::Boolean, 4103 MIRType::Int32, MIRType::Double, MIRType::Float32, 4104 MIRType::String, MIRType::BigInt})) { 4105 mightHaveSideEffects_ = true; 4106 } 4107 4108 // If this instruction is not effectful, mark it as movable and set the 4109 // Guard flag if needed. If the operation is effectful it won't be 4110 // optimized anyway so there's no need to set any flags. 4111 if (!isEffectful()) { 4112 setMovable(); 4113 // Objects might override toString; Symbol throws. We bailout in those 4114 // cases and run side-effects in baseline instead. 4115 if (mightHaveSideEffects_) { 4116 setGuard(); 4117 } 4118 } 4119 } 4120 4121 public: 4122 INSTRUCTION_HEADER(ToString) 4123 TRIVIAL_NEW_WRAPPERS 4124 4125 MDefinition* foldsTo(TempAllocator& alloc) override; 4126 4127 bool congruentTo(const MDefinition* ins) const override { 4128 if (!ins->isToString()) { 4129 return false; 4130 } 4131 if (sideEffects_ != ins->toToString()->sideEffects_) { 4132 return false; 4133 } 4134 return congruentIfOperandsEqual(ins); 4135 } 4136 4137 AliasSet getAliasSet() const override { 4138 if (supportSideEffects() && mightHaveSideEffects_) { 4139 return AliasSet::Store(AliasSet::Any); 4140 } 4141 return AliasSet::None(); 4142 } 4143 4144 bool mightHaveSideEffects() const { return mightHaveSideEffects_; } 4145 4146 bool supportSideEffects() const { 4147 return sideEffects_ == SideEffectHandling::Supported; 4148 } 4149 4150 bool needsSnapshot() const { 4151 return sideEffects_ == SideEffectHandling::Bailout && mightHaveSideEffects_; 4152 } 4153 4154 ALLOW_CLONE(MToString) 4155 }; 4156 4157 class MBitNot : public MUnaryInstruction, public BitwisePolicy::Data { 4158 MBitNot(MDefinition* input, MIRType type) 4159 : MUnaryInstruction(classOpcode, input) { 4160 MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64); 4161 setResultType(type); 4162 setMovable(); 4163 } 4164 4165 public: 4166 INSTRUCTION_HEADER(BitNot) 4167 TRIVIAL_NEW_WRAPPERS 4168 4169 MDefinition* foldsTo(TempAllocator& alloc) override; 4170 4171 bool congruentTo(const MDefinition* ins) const override { 4172 return congruentIfOperandsEqual(ins); 4173 } 4174 AliasSet getAliasSet() const override { return AliasSet::None(); } 4175 void computeRange(TempAllocator& alloc) override; 4176 4177 [[nodiscard]] bool writeRecoverData( 4178 CompactBufferWriter& writer) const override; 4179 bool canRecoverOnBailout() const override { return type() != MIRType::Int64; } 4180 4181 ALLOW_CLONE(MBitNot) 4182 }; 4183 4184 class MTypeOf : public MUnaryInstruction, 4185 public BoxExceptPolicy<0, MIRType::Object>::Data { 4186 explicit MTypeOf(MDefinition* def) : MUnaryInstruction(classOpcode, def) { 4187 setResultType(MIRType::Int32); 4188 setMovable(); 4189 } 4190 TypeDataList observed_; 4191 4192 public: 4193 INSTRUCTION_HEADER(TypeOf) 4194 TRIVIAL_NEW_WRAPPERS 4195 4196 void setObservedTypes(const TypeDataList& observed) { observed_ = observed; } 4197 bool hasObservedTypes() const { return observed_.count() > 0; } 4198 const TypeDataList& observedTypes() const { return observed_; } 4199 4200 MDefinition* foldsTo(TempAllocator& alloc) override; 4201 4202 AliasSet getAliasSet() const override { return AliasSet::None(); } 4203 4204 bool congruentTo(const MDefinition* ins) const override { 4205 return congruentIfOperandsEqual(ins); 4206 } 4207 4208 [[nodiscard]] bool writeRecoverData( 4209 CompactBufferWriter& writer) const override; 4210 bool canRecoverOnBailout() const override { return true; } 4211 }; 4212 4213 class MTypeOfIs : public MUnaryInstruction, public NoTypePolicy::Data { 4214 JSOp jsop_; 4215 JSType jstype_; 4216 4217 MTypeOfIs(MDefinition* def, JSOp jsop, JSType jstype) 4218 : MUnaryInstruction(classOpcode, def), jsop_(jsop), jstype_(jstype) { 4219 MOZ_ASSERT(def->type() == MIRType::Object || def->type() == MIRType::Value); 4220 4221 setResultType(MIRType::Boolean); 4222 setMovable(); 4223 } 4224 4225 public: 4226 INSTRUCTION_HEADER(TypeOfIs) 4227 TRIVIAL_NEW_WRAPPERS 4228 4229 JSOp jsop() const { return jsop_; } 4230 JSType jstype() const { return jstype_; } 4231 4232 AliasSet getAliasSet() const override { return AliasSet::None(); } 4233 4234 bool congruentTo(const MDefinition* ins) const override { 4235 if (!congruentIfOperandsEqual(ins)) { 4236 return false; 4237 } 4238 return jsop() == ins->toTypeOfIs()->jsop() && 4239 jstype() == ins->toTypeOfIs()->jstype(); 4240 } 4241 4242 #ifdef JS_JITSPEW 4243 void printOpcode(GenericPrinter& out) const override; 4244 #endif 4245 }; 4246 4247 class MBinaryBitwiseInstruction : public MBinaryInstruction, 4248 public BitwisePolicy::Data { 4249 protected: 4250 MBinaryBitwiseInstruction(Opcode op, MDefinition* left, MDefinition* right, 4251 MIRType type) 4252 : MBinaryInstruction(op, left, right), 4253 maskMatchesLeftRange(false), 4254 maskMatchesRightRange(false) { 4255 MOZ_ASSERT(IsIntType(type) || (isUrsh() && type == MIRType::Double)); 4256 setResultType(type); 4257 setMovable(); 4258 } 4259 4260 bool maskMatchesLeftRange; 4261 bool maskMatchesRightRange; 4262 4263 public: 4264 MDefinition* foldsTo(TempAllocator& alloc) override; 4265 MDefinition* foldUnnecessaryBitop(); 4266 virtual MDefinition* foldIfZero(size_t operand) = 0; 4267 virtual MDefinition* foldIfNegOne(size_t operand) = 0; 4268 virtual MDefinition* foldIfEqual() = 0; 4269 virtual MDefinition* foldIfAllBitsSet(size_t operand) = 0; 4270 void collectRangeInfoPreTrunc() override; 4271 4272 bool congruentTo(const MDefinition* ins) const override { 4273 return binaryCongruentTo(ins); 4274 } 4275 AliasSet getAliasSet() const override { return AliasSet::None(); } 4276 4277 TruncateKind operandTruncateKind(size_t index) const override; 4278 }; 4279 4280 class MBitAnd : public MBinaryBitwiseInstruction { 4281 MBitAnd(MDefinition* left, MDefinition* right, MIRType type) 4282 : MBinaryBitwiseInstruction(classOpcode, left, right, type) { 4283 setCommutative(); 4284 } 4285 4286 public: 4287 INSTRUCTION_HEADER(BitAnd) 4288 TRIVIAL_NEW_WRAPPERS 4289 4290 MDefinition* foldIfZero(size_t operand) override { 4291 return getOperand(operand); // 0 & x => 0; 4292 } 4293 MDefinition* foldIfNegOne(size_t operand) override { 4294 return getOperand(1 - operand); // x & -1 => x 4295 } 4296 MDefinition* foldIfEqual() override { 4297 return getOperand(0); // x & x => x; 4298 } 4299 MDefinition* foldIfAllBitsSet(size_t operand) override { 4300 // e.g. for uint16: x & 0xffff => x; 4301 return getOperand(1 - operand); 4302 } 4303 void computeRange(TempAllocator& alloc) override; 4304 4305 [[nodiscard]] bool writeRecoverData( 4306 CompactBufferWriter& writer) const override; 4307 bool canRecoverOnBailout() const override { return type() != MIRType::Int64; } 4308 4309 ALLOW_CLONE(MBitAnd) 4310 }; 4311 4312 class MBitOr : public MBinaryBitwiseInstruction { 4313 MBitOr(MDefinition* left, MDefinition* right, MIRType type) 4314 : MBinaryBitwiseInstruction(classOpcode, left, right, type) { 4315 setCommutative(); 4316 } 4317 4318 public: 4319 INSTRUCTION_HEADER(BitOr) 4320 TRIVIAL_NEW_WRAPPERS 4321 4322 MDefinition* foldIfZero(size_t operand) override { 4323 return getOperand(1 - 4324 operand); // 0 | x => x, so if ith is 0, return (1-i)th 4325 } 4326 MDefinition* foldIfNegOne(size_t operand) override { 4327 return getOperand(operand); // x | -1 => -1 4328 } 4329 MDefinition* foldIfEqual() override { 4330 return getOperand(0); // x | x => x 4331 } 4332 MDefinition* foldIfAllBitsSet(size_t operand) override { return this; } 4333 void computeRange(TempAllocator& alloc) override; 4334 [[nodiscard]] bool writeRecoverData( 4335 CompactBufferWriter& writer) const override; 4336 bool canRecoverOnBailout() const override { return type() != MIRType::Int64; } 4337 4338 ALLOW_CLONE(MBitOr) 4339 }; 4340 4341 class MBitXor : public MBinaryBitwiseInstruction { 4342 MBitXor(MDefinition* left, MDefinition* right, MIRType type) 4343 : MBinaryBitwiseInstruction(classOpcode, left, right, type) { 4344 setCommutative(); 4345 } 4346 4347 public: 4348 INSTRUCTION_HEADER(BitXor) 4349 TRIVIAL_NEW_WRAPPERS 4350 4351 MDefinition* foldIfZero(size_t operand) override { 4352 return getOperand(1 - operand); // 0 ^ x => x 4353 } 4354 MDefinition* foldIfNegOne(size_t operand) override { return this; } 4355 MDefinition* foldIfEqual() override { return this; } 4356 MDefinition* foldIfAllBitsSet(size_t operand) override { return this; } 4357 void computeRange(TempAllocator& alloc) override; 4358 4359 [[nodiscard]] bool writeRecoverData( 4360 CompactBufferWriter& writer) const override; 4361 bool canRecoverOnBailout() const override { return type() != MIRType::Int64; } 4362 4363 ALLOW_CLONE(MBitXor) 4364 }; 4365 4366 class MShiftInstruction : public MBinaryBitwiseInstruction { 4367 protected: 4368 MShiftInstruction(Opcode op, MDefinition* left, MDefinition* right, 4369 MIRType type) 4370 : MBinaryBitwiseInstruction(op, left, right, type) {} 4371 4372 public: 4373 MDefinition* foldIfNegOne(size_t operand) override { return this; } 4374 MDefinition* foldIfEqual() override { return this; } 4375 MDefinition* foldIfAllBitsSet(size_t operand) override { return this; } 4376 }; 4377 4378 class MLsh : public MShiftInstruction { 4379 MLsh(MDefinition* left, MDefinition* right, MIRType type) 4380 : MShiftInstruction(classOpcode, left, right, type) {} 4381 4382 public: 4383 INSTRUCTION_HEADER(Lsh) 4384 TRIVIAL_NEW_WRAPPERS 4385 4386 MDefinition* foldIfZero(size_t operand) override { 4387 // 0 << x => 0 4388 // x << 0 => x 4389 return getOperand(0); 4390 } 4391 4392 void computeRange(TempAllocator& alloc) override; 4393 [[nodiscard]] bool writeRecoverData( 4394 CompactBufferWriter& writer) const override; 4395 bool canRecoverOnBailout() const override { 4396 return IsTypeRepresentableAsDouble(type()); 4397 } 4398 4399 ALLOW_CLONE(MLsh) 4400 }; 4401 4402 class MRsh : public MShiftInstruction { 4403 MRsh(MDefinition* left, MDefinition* right, MIRType type) 4404 : MShiftInstruction(classOpcode, left, right, type) {} 4405 4406 public: 4407 INSTRUCTION_HEADER(Rsh) 4408 TRIVIAL_NEW_WRAPPERS 4409 4410 MDefinition* foldIfZero(size_t operand) override { 4411 // 0 >> x => 0 4412 // x >> 0 => x 4413 return getOperand(0); 4414 } 4415 void computeRange(TempAllocator& alloc) override; 4416 4417 [[nodiscard]] bool writeRecoverData( 4418 CompactBufferWriter& writer) const override; 4419 bool canRecoverOnBailout() const override { 4420 return IsTypeRepresentableAsDouble(type()); 4421 } 4422 4423 MDefinition* foldsTo(TempAllocator& alloc) override; 4424 4425 ALLOW_CLONE(MRsh) 4426 }; 4427 4428 class MUrsh : public MShiftInstruction { 4429 bool bailoutsDisabled_; 4430 4431 MUrsh(MDefinition* left, MDefinition* right, MIRType type) 4432 : MShiftInstruction(classOpcode, left, right, type), 4433 bailoutsDisabled_(false) {} 4434 4435 public: 4436 INSTRUCTION_HEADER(Ursh) 4437 TRIVIAL_NEW_WRAPPERS 4438 4439 static MUrsh* NewWasm(TempAllocator& alloc, MDefinition* left, 4440 MDefinition* right, MIRType type); 4441 4442 MDefinition* foldIfZero(size_t operand) override { 4443 // 0 >>> x => 0 4444 if (operand == 0) { 4445 return getOperand(0); 4446 } 4447 4448 return this; 4449 } 4450 4451 bool bailoutsDisabled() const { return bailoutsDisabled_; } 4452 4453 bool fallible() const; 4454 4455 void computeRange(TempAllocator& alloc) override; 4456 void collectRangeInfoPreTrunc() override; 4457 4458 [[nodiscard]] bool writeRecoverData( 4459 CompactBufferWriter& writer) const override; 4460 bool canRecoverOnBailout() const override { 4461 return IsTypeRepresentableAsDouble(type()); 4462 } 4463 4464 ALLOW_CLONE(MUrsh) 4465 }; 4466 4467 class MSignExtendInt32 : public MUnaryInstruction, public NoTypePolicy::Data { 4468 public: 4469 enum Mode { Byte, Half }; 4470 4471 private: 4472 Mode mode_; 4473 4474 MSignExtendInt32(MDefinition* op, Mode mode) 4475 : MUnaryInstruction(classOpcode, op), mode_(mode) { 4476 MOZ_ASSERT(op->type() == MIRType::Int32); 4477 setResultType(MIRType::Int32); 4478 setMovable(); 4479 } 4480 4481 public: 4482 INSTRUCTION_HEADER(SignExtendInt32) 4483 TRIVIAL_NEW_WRAPPERS 4484 4485 Mode mode() const { return mode_; } 4486 4487 MDefinition* foldsTo(TempAllocator& alloc) override; 4488 bool congruentTo(const MDefinition* ins) const override { 4489 if (!congruentIfOperandsEqual(ins)) { 4490 return false; 4491 } 4492 return ins->toSignExtendInt32()->mode_ == mode_; 4493 } 4494 AliasSet getAliasSet() const override { return AliasSet::None(); } 4495 4496 [[nodiscard]] bool writeRecoverData( 4497 CompactBufferWriter& writer) const override; 4498 bool canRecoverOnBailout() const override { return true; } 4499 4500 ALLOW_CLONE(MSignExtendInt32) 4501 }; 4502 4503 class MSignExtendInt64 : public MUnaryInstruction, public NoTypePolicy::Data { 4504 public: 4505 enum Mode { Byte, Half, Word }; 4506 4507 private: 4508 Mode mode_; 4509 4510 MSignExtendInt64(MDefinition* op, Mode mode) 4511 : MUnaryInstruction(classOpcode, op), mode_(mode) { 4512 MOZ_ASSERT(op->type() == MIRType::Int64); 4513 setResultType(MIRType::Int64); 4514 setMovable(); 4515 } 4516 4517 public: 4518 INSTRUCTION_HEADER(SignExtendInt64) 4519 TRIVIAL_NEW_WRAPPERS 4520 4521 Mode mode() const { return mode_; } 4522 4523 MDefinition* foldsTo(TempAllocator& alloc) override; 4524 bool congruentTo(const MDefinition* ins) const override { 4525 if (!congruentIfOperandsEqual(ins)) { 4526 return false; 4527 } 4528 return ins->toSignExtendInt64()->mode_ == mode_; 4529 } 4530 AliasSet getAliasSet() const override { return AliasSet::None(); } 4531 4532 ALLOW_CLONE(MSignExtendInt64) 4533 }; 4534 4535 class MSignExtendIntPtr : public MUnaryInstruction, public NoTypePolicy::Data { 4536 public: 4537 enum Mode { Byte, Half, Word }; 4538 4539 private: 4540 Mode mode_; 4541 4542 MSignExtendIntPtr(MDefinition* op, Mode mode) 4543 : MUnaryInstruction(classOpcode, op), mode_(mode) { 4544 MOZ_ASSERT(op->type() == MIRType::IntPtr); 4545 setResultType(MIRType::IntPtr); 4546 setMovable(); 4547 } 4548 4549 public: 4550 INSTRUCTION_HEADER(SignExtendIntPtr) 4551 TRIVIAL_NEW_WRAPPERS 4552 4553 Mode mode() const { return mode_; } 4554 4555 MDefinition* foldsTo(TempAllocator& alloc) override; 4556 bool congruentTo(const MDefinition* ins) const override { 4557 if (!congruentIfOperandsEqual(ins)) { 4558 return false; 4559 } 4560 return ins->toSignExtendIntPtr()->mode_ == mode_; 4561 } 4562 AliasSet getAliasSet() const override { return AliasSet::None(); } 4563 4564 ALLOW_CLONE(MSignExtendIntPtr) 4565 }; 4566 4567 class MBinaryArithInstruction : public MBinaryInstruction, 4568 public ArithPolicy::Data { 4569 // Implicit truncate flag is set by the truncate backward range analysis 4570 // optimization phase, and by wasm pre-processing. It is used in 4571 // NeedNegativeZeroCheck to check if the result of a multiplication needs to 4572 // produce -0 double value, and for avoiding overflow checks. 4573 4574 // This optimization happens when the multiplication cannot be truncated 4575 // even if all uses are truncating its result, such as when the range 4576 // analysis detect a precision loss in the multiplication. 4577 TruncateKind implicitTruncate_; 4578 4579 // Whether we must preserve NaN semantics, and in particular not fold 4580 // (x op id) or (id op x) to x, or replace a division by a multiply of the 4581 // exact reciprocal. 4582 bool mustPreserveNaN_; 4583 4584 protected: 4585 MBinaryArithInstruction(Opcode op, MDefinition* left, MDefinition* right, 4586 MIRType type) 4587 : MBinaryInstruction(op, left, right), 4588 implicitTruncate_(TruncateKind::NoTruncate), 4589 mustPreserveNaN_(false) { 4590 MOZ_ASSERT(IsNumberType(type)); 4591 setResultType(type); 4592 setMovable(); 4593 } 4594 4595 public: 4596 void setMustPreserveNaN(bool b) { mustPreserveNaN_ = b; } 4597 bool mustPreserveNaN() const { return mustPreserveNaN_; } 4598 4599 MDefinition* foldsTo(TempAllocator& alloc) override; 4600 #ifdef JS_JITSPEW 4601 void printOpcode(GenericPrinter& out) const override; 4602 #endif 4603 4604 virtual double getIdentity() const = 0; 4605 4606 void setSpecialization(MIRType type) { 4607 MOZ_ASSERT(IsNumberType(type)); 4608 setResultType(type); 4609 } 4610 4611 virtual void trySpecializeFloat32(TempAllocator& alloc) override; 4612 4613 bool congruentTo(const MDefinition* ins) const override { 4614 if (!binaryCongruentTo(ins)) { 4615 return false; 4616 } 4617 const auto* other = static_cast<const MBinaryArithInstruction*>(ins); 4618 return other->mustPreserveNaN_ == mustPreserveNaN_; 4619 } 4620 AliasSet getAliasSet() const override { return AliasSet::None(); } 4621 4622 bool isTruncated() const { 4623 return implicitTruncate_ == TruncateKind::Truncate; 4624 } 4625 TruncateKind truncateKind() const { return implicitTruncate_; } 4626 void setTruncateKind(TruncateKind kind) { 4627 implicitTruncate_ = std::max(implicitTruncate_, kind); 4628 } 4629 }; 4630 4631 class MMinMax : public MBinaryInstruction, public ArithPolicy::Data { 4632 bool isMax_; 4633 4634 MMinMax(MDefinition* left, MDefinition* right, MIRType type, bool isMax) 4635 : MBinaryInstruction(classOpcode, left, right), isMax_(isMax) { 4636 MOZ_ASSERT(IsNumberType(type)); 4637 setResultType(type); 4638 setMovable(); 4639 setCommutative(); 4640 } 4641 4642 public: 4643 INSTRUCTION_HEADER(MinMax) 4644 TRIVIAL_NEW_WRAPPERS 4645 4646 template <typename... Args> 4647 static MMinMax* NewMin(Args&&... args) { 4648 return New(std::forward<Args>(args)..., /* isMax= */ false); 4649 } 4650 4651 template <typename... Args> 4652 static MMinMax* NewMax(Args&&... args) { 4653 return New(std::forward<Args>(args)..., /* isMax= */ true); 4654 } 4655 4656 static MMinMax* NewWasm(TempAllocator& alloc, MDefinition* left, 4657 MDefinition* right, MIRType type, bool isMax) { 4658 return New(alloc, left, right, type, isMax); 4659 } 4660 4661 bool isMax() const { return isMax_; } 4662 4663 bool congruentTo(const MDefinition* ins) const override { 4664 if (!congruentIfOperandsEqual(ins)) { 4665 return false; 4666 } 4667 const MMinMax* other = ins->toMinMax(); 4668 return other->isMax() == isMax(); 4669 } 4670 4671 AliasSet getAliasSet() const override { return AliasSet::None(); } 4672 MDefinition* foldsTo(TempAllocator& alloc) override; 4673 void computeRange(TempAllocator& alloc) override; 4674 [[nodiscard]] bool writeRecoverData( 4675 CompactBufferWriter& writer) const override; 4676 bool canRecoverOnBailout() const override { 4677 return IsTypeRepresentableAsDouble(type()); 4678 } 4679 4680 bool isFloat32Commutative() const override { return true; } 4681 void trySpecializeFloat32(TempAllocator& alloc) override; 4682 4683 #ifdef JS_JITSPEW 4684 void printOpcode(GenericPrinter& out) const override; 4685 #endif 4686 4687 ALLOW_CLONE(MMinMax) 4688 }; 4689 4690 class MMinMaxArray : public MUnaryInstruction, public SingleObjectPolicy::Data { 4691 bool isMax_; 4692 4693 MMinMaxArray(MDefinition* array, MIRType type, bool isMax) 4694 : MUnaryInstruction(classOpcode, array), isMax_(isMax) { 4695 MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Double); 4696 setResultType(type); 4697 4698 // We can't DCE this, even if the result is unused, in case one of the 4699 // elements of the array is an object with a `valueOf` function that 4700 // must be called. 4701 setGuard(); 4702 } 4703 4704 public: 4705 INSTRUCTION_HEADER(MinMaxArray) 4706 TRIVIAL_NEW_WRAPPERS 4707 NAMED_OPERANDS((0, array)) 4708 4709 bool isMax() const { return isMax_; } 4710 4711 bool congruentTo(const MDefinition* ins) const override { 4712 if (!ins->isMinMaxArray() || ins->toMinMaxArray()->isMax() != isMax()) { 4713 return false; 4714 } 4715 return congruentIfOperandsEqual(ins); 4716 } 4717 4718 AliasSet getAliasSet() const override { 4719 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::Element); 4720 } 4721 4722 #ifdef JS_JITSPEW 4723 void printOpcode(GenericPrinter& out) const override; 4724 #endif 4725 }; 4726 4727 class MAbs : public MUnaryInstruction, public ArithPolicy::Data { 4728 bool implicitTruncate_; 4729 4730 MAbs(MDefinition* num, MIRType type) 4731 : MUnaryInstruction(classOpcode, num), implicitTruncate_(false) { 4732 MOZ_ASSERT(IsNumberType(type)); 4733 setResultType(type); 4734 setMovable(); 4735 } 4736 4737 public: 4738 INSTRUCTION_HEADER(Abs) 4739 TRIVIAL_NEW_WRAPPERS 4740 4741 static MAbs* NewWasm(TempAllocator& alloc, MDefinition* num, MIRType type) { 4742 auto* ins = new (alloc) MAbs(num, type); 4743 if (type == MIRType::Int32) { 4744 ins->implicitTruncate_ = true; 4745 } 4746 return ins; 4747 } 4748 4749 bool congruentTo(const MDefinition* ins) const override { 4750 return congruentIfOperandsEqual(ins); 4751 } 4752 bool fallible() const; 4753 4754 AliasSet getAliasSet() const override { return AliasSet::None(); } 4755 void computeRange(TempAllocator& alloc) override; 4756 bool isFloat32Commutative() const override { return true; } 4757 void trySpecializeFloat32(TempAllocator& alloc) override; 4758 4759 [[nodiscard]] bool writeRecoverData( 4760 CompactBufferWriter& writer) const override; 4761 bool canRecoverOnBailout() const override { return true; } 4762 4763 ALLOW_CLONE(MAbs) 4764 }; 4765 4766 class MClz : public MUnaryInstruction, public BitwisePolicy::Data { 4767 bool operandIsNeverZero_; 4768 4769 explicit MClz(MDefinition* num, MIRType type) 4770 : MUnaryInstruction(classOpcode, num), operandIsNeverZero_(false) { 4771 MOZ_ASSERT(IsIntType(type)); 4772 MOZ_ASSERT(IsNumberType(num->type())); 4773 setResultType(type); 4774 setMovable(); 4775 } 4776 4777 public: 4778 INSTRUCTION_HEADER(Clz) 4779 TRIVIAL_NEW_WRAPPERS 4780 NAMED_OPERANDS((0, num)) 4781 4782 bool congruentTo(const MDefinition* ins) const override { 4783 return congruentIfOperandsEqual(ins); 4784 } 4785 4786 AliasSet getAliasSet() const override { return AliasSet::None(); } 4787 4788 bool operandIsNeverZero() const { return operandIsNeverZero_; } 4789 4790 MDefinition* foldsTo(TempAllocator& alloc) override; 4791 void computeRange(TempAllocator& alloc) override; 4792 void collectRangeInfoPreTrunc() override; 4793 4794 ALLOW_CLONE(MClz) 4795 }; 4796 4797 class MCtz : public MUnaryInstruction, public BitwisePolicy::Data { 4798 bool operandIsNeverZero_; 4799 4800 explicit MCtz(MDefinition* num, MIRType type) 4801 : MUnaryInstruction(classOpcode, num), operandIsNeverZero_(false) { 4802 MOZ_ASSERT(IsIntType(type)); 4803 MOZ_ASSERT(IsNumberType(num->type())); 4804 setResultType(type); 4805 setMovable(); 4806 } 4807 4808 public: 4809 INSTRUCTION_HEADER(Ctz) 4810 TRIVIAL_NEW_WRAPPERS 4811 NAMED_OPERANDS((0, num)) 4812 4813 bool congruentTo(const MDefinition* ins) const override { 4814 return congruentIfOperandsEqual(ins); 4815 } 4816 4817 AliasSet getAliasSet() const override { return AliasSet::None(); } 4818 4819 bool operandIsNeverZero() const { return operandIsNeverZero_; } 4820 4821 MDefinition* foldsTo(TempAllocator& alloc) override; 4822 void computeRange(TempAllocator& alloc) override; 4823 void collectRangeInfoPreTrunc() override; 4824 4825 ALLOW_CLONE(MCtz) 4826 }; 4827 4828 class MPopcnt : public MUnaryInstruction, public BitwisePolicy::Data { 4829 explicit MPopcnt(MDefinition* num, MIRType type) 4830 : MUnaryInstruction(classOpcode, num) { 4831 MOZ_ASSERT(IsNumberType(num->type())); 4832 MOZ_ASSERT(IsIntType(type)); 4833 setResultType(type); 4834 setMovable(); 4835 } 4836 4837 public: 4838 INSTRUCTION_HEADER(Popcnt) 4839 TRIVIAL_NEW_WRAPPERS 4840 NAMED_OPERANDS((0, num)) 4841 4842 bool congruentTo(const MDefinition* ins) const override { 4843 return congruentIfOperandsEqual(ins); 4844 } 4845 4846 AliasSet getAliasSet() const override { return AliasSet::None(); } 4847 4848 MDefinition* foldsTo(TempAllocator& alloc) override; 4849 void computeRange(TempAllocator& alloc) override; 4850 4851 ALLOW_CLONE(MPopcnt) 4852 }; 4853 4854 // Inline implementation of Math.sqrt(). 4855 class MSqrt : public MUnaryInstruction, public FloatingPointPolicy<0>::Data { 4856 MSqrt(MDefinition* num, MIRType type) : MUnaryInstruction(classOpcode, num) { 4857 setResultType(type); 4858 specialization_ = type; 4859 setMovable(); 4860 } 4861 4862 public: 4863 INSTRUCTION_HEADER(Sqrt) 4864 TRIVIAL_NEW_WRAPPERS 4865 4866 bool congruentTo(const MDefinition* ins) const override { 4867 return congruentIfOperandsEqual(ins); 4868 } 4869 4870 AliasSet getAliasSet() const override { return AliasSet::None(); } 4871 void computeRange(TempAllocator& alloc) override; 4872 4873 bool isFloat32Commutative() const override { return true; } 4874 void trySpecializeFloat32(TempAllocator& alloc) override; 4875 4876 [[nodiscard]] bool writeRecoverData( 4877 CompactBufferWriter& writer) const override; 4878 bool canRecoverOnBailout() const override { return true; } 4879 4880 ALLOW_CLONE(MSqrt) 4881 }; 4882 4883 class MCopySign : public MBinaryInstruction, public NoTypePolicy::Data { 4884 MCopySign(MDefinition* lhs, MDefinition* rhs, MIRType type) 4885 : MBinaryInstruction(classOpcode, lhs, rhs) { 4886 setResultType(type); 4887 setMovable(); 4888 } 4889 4890 public: 4891 INSTRUCTION_HEADER(CopySign) 4892 TRIVIAL_NEW_WRAPPERS 4893 4894 bool congruentTo(const MDefinition* ins) const override { 4895 return congruentIfOperandsEqual(ins); 4896 } 4897 AliasSet getAliasSet() const override { return AliasSet::None(); } 4898 4899 ALLOW_CLONE(MCopySign) 4900 }; 4901 4902 // Inline implementation of Math.hypot(). 4903 class MHypot : public MVariadicInstruction, public AllDoublePolicy::Data { 4904 MHypot() : MVariadicInstruction(classOpcode) { 4905 setResultType(MIRType::Double); 4906 setMovable(); 4907 } 4908 4909 public: 4910 INSTRUCTION_HEADER(Hypot) 4911 static MHypot* New(TempAllocator& alloc, const MDefinitionVector& vector); 4912 4913 bool congruentTo(const MDefinition* ins) const override { 4914 return congruentIfOperandsEqual(ins); 4915 } 4916 4917 AliasSet getAliasSet() const override { return AliasSet::None(); } 4918 4919 bool possiblyCalls() const override { return true; } 4920 4921 [[nodiscard]] bool writeRecoverData( 4922 CompactBufferWriter& writer) const override; 4923 bool canRecoverOnBailout() const override { return true; } 4924 4925 bool canClone() const override { return true; } 4926 4927 MInstruction* clone(TempAllocator& alloc, 4928 const MDefinitionVector& inputs) const override { 4929 return MHypot::New(alloc, inputs); 4930 } 4931 }; 4932 4933 // Inline implementation of Math.pow(). 4934 // 4935 // Supports the following three specializations: 4936 // 4937 // 1. MPow(FloatingPoint, FloatingPoint) -> Double 4938 // - The most general mode, calls js::ecmaPow. 4939 // - Never performs a bailout. 4940 // 2. MPow(FloatingPoint, Int32) -> Double 4941 // - Optimization to call js::powi instead of js::ecmaPow. 4942 // - Never performs a bailout. 4943 // 3. MPow(Int32, Int32) -> Int32 4944 // - Performs the complete exponentiation operation in assembly code. 4945 // - Bails out if the result doesn't fit in Int32. 4946 class MPow : public MBinaryInstruction, public PowPolicy::Data { 4947 // If true, the result is guaranteed to never be negative zero, as long as the 4948 // power is a positive number. 4949 bool canBeNegativeZero_; 4950 4951 MPow(MDefinition* input, MDefinition* power, MIRType specialization) 4952 : MBinaryInstruction(classOpcode, input, power) { 4953 MOZ_ASSERT(specialization == MIRType::Int32 || 4954 specialization == MIRType::Double); 4955 setResultType(specialization); 4956 setMovable(); 4957 4958 // The result can't be negative zero if the base is an Int32 value. 4959 canBeNegativeZero_ = input->type() != MIRType::Int32; 4960 } 4961 4962 // Helpers for `foldsTo` 4963 MDefinition* foldsConstant(TempAllocator& alloc); 4964 MDefinition* foldsConstantPower(TempAllocator& alloc); 4965 4966 bool canBeNegativeZero() const { return canBeNegativeZero_; } 4967 4968 public: 4969 INSTRUCTION_HEADER(Pow) 4970 TRIVIAL_NEW_WRAPPERS 4971 4972 MDefinition* input() const { return lhs(); } 4973 MDefinition* power() const { return rhs(); } 4974 4975 bool congruentTo(const MDefinition* ins) const override { 4976 return congruentIfOperandsEqual(ins); 4977 } 4978 AliasSet getAliasSet() const override { return AliasSet::None(); } 4979 bool possiblyCalls() const override { return type() != MIRType::Int32; } 4980 [[nodiscard]] bool writeRecoverData( 4981 CompactBufferWriter& writer) const override; 4982 bool canRecoverOnBailout() const override { return true; } 4983 4984 MDefinition* foldsTo(TempAllocator& alloc) override; 4985 4986 ALLOW_CLONE(MPow) 4987 }; 4988 4989 // Inline implementation of Math.pow(x, 0.5), which subtly differs from 4990 // Math.sqrt(x). 4991 class MPowHalf : public MUnaryInstruction, public DoublePolicy<0>::Data { 4992 bool operandIsNeverNegativeInfinity_; 4993 bool operandIsNeverNegativeZero_; 4994 bool operandIsNeverNaN_; 4995 4996 explicit MPowHalf(MDefinition* input) 4997 : MUnaryInstruction(classOpcode, input), 4998 operandIsNeverNegativeInfinity_(false), 4999 operandIsNeverNegativeZero_(false), 5000 operandIsNeverNaN_(false) { 5001 setResultType(MIRType::Double); 5002 setMovable(); 5003 } 5004 5005 public: 5006 INSTRUCTION_HEADER(PowHalf) 5007 TRIVIAL_NEW_WRAPPERS 5008 5009 bool congruentTo(const MDefinition* ins) const override { 5010 return congruentIfOperandsEqual(ins); 5011 } 5012 bool operandIsNeverNegativeInfinity() const { 5013 return operandIsNeverNegativeInfinity_; 5014 } 5015 bool operandIsNeverNegativeZero() const { 5016 return operandIsNeverNegativeZero_; 5017 } 5018 bool operandIsNeverNaN() const { return operandIsNeverNaN_; } 5019 AliasSet getAliasSet() const override { return AliasSet::None(); } 5020 void collectRangeInfoPreTrunc() override; 5021 [[nodiscard]] bool writeRecoverData( 5022 CompactBufferWriter& writer) const override; 5023 bool canRecoverOnBailout() const override { return true; } 5024 5025 ALLOW_CLONE(MPowHalf) 5026 }; 5027 5028 class MSign : public MUnaryInstruction, public SignPolicy::Data { 5029 private: 5030 MSign(MDefinition* input, MIRType resultType) 5031 : MUnaryInstruction(classOpcode, input) { 5032 MOZ_ASSERT(IsNumberType(input->type())); 5033 MOZ_ASSERT(resultType == MIRType::Int32 || resultType == MIRType::Double); 5034 specialization_ = input->type(); 5035 setResultType(resultType); 5036 setMovable(); 5037 } 5038 5039 public: 5040 INSTRUCTION_HEADER(Sign) 5041 TRIVIAL_NEW_WRAPPERS 5042 5043 bool congruentTo(const MDefinition* ins) const override { 5044 return congruentIfOperandsEqual(ins); 5045 } 5046 5047 AliasSet getAliasSet() const override { return AliasSet::None(); } 5048 5049 MDefinition* foldsTo(TempAllocator& alloc) override; 5050 5051 void computeRange(TempAllocator& alloc) override; 5052 [[nodiscard]] bool writeRecoverData( 5053 CompactBufferWriter& writer) const override; 5054 bool canRecoverOnBailout() const override { return true; } 5055 5056 ALLOW_CLONE(MSign) 5057 }; 5058 5059 class MMathFunction : public MUnaryInstruction, 5060 public FloatingPointPolicy<0>::Data { 5061 UnaryMathFunction function_; 5062 5063 // A nullptr cache means this function will neither access nor update the 5064 // cache. 5065 MMathFunction(MDefinition* input, UnaryMathFunction function) 5066 : MUnaryInstruction(classOpcode, input), function_(function) { 5067 setResultType(MIRType::Double); 5068 specialization_ = MIRType::Double; 5069 setMovable(); 5070 } 5071 5072 public: 5073 INSTRUCTION_HEADER(MathFunction) 5074 TRIVIAL_NEW_WRAPPERS 5075 5076 UnaryMathFunction function() const { return function_; } 5077 5078 bool congruentTo(const MDefinition* ins) const override { 5079 if (!ins->isMathFunction()) { 5080 return false; 5081 } 5082 if (ins->toMathFunction()->function() != function()) { 5083 return false; 5084 } 5085 return congruentIfOperandsEqual(ins); 5086 } 5087 5088 AliasSet getAliasSet() const override { return AliasSet::None(); } 5089 5090 bool possiblyCalls() const override { return true; } 5091 5092 MDefinition* foldsTo(TempAllocator& alloc) override; 5093 5094 #ifdef JS_JITSPEW 5095 void printOpcode(GenericPrinter& out) const override; 5096 #endif 5097 5098 static const char* FunctionName(UnaryMathFunction function); 5099 5100 bool isFloat32Commutative() const override; 5101 void trySpecializeFloat32(TempAllocator& alloc) override; 5102 5103 void computeRange(TempAllocator& alloc) override; 5104 5105 [[nodiscard]] bool writeRecoverData( 5106 CompactBufferWriter& writer) const override; 5107 bool canRecoverOnBailout() const override { return true; } 5108 5109 ALLOW_CLONE(MMathFunction) 5110 }; 5111 5112 class MAdd : public MBinaryArithInstruction { 5113 MAdd(MDefinition* left, MDefinition* right, MIRType type) 5114 : MBinaryArithInstruction(classOpcode, left, right, type) { 5115 setCommutative(); 5116 } 5117 5118 MAdd(MDefinition* left, MDefinition* right, TruncateKind truncateKind) 5119 : MAdd(left, right, MIRType::Int32) { 5120 setTruncateKind(truncateKind); 5121 } 5122 5123 public: 5124 INSTRUCTION_HEADER(Add) 5125 TRIVIAL_NEW_WRAPPERS 5126 5127 static MAdd* NewWasm(TempAllocator& alloc, MDefinition* left, 5128 MDefinition* right, MIRType type) { 5129 auto* ret = new (alloc) MAdd(left, right, type); 5130 if (type == MIRType::Int32) { 5131 ret->setTruncateKind(TruncateKind::Truncate); 5132 } 5133 return ret; 5134 } 5135 5136 bool isFloat32Commutative() const override { return true; } 5137 5138 double getIdentity() const override { return 0; } 5139 5140 bool fallible() const; 5141 void computeRange(TempAllocator& alloc) override; 5142 bool canTruncate() const override; 5143 void truncate(TruncateKind kind) override; 5144 TruncateKind operandTruncateKind(size_t index) const override; 5145 5146 [[nodiscard]] bool writeRecoverData( 5147 CompactBufferWriter& writer) const override; 5148 bool canRecoverOnBailout() const override { 5149 return IsTypeRepresentableAsDouble(type()); 5150 } 5151 5152 ALLOW_CLONE(MAdd) 5153 }; 5154 5155 class MSub : public MBinaryArithInstruction { 5156 MSub(MDefinition* left, MDefinition* right, MIRType type) 5157 : MBinaryArithInstruction(classOpcode, left, right, type) {} 5158 5159 public: 5160 INSTRUCTION_HEADER(Sub) 5161 TRIVIAL_NEW_WRAPPERS 5162 5163 static MSub* NewWasm(TempAllocator& alloc, MDefinition* left, 5164 MDefinition* right, MIRType type, bool mustPreserveNaN) { 5165 auto* ret = new (alloc) MSub(left, right, type); 5166 ret->setMustPreserveNaN(mustPreserveNaN); 5167 if (type == MIRType::Int32) { 5168 ret->setTruncateKind(TruncateKind::Truncate); 5169 } 5170 return ret; 5171 } 5172 5173 MDefinition* foldsTo(TempAllocator& alloc) override; 5174 5175 double getIdentity() const override { return 0; } 5176 5177 bool isFloat32Commutative() const override { return true; } 5178 5179 bool fallible() const; 5180 void computeRange(TempAllocator& alloc) override; 5181 bool canTruncate() const override; 5182 void truncate(TruncateKind kind) override; 5183 TruncateKind operandTruncateKind(size_t index) const override; 5184 5185 [[nodiscard]] bool writeRecoverData( 5186 CompactBufferWriter& writer) const override; 5187 bool canRecoverOnBailout() const override { 5188 return IsTypeRepresentableAsDouble(type()); 5189 } 5190 5191 ALLOW_CLONE(MSub) 5192 }; 5193 5194 class MMul : public MBinaryArithInstruction { 5195 public: 5196 enum Mode { Normal, Integer }; 5197 5198 private: 5199 // Annotation the result could be a negative zero 5200 // and we need to guard this during execution. 5201 bool canBeNegativeZero_; 5202 5203 Mode mode_; 5204 5205 MMul(MDefinition* left, MDefinition* right, MIRType type, Mode mode) 5206 : MBinaryArithInstruction(classOpcode, left, right, type), 5207 canBeNegativeZero_(true), 5208 mode_(mode) { 5209 setCommutative(); 5210 if (mode == Integer) { 5211 // This implements the required behavior for Math.imul, which 5212 // can never fail and always truncates its output to int32. 5213 canBeNegativeZero_ = false; 5214 setTruncateKind(TruncateKind::Truncate); 5215 } 5216 MOZ_ASSERT_IF(mode != Integer, mode == Normal); 5217 } 5218 5219 public: 5220 INSTRUCTION_HEADER(Mul) 5221 5222 static MMul* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, 5223 MIRType type, Mode mode = Normal) { 5224 return new (alloc) MMul(left, right, type, mode); 5225 } 5226 static MMul* NewWasm(TempAllocator& alloc, MDefinition* left, 5227 MDefinition* right, MIRType type, Mode mode, 5228 bool mustPreserveNaN) { 5229 auto* ret = new (alloc) MMul(left, right, type, mode); 5230 ret->setMustPreserveNaN(mustPreserveNaN); 5231 return ret; 5232 } 5233 5234 MDefinition* foldsTo(TempAllocator& alloc) override; 5235 void analyzeEdgeCasesForward() override; 5236 void analyzeEdgeCasesBackward() override; 5237 void collectRangeInfoPreTrunc() override; 5238 5239 double getIdentity() const override { return 1; } 5240 5241 bool congruentTo(const MDefinition* ins) const override { 5242 if (!ins->isMul()) { 5243 return false; 5244 } 5245 5246 const MMul* mul = ins->toMul(); 5247 if (canBeNegativeZero_ != mul->canBeNegativeZero()) { 5248 return false; 5249 } 5250 5251 if (mode_ != mul->mode()) { 5252 return false; 5253 } 5254 5255 if (mustPreserveNaN() != mul->mustPreserveNaN()) { 5256 return false; 5257 } 5258 5259 return binaryCongruentTo(ins); 5260 } 5261 5262 bool canOverflow() const; 5263 5264 bool canBeNegativeZero() const { return canBeNegativeZero_; } 5265 void setCanBeNegativeZero(bool negativeZero) { 5266 canBeNegativeZero_ = negativeZero; 5267 } 5268 5269 bool fallible() const { return canBeNegativeZero_ || canOverflow(); } 5270 5271 bool isFloat32Commutative() const override { return true; } 5272 5273 void computeRange(TempAllocator& alloc) override; 5274 bool canTruncate() const override; 5275 void truncate(TruncateKind kind) override; 5276 TruncateKind operandTruncateKind(size_t index) const override; 5277 5278 Mode mode() const { return mode_; } 5279 5280 [[nodiscard]] bool writeRecoverData( 5281 CompactBufferWriter& writer) const override; 5282 bool canRecoverOnBailout() const override { 5283 return IsTypeRepresentableAsDouble(type()); 5284 } 5285 5286 ALLOW_CLONE(MMul) 5287 }; 5288 5289 class MDiv : public MBinaryArithInstruction { 5290 bool canBeNegativeZero_; 5291 bool canBeNegativeOverflow_; 5292 bool canBeDivideByZero_; 5293 bool canBeNegativeDividend_; 5294 bool unsigned_; // If false, signedness will be derived from operands 5295 bool trapOnError_; 5296 wasm::TrapSiteDesc trapSiteDesc_; 5297 5298 MDiv(MDefinition* left, MDefinition* right, MIRType type) 5299 : MBinaryArithInstruction(classOpcode, left, right, type), 5300 canBeNegativeZero_(true), 5301 canBeNegativeOverflow_(true), 5302 canBeDivideByZero_(true), 5303 canBeNegativeDividend_(true), 5304 unsigned_(false), 5305 trapOnError_(false) {} 5306 5307 public: 5308 INSTRUCTION_HEADER(Div) 5309 5310 static MDiv* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, 5311 MIRType type) { 5312 return new (alloc) MDiv(left, right, type); 5313 } 5314 static MDiv* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, 5315 MIRType type, bool unsignd, bool trapOnError = false, 5316 wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc(), 5317 bool mustPreserveNaN = false) { 5318 auto* div = new (alloc) MDiv(left, right, type); 5319 div->unsigned_ = unsignd; 5320 div->trapOnError_ = trapOnError; 5321 div->trapSiteDesc_ = trapSiteDesc; 5322 if (trapOnError) { 5323 div->setGuard(); // not removable because of possible side-effects. 5324 div->setNotMovable(); 5325 } 5326 div->setMustPreserveNaN(mustPreserveNaN); 5327 if (type == MIRType::Int32) { 5328 div->setTruncateKind(TruncateKind::Truncate); 5329 } 5330 return div; 5331 } 5332 5333 MDefinition* foldsTo(TempAllocator& alloc) override; 5334 void analyzeEdgeCasesForward() override; 5335 void analyzeEdgeCasesBackward() override; 5336 5337 double getIdentity() const override { MOZ_CRASH("not used"); } 5338 5339 bool canBeNegativeZero() const { 5340 // This flag is only valid for integer division. 5341 MOZ_ASSERT(type() == MIRType::Int32); 5342 return canBeNegativeZero_; 5343 } 5344 void setCanBeNegativeZero(bool negativeZero) { 5345 canBeNegativeZero_ = negativeZero; 5346 } 5347 5348 bool canBeNegativeOverflow() const { return canBeNegativeOverflow_; } 5349 5350 bool canBeDivideByZero() const { return canBeDivideByZero_; } 5351 5352 bool canBeNegativeDividend() const { 5353 // "Dividend" is an ambiguous concept for unsigned truncated 5354 // division, because of the truncation procedure: 5355 // ((x>>>0)/2)|0, for example, gets transformed in 5356 // MDiv::truncate into a node with lhs representing x (not 5357 // x>>>0) and rhs representing the constant 2; in other words, 5358 // the MIR node corresponds to "cast operands to unsigned and 5359 // divide" operation. In this case, is the dividend x or is it 5360 // x>>>0? In order to resolve such ambiguities, we disallow 5361 // the usage of this method for unsigned division. 5362 MOZ_ASSERT(!unsigned_); 5363 return canBeNegativeDividend_; 5364 } 5365 5366 bool isUnsigned() const { return unsigned_; } 5367 5368 bool isTruncatedIndirectly() const { 5369 return truncateKind() >= TruncateKind::IndirectTruncate; 5370 } 5371 5372 bool canTruncateInfinities() const { return isTruncated(); } 5373 bool canTruncateRemainder() const { return isTruncated(); } 5374 bool canTruncateOverflow() const { 5375 return isTruncated() || isTruncatedIndirectly(); 5376 } 5377 bool canTruncateNegativeZero() const { 5378 return isTruncated() || isTruncatedIndirectly(); 5379 } 5380 5381 bool trapOnError() const { return trapOnError_; } 5382 const wasm::TrapSiteDesc& trapSiteDesc() const { 5383 MOZ_ASSERT(trapSiteDesc_.isValid()); 5384 return trapSiteDesc_; 5385 } 5386 5387 bool isFloat32Commutative() const override { return true; } 5388 5389 void computeRange(TempAllocator& alloc) override; 5390 bool fallible() const; 5391 bool canTruncate() const override; 5392 void truncate(TruncateKind kind) override; 5393 void collectRangeInfoPreTrunc() override; 5394 TruncateKind operandTruncateKind(size_t index) const override; 5395 5396 [[nodiscard]] bool writeRecoverData( 5397 CompactBufferWriter& writer) const override; 5398 bool canRecoverOnBailout() const override { return type() != MIRType::Int64; } 5399 5400 bool congruentTo(const MDefinition* ins) const override { 5401 if (!MBinaryArithInstruction::congruentTo(ins)) { 5402 return false; 5403 } 5404 const MDiv* other = ins->toDiv(); 5405 MOZ_ASSERT(other->trapOnError() == trapOnError_); 5406 return unsigned_ == other->isUnsigned(); 5407 } 5408 5409 ALLOW_CLONE(MDiv) 5410 }; 5411 5412 class MMod : public MBinaryArithInstruction { 5413 bool unsigned_; // If false, signedness will be derived from operands 5414 bool canBeNegativeDividend_; 5415 bool canBePowerOfTwoDivisor_; 5416 bool canBeDivideByZero_; 5417 bool trapOnError_; 5418 wasm::TrapSiteDesc trapSiteDesc_; 5419 5420 MMod(MDefinition* left, MDefinition* right, MIRType type) 5421 : MBinaryArithInstruction(classOpcode, left, right, type), 5422 unsigned_(false), 5423 canBeNegativeDividend_(true), 5424 canBePowerOfTwoDivisor_(true), 5425 canBeDivideByZero_(true), 5426 trapOnError_(false) {} 5427 5428 public: 5429 INSTRUCTION_HEADER(Mod) 5430 5431 static MMod* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, 5432 MIRType type) { 5433 return new (alloc) MMod(left, right, type); 5434 } 5435 static MMod* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, 5436 MIRType type, bool unsignd, bool trapOnError = false, 5437 wasm::TrapSiteDesc trapSiteDesc = wasm::TrapSiteDesc()) { 5438 auto* mod = new (alloc) MMod(left, right, type); 5439 mod->unsigned_ = unsignd; 5440 mod->trapOnError_ = trapOnError; 5441 mod->trapSiteDesc_ = trapSiteDesc; 5442 if (trapOnError) { 5443 mod->setGuard(); // not removable because of possible side-effects. 5444 mod->setNotMovable(); 5445 } 5446 if (type == MIRType::Int32) { 5447 mod->setTruncateKind(TruncateKind::Truncate); 5448 } 5449 return mod; 5450 } 5451 5452 MDefinition* foldsTo(TempAllocator& alloc) override; 5453 5454 double getIdentity() const override { MOZ_CRASH("not used"); } 5455 5456 bool canBeNegativeDividend() const { 5457 MOZ_ASSERT(type() == MIRType::Int32 || type() == MIRType::Int64); 5458 MOZ_ASSERT(!unsigned_); 5459 return canBeNegativeDividend_; 5460 } 5461 5462 bool canBeDivideByZero() const { 5463 MOZ_ASSERT(type() == MIRType::Int32 || type() == MIRType::Int64); 5464 return canBeDivideByZero_; 5465 } 5466 5467 bool canBePowerOfTwoDivisor() const { 5468 MOZ_ASSERT(type() == MIRType::Int32); 5469 return canBePowerOfTwoDivisor_; 5470 } 5471 5472 void analyzeEdgeCasesForward() override; 5473 5474 bool isUnsigned() const { return unsigned_; } 5475 5476 bool trapOnError() const { return trapOnError_; } 5477 const wasm::TrapSiteDesc& trapSiteDesc() const { 5478 MOZ_ASSERT(trapSiteDesc_.isValid()); 5479 return trapSiteDesc_; 5480 } 5481 5482 [[nodiscard]] bool writeRecoverData( 5483 CompactBufferWriter& writer) const override; 5484 bool canRecoverOnBailout() const override { return type() != MIRType::Int64; } 5485 5486 bool fallible() const; 5487 5488 void computeRange(TempAllocator& alloc) override; 5489 bool canTruncate() const override; 5490 void truncate(TruncateKind kind) override; 5491 void collectRangeInfoPreTrunc() override; 5492 TruncateKind operandTruncateKind(size_t index) const override; 5493 5494 bool congruentTo(const MDefinition* ins) const override { 5495 return MBinaryArithInstruction::congruentTo(ins) && 5496 unsigned_ == ins->toMod()->isUnsigned(); 5497 } 5498 5499 bool possiblyCalls() const override { return type() == MIRType::Double; } 5500 5501 ALLOW_CLONE(MMod) 5502 }; 5503 5504 class MBigIntBinaryArithInstruction : public MBinaryInstruction, 5505 public BigIntArithPolicy::Data { 5506 protected: 5507 MBigIntBinaryArithInstruction(Opcode op, MDefinition* left, 5508 MDefinition* right) 5509 : MBinaryInstruction(op, left, right) { 5510 setResultType(MIRType::BigInt); 5511 setMovable(); 5512 } 5513 5514 public: 5515 bool congruentTo(const MDefinition* ins) const override { 5516 return binaryCongruentTo(ins); 5517 } 5518 5519 AliasSet getAliasSet() const override { return AliasSet::None(); } 5520 }; 5521 5522 class MBigIntAdd : public MBigIntBinaryArithInstruction { 5523 MBigIntAdd(MDefinition* left, MDefinition* right) 5524 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5525 setCommutative(); 5526 5527 // Don't guard this instruction even though adding two BigInts can throw 5528 // JSMSG_BIGINT_TOO_LARGE. This matches the behavior when adding too large 5529 // strings in MConcat. 5530 } 5531 5532 public: 5533 INSTRUCTION_HEADER(BigIntAdd) 5534 TRIVIAL_NEW_WRAPPERS 5535 5536 [[nodiscard]] bool writeRecoverData( 5537 CompactBufferWriter& writer) const override; 5538 bool canRecoverOnBailout() const override { return true; } 5539 5540 ALLOW_CLONE(MBigIntAdd) 5541 }; 5542 5543 class MBigIntSub : public MBigIntBinaryArithInstruction { 5544 MBigIntSub(MDefinition* left, MDefinition* right) 5545 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5546 // See MBigIntAdd for why we don't guard this instruction. 5547 } 5548 5549 public: 5550 INSTRUCTION_HEADER(BigIntSub) 5551 TRIVIAL_NEW_WRAPPERS 5552 5553 [[nodiscard]] bool writeRecoverData( 5554 CompactBufferWriter& writer) const override; 5555 bool canRecoverOnBailout() const override { return true; } 5556 5557 ALLOW_CLONE(MBigIntSub) 5558 }; 5559 5560 class MBigIntMul : public MBigIntBinaryArithInstruction { 5561 MBigIntMul(MDefinition* left, MDefinition* right) 5562 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5563 setCommutative(); 5564 5565 // See MBigIntAdd for why we don't guard this instruction. 5566 } 5567 5568 public: 5569 INSTRUCTION_HEADER(BigIntMul) 5570 TRIVIAL_NEW_WRAPPERS 5571 5572 [[nodiscard]] bool writeRecoverData( 5573 CompactBufferWriter& writer) const override; 5574 bool canRecoverOnBailout() const override { return true; } 5575 5576 ALLOW_CLONE(MBigIntMul) 5577 }; 5578 5579 class MBigIntDiv : public MBigIntBinaryArithInstruction { 5580 bool canBeDivideByZero_; 5581 5582 MBigIntDiv(MDefinition* left, MDefinition* right) 5583 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5584 MOZ_ASSERT(right->type() == MIRType::BigInt); 5585 canBeDivideByZero_ = 5586 !right->isConstant() || right->toConstant()->toBigInt()->isZero(); 5587 5588 // Throws when the divisor is zero. 5589 if (canBeDivideByZero_) { 5590 setGuard(); 5591 setNotMovable(); 5592 } 5593 } 5594 5595 public: 5596 INSTRUCTION_HEADER(BigIntDiv) 5597 TRIVIAL_NEW_WRAPPERS 5598 5599 bool canBeDivideByZero() const { return canBeDivideByZero_; } 5600 5601 AliasSet getAliasSet() const override { 5602 if (canBeDivideByZero()) { 5603 return AliasSet::Store(AliasSet::ExceptionState); 5604 } 5605 return AliasSet::None(); 5606 } 5607 5608 [[nodiscard]] bool writeRecoverData( 5609 CompactBufferWriter& writer) const override; 5610 bool canRecoverOnBailout() const override { return !canBeDivideByZero(); } 5611 5612 ALLOW_CLONE(MBigIntDiv) 5613 }; 5614 5615 class MBigIntMod : public MBigIntBinaryArithInstruction { 5616 bool canBeDivideByZero_; 5617 5618 MBigIntMod(MDefinition* left, MDefinition* right) 5619 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5620 MOZ_ASSERT(right->type() == MIRType::BigInt); 5621 canBeDivideByZero_ = 5622 !right->isConstant() || right->toConstant()->toBigInt()->isZero(); 5623 5624 // Throws when the divisor is zero. 5625 if (canBeDivideByZero_) { 5626 setGuard(); 5627 setNotMovable(); 5628 } 5629 } 5630 5631 public: 5632 INSTRUCTION_HEADER(BigIntMod) 5633 TRIVIAL_NEW_WRAPPERS 5634 5635 bool canBeDivideByZero() const { return canBeDivideByZero_; } 5636 5637 AliasSet getAliasSet() const override { 5638 if (canBeDivideByZero()) { 5639 return AliasSet::Store(AliasSet::ExceptionState); 5640 } 5641 return AliasSet::None(); 5642 } 5643 5644 [[nodiscard]] bool writeRecoverData( 5645 CompactBufferWriter& writer) const override; 5646 bool canRecoverOnBailout() const override { return !canBeDivideByZero(); } 5647 5648 ALLOW_CLONE(MBigIntMod) 5649 }; 5650 5651 class MBigIntPow : public MBigIntBinaryArithInstruction { 5652 bool canBeNegativeExponent_; 5653 5654 MBigIntPow(MDefinition* left, MDefinition* right) 5655 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5656 MOZ_ASSERT(right->type() == MIRType::BigInt); 5657 canBeNegativeExponent_ = 5658 !right->isConstant() || right->toConstant()->toBigInt()->isNegative(); 5659 5660 // Throws when the exponent is negative. 5661 if (canBeNegativeExponent_) { 5662 setGuard(); 5663 setNotMovable(); 5664 } 5665 } 5666 5667 public: 5668 INSTRUCTION_HEADER(BigIntPow) 5669 TRIVIAL_NEW_WRAPPERS 5670 5671 bool canBeNegativeExponent() const { return canBeNegativeExponent_; } 5672 5673 AliasSet getAliasSet() const override { 5674 if (canBeNegativeExponent()) { 5675 return AliasSet::Store(AliasSet::ExceptionState); 5676 } 5677 return AliasSet::None(); 5678 } 5679 5680 MDefinition* foldsTo(TempAllocator& alloc) override; 5681 5682 [[nodiscard]] bool writeRecoverData( 5683 CompactBufferWriter& writer) const override; 5684 bool canRecoverOnBailout() const override { return !canBeNegativeExponent(); } 5685 5686 ALLOW_CLONE(MBigIntPow) 5687 }; 5688 5689 class MBigIntBitAnd : public MBigIntBinaryArithInstruction { 5690 MBigIntBitAnd(MDefinition* left, MDefinition* right) 5691 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5692 setCommutative(); 5693 5694 // We don't need to guard this instruction because it can only fail on OOM. 5695 } 5696 5697 public: 5698 INSTRUCTION_HEADER(BigIntBitAnd) 5699 TRIVIAL_NEW_WRAPPERS 5700 5701 [[nodiscard]] bool writeRecoverData( 5702 CompactBufferWriter& writer) const override; 5703 bool canRecoverOnBailout() const override { return true; } 5704 5705 ALLOW_CLONE(MBigIntBitAnd) 5706 }; 5707 5708 class MBigIntBitOr : public MBigIntBinaryArithInstruction { 5709 MBigIntBitOr(MDefinition* left, MDefinition* right) 5710 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5711 setCommutative(); 5712 5713 // We don't need to guard this instruction because it can only fail on OOM. 5714 } 5715 5716 public: 5717 INSTRUCTION_HEADER(BigIntBitOr) 5718 TRIVIAL_NEW_WRAPPERS 5719 5720 [[nodiscard]] bool writeRecoverData( 5721 CompactBufferWriter& writer) const override; 5722 bool canRecoverOnBailout() const override { return true; } 5723 5724 ALLOW_CLONE(MBigIntBitOr) 5725 }; 5726 5727 class MBigIntBitXor : public MBigIntBinaryArithInstruction { 5728 MBigIntBitXor(MDefinition* left, MDefinition* right) 5729 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5730 setCommutative(); 5731 5732 // We don't need to guard this instruction because it can only fail on OOM. 5733 } 5734 5735 public: 5736 INSTRUCTION_HEADER(BigIntBitXor) 5737 TRIVIAL_NEW_WRAPPERS 5738 5739 [[nodiscard]] bool writeRecoverData( 5740 CompactBufferWriter& writer) const override; 5741 bool canRecoverOnBailout() const override { return true; } 5742 5743 ALLOW_CLONE(MBigIntBitXor) 5744 }; 5745 5746 class MBigIntLsh : public MBigIntBinaryArithInstruction { 5747 MBigIntLsh(MDefinition* left, MDefinition* right) 5748 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5749 // See MBigIntAdd for why we don't guard this instruction. 5750 } 5751 5752 public: 5753 INSTRUCTION_HEADER(BigIntLsh) 5754 TRIVIAL_NEW_WRAPPERS 5755 5756 [[nodiscard]] bool writeRecoverData( 5757 CompactBufferWriter& writer) const override; 5758 bool canRecoverOnBailout() const override { return true; } 5759 5760 ALLOW_CLONE(MBigIntLsh) 5761 }; 5762 5763 class MBigIntRsh : public MBigIntBinaryArithInstruction { 5764 MBigIntRsh(MDefinition* left, MDefinition* right) 5765 : MBigIntBinaryArithInstruction(classOpcode, left, right) { 5766 // See MBigIntAdd for why we don't guard this instruction. 5767 } 5768 5769 public: 5770 INSTRUCTION_HEADER(BigIntRsh) 5771 TRIVIAL_NEW_WRAPPERS 5772 5773 [[nodiscard]] bool writeRecoverData( 5774 CompactBufferWriter& writer) const override; 5775 bool canRecoverOnBailout() const override { return true; } 5776 5777 ALLOW_CLONE(MBigIntRsh) 5778 }; 5779 5780 class MBigIntUnaryArithInstruction : public MUnaryInstruction, 5781 public BigIntArithPolicy::Data { 5782 protected: 5783 MBigIntUnaryArithInstruction(Opcode op, MDefinition* input) 5784 : MUnaryInstruction(op, input) { 5785 setResultType(MIRType::BigInt); 5786 setMovable(); 5787 } 5788 5789 public: 5790 bool congruentTo(const MDefinition* ins) const override { 5791 return congruentIfOperandsEqual(ins); 5792 } 5793 5794 AliasSet getAliasSet() const override { return AliasSet::None(); } 5795 }; 5796 5797 class MBigIntIncrement : public MBigIntUnaryArithInstruction { 5798 explicit MBigIntIncrement(MDefinition* input) 5799 : MBigIntUnaryArithInstruction(classOpcode, input) { 5800 // See MBigIntAdd for why we don't guard this instruction. 5801 } 5802 5803 public: 5804 INSTRUCTION_HEADER(BigIntIncrement) 5805 TRIVIAL_NEW_WRAPPERS 5806 5807 [[nodiscard]] bool writeRecoverData( 5808 CompactBufferWriter& writer) const override; 5809 bool canRecoverOnBailout() const override { return true; } 5810 5811 ALLOW_CLONE(MBigIntIncrement) 5812 }; 5813 5814 class MBigIntDecrement : public MBigIntUnaryArithInstruction { 5815 explicit MBigIntDecrement(MDefinition* input) 5816 : MBigIntUnaryArithInstruction(classOpcode, input) { 5817 // See MBigIntAdd for why we don't guard this instruction. 5818 } 5819 5820 public: 5821 INSTRUCTION_HEADER(BigIntDecrement) 5822 TRIVIAL_NEW_WRAPPERS 5823 5824 [[nodiscard]] bool writeRecoverData( 5825 CompactBufferWriter& writer) const override; 5826 bool canRecoverOnBailout() const override { return true; } 5827 5828 ALLOW_CLONE(MBigIntDecrement) 5829 }; 5830 5831 class MBigIntNegate : public MBigIntUnaryArithInstruction { 5832 explicit MBigIntNegate(MDefinition* input) 5833 : MBigIntUnaryArithInstruction(classOpcode, input) { 5834 // We don't need to guard this instruction because it can only fail on OOM. 5835 } 5836 5837 public: 5838 INSTRUCTION_HEADER(BigIntNegate) 5839 TRIVIAL_NEW_WRAPPERS 5840 5841 [[nodiscard]] bool writeRecoverData( 5842 CompactBufferWriter& writer) const override; 5843 bool canRecoverOnBailout() const override { return true; } 5844 5845 ALLOW_CLONE(MBigIntNegate) 5846 }; 5847 5848 class MBigIntBitNot : public MBigIntUnaryArithInstruction { 5849 explicit MBigIntBitNot(MDefinition* input) 5850 : MBigIntUnaryArithInstruction(classOpcode, input) { 5851 // See MBigIntAdd for why we don't guard this instruction. 5852 } 5853 5854 public: 5855 INSTRUCTION_HEADER(BigIntBitNot) 5856 TRIVIAL_NEW_WRAPPERS 5857 5858 [[nodiscard]] bool writeRecoverData( 5859 CompactBufferWriter& writer) const override; 5860 bool canRecoverOnBailout() const override { return true; } 5861 5862 ALLOW_CLONE(MBigIntBitNot) 5863 }; 5864 5865 class MBigIntPtrBinaryArithInstruction : public MBinaryInstruction, 5866 public NoTypePolicy::Data { 5867 protected: 5868 MBigIntPtrBinaryArithInstruction(Opcode op, MDefinition* left, 5869 MDefinition* right) 5870 : MBinaryInstruction(op, left, right) { 5871 MOZ_ASSERT(left->type() == MIRType::IntPtr); 5872 MOZ_ASSERT(right->type() == MIRType::IntPtr); 5873 setResultType(MIRType::IntPtr); 5874 setMovable(); 5875 } 5876 5877 static bool isMaybeZero(MDefinition* ins); 5878 static bool isMaybeNegative(MDefinition* ins); 5879 5880 public: 5881 MDefinition* foldsTo(TempAllocator& alloc) override; 5882 5883 bool congruentTo(const MDefinition* ins) const override { 5884 return binaryCongruentTo(ins); 5885 } 5886 5887 AliasSet getAliasSet() const override { return AliasSet::None(); } 5888 }; 5889 5890 class MBigIntPtrAdd : public MBigIntPtrBinaryArithInstruction { 5891 MBigIntPtrAdd(MDefinition* left, MDefinition* right) 5892 : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) { 5893 setCommutative(); 5894 } 5895 5896 public: 5897 INSTRUCTION_HEADER(BigIntPtrAdd) 5898 TRIVIAL_NEW_WRAPPERS 5899 5900 [[nodiscard]] bool writeRecoverData( 5901 CompactBufferWriter& writer) const override; 5902 bool canRecoverOnBailout() const override { return true; } 5903 5904 ALLOW_CLONE(MBigIntPtrAdd) 5905 }; 5906 5907 class MBigIntPtrSub : public MBigIntPtrBinaryArithInstruction { 5908 MBigIntPtrSub(MDefinition* left, MDefinition* right) 5909 : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) {} 5910 5911 public: 5912 INSTRUCTION_HEADER(BigIntPtrSub) 5913 TRIVIAL_NEW_WRAPPERS 5914 5915 [[nodiscard]] bool writeRecoverData( 5916 CompactBufferWriter& writer) const override; 5917 bool canRecoverOnBailout() const override { return true; } 5918 5919 ALLOW_CLONE(MBigIntPtrSub) 5920 }; 5921 5922 class MBigIntPtrMul : public MBigIntPtrBinaryArithInstruction { 5923 MBigIntPtrMul(MDefinition* left, MDefinition* right) 5924 : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) { 5925 setCommutative(); 5926 } 5927 5928 public: 5929 INSTRUCTION_HEADER(BigIntPtrMul) 5930 TRIVIAL_NEW_WRAPPERS 5931 5932 [[nodiscard]] bool writeRecoverData( 5933 CompactBufferWriter& writer) const override; 5934 bool canRecoverOnBailout() const override { return true; } 5935 5936 ALLOW_CLONE(MBigIntPtrMul) 5937 }; 5938 5939 class MBigIntPtrDiv : public MBigIntPtrBinaryArithInstruction { 5940 bool canBeDivideByZero_; 5941 5942 MBigIntPtrDiv(MDefinition* left, MDefinition* right) 5943 : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) { 5944 canBeDivideByZero_ = isMaybeZero(right); 5945 5946 // Bails when the divisor is zero. 5947 if (canBeDivideByZero_) { 5948 setGuard(); 5949 } 5950 } 5951 5952 public: 5953 INSTRUCTION_HEADER(BigIntPtrDiv) 5954 TRIVIAL_NEW_WRAPPERS 5955 5956 bool canBeDivideByZero() const { return canBeDivideByZero_; } 5957 5958 [[nodiscard]] bool writeRecoverData( 5959 CompactBufferWriter& writer) const override; 5960 bool canRecoverOnBailout() const override { return true; } 5961 5962 ALLOW_CLONE(MBigIntPtrDiv) 5963 }; 5964 5965 class MBigIntPtrMod : public MBigIntPtrBinaryArithInstruction { 5966 bool canBeDivideByZero_; 5967 5968 MBigIntPtrMod(MDefinition* left, MDefinition* right) 5969 : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) { 5970 canBeDivideByZero_ = isMaybeZero(right); 5971 5972 // Bails when the divisor is zero. 5973 if (canBeDivideByZero_) { 5974 setGuard(); 5975 } 5976 } 5977 5978 public: 5979 INSTRUCTION_HEADER(BigIntPtrMod) 5980 TRIVIAL_NEW_WRAPPERS 5981 5982 bool canBeDivideByZero() const { return canBeDivideByZero_; } 5983 5984 [[nodiscard]] bool writeRecoverData( 5985 CompactBufferWriter& writer) const override; 5986 bool canRecoverOnBailout() const override { return true; } 5987 5988 ALLOW_CLONE(MBigIntPtrMod) 5989 }; 5990 5991 class MBigIntPtrPow : public MBigIntPtrBinaryArithInstruction { 5992 bool canBeNegativeExponent_; 5993 5994 MBigIntPtrPow(MDefinition* left, MDefinition* right) 5995 : MBigIntPtrBinaryArithInstruction(classOpcode, left, right) { 5996 canBeNegativeExponent_ = isMaybeNegative(right); 5997 5998 // Bails when the exponent is negative. 5999 if (canBeNegativeExponent_) { 6000 setGuard(); 6001 } 6002 } 6003 6004 public: 6005 INSTRUCTION_HEADER(BigIntPtrPow) 6006 TRIVIAL_NEW_WRAPPERS 6007 6008 bool canBeNegativeExponent() const { return canBeNegativeExponent_; } 6009 6010 MDefinition* foldsTo(TempAllocator& alloc) override; 6011 6012 [[nodiscard]] bool writeRecoverData( 6013 CompactBufferWriter& writer) const override; 6014 bool canRecoverOnBailout() const override { return true; } 6015 6016 ALLOW_CLONE(MBigIntPtrPow) 6017 }; 6018 6019 class MBigIntPtrBinaryBitwiseInstruction : public MBinaryInstruction, 6020 public NoTypePolicy::Data { 6021 protected: 6022 MBigIntPtrBinaryBitwiseInstruction(Opcode op, MDefinition* left, 6023 MDefinition* right) 6024 : MBinaryInstruction(op, left, right) { 6025 MOZ_ASSERT(left->type() == MIRType::IntPtr); 6026 MOZ_ASSERT(right->type() == MIRType::IntPtr); 6027 setResultType(MIRType::IntPtr); 6028 setMovable(); 6029 } 6030 6031 public: 6032 MDefinition* foldsTo(TempAllocator& alloc) override; 6033 6034 bool congruentTo(const MDefinition* ins) const override { 6035 return binaryCongruentTo(ins); 6036 } 6037 6038 AliasSet getAliasSet() const override { return AliasSet::None(); } 6039 }; 6040 6041 class MBigIntPtrBitAnd : public MBigIntPtrBinaryBitwiseInstruction { 6042 MBigIntPtrBitAnd(MDefinition* left, MDefinition* right) 6043 : MBigIntPtrBinaryBitwiseInstruction(classOpcode, left, right) { 6044 setCommutative(); 6045 } 6046 6047 public: 6048 INSTRUCTION_HEADER(BigIntPtrBitAnd) 6049 TRIVIAL_NEW_WRAPPERS 6050 6051 [[nodiscard]] bool writeRecoverData( 6052 CompactBufferWriter& writer) const override; 6053 bool canRecoverOnBailout() const override { return true; } 6054 6055 ALLOW_CLONE(MBigIntPtrBitAnd) 6056 }; 6057 6058 class MBigIntPtrBitOr : public MBigIntPtrBinaryBitwiseInstruction { 6059 MBigIntPtrBitOr(MDefinition* left, MDefinition* right) 6060 : MBigIntPtrBinaryBitwiseInstruction(classOpcode, left, right) { 6061 setCommutative(); 6062 } 6063 6064 public: 6065 INSTRUCTION_HEADER(BigIntPtrBitOr) 6066 TRIVIAL_NEW_WRAPPERS 6067 6068 [[nodiscard]] bool writeRecoverData( 6069 CompactBufferWriter& writer) const override; 6070 bool canRecoverOnBailout() const override { return true; } 6071 6072 ALLOW_CLONE(MBigIntPtrBitOr) 6073 }; 6074 6075 class MBigIntPtrBitXor : public MBigIntPtrBinaryBitwiseInstruction { 6076 MBigIntPtrBitXor(MDefinition* left, MDefinition* right) 6077 : MBigIntPtrBinaryBitwiseInstruction(classOpcode, left, right) { 6078 setCommutative(); 6079 } 6080 6081 public: 6082 INSTRUCTION_HEADER(BigIntPtrBitXor) 6083 TRIVIAL_NEW_WRAPPERS 6084 6085 [[nodiscard]] bool writeRecoverData( 6086 CompactBufferWriter& writer) const override; 6087 bool canRecoverOnBailout() const override { return true; } 6088 6089 ALLOW_CLONE(MBigIntPtrBitXor) 6090 }; 6091 6092 class MBigIntPtrLsh : public MBigIntPtrBinaryBitwiseInstruction { 6093 MBigIntPtrLsh(MDefinition* left, MDefinition* right) 6094 : MBigIntPtrBinaryBitwiseInstruction(classOpcode, left, right) {} 6095 6096 public: 6097 INSTRUCTION_HEADER(BigIntPtrLsh) 6098 TRIVIAL_NEW_WRAPPERS 6099 6100 bool fallible() const { 6101 return !rhs()->isConstant() || rhs()->toConstant()->toIntPtr() > 0; 6102 } 6103 6104 [[nodiscard]] bool writeRecoverData( 6105 CompactBufferWriter& writer) const override; 6106 bool canRecoverOnBailout() const override { return true; } 6107 6108 ALLOW_CLONE(MBigIntPtrLsh) 6109 }; 6110 6111 class MBigIntPtrRsh : public MBigIntPtrBinaryBitwiseInstruction { 6112 MBigIntPtrRsh(MDefinition* left, MDefinition* right) 6113 : MBigIntPtrBinaryBitwiseInstruction(classOpcode, left, right) {} 6114 6115 public: 6116 INSTRUCTION_HEADER(BigIntPtrRsh) 6117 TRIVIAL_NEW_WRAPPERS 6118 6119 bool fallible() const { 6120 return !rhs()->isConstant() || rhs()->toConstant()->toIntPtr() < 0; 6121 } 6122 6123 [[nodiscard]] bool writeRecoverData( 6124 CompactBufferWriter& writer) const override; 6125 bool canRecoverOnBailout() const override { return true; } 6126 6127 ALLOW_CLONE(MBigIntPtrRsh) 6128 }; 6129 6130 class MBigIntPtrBitNot : public MUnaryInstruction, public NoTypePolicy::Data { 6131 explicit MBigIntPtrBitNot(MDefinition* input) 6132 : MUnaryInstruction(classOpcode, input) { 6133 MOZ_ASSERT(input->type() == MIRType::IntPtr); 6134 setResultType(MIRType::IntPtr); 6135 setMovable(); 6136 } 6137 6138 public: 6139 INSTRUCTION_HEADER(BigIntPtrBitNot) 6140 TRIVIAL_NEW_WRAPPERS 6141 6142 MDefinition* foldsTo(TempAllocator& alloc) override; 6143 6144 bool congruentTo(const MDefinition* ins) const override { 6145 return congruentIfOperandsEqual(ins); 6146 } 6147 AliasSet getAliasSet() const override { return AliasSet::None(); } 6148 6149 [[nodiscard]] bool writeRecoverData( 6150 CompactBufferWriter& writer) const override; 6151 bool canRecoverOnBailout() const override { return true; } 6152 6153 ALLOW_CLONE(MBigIntPtrBitNot) 6154 }; 6155 6156 class MConcat : public MBinaryInstruction, 6157 public MixPolicy<ConvertToStringPolicy<0>, 6158 ConvertToStringPolicy<1>>::Data { 6159 MConcat(MDefinition* left, MDefinition* right) 6160 : MBinaryInstruction(classOpcode, left, right) { 6161 // At least one input should be definitely string 6162 MOZ_ASSERT(left->type() == MIRType::String || 6163 right->type() == MIRType::String); 6164 6165 setMovable(); 6166 setResultType(MIRType::String); 6167 } 6168 6169 public: 6170 INSTRUCTION_HEADER(Concat) 6171 TRIVIAL_NEW_WRAPPERS 6172 6173 MDefinition* foldsTo(TempAllocator& alloc) override; 6174 bool congruentTo(const MDefinition* ins) const override { 6175 return congruentIfOperandsEqual(ins); 6176 } 6177 AliasSet getAliasSet() const override { return AliasSet::None(); } 6178 6179 [[nodiscard]] bool writeRecoverData( 6180 CompactBufferWriter& writer) const override; 6181 bool canRecoverOnBailout() const override { return true; } 6182 6183 ALLOW_CLONE(MConcat) 6184 }; 6185 6186 class MStringConvertCase : public MUnaryInstruction, 6187 public StringPolicy<0>::Data { 6188 public: 6189 enum Mode { LowerCase, UpperCase }; 6190 6191 private: 6192 Mode mode_; 6193 6194 MStringConvertCase(MDefinition* string, Mode mode) 6195 : MUnaryInstruction(classOpcode, string), mode_(mode) { 6196 setResultType(MIRType::String); 6197 setMovable(); 6198 } 6199 6200 public: 6201 INSTRUCTION_HEADER(StringConvertCase) 6202 TRIVIAL_NEW_WRAPPERS 6203 NAMED_OPERANDS((0, string)) 6204 6205 MDefinition* foldsTo(TempAllocator& alloc) override; 6206 bool congruentTo(const MDefinition* ins) const override { 6207 return congruentIfOperandsEqual(ins) && 6208 ins->toStringConvertCase()->mode() == mode(); 6209 } 6210 AliasSet getAliasSet() const override { return AliasSet::None(); } 6211 bool possiblyCalls() const override { return true; } 6212 Mode mode() const { return mode_; } 6213 }; 6214 6215 class MCharCodeConvertCase : public MUnaryInstruction, 6216 public UnboxedInt32Policy<0>::Data { 6217 public: 6218 enum Mode { LowerCase, UpperCase }; 6219 6220 private: 6221 Mode mode_; 6222 6223 MCharCodeConvertCase(MDefinition* code, Mode mode) 6224 : MUnaryInstruction(classOpcode, code), mode_(mode) { 6225 setResultType(MIRType::String); 6226 setMovable(); 6227 } 6228 6229 public: 6230 INSTRUCTION_HEADER(CharCodeConvertCase) 6231 TRIVIAL_NEW_WRAPPERS 6232 NAMED_OPERANDS((0, code)) 6233 6234 bool congruentTo(const MDefinition* ins) const override { 6235 return congruentIfOperandsEqual(ins) && 6236 ins->toCharCodeConvertCase()->mode() == mode(); 6237 } 6238 AliasSet getAliasSet() const override { return AliasSet::None(); } 6239 Mode mode() const { return mode_; } 6240 }; 6241 6242 // This is a 3 state flag used by FlagPhiInputsAsImplicitlyUsed to record and 6243 // propagate the information about the consumers of a Phi instruction. This is 6244 // then used to set ImplicitlyUsed flags on the inputs of such Phi instructions. 6245 enum class PhiUsage : uint8_t { Unknown, Unused, Used }; 6246 6247 using PhiVector = Vector<MPhi*, 4, JitAllocPolicy>; 6248 6249 class MPhi final : public MDefinition, 6250 public InlineListNode<MPhi>, 6251 public NoTypePolicy::Data { 6252 using InputVector = js::Vector<MUse, 2, JitAllocPolicy>; 6253 InputVector inputs_; 6254 6255 // Important: if you add fields to this class, be sure to update its ::clone 6256 // method accordingly. 6257 TruncateKind truncateKind_; 6258 bool triedToSpecialize_; 6259 bool isIterator_; 6260 bool canProduceFloat32_; 6261 bool canConsumeFloat32_; 6262 // Record the state of the data flow before any mutation made to the control 6263 // flow, such that removing branches is properly accounted for. 6264 PhiUsage usageAnalysis_; 6265 6266 protected: 6267 MUse* getUseFor(size_t index) override { 6268 MOZ_ASSERT(index < numOperands()); 6269 return &inputs_[index]; 6270 } 6271 const MUse* getUseFor(size_t index) const override { return &inputs_[index]; } 6272 6273 public: 6274 INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(Phi) 6275 virtual const TypePolicy* typePolicy(); 6276 virtual MIRType typePolicySpecialization(); 6277 6278 MPhi(TempAllocator& alloc, MIRType resultType) 6279 : MDefinition(classOpcode), 6280 inputs_(alloc), 6281 truncateKind_(TruncateKind::NoTruncate), 6282 triedToSpecialize_(false), 6283 isIterator_(false), 6284 canProduceFloat32_(false), 6285 canConsumeFloat32_(false), 6286 usageAnalysis_(PhiUsage::Unknown) { 6287 setResultType(resultType); 6288 } 6289 6290 static MPhi* New(TempAllocator& alloc, MIRType resultType = MIRType::Value) { 6291 return new (alloc) MPhi(alloc, resultType); 6292 } 6293 static MPhi* New(TempAllocator::Fallible alloc, 6294 MIRType resultType = MIRType::Value) { 6295 return new (alloc) MPhi(alloc.alloc, resultType); 6296 } 6297 6298 MPhi* clone(TempAllocator& alloc, const MDefinitionVector& inputs) const { 6299 MOZ_ASSERT(inputs.length() == inputs_.length()); 6300 MPhi* phi = MPhi::New(alloc); 6301 if (!phi || !phi->reserveLength(inputs.length())) { 6302 return nullptr; 6303 } 6304 for (const auto& inp : inputs) { 6305 phi->addInput(inp); 6306 } 6307 phi->truncateKind_ = truncateKind_; 6308 phi->triedToSpecialize_ = triedToSpecialize_; 6309 phi->isIterator_ = isIterator_; 6310 phi->canProduceFloat32_ = canProduceFloat32_; 6311 phi->canConsumeFloat32_ = canConsumeFloat32_; 6312 phi->usageAnalysis_ = usageAnalysis_; 6313 phi->setResultType(type()); 6314 return phi; 6315 } 6316 6317 void removeOperand(size_t index); 6318 void removeAllOperands(); 6319 6320 MDefinition* getOperand(size_t index) const override { 6321 return inputs_[index].producer(); 6322 } 6323 size_t numOperands() const override { return inputs_.length(); } 6324 size_t indexOf(const MUse* u) const final { 6325 MOZ_ASSERT(u >= &inputs_[0]); 6326 MOZ_ASSERT(u <= &inputs_[numOperands() - 1]); 6327 return u - &inputs_[0]; 6328 } 6329 void replaceOperand(size_t index, MDefinition* operand) final { 6330 inputs_[index].replaceProducer(operand); 6331 } 6332 bool triedToSpecialize() const { return triedToSpecialize_; } 6333 void specialize(MIRType type) { 6334 triedToSpecialize_ = true; 6335 setResultType(type); 6336 } 6337 6338 #ifdef DEBUG 6339 // Assert that this is a phi in a loop header with a unique predecessor and 6340 // a unique backedge. 6341 void assertLoopPhi() const; 6342 #else 6343 void assertLoopPhi() const {} 6344 #endif 6345 6346 // Assuming this phi is in a loop header with a unique loop entry, return 6347 // the phi operand along the loop entry. 6348 MDefinition* getLoopPredecessorOperand() const; 6349 6350 // Assuming this phi is in a loop header with a unique loop entry, return 6351 // the phi operand along the loop backedge. 6352 MDefinition* getLoopBackedgeOperand() const; 6353 6354 // Whether this phi's type already includes information for def. 6355 bool typeIncludes(MDefinition* def); 6356 6357 // Mark all phis in |iterators|, and the phis they flow into, as having 6358 // implicit uses. 6359 [[nodiscard]] static bool markIteratorPhis(const PhiVector& iterators); 6360 6361 // Initializes the operands vector to the given capacity, 6362 // permitting use of addInput() instead of addInputSlow(). 6363 [[nodiscard]] bool reserveLength(size_t length) { 6364 return inputs_.reserve(length); 6365 } 6366 6367 // Use only if capacity has been reserved by reserveLength 6368 void addInput(MDefinition* ins) { 6369 MOZ_ASSERT_IF(type() != MIRType::Value, ins->type() == type()); 6370 inputs_.infallibleEmplaceBack(ins, this); 6371 } 6372 6373 // Append a new input to the input vector. May fail. 6374 [[nodiscard]] bool addInputFallible(MDefinition* ins) { 6375 MOZ_ASSERT_IF(type() != MIRType::Value, ins->type() == type()); 6376 return inputs_.emplaceBack(ins, this); 6377 } 6378 6379 // Appends a new input to the input vector. May perform reallocation. 6380 // Prefer reserveLength() and addInput() instead, where possible. 6381 [[nodiscard]] bool addInputSlow(MDefinition* ins) { 6382 MOZ_ASSERT_IF(type() != MIRType::Value, ins->type() == type()); 6383 return inputs_.emplaceBack(ins, this); 6384 } 6385 6386 // Appends a new input to the input vector. Infallible because 6387 // we know the inputs fits in the vector's inline storage. 6388 void addInlineInput(MDefinition* ins) { 6389 MOZ_ASSERT(inputs_.length() < InputVector::InlineLength); 6390 MOZ_ALWAYS_TRUE(addInputSlow(ins)); 6391 } 6392 6393 MDefinition* foldsTo(TempAllocator& alloc) override; 6394 MDefinition* foldsTernary(TempAllocator& alloc); 6395 6396 bool congruentTo(const MDefinition* ins) const override; 6397 6398 // Mark this phi-node as having replaced all uses of |other|, as during GVN. 6399 // For use when GVN eliminates phis which are not equivalent to one another. 6400 void updateForReplacement(MPhi* other); 6401 6402 bool isIterator() const { return isIterator_; } 6403 void setIterator() { isIterator_ = true; } 6404 6405 AliasSet getAliasSet() const override { return AliasSet::None(); } 6406 void computeRange(TempAllocator& alloc) override; 6407 6408 MDefinition* operandIfRedundant(); 6409 6410 bool canProduceFloat32() const override { return canProduceFloat32_; } 6411 6412 void setCanProduceFloat32(bool can) { canProduceFloat32_ = can; } 6413 6414 bool canConsumeFloat32(MUse* use) const override { 6415 return canConsumeFloat32_; 6416 } 6417 6418 void setCanConsumeFloat32(bool can) { canConsumeFloat32_ = can; } 6419 6420 TruncateKind operandTruncateKind(size_t index) const override; 6421 bool canTruncate() const override; 6422 void truncate(TruncateKind kind) override; 6423 6424 PhiUsage getUsageAnalysis() const { return usageAnalysis_; } 6425 void setUsageAnalysis(PhiUsage pu) { 6426 MOZ_ASSERT(usageAnalysis_ == PhiUsage::Unknown); 6427 usageAnalysis_ = pu; 6428 MOZ_ASSERT(usageAnalysis_ != PhiUsage::Unknown); 6429 } 6430 6431 wasm::MaybeRefType computeWasmRefType() const override { 6432 if (numOperands() == 0) { 6433 return wasm::MaybeRefType(); 6434 } 6435 wasm::MaybeRefType firstRefType = getOperand(0)->wasmRefType(); 6436 if (firstRefType.isNothing()) { 6437 return wasm::MaybeRefType(); 6438 } 6439 6440 wasm::RefType topTypeOfOperands = firstRefType.value(); 6441 for (size_t i = 1; i < numOperands(); i++) { 6442 MDefinition* op = getOperand(i); 6443 wasm::MaybeRefType opType = op->wasmRefType(); 6444 if (opType.isNothing()) { 6445 return wasm::MaybeRefType(); 6446 } 6447 topTypeOfOperands = 6448 wasm::RefType::leastUpperBound(topTypeOfOperands, opType.value()); 6449 } 6450 return wasm::MaybeRefType(topTypeOfOperands); 6451 } 6452 }; 6453 6454 // The goal of a Beta node is to split a def at a conditionally taken 6455 // branch, so that uses dominated by it have a different name. 6456 class MBeta : public MUnaryInstruction, public NoTypePolicy::Data { 6457 private: 6458 // This is the range induced by a comparison and branch in a preceding 6459 // block. Note that this does not reflect any range constraints from 6460 // the input value itself, so this value may differ from the range() 6461 // range after it is computed. 6462 const Range* comparison_; 6463 6464 MBeta(MDefinition* val, const Range* comp) 6465 : MUnaryInstruction(classOpcode, val), comparison_(comp) { 6466 setResultType(val->type()); 6467 } 6468 6469 public: 6470 INSTRUCTION_HEADER(Beta) 6471 TRIVIAL_NEW_WRAPPERS 6472 6473 #ifdef JS_JITSPEW 6474 void printOpcode(GenericPrinter& out) const override; 6475 #endif 6476 6477 AliasSet getAliasSet() const override { return AliasSet::None(); } 6478 6479 void computeRange(TempAllocator& alloc) override; 6480 }; 6481 6482 // If input evaluates to false (i.e. it's NaN, 0 or -0), 0 is returned, else the 6483 // input is returned 6484 class MNaNToZero : public MUnaryInstruction, public DoublePolicy<0>::Data { 6485 bool operandIsNeverNaN_; 6486 bool operandIsNeverNegativeZero_; 6487 6488 explicit MNaNToZero(MDefinition* input) 6489 : MUnaryInstruction(classOpcode, input), 6490 operandIsNeverNaN_(false), 6491 operandIsNeverNegativeZero_(false) { 6492 setResultType(MIRType::Double); 6493 setMovable(); 6494 } 6495 6496 public: 6497 INSTRUCTION_HEADER(NaNToZero) 6498 TRIVIAL_NEW_WRAPPERS 6499 6500 bool operandIsNeverNaN() const { return operandIsNeverNaN_; } 6501 6502 bool operandIsNeverNegativeZero() const { 6503 return operandIsNeverNegativeZero_; 6504 } 6505 6506 void collectRangeInfoPreTrunc() override; 6507 6508 AliasSet getAliasSet() const override { return AliasSet::None(); } 6509 6510 void computeRange(TempAllocator& alloc) override; 6511 6512 bool writeRecoverData(CompactBufferWriter& writer) const override; 6513 bool canRecoverOnBailout() const override { return true; } 6514 6515 ALLOW_CLONE(MNaNToZero) 6516 }; 6517 6518 // MIR representation of a Value on the OSR BaselineFrame. 6519 // The Value is indexed off of OsrFrameReg. 6520 class MOsrValue : public MUnaryInstruction, public NoTypePolicy::Data { 6521 private: 6522 ptrdiff_t frameOffset_; 6523 6524 MOsrValue(MOsrEntry* entry, ptrdiff_t frameOffset) 6525 : MUnaryInstruction(classOpcode, entry), frameOffset_(frameOffset) { 6526 setResultType(MIRType::Value); 6527 } 6528 6529 public: 6530 INSTRUCTION_HEADER(OsrValue) 6531 TRIVIAL_NEW_WRAPPERS 6532 6533 ptrdiff_t frameOffset() const { return frameOffset_; } 6534 6535 MOsrEntry* entry() { return getOperand(0)->toOsrEntry(); } 6536 6537 AliasSet getAliasSet() const override { return AliasSet::None(); } 6538 }; 6539 6540 // MIR representation of a JSObject scope chain pointer on the OSR 6541 // BaselineFrame. The pointer is indexed off of OsrFrameReg. 6542 class MOsrEnvironmentChain : public MUnaryInstruction, 6543 public NoTypePolicy::Data { 6544 private: 6545 explicit MOsrEnvironmentChain(MOsrEntry* entry) 6546 : MUnaryInstruction(classOpcode, entry) { 6547 setResultType(MIRType::Object); 6548 } 6549 6550 public: 6551 INSTRUCTION_HEADER(OsrEnvironmentChain) 6552 TRIVIAL_NEW_WRAPPERS 6553 6554 MOsrEntry* entry() { return getOperand(0)->toOsrEntry(); } 6555 }; 6556 6557 // MIR representation of a JSObject ArgumentsObject pointer on the OSR 6558 // BaselineFrame. The pointer is indexed off of OsrFrameReg. 6559 class MOsrArgumentsObject : public MUnaryInstruction, 6560 public NoTypePolicy::Data { 6561 private: 6562 explicit MOsrArgumentsObject(MOsrEntry* entry) 6563 : MUnaryInstruction(classOpcode, entry) { 6564 setResultType(MIRType::Object); 6565 } 6566 6567 public: 6568 INSTRUCTION_HEADER(OsrArgumentsObject) 6569 TRIVIAL_NEW_WRAPPERS 6570 6571 MOsrEntry* entry() { return getOperand(0)->toOsrEntry(); } 6572 }; 6573 6574 // MIR representation of the return value on the OSR BaselineFrame. 6575 // The Value is indexed off of OsrFrameReg. 6576 class MOsrReturnValue : public MUnaryInstruction, public NoTypePolicy::Data { 6577 private: 6578 explicit MOsrReturnValue(MOsrEntry* entry) 6579 : MUnaryInstruction(classOpcode, entry) { 6580 setResultType(MIRType::Value); 6581 } 6582 6583 public: 6584 INSTRUCTION_HEADER(OsrReturnValue) 6585 TRIVIAL_NEW_WRAPPERS 6586 6587 MOsrEntry* entry() { return getOperand(0)->toOsrEntry(); } 6588 }; 6589 6590 class MBinaryCache : public MBinaryInstruction, 6591 public MixPolicy<BoxPolicy<0>, BoxPolicy<1>>::Data { 6592 explicit MBinaryCache(MDefinition* left, MDefinition* right, MIRType resType) 6593 : MBinaryInstruction(classOpcode, left, right) { 6594 setResultType(resType); 6595 } 6596 6597 public: 6598 INSTRUCTION_HEADER(BinaryCache) 6599 TRIVIAL_NEW_WRAPPERS 6600 }; 6601 6602 // Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving 6603 // it to baseline to throw at the correct pc. 6604 class MLexicalCheck : public MUnaryInstruction, public BoxPolicy<0>::Data { 6605 explicit MLexicalCheck(MDefinition* input) 6606 : MUnaryInstruction(classOpcode, input) { 6607 setResultType(MIRType::Value); 6608 setMovable(); 6609 setGuard(); 6610 6611 // If this instruction bails out, we will set a flag to prevent 6612 // lexical checks in this script from being moved. 6613 setBailoutKind(BailoutKind::UninitializedLexical); 6614 } 6615 6616 public: 6617 INSTRUCTION_HEADER(LexicalCheck) 6618 TRIVIAL_NEW_WRAPPERS 6619 6620 AliasSet getAliasSet() const override { return AliasSet::None(); } 6621 6622 bool congruentTo(const MDefinition* ins) const override { 6623 return congruentIfOperandsEqual(ins); 6624 } 6625 }; 6626 6627 // Unconditionally throw a known error number. 6628 class MThrowMsg : public MNullaryInstruction { 6629 const ThrowMsgKind throwMsgKind_; 6630 6631 explicit MThrowMsg(ThrowMsgKind throwMsgKind) 6632 : MNullaryInstruction(classOpcode), throwMsgKind_(throwMsgKind) { 6633 setGuard(); 6634 setResultType(MIRType::None); 6635 } 6636 6637 public: 6638 INSTRUCTION_HEADER(ThrowMsg) 6639 TRIVIAL_NEW_WRAPPERS 6640 6641 ThrowMsgKind throwMsgKind() const { return throwMsgKind_; } 6642 6643 AliasSet getAliasSet() const override { 6644 return AliasSet::Store(AliasSet::ExceptionState); 6645 } 6646 }; 6647 6648 class MGetFirstDollarIndex : public MUnaryInstruction, 6649 public StringPolicy<0>::Data { 6650 explicit MGetFirstDollarIndex(MDefinition* str) 6651 : MUnaryInstruction(classOpcode, str) { 6652 setResultType(MIRType::Int32); 6653 6654 // Codegen assumes string length > 0. Don't allow LICM to move this 6655 // before the .length > 1 check in RegExpReplace in RegExp.js. 6656 MOZ_ASSERT(!isMovable()); 6657 } 6658 6659 public: 6660 INSTRUCTION_HEADER(GetFirstDollarIndex) 6661 TRIVIAL_NEW_WRAPPERS 6662 NAMED_OPERANDS((0, str)) 6663 6664 AliasSet getAliasSet() const override { return AliasSet::None(); } 6665 6666 MDefinition* foldsTo(TempAllocator& alloc) override; 6667 }; 6668 6669 class MStringReplace : public MTernaryInstruction, 6670 public MixPolicy<StringPolicy<0>, StringPolicy<1>, 6671 StringPolicy<2>>::Data { 6672 private: 6673 bool isFlatReplacement_; 6674 6675 MStringReplace(MDefinition* string, MDefinition* pattern, 6676 MDefinition* replacement) 6677 : MTernaryInstruction(classOpcode, string, pattern, replacement), 6678 isFlatReplacement_(false) { 6679 setMovable(); 6680 setResultType(MIRType::String); 6681 } 6682 6683 public: 6684 INSTRUCTION_HEADER(StringReplace) 6685 TRIVIAL_NEW_WRAPPERS 6686 NAMED_OPERANDS((0, string), (1, pattern), (2, replacement)) 6687 6688 void setFlatReplacement() { 6689 MOZ_ASSERT(!isFlatReplacement_); 6690 isFlatReplacement_ = true; 6691 } 6692 6693 bool isFlatReplacement() const { return isFlatReplacement_; } 6694 6695 bool congruentTo(const MDefinition* ins) const override { 6696 if (!ins->isStringReplace()) { 6697 return false; 6698 } 6699 if (isFlatReplacement_ != ins->toStringReplace()->isFlatReplacement()) { 6700 return false; 6701 } 6702 return congruentIfOperandsEqual(ins); 6703 } 6704 6705 AliasSet getAliasSet() const override { return AliasSet::None(); } 6706 6707 [[nodiscard]] bool writeRecoverData( 6708 CompactBufferWriter& writer) const override; 6709 bool canRecoverOnBailout() const override { 6710 if (isFlatReplacement_) { 6711 MOZ_ASSERT(!pattern()->isRegExp()); 6712 return true; 6713 } 6714 return false; 6715 } 6716 6717 bool possiblyCalls() const override { return true; } 6718 }; 6719 6720 class MLambda : public MBinaryInstruction, public SingleObjectPolicy::Data { 6721 MLambda(MDefinition* envChain, MConstant* cst, gc::Heap initialHeap) 6722 : MBinaryInstruction(classOpcode, envChain, cst), 6723 initialHeap_(initialHeap) { 6724 setResultType(MIRType::Object); 6725 } 6726 6727 // Heap where the lambda should be allocated. 6728 gc::Heap initialHeap_; 6729 6730 public: 6731 INSTRUCTION_HEADER(Lambda) 6732 TRIVIAL_NEW_WRAPPERS 6733 NAMED_OPERANDS((0, environmentChain)) 6734 6735 MConstant* functionOperand() const { return getOperand(1)->toConstant(); } 6736 JSFunction* templateFunction() const { 6737 return &functionOperand()->toObject().as<JSFunction>(); 6738 } 6739 6740 gc::Heap initialHeap() const { return initialHeap_; } 6741 6742 [[nodiscard]] bool writeRecoverData( 6743 CompactBufferWriter& writer) const override; 6744 bool canRecoverOnBailout() const override { return true; } 6745 }; 6746 6747 class MFunctionWithProto : public MTernaryInstruction, 6748 public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>, 6749 ObjectPolicy<2>>::Data { 6750 CompilerFunction fun_; 6751 6752 MFunctionWithProto(MDefinition* envChain, MDefinition* prototype, 6753 MConstant* cst) 6754 : MTernaryInstruction(classOpcode, envChain, prototype, cst), 6755 fun_(&cst->toObject().as<JSFunction>()) { 6756 setResultType(MIRType::Object); 6757 } 6758 6759 public: 6760 INSTRUCTION_HEADER(FunctionWithProto) 6761 TRIVIAL_NEW_WRAPPERS 6762 NAMED_OPERANDS((0, environmentChain), (1, prototype)) 6763 6764 MConstant* functionOperand() const { return getOperand(2)->toConstant(); } 6765 JSFunction* function() const { return fun_; } 6766 [[nodiscard]] bool writeRecoverData( 6767 CompactBufferWriter& writer) const override; 6768 bool canRecoverOnBailout() const override { return true; } 6769 6770 bool possiblyCalls() const override { return true; } 6771 }; 6772 6773 class MGetNextEntryForIterator 6774 : public MBinaryInstruction, 6775 public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>>::Data { 6776 public: 6777 enum Mode { Map, Set }; 6778 6779 private: 6780 Mode mode_; 6781 6782 explicit MGetNextEntryForIterator(MDefinition* iter, MDefinition* result, 6783 Mode mode) 6784 : MBinaryInstruction(classOpcode, iter, result), mode_(mode) { 6785 setResultType(MIRType::Boolean); 6786 } 6787 6788 public: 6789 INSTRUCTION_HEADER(GetNextEntryForIterator) 6790 TRIVIAL_NEW_WRAPPERS 6791 NAMED_OPERANDS((0, iter), (1, result)) 6792 6793 Mode mode() const { return mode_; } 6794 }; 6795 6796 // Convert a Double into an IntPtr value for accessing a TypedArray or DataView 6797 // element. If the input is non-finite, not an integer, negative, or outside the 6798 // IntPtr range, either bails out or produces a value which is known to trigger 6799 // an out-of-bounds access (this depends on the supportOOB flag). 6800 class MGuardNumberToIntPtrIndex : public MUnaryInstruction, 6801 public DoublePolicy<0>::Data { 6802 // If true, produce an out-of-bounds index for non-IntPtr doubles instead of 6803 // bailing out. 6804 const bool supportOOB_; 6805 6806 MGuardNumberToIntPtrIndex(MDefinition* def, bool supportOOB) 6807 : MUnaryInstruction(classOpcode, def), supportOOB_(supportOOB) { 6808 MOZ_ASSERT(IsNumberType(def->type())); 6809 setResultType(MIRType::IntPtr); 6810 setMovable(); 6811 if (!supportOOB) { 6812 setGuard(); 6813 } 6814 } 6815 6816 public: 6817 INSTRUCTION_HEADER(GuardNumberToIntPtrIndex) 6818 TRIVIAL_NEW_WRAPPERS 6819 6820 bool supportOOB() const { return supportOOB_; } 6821 6822 MDefinition* foldsTo(TempAllocator& alloc) override; 6823 6824 bool congruentTo(const MDefinition* ins) const override { 6825 if (!ins->isGuardNumberToIntPtrIndex()) { 6826 return false; 6827 } 6828 if (ins->toGuardNumberToIntPtrIndex()->supportOOB() != supportOOB()) { 6829 return false; 6830 } 6831 return congruentIfOperandsEqual(ins); 6832 } 6833 6834 AliasSet getAliasSet() const override { return AliasSet::None(); } 6835 6836 ALLOW_CLONE(MGuardNumberToIntPtrIndex) 6837 }; 6838 6839 // Perform !-operation 6840 class MNot : public MUnaryInstruction, public TestPolicy::Data { 6841 bool operandIsNeverNaN_; 6842 TypeDataList observedTypes_; 6843 6844 explicit MNot(MDefinition* input) 6845 : MUnaryInstruction(classOpcode, input), operandIsNeverNaN_(false) { 6846 setResultType(MIRType::Boolean); 6847 setMovable(); 6848 } 6849 6850 public: 6851 static MNot* NewInt32(TempAllocator& alloc, MDefinition* input) { 6852 MOZ_ASSERT(input->type() == MIRType::Int32 || 6853 input->type() == MIRType::Int64); 6854 auto* ins = new (alloc) MNot(input); 6855 ins->setResultType(MIRType::Int32); 6856 return ins; 6857 } 6858 6859 INSTRUCTION_HEADER(Not) 6860 TRIVIAL_NEW_WRAPPERS 6861 6862 void setObservedTypes(const TypeDataList& observed) { 6863 observedTypes_ = observed; 6864 } 6865 const TypeDataList& observedTypes() const { return observedTypes_; } 6866 6867 MDefinition* foldsTo(TempAllocator& alloc) override; 6868 6869 bool operandIsNeverNaN() const { return operandIsNeverNaN_; } 6870 6871 virtual AliasSet getAliasSet() const override { return AliasSet::None(); } 6872 void collectRangeInfoPreTrunc() override; 6873 6874 void trySpecializeFloat32(TempAllocator& alloc) override; 6875 bool isFloat32Commutative() const override { return true; } 6876 #ifdef DEBUG 6877 bool isConsistentFloat32Use(MUse* use) const override { return true; } 6878 #endif 6879 bool congruentTo(const MDefinition* ins) const override { 6880 return congruentIfOperandsEqual(ins); 6881 } 6882 [[nodiscard]] bool writeRecoverData( 6883 CompactBufferWriter& writer) const override; 6884 bool canRecoverOnBailout() const override { 6885 return type() == MIRType::Boolean; 6886 } 6887 6888 ALLOW_CLONE(MNot) 6889 }; 6890 6891 // Bailout if index + minimum < 0 or index + maximum >= length. The length used 6892 // in a bounds check must not be negative, or the wrong result may be computed 6893 // (unsigned comparisons may be used). 6894 class MBoundsCheck 6895 : public MBinaryInstruction, 6896 public MixPolicy<Int32OrIntPtrPolicy<0>, Int32OrIntPtrPolicy<1>>::Data { 6897 // Range over which to perform the bounds check, may be modified by GVN. 6898 int32_t minimum_; 6899 int32_t maximum_; 6900 bool fallible_; 6901 6902 MBoundsCheck(MDefinition* index, MDefinition* length) 6903 : MBinaryInstruction(classOpcode, index, length), 6904 minimum_(0), 6905 maximum_(0), 6906 fallible_(true) { 6907 setGuard(); 6908 setMovable(); 6909 MOZ_ASSERT(index->type() == MIRType::Int32 || 6910 index->type() == MIRType::IntPtr); 6911 MOZ_ASSERT(index->type() == length->type()); 6912 6913 // Returns the checked index. 6914 setResultType(index->type()); 6915 } 6916 6917 public: 6918 INSTRUCTION_HEADER(BoundsCheck) 6919 TRIVIAL_NEW_WRAPPERS 6920 NAMED_OPERANDS((0, index), (1, length)) 6921 6922 int32_t minimum() const { return minimum_; } 6923 void setMinimum(int32_t n) { 6924 MOZ_ASSERT(fallible_); 6925 minimum_ = n; 6926 } 6927 int32_t maximum() const { return maximum_; } 6928 void setMaximum(int32_t n) { 6929 MOZ_ASSERT(fallible_); 6930 maximum_ = n; 6931 } 6932 MDefinition* foldsTo(TempAllocator& alloc) override; 6933 bool congruentTo(const MDefinition* ins) const override { 6934 if (!ins->isBoundsCheck()) { 6935 return false; 6936 } 6937 const MBoundsCheck* other = ins->toBoundsCheck(); 6938 if (minimum() != other->minimum() || maximum() != other->maximum()) { 6939 return false; 6940 } 6941 if (fallible() != other->fallible()) { 6942 return false; 6943 } 6944 return congruentIfOperandsEqual(other); 6945 } 6946 virtual AliasSet getAliasSet() const override { return AliasSet::None(); } 6947 void computeRange(TempAllocator& alloc) override; 6948 bool fallible() const { return fallible_; } 6949 void collectRangeInfoPreTrunc() override; 6950 6951 ALLOW_CLONE(MBoundsCheck) 6952 }; 6953 6954 // Bailout if index < minimum. 6955 class MBoundsCheckLower : public MUnaryInstruction, 6956 public UnboxedInt32Policy<0>::Data { 6957 int32_t minimum_; 6958 bool fallible_; 6959 6960 explicit MBoundsCheckLower(MDefinition* index) 6961 : MUnaryInstruction(classOpcode, index), minimum_(0), fallible_(true) { 6962 setGuard(); 6963 setMovable(); 6964 MOZ_ASSERT(index->type() == MIRType::Int32); 6965 } 6966 6967 public: 6968 INSTRUCTION_HEADER(BoundsCheckLower) 6969 TRIVIAL_NEW_WRAPPERS 6970 NAMED_OPERANDS((0, index)) 6971 6972 int32_t minimum() const { return minimum_; } 6973 void setMinimum(int32_t n) { minimum_ = n; } 6974 AliasSet getAliasSet() const override { return AliasSet::None(); } 6975 bool fallible() const { return fallible_; } 6976 void collectRangeInfoPreTrunc() override; 6977 }; 6978 6979 class MSpectreMaskIndex 6980 : public MBinaryInstruction, 6981 public MixPolicy<Int32OrIntPtrPolicy<0>, Int32OrIntPtrPolicy<1>>::Data { 6982 MSpectreMaskIndex(MDefinition* index, MDefinition* length) 6983 : MBinaryInstruction(classOpcode, index, length) { 6984 // Note: this instruction does not need setGuard(): if there are no uses 6985 // it's fine for DCE to eliminate this instruction. 6986 setMovable(); 6987 MOZ_ASSERT(index->type() == MIRType::Int32 || 6988 index->type() == MIRType::IntPtr); 6989 MOZ_ASSERT(index->type() == length->type()); 6990 6991 // Returns the masked index. 6992 setResultType(index->type()); 6993 } 6994 6995 public: 6996 INSTRUCTION_HEADER(SpectreMaskIndex) 6997 TRIVIAL_NEW_WRAPPERS 6998 NAMED_OPERANDS((0, index), (1, length)) 6999 7000 bool congruentTo(const MDefinition* ins) const override { 7001 return congruentIfOperandsEqual(ins); 7002 } 7003 virtual AliasSet getAliasSet() const override { return AliasSet::None(); } 7004 void computeRange(TempAllocator& alloc) override; 7005 7006 ALLOW_CLONE(MSpectreMaskIndex) 7007 }; 7008 7009 // Load a value from a dense array's element vector. Bails out if the element is 7010 // a hole. 7011 class MLoadElement : public MBinaryInstruction, public NoTypePolicy::Data { 7012 bool needsHoleCheck_; 7013 7014 MLoadElement(MDefinition* elements, MDefinition* index, bool needsHoleCheck) 7015 : MBinaryInstruction(classOpcode, elements, index), 7016 needsHoleCheck_(needsHoleCheck) { 7017 if (needsHoleCheck) { 7018 // Uses may be optimized away based on this instruction's result 7019 // type. This means it's invalid to DCE this instruction, as we 7020 // have to invalidate when we read a hole. 7021 setGuard(); 7022 } 7023 setResultType(MIRType::Value); 7024 setMovable(); 7025 MOZ_ASSERT(elements->type() == MIRType::Elements); 7026 MOZ_ASSERT(index->type() == MIRType::Int32); 7027 } 7028 7029 public: 7030 INSTRUCTION_HEADER(LoadElement) 7031 TRIVIAL_NEW_WRAPPERS 7032 NAMED_OPERANDS((0, elements), (1, index)) 7033 7034 bool needsHoleCheck() const { return needsHoleCheck_; } 7035 7036 bool congruentTo(const MDefinition* ins) const override { 7037 return congruentIfOperandsEqual(ins); 7038 } 7039 AliasType mightAlias(const MDefinition* store) const override; 7040 MDefinition* foldsTo(TempAllocator& alloc) override; 7041 AliasSet getAliasSet() const override { 7042 return AliasSet::Load(AliasSet::Element); 7043 } 7044 7045 ALLOW_CLONE(MLoadElement) 7046 }; 7047 7048 class MLoadElementAndUnbox : public MBinaryInstruction, 7049 public NoTypePolicy::Data { 7050 MUnbox::Mode mode_; 7051 7052 MLoadElementAndUnbox(MDefinition* elements, MDefinition* index, 7053 MUnbox::Mode mode, MIRType type) 7054 : MBinaryInstruction(classOpcode, elements, index), mode_(mode) { 7055 setResultType(type); 7056 setMovable(); 7057 if (mode_ == MUnbox::Fallible) { 7058 setGuard(); 7059 } 7060 } 7061 7062 public: 7063 INSTRUCTION_HEADER(LoadElementAndUnbox) 7064 TRIVIAL_NEW_WRAPPERS 7065 NAMED_OPERANDS((0, elements), (1, index)) 7066 7067 MUnbox::Mode mode() const { return mode_; } 7068 bool fallible() const { return mode_ != MUnbox::Infallible; } 7069 7070 bool congruentTo(const MDefinition* ins) const override { 7071 if (!ins->isLoadElementAndUnbox() || 7072 mode() != ins->toLoadElementAndUnbox()->mode()) { 7073 return false; 7074 } 7075 return congruentIfOperandsEqual(ins); 7076 } 7077 7078 AliasSet getAliasSet() const override { 7079 return AliasSet::Load(AliasSet::Element); 7080 } 7081 7082 ALLOW_CLONE(MLoadElementAndUnbox); 7083 }; 7084 7085 // Load a value from the elements vector of a native object. If the index is 7086 // out-of-bounds, or the indexed slot has a hole, undefined is returned instead. 7087 class MLoadElementHole : public MTernaryInstruction, public NoTypePolicy::Data { 7088 bool needsNegativeIntCheck_ = true; 7089 7090 MLoadElementHole(MDefinition* elements, MDefinition* index, 7091 MDefinition* initLength) 7092 : MTernaryInstruction(classOpcode, elements, index, initLength) { 7093 setResultType(MIRType::Value); 7094 setMovable(); 7095 7096 // Set the guard flag to make sure we bail when we see a negative 7097 // index. We can clear this flag (and needsNegativeIntCheck_) in 7098 // collectRangeInfoPreTrunc. 7099 setGuard(); 7100 7101 MOZ_ASSERT(elements->type() == MIRType::Elements); 7102 MOZ_ASSERT(index->type() == MIRType::Int32); 7103 MOZ_ASSERT(initLength->type() == MIRType::Int32); 7104 } 7105 7106 public: 7107 INSTRUCTION_HEADER(LoadElementHole) 7108 TRIVIAL_NEW_WRAPPERS 7109 NAMED_OPERANDS((0, elements), (1, index), (2, initLength)) 7110 7111 bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; } 7112 bool congruentTo(const MDefinition* ins) const override { 7113 if (!ins->isLoadElementHole()) { 7114 return false; 7115 } 7116 const MLoadElementHole* other = ins->toLoadElementHole(); 7117 if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) { 7118 return false; 7119 } 7120 return congruentIfOperandsEqual(other); 7121 } 7122 AliasSet getAliasSet() const override { 7123 return AliasSet::Load(AliasSet::Element); 7124 } 7125 void collectRangeInfoPreTrunc() override; 7126 7127 ALLOW_CLONE(MLoadElementHole) 7128 }; 7129 7130 // Store a value to a dense array slots vector. 7131 class MStoreElement : public MTernaryInstruction, 7132 public NoFloatPolicy<2>::Data { 7133 bool needsHoleCheck_; 7134 bool needsBarrier_; 7135 7136 MStoreElement(MDefinition* elements, MDefinition* index, MDefinition* value, 7137 bool needsHoleCheck, bool needsBarrier) 7138 : MTernaryInstruction(classOpcode, elements, index, value) { 7139 needsHoleCheck_ = needsHoleCheck; 7140 needsBarrier_ = needsBarrier; 7141 MOZ_ASSERT(elements->type() == MIRType::Elements); 7142 MOZ_ASSERT(index->type() == MIRType::Int32); 7143 MOZ_ASSERT(value->type() != MIRType::MagicHole); 7144 } 7145 7146 public: 7147 INSTRUCTION_HEADER(StoreElement) 7148 TRIVIAL_NEW_WRAPPERS 7149 NAMED_OPERANDS((0, elements), (1, index), (2, value)) 7150 7151 static MStoreElement* NewUnbarriered(TempAllocator& alloc, 7152 MDefinition* elements, 7153 MDefinition* index, MDefinition* value, 7154 bool needsHoleCheck) { 7155 return new (alloc) 7156 MStoreElement(elements, index, value, needsHoleCheck, false); 7157 } 7158 7159 static MStoreElement* NewBarriered(TempAllocator& alloc, 7160 MDefinition* elements, MDefinition* index, 7161 MDefinition* value, bool needsHoleCheck) { 7162 return new (alloc) 7163 MStoreElement(elements, index, value, needsHoleCheck, true); 7164 } 7165 7166 AliasSet getAliasSet() const override { 7167 return AliasSet::Store(AliasSet::Element); 7168 } 7169 bool needsBarrier() const { return needsBarrier_; } 7170 bool needsHoleCheck() const { return needsHoleCheck_; } 7171 bool fallible() const { return needsHoleCheck(); } 7172 7173 ALLOW_CLONE(MStoreElement) 7174 }; 7175 7176 // Stores MagicValue(JS_ELEMENTS_HOLE) and marks the elements as non-packed. 7177 class MStoreHoleValueElement : public MBinaryInstruction, 7178 public NoTypePolicy::Data { 7179 MStoreHoleValueElement(MDefinition* elements, MDefinition* index) 7180 : MBinaryInstruction(classOpcode, elements, index) { 7181 MOZ_ASSERT(elements->type() == MIRType::Elements); 7182 MOZ_ASSERT(index->type() == MIRType::Int32); 7183 } 7184 7185 public: 7186 INSTRUCTION_HEADER(StoreHoleValueElement) 7187 TRIVIAL_NEW_WRAPPERS 7188 NAMED_OPERANDS((0, elements), (1, index)) 7189 7190 AliasSet getAliasSet() const override { 7191 return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields); 7192 } 7193 7194 ALLOW_CLONE(MStoreHoleValueElement) 7195 }; 7196 7197 // Like MStoreElement, but also supports index == initialized length. The 7198 // downside is that we cannot hoist the elements vector and bounds check, since 7199 // this instruction may update the (initialized) length and reallocate the 7200 // elements vector. 7201 class MStoreElementHole 7202 : public MQuaternaryInstruction, 7203 public MixPolicy<SingleObjectPolicy, NoFloatPolicy<3>>::Data { 7204 MStoreElementHole(MDefinition* object, MDefinition* elements, 7205 MDefinition* index, MDefinition* value) 7206 : MQuaternaryInstruction(classOpcode, object, elements, index, value) { 7207 MOZ_ASSERT(elements->type() == MIRType::Elements); 7208 MOZ_ASSERT(index->type() == MIRType::Int32); 7209 MOZ_ASSERT(value->type() != MIRType::MagicHole); 7210 } 7211 7212 public: 7213 INSTRUCTION_HEADER(StoreElementHole) 7214 TRIVIAL_NEW_WRAPPERS 7215 NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value)) 7216 7217 AliasSet getAliasSet() const override { 7218 // StoreElementHole can update the initialized length, the array length 7219 // or reallocate obj->elements. 7220 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); 7221 } 7222 7223 ALLOW_CLONE(MStoreElementHole) 7224 }; 7225 7226 // Array.prototype.pop or Array.prototype.shift on a dense array. 7227 class MArrayPopShift : public MUnaryInstruction, 7228 public SingleObjectPolicy::Data { 7229 public: 7230 enum Mode { Pop, Shift }; 7231 7232 private: 7233 Mode mode_; 7234 7235 MArrayPopShift(MDefinition* object, Mode mode) 7236 : MUnaryInstruction(classOpcode, object), mode_(mode) { 7237 setResultType(MIRType::Value); 7238 } 7239 7240 public: 7241 INSTRUCTION_HEADER(ArrayPopShift) 7242 TRIVIAL_NEW_WRAPPERS 7243 NAMED_OPERANDS((0, object)) 7244 7245 bool mode() const { return mode_; } 7246 AliasSet getAliasSet() const override { 7247 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); 7248 } 7249 7250 ALLOW_CLONE(MArrayPopShift) 7251 }; 7252 7253 // Load an unboxed scalar value from an array buffer view or other object. 7254 class MLoadUnboxedScalar : public MBinaryInstruction, 7255 public NoTypePolicy::Data { 7256 Scalar::Type storageType_; 7257 MemoryBarrierRequirement requiresBarrier_; 7258 7259 MLoadUnboxedScalar(MDefinition* elements, MDefinition* index, 7260 Scalar::Type storageType, 7261 MemoryBarrierRequirement requiresBarrier = 7262 MemoryBarrierRequirement::NotRequired) 7263 : MBinaryInstruction(classOpcode, elements, index), 7264 storageType_(storageType), 7265 requiresBarrier_(requiresBarrier) { 7266 setResultType(MIRType::Value); 7267 if (requiresBarrier_ == MemoryBarrierRequirement::Required) { 7268 setGuard(); // Not removable or movable 7269 } else { 7270 setMovable(); 7271 } 7272 MOZ_ASSERT(elements->type() == MIRType::Elements); 7273 MOZ_ASSERT(index->type() == MIRType::IntPtr); 7274 MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType); 7275 } 7276 7277 public: 7278 INSTRUCTION_HEADER(LoadUnboxedScalar) 7279 TRIVIAL_NEW_WRAPPERS 7280 NAMED_OPERANDS((0, elements), (1, index)) 7281 7282 Scalar::Type storageType() const { return storageType_; } 7283 bool fallible() const { 7284 // Bailout if the result does not fit in an int32. 7285 return storageType_ == Scalar::Uint32 && type() == MIRType::Int32; 7286 } 7287 auto requiresMemoryBarrier() const { return requiresBarrier_; } 7288 AliasSet getAliasSet() const override { 7289 // When a barrier is needed make the instruction effectful by 7290 // giving it a "store" effect. 7291 if (requiresBarrier_ == MemoryBarrierRequirement::Required) { 7292 return AliasSet::Store(AliasSet::UnboxedElement); 7293 } 7294 return AliasSet::Load(AliasSet::UnboxedElement); 7295 } 7296 7297 bool congruentTo(const MDefinition* ins) const override { 7298 if (requiresBarrier_ == MemoryBarrierRequirement::Required) { 7299 return false; 7300 } 7301 if (!ins->isLoadUnboxedScalar()) { 7302 return false; 7303 } 7304 const MLoadUnboxedScalar* other = ins->toLoadUnboxedScalar(); 7305 if (storageType_ != other->storageType_) { 7306 return false; 7307 } 7308 return congruentIfOperandsEqual(other); 7309 } 7310 7311 #ifdef JS_JITSPEW 7312 void printOpcode(GenericPrinter& out) const override; 7313 #endif 7314 7315 void computeRange(TempAllocator& alloc) override; 7316 7317 bool canProduceFloat32() const override { 7318 return storageType_ == Scalar::Float32 || storageType_ == Scalar::Float16; 7319 } 7320 7321 ALLOW_CLONE(MLoadUnboxedScalar) 7322 }; 7323 7324 // Load an unboxed scalar value from a dataview object. 7325 class MLoadDataViewElement : public MTernaryInstruction, 7326 public NoTypePolicy::Data { 7327 Scalar::Type storageType_; 7328 7329 MLoadDataViewElement(MDefinition* elements, MDefinition* index, 7330 MDefinition* littleEndian, Scalar::Type storageType) 7331 : MTernaryInstruction(classOpcode, elements, index, littleEndian), 7332 storageType_(storageType) { 7333 setResultType(MIRType::Value); 7334 setMovable(); 7335 MOZ_ASSERT(elements->type() == MIRType::Elements); 7336 MOZ_ASSERT(index->type() == MIRType::IntPtr); 7337 MOZ_ASSERT(littleEndian->type() == MIRType::Boolean); 7338 MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType); 7339 MOZ_ASSERT(Scalar::byteSize(storageType) > 1); 7340 } 7341 7342 public: 7343 INSTRUCTION_HEADER(LoadDataViewElement) 7344 TRIVIAL_NEW_WRAPPERS 7345 NAMED_OPERANDS((0, elements), (1, index), (2, littleEndian)) 7346 7347 Scalar::Type storageType() const { return storageType_; } 7348 bool fallible() const { 7349 // Bailout if the result does not fit in an int32. 7350 return storageType_ == Scalar::Uint32 && type() == MIRType::Int32; 7351 } 7352 AliasSet getAliasSet() const override { 7353 return AliasSet::Load(AliasSet::UnboxedElement); 7354 } 7355 7356 bool congruentTo(const MDefinition* ins) const override { 7357 if (!ins->isLoadDataViewElement()) { 7358 return false; 7359 } 7360 const MLoadDataViewElement* other = ins->toLoadDataViewElement(); 7361 if (storageType_ != other->storageType_) { 7362 return false; 7363 } 7364 return congruentIfOperandsEqual(other); 7365 } 7366 7367 #ifdef JS_JITSPEW 7368 void printOpcode(GenericPrinter& out) const override; 7369 #endif 7370 7371 void computeRange(TempAllocator& alloc) override; 7372 7373 bool canProduceFloat32() const override { 7374 return storageType_ == Scalar::Float32 || storageType_ == Scalar::Float16; 7375 } 7376 7377 ALLOW_CLONE(MLoadDataViewElement) 7378 }; 7379 7380 // Load a value from a typed array. Out-of-bounds accesses are handled in-line. 7381 class MLoadTypedArrayElementHole : public MTernaryInstruction, 7382 public NoTypePolicy::Data { 7383 Scalar::Type arrayType_; 7384 bool forceDouble_; 7385 7386 MLoadTypedArrayElementHole(MDefinition* elements, MDefinition* index, 7387 MDefinition* length, Scalar::Type arrayType, 7388 bool forceDouble) 7389 : MTernaryInstruction(classOpcode, elements, index, length), 7390 arrayType_(arrayType), 7391 forceDouble_(forceDouble) { 7392 setResultType(MIRType::Value); 7393 setMovable(); 7394 MOZ_ASSERT(elements->type() == MIRType::Elements); 7395 MOZ_ASSERT(index->type() == MIRType::IntPtr); 7396 MOZ_ASSERT(length->type() == MIRType::IntPtr); 7397 MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType); 7398 } 7399 7400 public: 7401 INSTRUCTION_HEADER(LoadTypedArrayElementHole) 7402 TRIVIAL_NEW_WRAPPERS 7403 NAMED_OPERANDS((0, elements), (1, index), (2, length)) 7404 7405 Scalar::Type arrayType() const { return arrayType_; } 7406 bool forceDouble() const { return forceDouble_; } 7407 bool fallible() const { 7408 return arrayType_ == Scalar::Uint32 && !forceDouble_; 7409 } 7410 bool congruentTo(const MDefinition* ins) const override { 7411 if (!ins->isLoadTypedArrayElementHole()) { 7412 return false; 7413 } 7414 const MLoadTypedArrayElementHole* other = 7415 ins->toLoadTypedArrayElementHole(); 7416 if (arrayType() != other->arrayType()) { 7417 return false; 7418 } 7419 if (forceDouble() != other->forceDouble()) { 7420 return false; 7421 } 7422 return congruentIfOperandsEqual(other); 7423 } 7424 AliasSet getAliasSet() const override { 7425 return AliasSet::Load(AliasSet::UnboxedElement); 7426 } 7427 bool canProduceFloat32() const override { 7428 return arrayType_ == Scalar::Float32 || arrayType_ == Scalar::Float16; 7429 } 7430 7431 ALLOW_CLONE(MLoadTypedArrayElementHole) 7432 }; 7433 7434 // Base class for MIR ops that write unboxed scalar values. 7435 class StoreUnboxedScalarBase { 7436 Scalar::Type writeType_; 7437 7438 protected: 7439 explicit StoreUnboxedScalarBase(Scalar::Type writeType) 7440 : writeType_(writeType) { 7441 MOZ_ASSERT(isIntegerWrite() || isFloatWrite() || isBigIntWrite()); 7442 } 7443 7444 public: 7445 Scalar::Type writeType() const { return writeType_; } 7446 bool isByteWrite() const { 7447 return writeType_ == Scalar::Int8 || writeType_ == Scalar::Uint8 || 7448 writeType_ == Scalar::Uint8Clamped; 7449 } 7450 bool isIntegerWrite() const { 7451 return isByteWrite() || writeType_ == Scalar::Int16 || 7452 writeType_ == Scalar::Uint16 || writeType_ == Scalar::Int32 || 7453 writeType_ == Scalar::Uint32; 7454 } 7455 bool isFloatWrite() const { 7456 return writeType_ == Scalar::Float16 || writeType_ == Scalar::Float32 || 7457 writeType_ == Scalar::Float64; 7458 } 7459 bool isBigIntWrite() const { return Scalar::isBigIntType(writeType_); } 7460 }; 7461 7462 // Store an unboxed scalar value to an array buffer view or other object. 7463 class MStoreUnboxedScalar : public MTernaryInstruction, 7464 public StoreUnboxedScalarBase, 7465 public StoreUnboxedScalarPolicy::Data { 7466 MemoryBarrierRequirement requiresBarrier_; 7467 7468 MStoreUnboxedScalar(MDefinition* elements, MDefinition* index, 7469 MDefinition* value, Scalar::Type storageType, 7470 MemoryBarrierRequirement requiresBarrier = 7471 MemoryBarrierRequirement::NotRequired) 7472 : MTernaryInstruction(classOpcode, elements, index, value), 7473 StoreUnboxedScalarBase(storageType), 7474 requiresBarrier_(requiresBarrier) { 7475 if (requiresBarrier_ == MemoryBarrierRequirement::Required) { 7476 setGuard(); // Not removable or movable 7477 } 7478 MOZ_ASSERT(elements->type() == MIRType::Elements); 7479 MOZ_ASSERT(index->type() == MIRType::IntPtr); 7480 MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType); 7481 } 7482 7483 public: 7484 INSTRUCTION_HEADER(StoreUnboxedScalar) 7485 TRIVIAL_NEW_WRAPPERS 7486 NAMED_OPERANDS((0, elements), (1, index), (2, value)) 7487 7488 AliasSet getAliasSet() const override { 7489 return AliasSet::Store(AliasSet::UnboxedElement); 7490 } 7491 auto requiresMemoryBarrier() const { return requiresBarrier_; } 7492 TruncateKind operandTruncateKind(size_t index) const override; 7493 7494 bool canConsumeFloat32(MUse* use) const override { 7495 return use == getUseFor(2) && writeType() == Scalar::Float32; 7496 } 7497 7498 #ifdef DEBUG 7499 // Float16 inputs are typed as float32, but this instruction can NOT consume 7500 // float32 when its write-type is float16. 7501 bool isConsistentFloat32Use(MUse* use) const override { 7502 return use == getUseFor(2) && 7503 (writeType() == Scalar::Float32 || writeType() == Scalar::Float16); 7504 } 7505 #endif 7506 7507 ALLOW_CLONE(MStoreUnboxedScalar) 7508 }; 7509 7510 // Store an unboxed scalar value to a dataview object. 7511 class MStoreDataViewElement : public MQuaternaryInstruction, 7512 public StoreUnboxedScalarBase, 7513 public StoreDataViewElementPolicy::Data { 7514 MStoreDataViewElement(MDefinition* elements, MDefinition* index, 7515 MDefinition* value, MDefinition* littleEndian, 7516 Scalar::Type storageType) 7517 : MQuaternaryInstruction(classOpcode, elements, index, value, 7518 littleEndian), 7519 StoreUnboxedScalarBase(storageType) { 7520 MOZ_ASSERT(elements->type() == MIRType::Elements); 7521 MOZ_ASSERT(index->type() == MIRType::IntPtr); 7522 MOZ_ASSERT(storageType >= 0 && storageType < Scalar::MaxTypedArrayViewType); 7523 MOZ_ASSERT(Scalar::byteSize(storageType) > 1); 7524 } 7525 7526 public: 7527 INSTRUCTION_HEADER(StoreDataViewElement) 7528 TRIVIAL_NEW_WRAPPERS 7529 NAMED_OPERANDS((0, elements), (1, index), (2, value), (3, littleEndian)) 7530 7531 AliasSet getAliasSet() const override { 7532 return AliasSet::Store(AliasSet::UnboxedElement); 7533 } 7534 TruncateKind operandTruncateKind(size_t index) const override; 7535 7536 bool canConsumeFloat32(MUse* use) const override { 7537 return use == getUseFor(2) && writeType() == Scalar::Float32; 7538 } 7539 7540 #ifdef DEBUG 7541 // Float16 inputs are typed as float32, but this instruction can NOT consume 7542 // float32 when its write-type is float16. 7543 bool isConsistentFloat32Use(MUse* use) const override { 7544 return use == getUseFor(2) && 7545 (writeType() == Scalar::Float32 || writeType() == Scalar::Float16); 7546 } 7547 #endif 7548 7549 ALLOW_CLONE(MStoreDataViewElement) 7550 }; 7551 7552 class MStoreTypedArrayElementHole : public MQuaternaryInstruction, 7553 public StoreUnboxedScalarBase, 7554 public StoreTypedArrayHolePolicy::Data { 7555 MStoreTypedArrayElementHole(MDefinition* elements, MDefinition* length, 7556 MDefinition* index, MDefinition* value, 7557 Scalar::Type arrayType) 7558 : MQuaternaryInstruction(classOpcode, elements, length, index, value), 7559 StoreUnboxedScalarBase(arrayType) { 7560 MOZ_ASSERT(elements->type() == MIRType::Elements); 7561 MOZ_ASSERT(length->type() == MIRType::IntPtr); 7562 MOZ_ASSERT(index->type() == MIRType::IntPtr); 7563 MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType); 7564 } 7565 7566 public: 7567 INSTRUCTION_HEADER(StoreTypedArrayElementHole) 7568 TRIVIAL_NEW_WRAPPERS 7569 NAMED_OPERANDS((0, elements), (1, length), (2, index), (3, value)) 7570 7571 Scalar::Type arrayType() const { return writeType(); } 7572 AliasSet getAliasSet() const override { 7573 return AliasSet::Store(AliasSet::UnboxedElement); 7574 } 7575 TruncateKind operandTruncateKind(size_t index) const override; 7576 7577 bool canConsumeFloat32(MUse* use) const override { 7578 return use == getUseFor(3) && arrayType() == Scalar::Float32; 7579 } 7580 7581 #ifdef DEBUG 7582 // Float16 inputs are typed as float32, but this instruction can NOT consume 7583 // float32 when its array-type is float16. 7584 bool isConsistentFloat32Use(MUse* use) const override { 7585 return use == getUseFor(3) && 7586 (arrayType() == Scalar::Float32 || arrayType() == Scalar::Float16); 7587 } 7588 #endif 7589 7590 ALLOW_CLONE(MStoreTypedArrayElementHole) 7591 }; 7592 7593 class MTypedArrayFill : public MQuaternaryInstruction, 7594 public StoreUnboxedScalarBase, 7595 public TypedArrayFillPolicy::Data { 7596 MTypedArrayFill(MDefinition* object, MDefinition* value, MDefinition* start, 7597 MDefinition* end, Scalar::Type arrayType) 7598 : MQuaternaryInstruction(classOpcode, object, value, start, end), 7599 StoreUnboxedScalarBase(arrayType) { 7600 MOZ_ASSERT(object->type() == MIRType::Object); 7601 MOZ_ASSERT(start->type() == MIRType::IntPtr); 7602 MOZ_ASSERT(end->type() == MIRType::IntPtr); 7603 MOZ_ASSERT(arrayType >= 0 && arrayType < Scalar::MaxTypedArrayViewType); 7604 } 7605 7606 public: 7607 INSTRUCTION_HEADER(TypedArrayFill) 7608 TRIVIAL_NEW_WRAPPERS 7609 NAMED_OPERANDS((0, object), (1, value), (2, start), (3, end)) 7610 7611 Scalar::Type elementType() const { return writeType(); } 7612 AliasSet getAliasSet() const override { 7613 // Loads typed array length and elements. 7614 constexpr auto load = 7615 AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset | 7616 AliasSet::ObjectFields | AliasSet::UnboxedElement); 7617 7618 // Stores into typed array elements. 7619 constexpr auto store = AliasSet::Store(AliasSet::UnboxedElement); 7620 7621 return load | store; 7622 } 7623 7624 TruncateKind operandTruncateKind(size_t index) const override; 7625 7626 bool canConsumeFloat32(MUse* use) const override { 7627 return use == getUseFor(1) && elementType() == Scalar::Float32; 7628 } 7629 7630 #ifdef DEBUG 7631 // Float16 inputs are typed as float32, but this instruction can NOT consume 7632 // float32 when its array-type is float16. 7633 bool isConsistentFloat32Use(MUse* use) const override { 7634 return use == getUseFor(1) && (elementType() == Scalar::Float32 || 7635 elementType() == Scalar::Float16); 7636 } 7637 #endif 7638 7639 bool possiblyCalls() const override { return true; } 7640 7641 ALLOW_CLONE(MTypedArrayFill) 7642 }; 7643 7644 // Inlined TypedArray.prototype.subarray 7645 class MTypedArraySubarray : public MTernaryInstruction, 7646 public MixPolicy<ObjectPolicy<0>, IntPtrPolicy<1>, 7647 IntPtrPolicy<2>>::Data { 7648 CompilerGCPointer<JSObject*> templateObject_; 7649 gc::Heap initialHeap_; 7650 bool scalarReplaced_ = false; 7651 7652 MTypedArraySubarray(MDefinition* object, MDefinition* start, 7653 MDefinition* length, JSObject* templateObject, 7654 gc::Heap initialHeap) 7655 : MTernaryInstruction(classOpcode, object, start, length), 7656 templateObject_(templateObject), 7657 initialHeap_(initialHeap) { 7658 setResultType(MIRType::Object); 7659 } 7660 7661 public: 7662 INSTRUCTION_HEADER(TypedArraySubarray) 7663 TRIVIAL_NEW_WRAPPERS 7664 NAMED_OPERANDS((0, object), (1, start), (2, length)) 7665 7666 JSObject* templateObject() const { return templateObject_; } 7667 gc::Heap initialHeap() const { return initialHeap_; } 7668 7669 bool isScalarReplaced() const { return scalarReplaced_; } 7670 void setScalarReplaced() { scalarReplaced_ = true; } 7671 7672 AliasSet getAliasSet() const override { 7673 if (scalarReplaced_) { 7674 return AliasSet::None(); 7675 } 7676 return AliasSet::Store(AliasSet::ObjectFields); 7677 } 7678 7679 bool possiblyCalls() const override { return true; } 7680 7681 [[nodiscard]] bool writeRecoverData( 7682 CompactBufferWriter& writer) const override; 7683 bool canRecoverOnBailout() const override { return scalarReplaced_; } 7684 }; 7685 7686 // Compute a 3-component "effective address": 7687 // base + index * scale + displacement 7688 class MEffectiveAddress3 : public MBinaryInstruction, 7689 public NoTypePolicy::Data { 7690 MEffectiveAddress3(MDefinition* base, MDefinition* index, Scale scale, 7691 int32_t displacement) 7692 : MBinaryInstruction(classOpcode, base, index), 7693 scale_(scale), 7694 displacement_(displacement) { 7695 MOZ_ASSERT(base->type() == MIRType::Int32); 7696 MOZ_ASSERT(index->type() == MIRType::Int32); 7697 setMovable(); 7698 setResultType(MIRType::Int32); 7699 } 7700 7701 Scale scale_; 7702 int32_t displacement_; 7703 7704 public: 7705 INSTRUCTION_HEADER(EffectiveAddress3) 7706 TRIVIAL_NEW_WRAPPERS 7707 7708 MDefinition* base() const { return lhs(); } 7709 MDefinition* index() const { return rhs(); } 7710 Scale scale() const { return scale_; } 7711 int32_t displacement() const { return displacement_; } 7712 7713 AliasSet getAliasSet() const override { return AliasSet::None(); } 7714 7715 #ifdef JS_JITSPEW 7716 void getExtras(ExtrasCollector* extras) const override { 7717 char buf[64]; 7718 SprintfLiteral(buf, "(disp=%d, scale=%s)", int(displacement_), 7719 StringFromScale(scale_)); 7720 extras->add(buf); 7721 } 7722 #endif 7723 7724 ALLOW_CLONE(MEffectiveAddress3) 7725 }; 7726 7727 // Compute a 2-component "effective address": 7728 // index * scale + displacement 7729 class MEffectiveAddress2 : public MUnaryInstruction, public NoTypePolicy::Data { 7730 MEffectiveAddress2(MDefinition* index, Scale scale, int32_t displacement) 7731 : MUnaryInstruction(classOpcode, index), 7732 scale_(scale), 7733 displacement_(displacement) { 7734 MOZ_ASSERT(index->type() == MIRType::Int32); 7735 setMovable(); 7736 setResultType(MIRType::Int32); 7737 } 7738 7739 Scale scale_; 7740 int32_t displacement_; 7741 7742 public: 7743 INSTRUCTION_HEADER(EffectiveAddress2) 7744 TRIVIAL_NEW_WRAPPERS 7745 7746 MDefinition* index() const { return input(); } 7747 Scale scale() const { return scale_; } 7748 int32_t displacement() const { return displacement_; } 7749 7750 AliasSet getAliasSet() const override { return AliasSet::None(); } 7751 7752 #ifdef JS_JITSPEW 7753 void getExtras(ExtrasCollector* extras) const override { 7754 char buf[64]; 7755 SprintfLiteral(buf, "(disp=%d, scale=%s)", int(displacement_), 7756 StringFromScale(scale_)); 7757 extras->add(buf); 7758 } 7759 #endif 7760 7761 ALLOW_CLONE(MEffectiveAddress2) 7762 }; 7763 7764 // Clamp input to range [0, 255] for Uint8ClampedArray. 7765 class MClampToUint8 : public MUnaryInstruction, public ClampPolicy::Data { 7766 explicit MClampToUint8(MDefinition* input) 7767 : MUnaryInstruction(classOpcode, input) { 7768 setResultType(MIRType::Int32); 7769 setMovable(); 7770 } 7771 7772 public: 7773 INSTRUCTION_HEADER(ClampToUint8) 7774 TRIVIAL_NEW_WRAPPERS 7775 7776 MDefinition* foldsTo(TempAllocator& alloc) override; 7777 7778 bool congruentTo(const MDefinition* ins) const override { 7779 return congruentIfOperandsEqual(ins); 7780 } 7781 AliasSet getAliasSet() const override { return AliasSet::None(); } 7782 void computeRange(TempAllocator& alloc) override; 7783 7784 ALLOW_CLONE(MClampToUint8) 7785 }; 7786 7787 class MLoadFixedSlot : public MUnaryInstruction, 7788 public SingleObjectPolicy::Data { 7789 size_t slot_; 7790 bool usedAsPropertyKey_ = false; 7791 7792 protected: 7793 MLoadFixedSlot(MDefinition* obj, size_t slot) 7794 : MUnaryInstruction(classOpcode, obj), slot_(slot) { 7795 setResultType(MIRType::Value); 7796 setMovable(); 7797 } 7798 7799 public: 7800 INSTRUCTION_HEADER(LoadFixedSlot) 7801 TRIVIAL_NEW_WRAPPERS 7802 NAMED_OPERANDS((0, object)) 7803 7804 size_t slot() const { return slot_; } 7805 7806 HashNumber valueHash() const override; 7807 7808 bool congruentTo(const MDefinition* ins) const override { 7809 if (!ins->isLoadFixedSlot()) { 7810 return false; 7811 } 7812 if (slot() != ins->toLoadFixedSlot()->slot()) { 7813 return false; 7814 } 7815 return congruentIfOperandsEqual(ins); 7816 } 7817 7818 MDefinition* foldsTo(TempAllocator& alloc) override; 7819 7820 AliasSet getAliasSet() const override { 7821 return AliasSet::Load(AliasSet::FixedSlot); 7822 } 7823 7824 AliasType mightAlias(const MDefinition* store) const override; 7825 7826 #ifdef JS_JITSPEW 7827 void printOpcode(GenericPrinter& out) const override; 7828 #endif 7829 7830 void setUsedAsPropertyKey() { usedAsPropertyKey_ = true; } 7831 bool usedAsPropertyKey() const { return usedAsPropertyKey_; } 7832 7833 ALLOW_CLONE(MLoadFixedSlot) 7834 }; 7835 7836 class MLoadFixedSlotAndUnbox : public MUnaryInstruction, 7837 public SingleObjectPolicy::Data { 7838 size_t slot_; 7839 MUnbox::Mode mode_; 7840 bool usedAsPropertyKey_; 7841 7842 MLoadFixedSlotAndUnbox(MDefinition* obj, size_t slot, MUnbox::Mode mode, 7843 MIRType type, bool usedAsPropertyKey = false) 7844 : MUnaryInstruction(classOpcode, obj), 7845 slot_(slot), 7846 mode_(mode), 7847 usedAsPropertyKey_(usedAsPropertyKey) { 7848 setResultType(type); 7849 setMovable(); 7850 if (mode_ == MUnbox::Fallible) { 7851 setGuard(); 7852 } 7853 } 7854 7855 public: 7856 INSTRUCTION_HEADER(LoadFixedSlotAndUnbox) 7857 TRIVIAL_NEW_WRAPPERS 7858 NAMED_OPERANDS((0, object)) 7859 7860 size_t slot() const { return slot_; } 7861 MUnbox::Mode mode() const { return mode_; } 7862 bool fallible() const { return mode_ != MUnbox::Infallible; } 7863 bool congruentTo(const MDefinition* ins) const override { 7864 if (!ins->isLoadFixedSlotAndUnbox() || 7865 slot() != ins->toLoadFixedSlotAndUnbox()->slot() || 7866 mode() != ins->toLoadFixedSlotAndUnbox()->mode()) { 7867 return false; 7868 } 7869 return congruentIfOperandsEqual(ins); 7870 } 7871 7872 MDefinition* foldsTo(TempAllocator& alloc) override; 7873 7874 AliasSet getAliasSet() const override { 7875 return AliasSet::Load(AliasSet::FixedSlot); 7876 } 7877 7878 AliasType mightAlias(const MDefinition* store) const override; 7879 7880 #ifdef JS_JITSPEW 7881 void printOpcode(GenericPrinter& out) const override; 7882 #endif 7883 7884 bool usedAsPropertyKey() const { return usedAsPropertyKey_; } 7885 7886 ALLOW_CLONE(MLoadFixedSlotAndUnbox); 7887 }; 7888 7889 class MLoadDynamicSlotAndUnbox : public MUnaryInstruction, 7890 public NoTypePolicy::Data { 7891 size_t slot_; 7892 MUnbox::Mode mode_; 7893 bool usedAsPropertyKey_ = false; 7894 7895 MLoadDynamicSlotAndUnbox(MDefinition* slots, size_t slot, MUnbox::Mode mode, 7896 MIRType type, bool usedAsPropertyKey = false) 7897 : MUnaryInstruction(classOpcode, slots), 7898 slot_(slot), 7899 mode_(mode), 7900 usedAsPropertyKey_(usedAsPropertyKey) { 7901 setResultType(type); 7902 setMovable(); 7903 if (mode_ == MUnbox::Fallible) { 7904 setGuard(); 7905 } 7906 } 7907 7908 public: 7909 INSTRUCTION_HEADER(LoadDynamicSlotAndUnbox) 7910 TRIVIAL_NEW_WRAPPERS 7911 NAMED_OPERANDS((0, slots)) 7912 7913 size_t slot() const { return slot_; } 7914 MUnbox::Mode mode() const { return mode_; } 7915 bool fallible() const { return mode_ != MUnbox::Infallible; } 7916 7917 bool congruentTo(const MDefinition* ins) const override { 7918 if (!ins->isLoadDynamicSlotAndUnbox() || 7919 slot() != ins->toLoadDynamicSlotAndUnbox()->slot() || 7920 mode() != ins->toLoadDynamicSlotAndUnbox()->mode()) { 7921 return false; 7922 } 7923 return congruentIfOperandsEqual(ins); 7924 } 7925 7926 AliasSet getAliasSet() const override { 7927 return AliasSet::Load(AliasSet::DynamicSlot); 7928 } 7929 7930 #ifdef JS_JITSPEW 7931 void printOpcode(GenericPrinter& out) const override; 7932 #endif 7933 7934 bool usedAsPropertyKey() const { return usedAsPropertyKey_; } 7935 7936 ALLOW_CLONE(MLoadDynamicSlotAndUnbox); 7937 }; 7938 7939 class MStoreFixedSlot 7940 : public MBinaryInstruction, 7941 public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1>>::Data { 7942 bool needsBarrier_; 7943 size_t slot_; 7944 7945 MStoreFixedSlot(MDefinition* obj, MDefinition* rval, size_t slot, 7946 bool barrier) 7947 : MBinaryInstruction(classOpcode, obj, rval), 7948 needsBarrier_(barrier), 7949 slot_(slot) {} 7950 7951 public: 7952 INSTRUCTION_HEADER(StoreFixedSlot) 7953 NAMED_OPERANDS((0, object), (1, value)) 7954 7955 static MStoreFixedSlot* NewUnbarriered(TempAllocator& alloc, MDefinition* obj, 7956 size_t slot, MDefinition* rval) { 7957 return new (alloc) MStoreFixedSlot(obj, rval, slot, false); 7958 } 7959 static MStoreFixedSlot* NewBarriered(TempAllocator& alloc, MDefinition* obj, 7960 size_t slot, MDefinition* rval) { 7961 return new (alloc) MStoreFixedSlot(obj, rval, slot, true); 7962 } 7963 7964 size_t slot() const { return slot_; } 7965 7966 AliasSet getAliasSet() const override { 7967 return AliasSet::Store(AliasSet::FixedSlot); 7968 } 7969 bool needsBarrier() const { return needsBarrier_; } 7970 void setNeedsBarrier(bool needsBarrier = true) { 7971 needsBarrier_ = needsBarrier; 7972 } 7973 7974 #ifdef JS_JITSPEW 7975 void printOpcode(GenericPrinter& out) const override; 7976 #endif 7977 7978 ALLOW_CLONE(MStoreFixedSlot) 7979 }; 7980 7981 class MStoreFixedSlotFromOffset 7982 : public MTernaryInstruction, 7983 public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, 7984 NoFloatPolicy<2>>::Data { 7985 bool needsBarrier_; 7986 7987 MStoreFixedSlotFromOffset(MDefinition* obj, MDefinition* offset, 7988 MDefinition* rval, bool barrier) 7989 : MTernaryInstruction(classOpcode, obj, offset, rval), 7990 needsBarrier_(barrier) { 7991 MOZ_ASSERT(obj->type() == MIRType::Object); 7992 } 7993 7994 public: 7995 INSTRUCTION_HEADER(StoreFixedSlotFromOffset) 7996 NAMED_OPERANDS((0, object), (1, offset), (2, value)) 7997 7998 static MStoreFixedSlotFromOffset* NewBarriered(TempAllocator& alloc, 7999 MDefinition* obj, 8000 MDefinition* offset, 8001 MDefinition* rval) { 8002 return new (alloc) MStoreFixedSlotFromOffset(obj, offset, rval, true); 8003 } 8004 8005 AliasSet getAliasSet() const override { 8006 return AliasSet::Store(AliasSet::FixedSlot); 8007 } 8008 bool needsBarrier() const { return needsBarrier_; } 8009 void setNeedsBarrier(bool needsBarrier = true) { 8010 needsBarrier_ = needsBarrier; 8011 } 8012 8013 ALLOW_CLONE(MStoreFixedSlotFromOffset) 8014 }; 8015 8016 class MGetPropertyCache : public MBinaryInstruction, 8017 public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, 8018 CacheIdPolicy<1>>::Data { 8019 MGetPropertyCache(MDefinition* obj, MDefinition* id) 8020 : MBinaryInstruction(classOpcode, obj, id) { 8021 setResultType(MIRType::Value); 8022 } 8023 8024 public: 8025 INSTRUCTION_HEADER(GetPropertyCache) 8026 TRIVIAL_NEW_WRAPPERS 8027 NAMED_OPERANDS((0, value), (1, idval)) 8028 }; 8029 8030 class MGetPropSuperCache 8031 : public MTernaryInstruction, 8032 public MixPolicy<ObjectPolicy<0>, BoxExceptPolicy<1, MIRType::Object>, 8033 CacheIdPolicy<2>>::Data { 8034 MGetPropSuperCache(MDefinition* obj, MDefinition* receiver, MDefinition* id) 8035 : MTernaryInstruction(classOpcode, obj, receiver, id) { 8036 setResultType(MIRType::Value); 8037 setGuard(); 8038 } 8039 8040 public: 8041 INSTRUCTION_HEADER(GetPropSuperCache) 8042 TRIVIAL_NEW_WRAPPERS 8043 NAMED_OPERANDS((0, object), (1, receiver), (2, idval)) 8044 }; 8045 8046 // Guard the object's proto is |expected|. 8047 class MGuardProto : public MBinaryInstruction, public SingleObjectPolicy::Data { 8048 MGuardProto(MDefinition* obj, MDefinition* expected) 8049 : MBinaryInstruction(classOpcode, obj, expected) { 8050 MOZ_ASSERT(expected->isConstant() || expected->isNurseryObject()); 8051 setGuard(); 8052 setMovable(); 8053 setResultType(MIRType::Object); 8054 } 8055 8056 public: 8057 INSTRUCTION_HEADER(GuardProto) 8058 TRIVIAL_NEW_WRAPPERS 8059 NAMED_OPERANDS((0, object), (1, expected)) 8060 8061 bool congruentTo(const MDefinition* ins) const override { 8062 return congruentIfOperandsEqual(ins); 8063 } 8064 8065 AliasSet getAliasSet() const override { 8066 return AliasSet::Load(AliasSet::ObjectFields); 8067 } 8068 AliasType mightAlias(const MDefinition* def) const override { 8069 // These instructions never modify the [[Prototype]]. 8070 if (def->isAddAndStoreSlot() || def->isAllocateAndStoreSlot()) { 8071 return AliasType::NoAlias; 8072 } 8073 return AliasType::MayAlias; 8074 } 8075 }; 8076 8077 // Guard the object has no proto. 8078 class MGuardNullProto : public MUnaryInstruction, 8079 public SingleObjectPolicy::Data { 8080 explicit MGuardNullProto(MDefinition* obj) 8081 : MUnaryInstruction(classOpcode, obj) { 8082 setGuard(); 8083 setMovable(); 8084 setResultType(MIRType::Object); 8085 } 8086 8087 public: 8088 INSTRUCTION_HEADER(GuardNullProto) 8089 TRIVIAL_NEW_WRAPPERS 8090 NAMED_OPERANDS((0, object)) 8091 8092 bool congruentTo(const MDefinition* ins) const override { 8093 return congruentIfOperandsEqual(ins); 8094 } 8095 AliasSet getAliasSet() const override { 8096 return AliasSet::Load(AliasSet::ObjectFields); 8097 } 8098 AliasType mightAlias(const MDefinition* def) const override { 8099 // These instructions never modify the [[Prototype]]. 8100 if (def->isAddAndStoreSlot() || def->isAllocateAndStoreSlot()) { 8101 return AliasType::NoAlias; 8102 } 8103 return AliasType::MayAlias; 8104 } 8105 }; 8106 8107 // Guard on a specific Value. 8108 class MGuardValue : public MUnaryInstruction, public BoxInputsPolicy::Data { 8109 ValueOrNurseryValueIndex expected_; 8110 8111 MGuardValue(MDefinition* val, ValueOrNurseryValueIndex expected) 8112 : MUnaryInstruction(classOpcode, val), expected_(expected) { 8113 setGuard(); 8114 setMovable(); 8115 setResultType(MIRType::Value); 8116 } 8117 MGuardValue(MDefinition* val, Value expected) 8118 : MGuardValue(val, ValueOrNurseryValueIndex::fromValue(expected)) {} 8119 8120 public: 8121 INSTRUCTION_HEADER(GuardValue) 8122 TRIVIAL_NEW_WRAPPERS 8123 NAMED_OPERANDS((0, value)) 8124 8125 ValueOrNurseryValueIndex expected() const { return expected_; } 8126 8127 bool congruentTo(const MDefinition* ins) const override { 8128 if (!ins->isGuardValue()) { 8129 return false; 8130 } 8131 if (expected() != ins->toGuardValue()->expected()) { 8132 return false; 8133 } 8134 return congruentIfOperandsEqual(ins); 8135 } 8136 MDefinition* foldsTo(TempAllocator& alloc) override; 8137 AliasSet getAliasSet() const override { return AliasSet::None(); } 8138 }; 8139 8140 // Guard on function flags 8141 class MGuardFunctionFlags : public MUnaryInstruction, 8142 public SingleObjectPolicy::Data { 8143 // At least one of the expected flags must be set, but not necessarily all 8144 // expected flags. 8145 uint16_t expectedFlags_; 8146 8147 // None of the unexpected flags must be set. 8148 uint16_t unexpectedFlags_; 8149 8150 explicit MGuardFunctionFlags(MDefinition* fun, uint16_t expectedFlags, 8151 uint16_t unexpectedFlags) 8152 : MUnaryInstruction(classOpcode, fun), 8153 expectedFlags_(expectedFlags), 8154 unexpectedFlags_(unexpectedFlags) { 8155 MOZ_ASSERT((expectedFlags & unexpectedFlags) == 0, 8156 "Can't guard inconsistent flags"); 8157 MOZ_ASSERT((expectedFlags | unexpectedFlags) != 0, 8158 "Can't guard zero flags"); 8159 setGuard(); 8160 setMovable(); 8161 setResultType(MIRType::Object); 8162 } 8163 8164 public: 8165 INSTRUCTION_HEADER(GuardFunctionFlags) 8166 TRIVIAL_NEW_WRAPPERS 8167 NAMED_OPERANDS((0, function)) 8168 8169 uint16_t expectedFlags() const { return expectedFlags_; }; 8170 uint16_t unexpectedFlags() const { return unexpectedFlags_; }; 8171 8172 bool congruentTo(const MDefinition* ins) const override { 8173 if (!ins->isGuardFunctionFlags()) { 8174 return false; 8175 } 8176 if (expectedFlags() != ins->toGuardFunctionFlags()->expectedFlags()) { 8177 return false; 8178 } 8179 if (unexpectedFlags() != ins->toGuardFunctionFlags()->unexpectedFlags()) { 8180 return false; 8181 } 8182 return congruentIfOperandsEqual(ins); 8183 } 8184 AliasSet getAliasSet() const override { 8185 return AliasSet::Load(AliasSet::ObjectFields); 8186 } 8187 }; 8188 8189 // Guard on an object's identity, inclusively or exclusively. 8190 class MGuardObjectIdentity : public MBinaryInstruction, 8191 public SingleObjectPolicy::Data { 8192 bool bailOnEquality_; 8193 8194 MGuardObjectIdentity(MDefinition* obj, MDefinition* expected, 8195 bool bailOnEquality) 8196 : MBinaryInstruction(classOpcode, obj, expected), 8197 bailOnEquality_(bailOnEquality) { 8198 setGuard(); 8199 setMovable(); 8200 setResultType(MIRType::Object); 8201 } 8202 8203 public: 8204 INSTRUCTION_HEADER(GuardObjectIdentity) 8205 TRIVIAL_NEW_WRAPPERS 8206 NAMED_OPERANDS((0, object), (1, expected)) 8207 8208 bool bailOnEquality() const { return bailOnEquality_; } 8209 MDefinition* foldsTo(TempAllocator& alloc) override; 8210 bool congruentTo(const MDefinition* ins) const override { 8211 if (!ins->isGuardObjectIdentity()) { 8212 return false; 8213 } 8214 if (bailOnEquality() != ins->toGuardObjectIdentity()->bailOnEquality()) { 8215 return false; 8216 } 8217 return congruentIfOperandsEqual(ins); 8218 } 8219 AliasSet getAliasSet() const override { return AliasSet::None(); } 8220 }; 8221 8222 // Guard on a specific JSFunction. Used instead of MGuardObjectIdentity, 8223 // so we can store some metadata related to the expected function. 8224 class MGuardSpecificFunction : public MBinaryInstruction, 8225 public SingleObjectPolicy::Data { 8226 uint16_t nargs_; 8227 FunctionFlags flags_; 8228 8229 MGuardSpecificFunction(MDefinition* obj, MDefinition* expected, 8230 uint16_t nargs, FunctionFlags flags) 8231 : MBinaryInstruction(classOpcode, obj, expected), 8232 nargs_(nargs), 8233 flags_(flags) { 8234 MOZ_ASSERT(expected->isConstant() || expected->isNurseryObject()); 8235 setGuard(); 8236 setMovable(); 8237 setResultType(MIRType::Object); 8238 } 8239 8240 public: 8241 INSTRUCTION_HEADER(GuardSpecificFunction) 8242 TRIVIAL_NEW_WRAPPERS 8243 NAMED_OPERANDS((0, function), (1, expected)) 8244 8245 uint16_t nargs() const { return nargs_; } 8246 FunctionFlags flags() const { return flags_; } 8247 8248 MDefinition* foldsTo(TempAllocator& alloc) override; 8249 bool congruentTo(const MDefinition* ins) const override { 8250 if (!ins->isGuardSpecificFunction()) { 8251 return false; 8252 } 8253 8254 auto* other = ins->toGuardSpecificFunction(); 8255 if (nargs() != other->nargs() || 8256 flags().toRaw() != other->flags().toRaw()) { 8257 return false; 8258 } 8259 return congruentIfOperandsEqual(other); 8260 } 8261 AliasSet getAliasSet() const override { return AliasSet::None(); } 8262 }; 8263 8264 class MGuardSpecificSymbol : public MUnaryInstruction, 8265 public SymbolPolicy<0>::Data { 8266 CompilerGCPointer<JS::Symbol*> expected_; 8267 8268 MGuardSpecificSymbol(MDefinition* symbol, JS::Symbol* expected) 8269 : MUnaryInstruction(classOpcode, symbol), expected_(expected) { 8270 setGuard(); 8271 setMovable(); 8272 setResultType(MIRType::Symbol); 8273 } 8274 8275 public: 8276 INSTRUCTION_HEADER(GuardSpecificSymbol) 8277 TRIVIAL_NEW_WRAPPERS 8278 NAMED_OPERANDS((0, symbol)) 8279 8280 JS::Symbol* expected() const { return expected_; } 8281 8282 bool congruentTo(const MDefinition* ins) const override { 8283 if (!ins->isGuardSpecificSymbol()) { 8284 return false; 8285 } 8286 if (expected() != ins->toGuardSpecificSymbol()->expected()) { 8287 return false; 8288 } 8289 return congruentIfOperandsEqual(ins); 8290 } 8291 MDefinition* foldsTo(TempAllocator& alloc) override; 8292 AliasSet getAliasSet() const override { return AliasSet::None(); } 8293 }; 8294 8295 class MGuardTagNotEqual 8296 : public MBinaryInstruction, 8297 public MixPolicy<UnboxedInt32Policy<0>, UnboxedInt32Policy<1>>::Data { 8298 MGuardTagNotEqual(MDefinition* left, MDefinition* right) 8299 : MBinaryInstruction(classOpcode, left, right) { 8300 setGuard(); 8301 setMovable(); 8302 setCommutative(); 8303 } 8304 8305 public: 8306 INSTRUCTION_HEADER(GuardTagNotEqual) 8307 TRIVIAL_NEW_WRAPPERS 8308 8309 AliasSet getAliasSet() const override { return AliasSet::None(); } 8310 8311 bool congruentTo(const MDefinition* ins) const override { 8312 return binaryCongruentTo(ins); 8313 } 8314 }; 8315 8316 // Load from vp[slot] (slots that are not inline in an object). 8317 class MLoadDynamicSlot : public MUnaryInstruction, public NoTypePolicy::Data { 8318 uint32_t slot_; 8319 bool usedAsPropertyKey_ = false; 8320 8321 MLoadDynamicSlot(MDefinition* slots, uint32_t slot) 8322 : MUnaryInstruction(classOpcode, slots), slot_(slot) { 8323 setResultType(MIRType::Value); 8324 setMovable(); 8325 MOZ_ASSERT(slots->type() == MIRType::Slots); 8326 } 8327 8328 public: 8329 INSTRUCTION_HEADER(LoadDynamicSlot) 8330 TRIVIAL_NEW_WRAPPERS 8331 NAMED_OPERANDS((0, slots)) 8332 8333 uint32_t slot() const { return slot_; } 8334 8335 HashNumber valueHash() const override; 8336 bool congruentTo(const MDefinition* ins) const override { 8337 if (!ins->isLoadDynamicSlot()) { 8338 return false; 8339 } 8340 if (slot() != ins->toLoadDynamicSlot()->slot()) { 8341 return false; 8342 } 8343 return congruentIfOperandsEqual(ins); 8344 } 8345 8346 MDefinition* foldsTo(TempAllocator& alloc) override; 8347 8348 AliasSet getAliasSet() const override { 8349 MOZ_ASSERT(slots()->type() == MIRType::Slots); 8350 return AliasSet::Load(AliasSet::DynamicSlot); 8351 } 8352 AliasType mightAlias(const MDefinition* store) const override; 8353 8354 #ifdef JS_JITSPEW 8355 void printOpcode(GenericPrinter& out) const override; 8356 #endif 8357 8358 void setUsedAsPropertyKey() { usedAsPropertyKey_ = true; } 8359 bool usedAsPropertyKey() const { return usedAsPropertyKey_; } 8360 8361 ALLOW_CLONE(MLoadDynamicSlot) 8362 }; 8363 8364 class MAddAndStoreSlot 8365 : public MBinaryInstruction, 8366 public MixPolicy<SingleObjectPolicy, BoxPolicy<1>>::Data { 8367 public: 8368 enum class Kind { 8369 FixedSlot, 8370 DynamicSlot, 8371 }; 8372 8373 private: 8374 Kind kind_; 8375 uint32_t slotOffset_; 8376 CompilerShape shape_; 8377 bool preserveWrapper_; 8378 8379 MAddAndStoreSlot(MDefinition* obj, MDefinition* value, Kind kind, 8380 uint32_t slotOffset, Shape* shape, bool preserveWrapper) 8381 : MBinaryInstruction(classOpcode, obj, value), 8382 kind_(kind), 8383 slotOffset_(slotOffset), 8384 shape_(shape), 8385 preserveWrapper_(preserveWrapper) {} 8386 8387 public: 8388 INSTRUCTION_HEADER(AddAndStoreSlot) 8389 TRIVIAL_NEW_WRAPPERS 8390 NAMED_OPERANDS((0, object), (1, value)) 8391 8392 Kind kind() const { return kind_; } 8393 uint32_t slotOffset() const { return slotOffset_; } 8394 Shape* shape() const { return shape_; } 8395 bool preserveWrapper() const { return preserveWrapper_; } 8396 8397 bool possiblyCalls() const override { return preserveWrapper_; } 8398 8399 AliasSet getAliasSet() const override { 8400 return AliasSet::Store(AliasSet::ObjectFields | 8401 (kind() == Kind::FixedSlot ? AliasSet::FixedSlot 8402 : AliasSet::DynamicSlot)); 8403 } 8404 }; 8405 8406 // Store to vp[slot] (slots that are not inline in an object). 8407 class MStoreDynamicSlot : public MBinaryInstruction, 8408 public NoFloatPolicy<1>::Data { 8409 uint32_t slot_; 8410 bool needsBarrier_; 8411 8412 MStoreDynamicSlot(MDefinition* slots, uint32_t slot, MDefinition* value, 8413 bool barrier) 8414 : MBinaryInstruction(classOpcode, slots, value), 8415 slot_(slot), 8416 needsBarrier_(barrier) { 8417 MOZ_ASSERT(slots->type() == MIRType::Slots); 8418 } 8419 8420 public: 8421 INSTRUCTION_HEADER(StoreDynamicSlot) 8422 NAMED_OPERANDS((0, slots), (1, value)) 8423 8424 static MStoreDynamicSlot* NewUnbarriered(TempAllocator& alloc, 8425 MDefinition* slots, uint32_t slot, 8426 MDefinition* value) { 8427 return new (alloc) MStoreDynamicSlot(slots, slot, value, false); 8428 } 8429 static MStoreDynamicSlot* NewBarriered(TempAllocator& alloc, 8430 MDefinition* slots, uint32_t slot, 8431 MDefinition* value) { 8432 return new (alloc) MStoreDynamicSlot(slots, slot, value, true); 8433 } 8434 8435 uint32_t slot() const { return slot_; } 8436 bool needsBarrier() const { return needsBarrier_; } 8437 AliasSet getAliasSet() const override { 8438 return AliasSet::Store(AliasSet::DynamicSlot); 8439 } 8440 8441 #ifdef JS_JITSPEW 8442 void printOpcode(GenericPrinter& out) const override; 8443 #endif 8444 8445 ALLOW_CLONE(MStoreDynamicSlot) 8446 }; 8447 8448 class MStoreDynamicSlotFromOffset 8449 : public MTernaryInstruction, 8450 public MixPolicy<UnboxedInt32Policy<1>, NoFloatPolicy<2>>::Data { 8451 MStoreDynamicSlotFromOffset(MDefinition* slots, MDefinition* offset, 8452 MDefinition* rval, bool barrier) 8453 : MTernaryInstruction(classOpcode, slots, offset, rval) { 8454 MOZ_ASSERT(slots->type() == MIRType::Slots); 8455 } 8456 8457 public: 8458 INSTRUCTION_HEADER(StoreDynamicSlotFromOffset) 8459 NAMED_OPERANDS((0, slots), (1, offset), (2, value)) 8460 8461 static MStoreDynamicSlotFromOffset* New(TempAllocator& alloc, 8462 MDefinition* slots, 8463 MDefinition* offset, 8464 MDefinition* rval) { 8465 return new (alloc) MStoreDynamicSlotFromOffset(slots, offset, rval, true); 8466 } 8467 8468 AliasSet getAliasSet() const override { 8469 return AliasSet::Store(AliasSet::DynamicSlot); 8470 } 8471 8472 ALLOW_CLONE(MStoreDynamicSlotFromOffset) 8473 }; 8474 8475 class MSetPropertyCache : public MTernaryInstruction, 8476 public MixPolicy<SingleObjectPolicy, CacheIdPolicy<1>, 8477 NoFloatPolicy<2>>::Data { 8478 bool strict_ : 1; 8479 8480 MSetPropertyCache(MDefinition* obj, MDefinition* id, MDefinition* value, 8481 bool strict) 8482 : MTernaryInstruction(classOpcode, obj, id, value), strict_(strict) {} 8483 8484 public: 8485 INSTRUCTION_HEADER(SetPropertyCache) 8486 TRIVIAL_NEW_WRAPPERS 8487 NAMED_OPERANDS((0, object), (1, idval), (2, value)) 8488 8489 bool strict() const { return strict_; } 8490 }; 8491 8492 class MMegamorphicSetElement : public MTernaryInstruction, 8493 public MegamorphicSetElementPolicy::Data { 8494 bool strict_; 8495 8496 MMegamorphicSetElement(MDefinition* object, MDefinition* index, 8497 MDefinition* value, bool strict) 8498 : MTernaryInstruction(classOpcode, object, index, value), 8499 strict_(strict) {} 8500 8501 public: 8502 INSTRUCTION_HEADER(MegamorphicSetElement) 8503 TRIVIAL_NEW_WRAPPERS 8504 NAMED_OPERANDS((0, object), (1, index), (2, value)) 8505 8506 bool strict() const { return strict_; } 8507 8508 bool possiblyCalls() const override { return true; } 8509 }; 8510 8511 class MSetDOMProperty : public MBinaryInstruction, 8512 public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data { 8513 const JSJitSetterOp func_; 8514 Realm* setterRealm_; 8515 DOMObjectKind objectKind_; 8516 8517 MSetDOMProperty(const JSJitSetterOp func, DOMObjectKind objectKind, 8518 Realm* setterRealm, MDefinition* obj, MDefinition* val) 8519 : MBinaryInstruction(classOpcode, obj, val), 8520 func_(func), 8521 setterRealm_(setterRealm), 8522 objectKind_(objectKind) {} 8523 8524 public: 8525 INSTRUCTION_HEADER(SetDOMProperty) 8526 TRIVIAL_NEW_WRAPPERS 8527 NAMED_OPERANDS((0, object), (1, value)) 8528 8529 JSJitSetterOp fun() const { return func_; } 8530 Realm* setterRealm() const { return setterRealm_; } 8531 DOMObjectKind objectKind() const { return objectKind_; } 8532 8533 bool possiblyCalls() const override { return true; } 8534 }; 8535 8536 class MGetDOMPropertyBase : public MVariadicInstruction, 8537 public ObjectPolicy<0>::Data { 8538 const JSJitInfo* info_; 8539 8540 protected: 8541 MGetDOMPropertyBase(Opcode op, const JSJitInfo* jitinfo) 8542 : MVariadicInstruction(op), info_(jitinfo) { 8543 MOZ_ASSERT(jitinfo); 8544 MOZ_ASSERT(jitinfo->type() == JSJitInfo::Getter); 8545 8546 // We are movable iff the jitinfo says we can be. 8547 if (isDomMovable()) { 8548 MOZ_ASSERT(jitinfo->aliasSet() != JSJitInfo::AliasEverything); 8549 setMovable(); 8550 } else { 8551 // If we're not movable, that means we shouldn't be DCEd either, 8552 // because we might throw an exception when called, and getting rid 8553 // of that is observable. 8554 setGuard(); 8555 } 8556 8557 setResultType(MIRType::Value); 8558 } 8559 8560 const JSJitInfo* info() const { return info_; } 8561 8562 [[nodiscard]] bool init(TempAllocator& alloc, MDefinition* obj, 8563 MDefinition* guard, MDefinition* globalGuard) { 8564 MOZ_ASSERT(obj); 8565 // guard can be null. 8566 // globalGuard can be null. 8567 size_t operandCount = 1; 8568 if (guard) { 8569 ++operandCount; 8570 } 8571 if (globalGuard) { 8572 ++operandCount; 8573 } 8574 if (!MVariadicInstruction::init(alloc, operandCount)) { 8575 return false; 8576 } 8577 initOperand(0, obj); 8578 8579 size_t operandIndex = 1; 8580 // Pin the guard, if we have one as an operand if we want to hoist later. 8581 if (guard) { 8582 initOperand(operandIndex++, guard); 8583 } 8584 8585 // And the same for the global guard, if we have one. 8586 if (globalGuard) { 8587 initOperand(operandIndex, globalGuard); 8588 } 8589 8590 return true; 8591 } 8592 8593 public: 8594 NAMED_OPERANDS((0, object)) 8595 8596 JSJitGetterOp fun() const { return info_->getter; } 8597 bool isInfallible() const { return info_->isInfallible; } 8598 bool isDomMovable() const { return info_->isMovable; } 8599 JSJitInfo::AliasSet domAliasSet() const { return info_->aliasSet(); } 8600 size_t domMemberSlotIndex() const { 8601 MOZ_ASSERT(info_->isAlwaysInSlot || info_->isLazilyCachedInSlot); 8602 return info_->slotIndex; 8603 } 8604 bool valueMayBeInSlot() const { return info_->isLazilyCachedInSlot; } 8605 8606 bool baseCongruentTo(const MGetDOMPropertyBase* ins) const { 8607 if (!isDomMovable()) { 8608 return false; 8609 } 8610 8611 // Checking the jitinfo is the same as checking the constant function 8612 if (!(info() == ins->info())) { 8613 return false; 8614 } 8615 8616 return congruentIfOperandsEqual(ins); 8617 } 8618 8619 AliasSet getAliasSet() const override { 8620 JSJitInfo::AliasSet aliasSet = domAliasSet(); 8621 if (aliasSet == JSJitInfo::AliasNone) { 8622 return AliasSet::None(); 8623 } 8624 if (aliasSet == JSJitInfo::AliasDOMSets) { 8625 return AliasSet::Load(AliasSet::DOMProperty); 8626 } 8627 MOZ_ASSERT(aliasSet == JSJitInfo::AliasEverything); 8628 return AliasSet::Store(AliasSet::Any); 8629 } 8630 }; 8631 8632 class MGetDOMProperty : public MGetDOMPropertyBase { 8633 Realm* getterRealm_; 8634 DOMObjectKind objectKind_; 8635 8636 MGetDOMProperty(const JSJitInfo* jitinfo, DOMObjectKind objectKind, 8637 Realm* getterRealm) 8638 : MGetDOMPropertyBase(classOpcode, jitinfo), 8639 getterRealm_(getterRealm), 8640 objectKind_(objectKind) {} 8641 8642 public: 8643 INSTRUCTION_HEADER(GetDOMProperty) 8644 8645 static MGetDOMProperty* New(TempAllocator& alloc, const JSJitInfo* info, 8646 DOMObjectKind objectKind, Realm* getterRealm, 8647 MDefinition* obj, MDefinition* guard, 8648 MDefinition* globalGuard) { 8649 auto* res = new (alloc) MGetDOMProperty(info, objectKind, getterRealm); 8650 if (!res || !res->init(alloc, obj, guard, globalGuard)) { 8651 return nullptr; 8652 } 8653 return res; 8654 } 8655 8656 Realm* getterRealm() const { return getterRealm_; } 8657 DOMObjectKind objectKind() const { return objectKind_; } 8658 8659 bool congruentTo(const MDefinition* ins) const override { 8660 if (!ins->isGetDOMProperty()) { 8661 return false; 8662 } 8663 8664 if (ins->toGetDOMProperty()->getterRealm() != getterRealm()) { 8665 return false; 8666 } 8667 8668 return baseCongruentTo(ins->toGetDOMProperty()); 8669 } 8670 8671 bool possiblyCalls() const override { return true; } 8672 }; 8673 8674 class MGetDOMMember : public MGetDOMPropertyBase { 8675 explicit MGetDOMMember(const JSJitInfo* jitinfo) 8676 : MGetDOMPropertyBase(classOpcode, jitinfo) { 8677 setResultType(MIRTypeFromValueType(jitinfo->returnType())); 8678 } 8679 8680 public: 8681 INSTRUCTION_HEADER(GetDOMMember) 8682 8683 static MGetDOMMember* New(TempAllocator& alloc, const JSJitInfo* info, 8684 MDefinition* obj, MDefinition* guard, 8685 MDefinition* globalGuard) { 8686 auto* res = new (alloc) MGetDOMMember(info); 8687 if (!res || !res->init(alloc, obj, guard, globalGuard)) { 8688 return nullptr; 8689 } 8690 return res; 8691 } 8692 8693 bool possiblyCalls() const override { return false; } 8694 8695 bool congruentTo(const MDefinition* ins) const override { 8696 if (!ins->isGetDOMMember()) { 8697 return false; 8698 } 8699 8700 return baseCongruentTo(ins->toGetDOMMember()); 8701 } 8702 }; 8703 8704 class MLoadDOMExpandoValueGuardGeneration : public MUnaryInstruction, 8705 public SingleObjectPolicy::Data { 8706 JS::ExpandoAndGeneration* expandoAndGeneration_; 8707 uint64_t generation_; 8708 8709 MLoadDOMExpandoValueGuardGeneration( 8710 MDefinition* proxy, JS::ExpandoAndGeneration* expandoAndGeneration, 8711 uint64_t generation) 8712 : MUnaryInstruction(classOpcode, proxy), 8713 expandoAndGeneration_(expandoAndGeneration), 8714 generation_(generation) { 8715 setGuard(); 8716 setMovable(); 8717 setResultType(MIRType::Value); 8718 } 8719 8720 public: 8721 INSTRUCTION_HEADER(LoadDOMExpandoValueGuardGeneration) 8722 TRIVIAL_NEW_WRAPPERS 8723 NAMED_OPERANDS((0, proxy)) 8724 8725 JS::ExpandoAndGeneration* expandoAndGeneration() const { 8726 return expandoAndGeneration_; 8727 } 8728 uint64_t generation() const { return generation_; } 8729 8730 bool congruentTo(const MDefinition* ins) const override { 8731 if (!ins->isLoadDOMExpandoValueGuardGeneration()) { 8732 return false; 8733 } 8734 const auto* other = ins->toLoadDOMExpandoValueGuardGeneration(); 8735 if (expandoAndGeneration() != other->expandoAndGeneration() || 8736 generation() != other->generation()) { 8737 return false; 8738 } 8739 return congruentIfOperandsEqual(ins); 8740 } 8741 AliasSet getAliasSet() const override { 8742 return AliasSet::Load(AliasSet::DOMProxyExpando); 8743 } 8744 }; 8745 8746 // Inlined assembly for Math.floor(double | float32) -> int32. 8747 class MFloor : public MUnaryInstruction, public FloatingPointPolicy<0>::Data { 8748 explicit MFloor(MDefinition* num) : MUnaryInstruction(classOpcode, num) { 8749 setResultType(MIRType::Int32); 8750 specialization_ = MIRType::Double; 8751 setMovable(); 8752 } 8753 8754 public: 8755 INSTRUCTION_HEADER(Floor) 8756 TRIVIAL_NEW_WRAPPERS 8757 8758 AliasSet getAliasSet() const override { return AliasSet::None(); } 8759 bool isFloat32Commutative() const override { return true; } 8760 void trySpecializeFloat32(TempAllocator& alloc) override; 8761 #ifdef DEBUG 8762 bool isConsistentFloat32Use(MUse* use) const override { return true; } 8763 #endif 8764 bool congruentTo(const MDefinition* ins) const override { 8765 return congruentIfOperandsEqual(ins); 8766 } 8767 void computeRange(TempAllocator& alloc) override; 8768 [[nodiscard]] bool writeRecoverData( 8769 CompactBufferWriter& writer) const override; 8770 bool canRecoverOnBailout() const override { return true; } 8771 8772 ALLOW_CLONE(MFloor) 8773 }; 8774 8775 // Inlined assembly version for Math.ceil(double | float32) -> int32. 8776 class MCeil : public MUnaryInstruction, public FloatingPointPolicy<0>::Data { 8777 explicit MCeil(MDefinition* num) : MUnaryInstruction(classOpcode, num) { 8778 setResultType(MIRType::Int32); 8779 specialization_ = MIRType::Double; 8780 setMovable(); 8781 } 8782 8783 public: 8784 INSTRUCTION_HEADER(Ceil) 8785 TRIVIAL_NEW_WRAPPERS 8786 8787 AliasSet getAliasSet() const override { return AliasSet::None(); } 8788 bool isFloat32Commutative() const override { return true; } 8789 void trySpecializeFloat32(TempAllocator& alloc) override; 8790 #ifdef DEBUG 8791 bool isConsistentFloat32Use(MUse* use) const override { return true; } 8792 #endif 8793 bool congruentTo(const MDefinition* ins) const override { 8794 return congruentIfOperandsEqual(ins); 8795 } 8796 void computeRange(TempAllocator& alloc) override; 8797 [[nodiscard]] bool writeRecoverData( 8798 CompactBufferWriter& writer) const override; 8799 bool canRecoverOnBailout() const override { return true; } 8800 8801 ALLOW_CLONE(MCeil) 8802 }; 8803 8804 // Inlined version of Math.round(double | float32) -> int32. 8805 class MRound : public MUnaryInstruction, public FloatingPointPolicy<0>::Data { 8806 explicit MRound(MDefinition* num) : MUnaryInstruction(classOpcode, num) { 8807 setResultType(MIRType::Int32); 8808 specialization_ = MIRType::Double; 8809 setMovable(); 8810 } 8811 8812 public: 8813 INSTRUCTION_HEADER(Round) 8814 TRIVIAL_NEW_WRAPPERS 8815 8816 AliasSet getAliasSet() const override { return AliasSet::None(); } 8817 8818 bool isFloat32Commutative() const override { return true; } 8819 void trySpecializeFloat32(TempAllocator& alloc) override; 8820 #ifdef DEBUG 8821 bool isConsistentFloat32Use(MUse* use) const override { return true; } 8822 #endif 8823 bool congruentTo(const MDefinition* ins) const override { 8824 return congruentIfOperandsEqual(ins); 8825 } 8826 8827 [[nodiscard]] bool writeRecoverData( 8828 CompactBufferWriter& writer) const override; 8829 bool canRecoverOnBailout() const override { return true; } 8830 8831 ALLOW_CLONE(MRound) 8832 }; 8833 8834 // Inlined version of Math.trunc(double | float32) -> int32. 8835 class MTrunc : public MUnaryInstruction, public FloatingPointPolicy<0>::Data { 8836 explicit MTrunc(MDefinition* num) : MUnaryInstruction(classOpcode, num) { 8837 setResultType(MIRType::Int32); 8838 specialization_ = MIRType::Double; 8839 setMovable(); 8840 } 8841 8842 public: 8843 INSTRUCTION_HEADER(Trunc) 8844 TRIVIAL_NEW_WRAPPERS 8845 8846 AliasSet getAliasSet() const override { return AliasSet::None(); } 8847 8848 bool isFloat32Commutative() const override { return true; } 8849 void trySpecializeFloat32(TempAllocator& alloc) override; 8850 #ifdef DEBUG 8851 bool isConsistentFloat32Use(MUse* use) const override { return true; } 8852 #endif 8853 bool congruentTo(const MDefinition* ins) const override { 8854 return congruentIfOperandsEqual(ins); 8855 } 8856 8857 [[nodiscard]] bool writeRecoverData( 8858 CompactBufferWriter& writer) const override; 8859 bool canRecoverOnBailout() const override { return true; } 8860 8861 ALLOW_CLONE(MTrunc) 8862 }; 8863 8864 // NearbyInt rounds the floating-point input to the nearest integer, according 8865 // to the RoundingMode. 8866 class MNearbyInt : public MUnaryInstruction, 8867 public FloatingPointPolicy<0>::Data { 8868 RoundingMode roundingMode_; 8869 8870 explicit MNearbyInt(MDefinition* num, MIRType resultType, 8871 RoundingMode roundingMode) 8872 : MUnaryInstruction(classOpcode, num), roundingMode_(roundingMode) { 8873 MOZ_ASSERT(HasAssemblerSupport(roundingMode)); 8874 8875 MOZ_ASSERT(IsFloatingPointType(resultType)); 8876 setResultType(resultType); 8877 specialization_ = resultType; 8878 8879 setMovable(); 8880 } 8881 8882 public: 8883 INSTRUCTION_HEADER(NearbyInt) 8884 TRIVIAL_NEW_WRAPPERS 8885 8886 static bool HasAssemblerSupport(RoundingMode mode) { 8887 return Assembler::HasRoundInstruction(mode); 8888 } 8889 8890 RoundingMode roundingMode() const { return roundingMode_; } 8891 8892 AliasSet getAliasSet() const override { return AliasSet::None(); } 8893 8894 bool isFloat32Commutative() const override { return true; } 8895 void trySpecializeFloat32(TempAllocator& alloc) override; 8896 #ifdef DEBUG 8897 bool isConsistentFloat32Use(MUse* use) const override { return true; } 8898 #endif 8899 8900 bool congruentTo(const MDefinition* ins) const override { 8901 return congruentIfOperandsEqual(ins) && 8902 ins->toNearbyInt()->roundingMode() == roundingMode_; 8903 } 8904 8905 #ifdef JS_JITSPEW 8906 void printOpcode(GenericPrinter& out) const override; 8907 #endif 8908 8909 [[nodiscard]] bool writeRecoverData( 8910 CompactBufferWriter& writer) const override; 8911 8912 bool canRecoverOnBailout() const override { 8913 switch (roundingMode_) { 8914 case RoundingMode::Up: 8915 case RoundingMode::Down: 8916 case RoundingMode::TowardsZero: 8917 return true; 8918 default: 8919 return false; 8920 } 8921 } 8922 8923 ALLOW_CLONE(MNearbyInt) 8924 }; 8925 8926 // Inlined version of Math.round(double) -> double. 8927 class MRoundToDouble : public MUnaryInstruction, 8928 public FloatingPointPolicy<0>::Data { 8929 explicit MRoundToDouble(MDefinition* num) 8930 : MUnaryInstruction(classOpcode, num) { 8931 MOZ_ASSERT(HasAssemblerSupport()); 8932 8933 setResultType(MIRType::Double); 8934 specialization_ = MIRType::Double; 8935 8936 setMovable(); 8937 } 8938 8939 public: 8940 INSTRUCTION_HEADER(RoundToDouble) 8941 TRIVIAL_NEW_WRAPPERS 8942 8943 static bool HasAssemblerSupport() { 8944 // Requires Ceil support in the assembler. 8945 return Assembler::HasRoundInstruction(RoundingMode::Up); 8946 } 8947 8948 AliasSet getAliasSet() const override { return AliasSet::None(); } 8949 8950 bool isFloat32Commutative() const override { return true; } 8951 void trySpecializeFloat32(TempAllocator& alloc) override; 8952 #ifdef DEBUG 8953 bool isConsistentFloat32Use(MUse* use) const override { return true; } 8954 #endif 8955 8956 bool congruentTo(const MDefinition* ins) const override { 8957 return congruentIfOperandsEqual(ins); 8958 } 8959 8960 [[nodiscard]] bool writeRecoverData( 8961 CompactBufferWriter& writer) const override; 8962 8963 bool canRecoverOnBailout() const override { return true; } 8964 8965 ALLOW_CLONE(MRoundToDouble) 8966 }; 8967 8968 class MGetIteratorCache : public MUnaryInstruction, 8969 public BoxExceptPolicy<0, MIRType::Object>::Data { 8970 explicit MGetIteratorCache(MDefinition* val) 8971 : MUnaryInstruction(classOpcode, val) { 8972 setResultType(MIRType::Object); 8973 } 8974 8975 public: 8976 INSTRUCTION_HEADER(GetIteratorCache) 8977 TRIVIAL_NEW_WRAPPERS 8978 NAMED_OPERANDS((0, value)) 8979 }; 8980 8981 // Implementation for 'in' operator using instruction cache 8982 class MInCache : public MBinaryInstruction, 8983 public MixPolicy<CacheIdPolicy<0>, ObjectPolicy<1>>::Data { 8984 MInCache(MDefinition* key, MDefinition* obj) 8985 : MBinaryInstruction(classOpcode, key, obj) { 8986 setResultType(MIRType::Boolean); 8987 } 8988 8989 public: 8990 INSTRUCTION_HEADER(InCache) 8991 TRIVIAL_NEW_WRAPPERS 8992 NAMED_OPERANDS((0, key), (1, object)) 8993 }; 8994 8995 // Test whether the index is in the array bounds or a hole. 8996 class MInArray : public MTernaryInstruction, public NoTypePolicy::Data { 8997 bool needsNegativeIntCheck_ = true; 8998 8999 MInArray(MDefinition* elements, MDefinition* index, MDefinition* initLength) 9000 : MTernaryInstruction(classOpcode, elements, index, initLength) { 9001 setResultType(MIRType::Boolean); 9002 setMovable(); 9003 9004 // Set the guard flag to make sure we bail when we see a negative index. 9005 // We can clear this flag (and needsNegativeIntCheck_) in 9006 // collectRangeInfoPreTrunc. 9007 setGuard(); 9008 9009 MOZ_ASSERT(elements->type() == MIRType::Elements); 9010 MOZ_ASSERT(index->type() == MIRType::Int32); 9011 MOZ_ASSERT(initLength->type() == MIRType::Int32); 9012 } 9013 9014 public: 9015 INSTRUCTION_HEADER(InArray) 9016 TRIVIAL_NEW_WRAPPERS 9017 NAMED_OPERANDS((0, elements), (1, index), (2, initLength)) 9018 9019 bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; } 9020 void collectRangeInfoPreTrunc() override; 9021 AliasSet getAliasSet() const override { 9022 return AliasSet::Load(AliasSet::Element); 9023 } 9024 bool congruentTo(const MDefinition* ins) const override { 9025 if (!ins->isInArray()) { 9026 return false; 9027 } 9028 const MInArray* other = ins->toInArray(); 9029 if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) { 9030 return false; 9031 } 9032 return congruentIfOperandsEqual(other); 9033 } 9034 }; 9035 9036 // Bail when the element is a hole. 9037 class MGuardElementNotHole : public MBinaryInstruction, 9038 public NoTypePolicy::Data { 9039 MGuardElementNotHole(MDefinition* elements, MDefinition* index) 9040 : MBinaryInstruction(classOpcode, elements, index) { 9041 setMovable(); 9042 setGuard(); 9043 MOZ_ASSERT(elements->type() == MIRType::Elements); 9044 MOZ_ASSERT(index->type() == MIRType::Int32); 9045 } 9046 9047 public: 9048 INSTRUCTION_HEADER(GuardElementNotHole) 9049 TRIVIAL_NEW_WRAPPERS 9050 NAMED_OPERANDS((0, elements), (1, index)) 9051 9052 AliasSet getAliasSet() const override { 9053 return AliasSet::Load(AliasSet::Element); 9054 } 9055 bool congruentTo(const MDefinition* ins) const override { 9056 return congruentIfOperandsEqual(ins); 9057 } 9058 }; 9059 9060 class MCheckPrivateFieldCache 9061 : public MBinaryInstruction, 9062 public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, 9063 CacheIdPolicy<1>>::Data { 9064 MCheckPrivateFieldCache(MDefinition* obj, MDefinition* id) 9065 : MBinaryInstruction(classOpcode, obj, id) { 9066 setResultType(MIRType::Boolean); 9067 } 9068 9069 public: 9070 INSTRUCTION_HEADER(CheckPrivateFieldCache) 9071 TRIVIAL_NEW_WRAPPERS 9072 NAMED_OPERANDS((0, value), (1, idval)) 9073 }; 9074 9075 class MHasOwnCache : public MBinaryInstruction, 9076 public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, 9077 CacheIdPolicy<1>>::Data { 9078 MHasOwnCache(MDefinition* obj, MDefinition* id) 9079 : MBinaryInstruction(classOpcode, obj, id) { 9080 setResultType(MIRType::Boolean); 9081 } 9082 9083 public: 9084 INSTRUCTION_HEADER(HasOwnCache) 9085 TRIVIAL_NEW_WRAPPERS 9086 NAMED_OPERANDS((0, value), (1, idval)) 9087 }; 9088 9089 // Implementation for instanceof operator with specific rhs. 9090 class MInstanceOf : public MBinaryInstruction, 9091 public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, 9092 ObjectPolicy<1>>::Data { 9093 MInstanceOf(MDefinition* obj, MDefinition* proto) 9094 : MBinaryInstruction(classOpcode, obj, proto) { 9095 setResultType(MIRType::Boolean); 9096 } 9097 9098 public: 9099 INSTRUCTION_HEADER(InstanceOf) 9100 TRIVIAL_NEW_WRAPPERS 9101 }; 9102 9103 // Given a value being written to another object, update the generational store 9104 // buffer if the value is in the nursery and object is in the tenured heap. 9105 class MPostWriteBarrier : public MBinaryInstruction, 9106 public ObjectPolicy<0>::Data { 9107 MPostWriteBarrier(MDefinition* obj, MDefinition* value) 9108 : MBinaryInstruction(classOpcode, obj, value) { 9109 setGuard(); 9110 } 9111 9112 public: 9113 INSTRUCTION_HEADER(PostWriteBarrier) 9114 TRIVIAL_NEW_WRAPPERS 9115 NAMED_OPERANDS((0, object), (1, value)) 9116 9117 AliasSet getAliasSet() const override { return AliasSet::None(); } 9118 9119 #ifdef DEBUG 9120 bool isConsistentFloat32Use(MUse* use) const override { 9121 // During lowering, values that neither have object nor value MIR type 9122 // are ignored, thus Float32 can show up at this point without any issue. 9123 return use == getUseFor(1); 9124 } 9125 #endif 9126 9127 ALLOW_CLONE(MPostWriteBarrier) 9128 }; 9129 9130 // Given a value being written to another object's elements at the specified 9131 // index, update the generational store buffer if the value is in the nursery 9132 // and object is in the tenured heap. 9133 class MPostWriteElementBarrier 9134 : public MTernaryInstruction, 9135 public MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<2>>::Data { 9136 MPostWriteElementBarrier(MDefinition* obj, MDefinition* value, 9137 MDefinition* index) 9138 : MTernaryInstruction(classOpcode, obj, value, index) { 9139 setGuard(); 9140 } 9141 9142 public: 9143 INSTRUCTION_HEADER(PostWriteElementBarrier) 9144 TRIVIAL_NEW_WRAPPERS 9145 NAMED_OPERANDS((0, object), (1, value), (2, index)) 9146 9147 AliasSet getAliasSet() const override { return AliasSet::None(); } 9148 9149 #ifdef DEBUG 9150 bool isConsistentFloat32Use(MUse* use) const override { 9151 // During lowering, values that neither have object nor value MIR type 9152 // are ignored, thus Float32 can show up at this point without any issue. 9153 return use == getUseFor(1); 9154 } 9155 #endif 9156 9157 ALLOW_CLONE(MPostWriteElementBarrier) 9158 }; 9159 9160 class MNewCallObject : public MUnaryInstruction, 9161 public SingleObjectPolicy::Data { 9162 gc::Heap initialHeap_; 9163 9164 public: 9165 INSTRUCTION_HEADER(NewCallObject) 9166 TRIVIAL_NEW_WRAPPERS 9167 9168 explicit MNewCallObject(MConstant* templateObj, gc::Heap initialHeap) 9169 : MUnaryInstruction(classOpcode, templateObj), initialHeap_(initialHeap) { 9170 setResultType(MIRType::Object); 9171 } 9172 9173 CallObject* templateObject() const { 9174 return &getOperand(0)->toConstant()->toObject().as<CallObject>(); 9175 } 9176 gc::Heap initialHeap() const { return initialHeap_; } 9177 9178 AliasSet getAliasSet() const override { return AliasSet::None(); } 9179 9180 [[nodiscard]] bool writeRecoverData( 9181 CompactBufferWriter& writer) const override; 9182 bool canRecoverOnBailout() const override { return true; } 9183 }; 9184 9185 class MNewStringObject : public MUnaryInstruction, 9186 public ConvertToStringPolicy<0>::Data { 9187 CompilerObject templateObj_; 9188 9189 MNewStringObject(MDefinition* input, JSObject* templateObj) 9190 : MUnaryInstruction(classOpcode, input), templateObj_(templateObj) { 9191 setResultType(MIRType::Object); 9192 } 9193 9194 public: 9195 INSTRUCTION_HEADER(NewStringObject) 9196 TRIVIAL_NEW_WRAPPERS 9197 9198 StringObject* templateObj() const; 9199 }; 9200 9201 // This is an alias for MLoadFixedSlot. 9202 class MEnclosingEnvironment : public MLoadFixedSlot { 9203 explicit MEnclosingEnvironment(MDefinition* obj) 9204 : MLoadFixedSlot(obj, EnvironmentObject::enclosingEnvironmentSlot()) { 9205 setResultType(MIRType::Object); 9206 } 9207 9208 public: 9209 static MEnclosingEnvironment* New(TempAllocator& alloc, MDefinition* obj) { 9210 return new (alloc) MEnclosingEnvironment(obj); 9211 } 9212 9213 AliasSet getAliasSet() const override { 9214 // EnvironmentObject reserved slots are immutable. 9215 return AliasSet::None(); 9216 } 9217 }; 9218 9219 // This is an element of a spaghetti stack which is used to represent the memory 9220 // context which has to be restored in case of a bailout. 9221 struct MStoreToRecover : public TempObject, 9222 public InlineSpaghettiStackNode<MStoreToRecover> { 9223 MDefinition* operand; 9224 9225 explicit MStoreToRecover(MDefinition* operand) : operand(operand) {} 9226 }; 9227 9228 using MStoresToRecoverList = InlineSpaghettiStack<MStoreToRecover>; 9229 9230 // A resume point contains the information needed to reconstruct the Baseline 9231 // Interpreter state from a position in Warp JIT code. A resume point is a 9232 // mapping of stack slots to MDefinitions. 9233 // 9234 // We capture stack state at critical points: 9235 // * (1) At the beginning of every basic block. 9236 // * (2) After every effectful operation. 9237 // 9238 // As long as these two properties are maintained, instructions can be moved, 9239 // hoisted, or, eliminated without problems, and ops without side effects do not 9240 // need to worry about capturing state at precisely the right point in time. 9241 // 9242 // Effectful instructions, of course, need to capture state after completion, 9243 // where the interpreter will not attempt to repeat the operation. For this, 9244 // ResumeAfter must be used. The state is attached directly to the effectful 9245 // instruction to ensure that no intermediate instructions could be injected 9246 // in between by a future analysis pass. 9247 // 9248 // During LIR construction, if an instruction can bail back to the interpreter, 9249 // we create an LSnapshot, which uses the last known resume point to request 9250 // register/stack assignments for every live value. 9251 class MResumePoint final : public MNode 9252 #ifdef DEBUG 9253 , 9254 public InlineForwardListNode<MResumePoint> 9255 #endif 9256 { 9257 private: 9258 friend class MBasicBlock; 9259 friend void AssertBasicGraphCoherency(MIRGraph& graph, bool force); 9260 9261 // List of stack slots needed to reconstruct the BaselineFrame. 9262 FixedList<MUse> operands_; 9263 9264 // List of stores needed to reconstruct the content of objects which are 9265 // emulated by EmulateStateOf variants. 9266 MStoresToRecoverList stores_; 9267 9268 jsbytecode* pc_; 9269 MInstruction* instruction_; 9270 ResumeMode mode_; 9271 bool isDiscarded_ = false; 9272 9273 MResumePoint(MBasicBlock* block, jsbytecode* pc, ResumeMode mode); 9274 void inherit(MBasicBlock* state); 9275 9276 // Calling isDefinition or isResumePoint on MResumePoint is unnecessary. 9277 bool isDefinition() const = delete; 9278 bool isResumePoint() const = delete; 9279 9280 void setBlock(MBasicBlock* block) { 9281 setBlockAndKind(block, Kind::ResumePoint); 9282 } 9283 9284 protected: 9285 // Initializes operands_ to an empty array of a fixed length. 9286 // The array may then be filled in by inherit(). 9287 [[nodiscard]] bool init(TempAllocator& alloc); 9288 9289 void clearOperand(size_t index) { 9290 // FixedList doesn't initialize its elements, so do an unchecked init. 9291 operands_[index].initUncheckedWithoutProducer(this); 9292 } 9293 9294 MUse* getUseFor(size_t index) override { return &operands_[index]; } 9295 const MUse* getUseFor(size_t index) const override { 9296 return &operands_[index]; 9297 } 9298 9299 public: 9300 static MResumePoint* New(TempAllocator& alloc, MBasicBlock* block, 9301 jsbytecode* pc, ResumeMode mode); 9302 9303 MBasicBlock* block() const { return resumePointBlock(); } 9304 9305 size_t numAllocatedOperands() const { return operands_.length(); } 9306 uint32_t stackDepth() const { return numAllocatedOperands(); } 9307 size_t numOperands() const override { return numAllocatedOperands(); } 9308 size_t indexOf(const MUse* u) const final { 9309 MOZ_ASSERT(u >= &operands_[0]); 9310 MOZ_ASSERT(u <= &operands_[numOperands() - 1]); 9311 return u - &operands_[0]; 9312 } 9313 void initOperand(size_t index, MDefinition* operand) { 9314 // FixedList doesn't initialize its elements, so do an unchecked init. 9315 operands_[index].initUnchecked(operand, this); 9316 } 9317 void replaceOperand(size_t index, MDefinition* operand) final { 9318 operands_[index].replaceProducer(operand); 9319 } 9320 9321 bool isObservableOperand(MUse* u) const; 9322 bool isObservableOperand(size_t index) const; 9323 bool isRecoverableOperand(MUse* u) const; 9324 9325 MDefinition* getOperand(size_t index) const override { 9326 return operands_[index].producer(); 9327 } 9328 jsbytecode* pc() const { return pc_; } 9329 MResumePoint* caller() const; 9330 uint32_t frameCount() const { 9331 uint32_t count = 1; 9332 for (MResumePoint* it = caller(); it; it = it->caller()) { 9333 count++; 9334 } 9335 return count; 9336 } 9337 MInstruction* instruction() { return instruction_; } 9338 void setInstruction(MInstruction* ins) { 9339 MOZ_ASSERT(!instruction_); 9340 instruction_ = ins; 9341 } 9342 void resetInstruction() { 9343 MOZ_ASSERT(instruction_); 9344 instruction_ = nullptr; 9345 } 9346 ResumeMode mode() const { return mode_; } 9347 9348 void releaseUses() { 9349 for (size_t i = 0, e = numOperands(); i < e; i++) { 9350 if (operands_[i].hasProducer()) { 9351 operands_[i].releaseProducer(); 9352 } 9353 } 9354 } 9355 9356 [[nodiscard]] bool writeRecoverData( 9357 CompactBufferWriter& writer) const override; 9358 9359 // Register a store instruction on the current resume point. This 9360 // instruction would be recovered when we are bailing out. The |cache| 9361 // argument can be any resume point, it is used to share memory if we are 9362 // doing the same modification. 9363 void addStore(TempAllocator& alloc, MDefinition* store, 9364 const MResumePoint* cache = nullptr); 9365 9366 MStoresToRecoverList::iterator storesBegin() const { return stores_.begin(); } 9367 MStoresToRecoverList::iterator storesEnd() const { return stores_.end(); } 9368 bool storesEmpty() const { return stores_.empty(); } 9369 9370 void setDiscarded() { isDiscarded_ = true; } 9371 bool isDiscarded() const { return isDiscarded_; } 9372 9373 #ifdef JS_JITSPEW 9374 virtual void dump(GenericPrinter& out) const override; 9375 virtual void dump() const override; 9376 #endif 9377 }; 9378 9379 class MIsCallable : public MUnaryInstruction, 9380 public BoxExceptPolicy<0, MIRType::Object>::Data { 9381 explicit MIsCallable(MDefinition* object) 9382 : MUnaryInstruction(classOpcode, object) { 9383 setResultType(MIRType::Boolean); 9384 setMovable(); 9385 } 9386 9387 public: 9388 INSTRUCTION_HEADER(IsCallable) 9389 TRIVIAL_NEW_WRAPPERS 9390 NAMED_OPERANDS((0, object)) 9391 9392 bool congruentTo(const MDefinition* ins) const override { 9393 return congruentIfOperandsEqual(ins); 9394 } 9395 9396 MDefinition* foldsTo(TempAllocator& alloc) override; 9397 AliasSet getAliasSet() const override { return AliasSet::None(); } 9398 }; 9399 9400 class MHasClass : public MUnaryInstruction, public SingleObjectPolicy::Data { 9401 const JSClass* class_; 9402 9403 MHasClass(MDefinition* object, const JSClass* clasp) 9404 : MUnaryInstruction(classOpcode, object), class_(clasp) { 9405 MOZ_ASSERT(object->type() == MIRType::Object); 9406 setResultType(MIRType::Boolean); 9407 setMovable(); 9408 } 9409 9410 public: 9411 INSTRUCTION_HEADER(HasClass) 9412 TRIVIAL_NEW_WRAPPERS 9413 NAMED_OPERANDS((0, object)) 9414 9415 const JSClass* getClass() const { return class_; } 9416 9417 MDefinition* foldsTo(TempAllocator& alloc) override; 9418 AliasSet getAliasSet() const override { return AliasSet::None(); } 9419 bool congruentTo(const MDefinition* ins) const override { 9420 if (!ins->isHasClass()) { 9421 return false; 9422 } 9423 if (getClass() != ins->toHasClass()->getClass()) { 9424 return false; 9425 } 9426 return congruentIfOperandsEqual(ins); 9427 } 9428 }; 9429 9430 class MGuardToClass : public MUnaryInstruction, 9431 public SingleObjectPolicy::Data { 9432 const JSClass* class_; 9433 9434 MGuardToClass(MDefinition* object, const JSClass* clasp) 9435 : MUnaryInstruction(classOpcode, object), class_(clasp) { 9436 MOZ_ASSERT(object->type() == MIRType::Object); 9437 MOZ_ASSERT(!clasp->isJSFunction(), "Use MGuardToFunction instead"); 9438 setResultType(MIRType::Object); 9439 setMovable(); 9440 9441 // We will bail out if the class type is incorrect, so we need to ensure we 9442 // don't eliminate this instruction 9443 setGuard(); 9444 } 9445 9446 public: 9447 INSTRUCTION_HEADER(GuardToClass) 9448 TRIVIAL_NEW_WRAPPERS 9449 NAMED_OPERANDS((0, object)) 9450 9451 const JSClass* getClass() const { return class_; } 9452 bool isArgumentsObjectClass() const { 9453 return class_ == &MappedArgumentsObject::class_ || 9454 class_ == &UnmappedArgumentsObject::class_; 9455 } 9456 9457 MDefinition* foldsTo(TempAllocator& alloc) override; 9458 AliasSet getAliasSet() const override { return AliasSet::None(); } 9459 bool congruentTo(const MDefinition* ins) const override { 9460 if (!ins->isGuardToClass()) { 9461 return false; 9462 } 9463 if (getClass() != ins->toGuardToClass()->getClass()) { 9464 return false; 9465 } 9466 return congruentIfOperandsEqual(ins); 9467 } 9468 }; 9469 9470 class MGuardToFunction : public MUnaryInstruction, 9471 public SingleObjectPolicy::Data { 9472 explicit MGuardToFunction(MDefinition* object) 9473 : MUnaryInstruction(classOpcode, object) { 9474 MOZ_ASSERT(object->type() == MIRType::Object); 9475 setResultType(MIRType::Object); 9476 setMovable(); 9477 9478 // We will bail out if the class type is incorrect, so we need to ensure we 9479 // don't eliminate this instruction 9480 setGuard(); 9481 } 9482 9483 public: 9484 INSTRUCTION_HEADER(GuardToFunction) 9485 TRIVIAL_NEW_WRAPPERS 9486 NAMED_OPERANDS((0, object)) 9487 9488 MDefinition* foldsTo(TempAllocator& alloc) override; 9489 AliasSet getAliasSet() const override { return AliasSet::None(); } 9490 bool congruentTo(const MDefinition* ins) const override { 9491 if (!ins->isGuardToFunction()) { 9492 return false; 9493 } 9494 return congruentIfOperandsEqual(ins); 9495 } 9496 }; 9497 9498 // Note: we might call a proxy trap, so this instruction is effectful. 9499 class MIsArray : public MUnaryInstruction, 9500 public BoxExceptPolicy<0, MIRType::Object>::Data { 9501 explicit MIsArray(MDefinition* value) 9502 : MUnaryInstruction(classOpcode, value) { 9503 setResultType(MIRType::Boolean); 9504 } 9505 9506 public: 9507 INSTRUCTION_HEADER(IsArray) 9508 TRIVIAL_NEW_WRAPPERS 9509 NAMED_OPERANDS((0, value)) 9510 9511 MDefinition* foldsTo(TempAllocator& alloc) override; 9512 }; 9513 9514 class MIsTypedArray : public MUnaryInstruction, 9515 public SingleObjectPolicy::Data { 9516 bool possiblyWrapped_; 9517 9518 explicit MIsTypedArray(MDefinition* value, bool possiblyWrapped) 9519 : MUnaryInstruction(classOpcode, value), 9520 possiblyWrapped_(possiblyWrapped) { 9521 setResultType(MIRType::Boolean); 9522 9523 if (possiblyWrapped) { 9524 // Proxy checks may throw, so we're neither removable nor movable. 9525 setGuard(); 9526 } else { 9527 setMovable(); 9528 } 9529 } 9530 9531 public: 9532 INSTRUCTION_HEADER(IsTypedArray) 9533 TRIVIAL_NEW_WRAPPERS 9534 NAMED_OPERANDS((0, value)) 9535 9536 bool isPossiblyWrapped() const { return possiblyWrapped_; } 9537 AliasSet getAliasSet() const override { 9538 if (isPossiblyWrapped()) { 9539 return AliasSet::Store(AliasSet::Any); 9540 } 9541 return AliasSet::None(); 9542 } 9543 }; 9544 9545 // Allocate the generator object for a frame. 9546 class MGenerator : public MTernaryInstruction, 9547 public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1>>::Data { 9548 explicit MGenerator(MDefinition* callee, MDefinition* environmentChain, 9549 MDefinition* argsObject) 9550 : MTernaryInstruction(classOpcode, callee, environmentChain, argsObject) { 9551 setResultType(MIRType::Object); 9552 }; 9553 9554 public: 9555 INSTRUCTION_HEADER(Generator) 9556 TRIVIAL_NEW_WRAPPERS 9557 NAMED_OPERANDS((0, callee), (1, environmentChain), (2, argsObject)) 9558 }; 9559 9560 class MMaybeExtractAwaitValue : public MBinaryInstruction, 9561 public BoxPolicy<0>::Data { 9562 explicit MMaybeExtractAwaitValue(MDefinition* value, MDefinition* canSkip) 9563 : MBinaryInstruction(classOpcode, value, canSkip) { 9564 setResultType(MIRType::Value); 9565 } 9566 9567 public: 9568 INSTRUCTION_HEADER(MaybeExtractAwaitValue) 9569 TRIVIAL_NEW_WRAPPERS 9570 NAMED_OPERANDS((0, value), (1, canSkip)) 9571 }; 9572 9573 class MAtomicIsLockFree : public MUnaryInstruction, 9574 public ConvertToInt32Policy<0>::Data { 9575 explicit MAtomicIsLockFree(MDefinition* value) 9576 : MUnaryInstruction(classOpcode, value) { 9577 setResultType(MIRType::Boolean); 9578 setMovable(); 9579 } 9580 9581 public: 9582 INSTRUCTION_HEADER(AtomicIsLockFree) 9583 TRIVIAL_NEW_WRAPPERS 9584 9585 MDefinition* foldsTo(TempAllocator& alloc) override; 9586 9587 AliasSet getAliasSet() const override { return AliasSet::None(); } 9588 9589 bool congruentTo(const MDefinition* ins) const override { 9590 return congruentIfOperandsEqual(ins); 9591 } 9592 9593 [[nodiscard]] bool writeRecoverData( 9594 CompactBufferWriter& writer) const override; 9595 bool canRecoverOnBailout() const override { return true; } 9596 9597 ALLOW_CLONE(MAtomicIsLockFree) 9598 }; 9599 9600 class MCompareExchangeTypedArrayElement 9601 : public MQuaternaryInstruction, 9602 public MixPolicy<TruncateToInt32OrToInt64Policy<2>, 9603 TruncateToInt32OrToInt64Policy<3>>::Data { 9604 Scalar::Type arrayType_; 9605 9606 explicit MCompareExchangeTypedArrayElement(MDefinition* elements, 9607 MDefinition* index, 9608 Scalar::Type arrayType, 9609 MDefinition* oldval, 9610 MDefinition* newval) 9611 : MQuaternaryInstruction(classOpcode, elements, index, oldval, newval), 9612 arrayType_(arrayType) { 9613 MOZ_ASSERT(elements->type() == MIRType::Elements); 9614 MOZ_ASSERT(index->type() == MIRType::IntPtr); 9615 setGuard(); // Not removable 9616 } 9617 9618 public: 9619 INSTRUCTION_HEADER(CompareExchangeTypedArrayElement) 9620 TRIVIAL_NEW_WRAPPERS 9621 NAMED_OPERANDS((0, elements), (1, index), (2, oldval), (3, newval)) 9622 9623 bool isByteArray() const { 9624 return (arrayType_ == Scalar::Int8 || arrayType_ == Scalar::Uint8); 9625 } 9626 Scalar::Type arrayType() const { return arrayType_; } 9627 AliasSet getAliasSet() const override { 9628 return AliasSet::Store(AliasSet::UnboxedElement); 9629 } 9630 }; 9631 9632 class MAtomicExchangeTypedArrayElement 9633 : public MTernaryInstruction, 9634 public TruncateToInt32OrToInt64Policy<2>::Data { 9635 Scalar::Type arrayType_; 9636 9637 MAtomicExchangeTypedArrayElement(MDefinition* elements, MDefinition* index, 9638 MDefinition* value, Scalar::Type arrayType) 9639 : MTernaryInstruction(classOpcode, elements, index, value), 9640 arrayType_(arrayType) { 9641 MOZ_ASSERT(elements->type() == MIRType::Elements); 9642 MOZ_ASSERT(index->type() == MIRType::IntPtr); 9643 MOZ_ASSERT(arrayType <= Scalar::Uint32 || Scalar::isBigIntType(arrayType)); 9644 setGuard(); // Not removable 9645 } 9646 9647 public: 9648 INSTRUCTION_HEADER(AtomicExchangeTypedArrayElement) 9649 TRIVIAL_NEW_WRAPPERS 9650 NAMED_OPERANDS((0, elements), (1, index), (2, value)) 9651 9652 bool isByteArray() const { 9653 return (arrayType_ == Scalar::Int8 || arrayType_ == Scalar::Uint8); 9654 } 9655 Scalar::Type arrayType() const { return arrayType_; } 9656 AliasSet getAliasSet() const override { 9657 return AliasSet::Store(AliasSet::UnboxedElement); 9658 } 9659 }; 9660 9661 class MAtomicTypedArrayElementBinop 9662 : public MTernaryInstruction, 9663 public TruncateToInt32OrToInt64Policy<2>::Data { 9664 private: 9665 AtomicOp op_; 9666 Scalar::Type arrayType_; 9667 bool forEffect_; 9668 9669 explicit MAtomicTypedArrayElementBinop(AtomicOp op, MDefinition* elements, 9670 MDefinition* index, 9671 Scalar::Type arrayType, 9672 MDefinition* value, bool forEffect) 9673 : MTernaryInstruction(classOpcode, elements, index, value), 9674 op_(op), 9675 arrayType_(arrayType), 9676 forEffect_(forEffect) { 9677 MOZ_ASSERT(elements->type() == MIRType::Elements); 9678 MOZ_ASSERT(index->type() == MIRType::IntPtr); 9679 MOZ_ASSERT(arrayType <= Scalar::Uint32 || Scalar::isBigIntType(arrayType)); 9680 setGuard(); // Not removable 9681 } 9682 9683 public: 9684 INSTRUCTION_HEADER(AtomicTypedArrayElementBinop) 9685 TRIVIAL_NEW_WRAPPERS 9686 NAMED_OPERANDS((0, elements), (1, index), (2, value)) 9687 9688 bool isByteArray() const { 9689 return (arrayType_ == Scalar::Int8 || arrayType_ == Scalar::Uint8); 9690 } 9691 AtomicOp operation() const { return op_; } 9692 Scalar::Type arrayType() const { return arrayType_; } 9693 bool isForEffect() const { return forEffect_; } 9694 AliasSet getAliasSet() const override { 9695 return AliasSet::Store(AliasSet::UnboxedElement); 9696 } 9697 }; 9698 9699 class MDebugger : public MNullaryInstruction { 9700 MDebugger() : MNullaryInstruction(classOpcode) { 9701 setBailoutKind(BailoutKind::Debugger); 9702 } 9703 9704 public: 9705 INSTRUCTION_HEADER(Debugger) 9706 TRIVIAL_NEW_WRAPPERS 9707 }; 9708 9709 // Used to load the prototype of an object known to have 9710 // a static prototype. 9711 class MObjectStaticProto : public MUnaryInstruction, 9712 public SingleObjectPolicy::Data { 9713 explicit MObjectStaticProto(MDefinition* object) 9714 : MUnaryInstruction(classOpcode, object) { 9715 setResultType(MIRType::Object); 9716 setMovable(); 9717 } 9718 9719 public: 9720 INSTRUCTION_HEADER(ObjectStaticProto) 9721 TRIVIAL_NEW_WRAPPERS 9722 NAMED_OPERANDS((0, object)) 9723 9724 bool congruentTo(const MDefinition* ins) const override { 9725 return congruentIfOperandsEqual(ins); 9726 } 9727 9728 AliasSet getAliasSet() const override { 9729 return AliasSet::Load(AliasSet::ObjectFields); 9730 } 9731 AliasType mightAlias(const MDefinition* def) const override { 9732 // These instructions never modify the [[Prototype]]. 9733 if (def->isAddAndStoreSlot() || def->isAllocateAndStoreSlot() || 9734 def->isStoreElementHole() || def->isArrayPush()) { 9735 return AliasType::NoAlias; 9736 } 9737 return AliasType::MayAlias; 9738 } 9739 }; 9740 9741 class MConstantProto : public MUnaryInstruction, 9742 public SingleObjectPolicy::Data { 9743 // NOTE: we're not going to actually use the underlying receiver object for 9744 // anything. This is just here for giving extra information to MGuardShape 9745 // to MGuardShape::mightAlias. Accordingly, we don't take it as an operand, 9746 // but instead just keep a pointer to it. This means we need to ensure it's 9747 // not discarded before we try to access it. If this is discarded, we 9748 // basically just become an MConstant for the object's proto, which is fine. 9749 const MDefinition* receiverObject_; 9750 9751 explicit MConstantProto(MDefinition* protoObject, 9752 const MDefinition* receiverObject) 9753 : MUnaryInstruction(classOpcode, protoObject), 9754 receiverObject_(receiverObject) { 9755 MOZ_ASSERT(protoObject->isConstant()); 9756 setResultType(MIRType::Object); 9757 setMovable(); 9758 } 9759 9760 ALLOW_CLONE(MConstantProto) 9761 9762 public: 9763 INSTRUCTION_HEADER(ConstantProto) 9764 TRIVIAL_NEW_WRAPPERS 9765 NAMED_OPERANDS((0, protoObject)) 9766 9767 HashNumber valueHash() const override; 9768 9769 bool congruentTo(const MDefinition* ins) const override { 9770 if (this == ins) { 9771 return true; 9772 } 9773 const MDefinition* receiverObject = getReceiverObject(); 9774 return congruentIfOperandsEqual(ins) && receiverObject && 9775 receiverObject == ins->toConstantProto()->getReceiverObject(); 9776 } 9777 9778 AliasSet getAliasSet() const override { return AliasSet::None(); } 9779 9780 const MDefinition* getReceiverObject() const { 9781 if (receiverObject_->isDiscarded()) { 9782 return nullptr; 9783 } 9784 return receiverObject_; 9785 } 9786 }; 9787 9788 class MObjectToIterator : public MUnaryInstruction, 9789 public ObjectPolicy<0>::Data { 9790 NativeIteratorListHead* enumeratorsAddr_; 9791 bool wantsIndices_ = false; 9792 bool skipRegistration_ = false; 9793 9794 explicit MObjectToIterator(MDefinition* object, 9795 NativeIteratorListHead* enumeratorsAddr) 9796 : MUnaryInstruction(classOpcode, object), 9797 enumeratorsAddr_(enumeratorsAddr) { 9798 setResultType(MIRType::Object); 9799 } 9800 9801 public: 9802 NativeIteratorListHead* enumeratorsAddr() const { return enumeratorsAddr_; } 9803 INSTRUCTION_HEADER(ObjectToIterator) 9804 TRIVIAL_NEW_WRAPPERS 9805 NAMED_OPERANDS((0, object)) 9806 9807 AliasSet getAliasSet() const override { 9808 return skipRegistration_ 9809 ? AliasSet::Load(AliasSet::ObjectFields | AliasSet::Element) 9810 : AliasSet::Store(AliasSet::Any); 9811 } 9812 9813 bool wantsIndices() const { return wantsIndices_; } 9814 void setWantsIndices(bool value) { wantsIndices_ = value; } 9815 9816 bool skipRegistration() const { return skipRegistration_; } 9817 void setSkipRegistration(bool value) { skipRegistration_ = value; } 9818 }; 9819 9820 class MPostIntPtrConversion : public MUnaryInstruction, 9821 public NoTypePolicy::Data { 9822 explicit MPostIntPtrConversion(MDefinition* input) 9823 : MUnaryInstruction(classOpcode, input) { 9824 // Passes through the input. 9825 setResultType(input->type()); 9826 9827 // Note: Must be non-movable so we can attach a resume point. 9828 } 9829 9830 public: 9831 INSTRUCTION_HEADER(PostIntPtrConversion) 9832 TRIVIAL_NEW_WRAPPERS 9833 9834 AliasSet getAliasSet() const override { return AliasSet::None(); } 9835 }; 9836 9837 class MCanonicalizeNaN : public MUnaryInstruction, public NoTypePolicy::Data { 9838 explicit MCanonicalizeNaN(MDefinition* input) 9839 : MUnaryInstruction(classOpcode, input) { 9840 MOZ_ASSERT(IsFloatingPointType(input->type())); 9841 setResultType(input->type()); 9842 setMovable(); 9843 } 9844 9845 public: 9846 INSTRUCTION_HEADER(CanonicalizeNaN) 9847 TRIVIAL_NEW_WRAPPERS 9848 9849 AliasSet getAliasSet() const override { return AliasSet::None(); } 9850 9851 bool congruentTo(const MDefinition* ins) const override { 9852 return congruentIfOperandsEqual(ins); 9853 } 9854 9855 bool canProduceFloat32() const override { return type() == MIRType::Float32; } 9856 9857 ALLOW_CLONE(MCanonicalizeNaN) 9858 }; 9859 9860 class MRotate : public MBinaryInstruction, public NoTypePolicy::Data { 9861 bool isLeftRotate_; 9862 9863 MRotate(MDefinition* input, MDefinition* count, MIRType type, 9864 bool isLeftRotate) 9865 : MBinaryInstruction(classOpcode, input, count), 9866 isLeftRotate_(isLeftRotate) { 9867 setMovable(); 9868 setResultType(type); 9869 // Prevent reordering. Although there's no problem eliding call result 9870 // definitions, there's also no need, as they cause no codegen. 9871 setGuard(); 9872 } 9873 9874 public: 9875 INSTRUCTION_HEADER(Rotate) 9876 TRIVIAL_NEW_WRAPPERS 9877 NAMED_OPERANDS((0, input), (1, count)) 9878 9879 AliasSet getAliasSet() const override { return AliasSet::None(); } 9880 bool congruentTo(const MDefinition* ins) const override { 9881 return congruentIfOperandsEqual(ins) && 9882 ins->toRotate()->isLeftRotate() == isLeftRotate_; 9883 } 9884 9885 bool isLeftRotate() const { return isLeftRotate_; } 9886 9887 ALLOW_CLONE(MRotate) 9888 }; 9889 9890 class MReinterpretCast : public MUnaryInstruction, public NoTypePolicy::Data { 9891 MReinterpretCast(MDefinition* val, MIRType toType) 9892 : MUnaryInstruction(classOpcode, val) { 9893 switch (val->type()) { 9894 case MIRType::Int32: 9895 MOZ_ASSERT(toType == MIRType::Float32); 9896 break; 9897 case MIRType::Float32: 9898 MOZ_ASSERT(toType == MIRType::Int32); 9899 break; 9900 case MIRType::Double: 9901 MOZ_ASSERT(toType == MIRType::Int64); 9902 break; 9903 case MIRType::Int64: 9904 MOZ_ASSERT(toType == MIRType::Double); 9905 break; 9906 default: 9907 MOZ_CRASH("unexpected reinterpret conversion"); 9908 } 9909 setMovable(); 9910 setResultType(toType); 9911 } 9912 9913 public: 9914 INSTRUCTION_HEADER(ReinterpretCast) 9915 TRIVIAL_NEW_WRAPPERS 9916 9917 AliasSet getAliasSet() const override { return AliasSet::None(); } 9918 bool congruentTo(const MDefinition* ins) const override { 9919 // No need to check type() here, because congruentIfOperandsEqual will 9920 // check it. 9921 return congruentIfOperandsEqual(ins); 9922 } 9923 9924 ALLOW_CLONE(MReinterpretCast) 9925 }; 9926 9927 // Used by MIR building to represent the bytecode result of an operation for 9928 // which an MBail was generated, to balance the basic block's MDefinition stack. 9929 class MUnreachableResult : public MNullaryInstruction { 9930 explicit MUnreachableResult(MIRType type) : MNullaryInstruction(classOpcode) { 9931 MOZ_ASSERT(type != MIRType::None); 9932 setResultType(type); 9933 } 9934 9935 public: 9936 INSTRUCTION_HEADER(UnreachableResult) 9937 TRIVIAL_NEW_WRAPPERS 9938 9939 bool congruentTo(const MDefinition* ins) const override { 9940 return congruentIfOperandsEqual(ins); 9941 } 9942 AliasSet getAliasSet() const override { return AliasSet::None(); } 9943 }; 9944 9945 #ifdef FUZZING_JS_FUZZILLI 9946 class MFuzzilliHash : public MUnaryInstruction, public NoTypePolicy::Data { 9947 explicit MFuzzilliHash(MDefinition* obj) 9948 : MUnaryInstruction(classOpcode, obj) { 9949 setResultType(MIRType::Int32); 9950 setMovable(); 9951 } 9952 9953 public: 9954 INSTRUCTION_HEADER(FuzzilliHash); 9955 TRIVIAL_NEW_WRAPPERS 9956 ALLOW_CLONE(MFuzzilliHash) 9957 9958 # ifdef DEBUG 9959 bool isConsistentFloat32Use(MUse* use) const override { return true; } 9960 # endif 9961 9962 AliasSet getAliasSet() const override { 9963 MDefinition* obj = getOperand(0); 9964 if (obj->type() == MIRType::Object || obj->type() == MIRType::Value) { 9965 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot | 9966 AliasSet::DynamicSlot | AliasSet::Element | 9967 AliasSet::UnboxedElement); 9968 } 9969 return AliasSet::None(); 9970 } 9971 }; 9972 9973 class MFuzzilliHashStore : public MUnaryInstruction, public NoTypePolicy::Data { 9974 explicit MFuzzilliHashStore(MDefinition* obj) 9975 : MUnaryInstruction(classOpcode, obj) { 9976 MOZ_ASSERT(obj->type() == MIRType::Int32); 9977 9978 setResultType(MIRType::None); 9979 } 9980 9981 public: 9982 INSTRUCTION_HEADER(FuzzilliHashStore); 9983 TRIVIAL_NEW_WRAPPERS 9984 ALLOW_CLONE(MFuzzilliHashStore) 9985 9986 // this is a store and hence effectful, however no other load can 9987 // alias with the store 9988 AliasSet getAliasSet() const override { 9989 return AliasSet::Store(AliasSet::FuzzilliHash); 9990 } 9991 }; 9992 #endif 9993 9994 void MUse::init(MDefinition* producer, MNode* consumer) { 9995 MOZ_ASSERT(!consumer_, "Initializing MUse that already has a consumer"); 9996 MOZ_ASSERT(!producer_, "Initializing MUse that already has a producer"); 9997 initUnchecked(producer, consumer); 9998 } 9999 10000 void MUse::initUnchecked(MDefinition* producer, MNode* consumer) { 10001 MOZ_ASSERT(consumer, "Initializing to null consumer"); 10002 consumer_ = consumer; 10003 producer_ = producer; 10004 producer_->addUseUnchecked(this); 10005 } 10006 10007 void MUse::initUncheckedWithoutProducer(MNode* consumer) { 10008 MOZ_ASSERT(consumer, "Initializing to null consumer"); 10009 consumer_ = consumer; 10010 producer_ = nullptr; 10011 } 10012 10013 void MUse::replaceProducer(MDefinition* producer) { 10014 MOZ_ASSERT(consumer_, "Resetting MUse without a consumer"); 10015 producer_->removeUse(this); 10016 producer_ = producer; 10017 producer_->addUse(this); 10018 } 10019 10020 void MUse::releaseProducer() { 10021 MOZ_ASSERT(consumer_, "Clearing MUse without a consumer"); 10022 producer_->removeUse(this); 10023 producer_ = nullptr; 10024 } 10025 10026 // Implement cast functions now that the compiler can see the inheritance. 10027 10028 MDefinition* MNode::toDefinition() { 10029 MOZ_ASSERT(isDefinition()); 10030 return (MDefinition*)this; 10031 } 10032 10033 MResumePoint* MNode::toResumePoint() { 10034 MOZ_ASSERT(isResumePoint()); 10035 return (MResumePoint*)this; 10036 } 10037 10038 MInstruction* MDefinition::toInstruction() { 10039 MOZ_ASSERT(!isPhi()); 10040 return (MInstruction*)this; 10041 } 10042 10043 const MInstruction* MDefinition::toInstruction() const { 10044 MOZ_ASSERT(!isPhi()); 10045 return (const MInstruction*)this; 10046 } 10047 10048 MControlInstruction* MDefinition::toControlInstruction() { 10049 MOZ_ASSERT(isControlInstruction()); 10050 return (MControlInstruction*)this; 10051 } 10052 10053 MConstant* MDefinition::maybeConstantValue() { 10054 MDefinition* op = this; 10055 if (op->isBox()) { 10056 op = op->toBox()->input(); 10057 } 10058 if (op->isConstant()) { 10059 return op->toConstant(); 10060 } 10061 return nullptr; 10062 } 10063 10064 } // namespace jit 10065 } // namespace js 10066 10067 #endif /* jit_MIR_h */