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