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