1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /*
8 * Everything needed to build actual MIR instructions: the actual opcodes and
9 * instructions, the instruction interface, and use chains.
10 */
11
12 #ifndef jit_MIR_h
13 #define jit_MIR_h
14
15 #include "mozilla/Array.h"
16 #include "mozilla/DebugOnly.h"
17
18 #include "builtin/SIMD.h"
19 #include "jit/AtomicOp.h"
20 #include "jit/BaselineIC.h"
21 #include "jit/FixedList.h"
22 #include "jit/InlineList.h"
23 #include "jit/JitAllocPolicy.h"
24 #include "jit/MacroAssembler.h"
25 #include "jit/MOpcodes.h"
26 #include "jit/TypedObjectPrediction.h"
27 #include "jit/TypePolicy.h"
28 #include "vm/ArrayObject.h"
29 #include "vm/ScopeObject.h"
30 #include "vm/SharedMem.h"
31 #include "vm/TypedArrayCommon.h"
32 #include "vm/UnboxedObject.h"
33
34 // Undo windows.h damage on Win64
35 #undef MemoryBarrier
36
37 namespace js {
38
39 class StringObject;
40
41 namespace jit {
42
43 class BaselineInspector;
44 class Range;
45
46 static inline
MIRTypeFromValue(const js::Value & vp)47 MIRType MIRTypeFromValue(const js::Value& vp)
48 {
49 if (vp.isDouble())
50 return MIRType_Double;
51 if (vp.isMagic()) {
52 switch (vp.whyMagic()) {
53 case JS_OPTIMIZED_ARGUMENTS:
54 return MIRType_MagicOptimizedArguments;
55 case JS_OPTIMIZED_OUT:
56 return MIRType_MagicOptimizedOut;
57 case JS_ELEMENTS_HOLE:
58 return MIRType_MagicHole;
59 case JS_IS_CONSTRUCTING:
60 return MIRType_MagicIsConstructing;
61 case JS_UNINITIALIZED_LEXICAL:
62 return MIRType_MagicUninitializedLexical;
63 default:
64 MOZ_ASSERT(!"Unexpected magic constant");
65 }
66 }
67 return MIRTypeFromValueType(vp.extractNonDoubleType());
68 }
69
70 #define MIR_FLAG_LIST(_) \
71 _(InWorklist) \
72 _(EmittedAtUses) \
73 _(Commutative) \
74 _(Movable) /* Allow passes like LICM to move this instruction */ \
75 _(Lowered) /* (Debug only) has a virtual register */ \
76 _(Guard) /* Not removable if uses == 0 */ \
77 \
78 /* Flag an instruction to be considered as a Guard if the instructions
79 * bails out on some inputs.
80 *
81 * Some optimizations can replace an instruction, and leave its operands
82 * unused. When the type information of the operand got used as a
83 * predicate of the transformation, then we have to flag the operands as
84 * GuardRangeBailouts.
85 *
86 * This flag prevents further optimization of instructions, which
87 * might remove the run-time checks (bailout conditions) used as a
88 * predicate of the previous transformation.
89 */ \
90 _(GuardRangeBailouts) \
91 \
92 /* Keep the flagged instruction in resume points and do not substitute this
93 * instruction by an UndefinedValue. This might be used by call inlining
94 * when a function argument is not used by the inlined instructions.
95 */ \
96 _(ImplicitlyUsed) \
97 \
98 /* The instruction has been marked dead for lazy removal from resume
99 * points.
100 */ \
101 _(Unused) \
102 \
103 /* When a branch is removed, the uses of multiple instructions are removed.
104 * The removal of branches is based on hypotheses. These hypotheses might
105 * fail, in which case we need to bailout from the current code.
106 *
107 * When we implement a destructive optimization, we need to consider the
108 * failing cases, and consider the fact that we might resume the execution
109 * into a branch which was removed from the compiler. As such, a
110 * destructive optimization need to take into acount removed branches.
111 *
112 * In order to let destructive optimizations know about removed branches, we
113 * have to annotate instructions with the UseRemoved flag. This flag
114 * annotates instruction which were used in removed branches.
115 */ \
116 _(UseRemoved) \
117 \
118 /* Marks if the current instruction should go to the bailout paths instead
119 * of producing code as part of the control flow. This flag can only be set
120 * on instructions which are only used by ResumePoint or by other flagged
121 * instructions.
122 */ \
123 _(RecoveredOnBailout) \
124 \
125 /* Some instructions might represent an object, but the memory of these
126 * objects might be incomplete if we have not recovered all the stores which
127 * were supposed to happen before. This flag is used to annotate
128 * instructions which might return a pointer to a memory area which is not
129 * yet fully initialized. This flag is used to ensure that stores are
130 * executed before returning the value.
131 */ \
132 _(IncompleteObject) \
133 \
134 /* The current instruction got discarded from the MIR Graph. This is useful
135 * when we want to iterate over resume points and instructions, while
136 * handling instructions which are discarded without reporting to the
137 * iterator.
138 */ \
139 _(Discarded)
140
141 class MDefinition;
142 class MInstruction;
143 class MBasicBlock;
144 class MNode;
145 class MUse;
146 class MPhi;
147 class MIRGraph;
148 class MResumePoint;
149 class MControlInstruction;
150
151 // Represents a use of a node.
152 class MUse : public TempObject, public InlineListNode<MUse>
153 {
154 // Grant access to setProducerUnchecked.
155 friend class MDefinition;
156 friend class MPhi;
157
158 MDefinition* producer_; // MDefinition that is being used.
159 MNode* consumer_; // The node that is using this operand.
160
161 // Low-level unchecked edit method for replaceAllUsesWith and
162 // MPhi::removeOperand. This doesn't update use lists!
163 // replaceAllUsesWith and MPhi::removeOperand do that manually.
setProducerUnchecked(MDefinition * producer)164 void setProducerUnchecked(MDefinition* producer) {
165 MOZ_ASSERT(consumer_);
166 MOZ_ASSERT(producer_);
167 MOZ_ASSERT(producer);
168 producer_ = producer;
169 }
170
171 public:
172 // Default constructor for use in vectors.
MUse()173 MUse()
174 : producer_(nullptr), consumer_(nullptr)
175 { }
176
177 // Move constructor for use in vectors. When an MUse is moved, it stays
178 // in its containing use list.
MUse(MUse && other)179 MUse(MUse&& other)
180 : InlineListNode<MUse>(mozilla::Move(other)),
181 producer_(other.producer_), consumer_(other.consumer_)
182 { }
183
184 // Construct an MUse initialized with |producer| and |consumer|.
MUse(MDefinition * producer,MNode * consumer)185 MUse(MDefinition* producer, MNode* consumer)
186 {
187 initUnchecked(producer, consumer);
188 }
189
190 // Set this use, which was previously clear.
191 inline void init(MDefinition* producer, MNode* consumer);
192 // Like init, but works even when the use contains uninitialized data.
193 inline void initUnchecked(MDefinition* producer, MNode* consumer);
194 // Like initUnchecked, but set the producer to nullptr.
195 inline void initUncheckedWithoutProducer(MNode* consumer);
196 // Set this use, which was not previously clear.
197 inline void replaceProducer(MDefinition* producer);
198 // Clear this use.
199 inline void releaseProducer();
200
producer()201 MDefinition* producer() const {
202 MOZ_ASSERT(producer_ != nullptr);
203 return producer_;
204 }
hasProducer()205 bool hasProducer() const {
206 return producer_ != nullptr;
207 }
consumer()208 MNode* consumer() const {
209 MOZ_ASSERT(consumer_ != nullptr);
210 return consumer_;
211 }
212
213 #ifdef DEBUG
214 // Return the operand index of this MUse in its consumer. This is DEBUG-only
215 // as normal code should instead to call indexOf on the casted consumer
216 // directly, to allow it to be devirtualized and inlined.
217 size_t index() const;
218 #endif
219 };
220
221 typedef InlineList<MUse>::iterator MUseIterator;
222
223 // A node is an entry in the MIR graph. It has two kinds:
224 // MInstruction: an instruction which appears in the IR stream.
225 // MResumePoint: a list of instructions that correspond to the state of the
226 // interpreter/Baseline stack.
227 //
228 // Nodes can hold references to MDefinitions. Each MDefinition has a list of
229 // nodes holding such a reference (its use chain).
230 class MNode : public TempObject
231 {
232 protected:
233 MBasicBlock* block_; // Containing basic block.
234
235 public:
236 enum Kind {
237 Definition,
238 ResumePoint
239 };
240
MNode()241 MNode()
242 : block_(nullptr)
243 { }
244
MNode(MBasicBlock * block)245 explicit MNode(MBasicBlock* block)
246 : block_(block)
247 { }
248
249 virtual Kind kind() const = 0;
250
251 // Returns the definition at a given operand.
252 virtual MDefinition* getOperand(size_t index) const = 0;
253 virtual size_t numOperands() const = 0;
254 virtual size_t indexOf(const MUse* u) const = 0;
255
isDefinition()256 bool isDefinition() const {
257 return kind() == Definition;
258 }
isResumePoint()259 bool isResumePoint() const {
260 return kind() == ResumePoint;
261 }
block()262 MBasicBlock* block() const {
263 return block_;
264 }
265 MBasicBlock* caller() const;
266
267 // Sets an already set operand, updating use information. If you're looking
268 // for setOperand, this is probably what you want.
269 virtual void replaceOperand(size_t index, MDefinition* operand) = 0;
270
271 // Resets the operand to an uninitialized state, breaking the link
272 // with the previous operand's producer.
releaseOperand(size_t index)273 void releaseOperand(size_t index) {
274 getUseFor(index)->releaseProducer();
275 }
hasOperand(size_t index)276 bool hasOperand(size_t index) const {
277 return getUseFor(index)->hasProducer();
278 }
279
280 inline MDefinition* toDefinition();
281 inline MResumePoint* toResumePoint();
282
283 virtual bool writeRecoverData(CompactBufferWriter& writer) const;
284
285 virtual void dump(GenericPrinter& out) const = 0;
286 virtual void dump() const = 0;
287
288 protected:
289 // Need visibility on getUseFor to avoid O(n^2) complexity.
290 friend void AssertBasicGraphCoherency(MIRGraph& graph);
291
292 // Gets the MUse corresponding to given operand.
293 virtual MUse* getUseFor(size_t index) = 0;
294 virtual const MUse* getUseFor(size_t index) const = 0;
295 };
296
297 class AliasSet {
298 private:
299 uint32_t flags_;
300
301 public:
302 enum Flag {
303 None_ = 0,
304 ObjectFields = 1 << 0, // shape, class, slots, length etc.
305 Element = 1 << 1, // A Value member of obj->elements or
306 // a typed object.
307 UnboxedElement = 1 << 2, // An unboxed scalar or reference member of
308 // a typed array, typed object, or unboxed
309 // object.
310 DynamicSlot = 1 << 3, // A Value member of obj->slots.
311 FixedSlot = 1 << 4, // A Value member of obj->fixedSlots().
312 DOMProperty = 1 << 5, // A DOM property
313 FrameArgument = 1 << 6, // An argument kept on the stack frame
314 AsmJSGlobalVar = 1 << 7, // An asm.js global var
315 AsmJSHeap = 1 << 8, // An asm.js heap load
316 TypedArrayLength = 1 << 9,// A typed array's length
317 Last = TypedArrayLength,
318 Any = Last | (Last - 1),
319
320 NumCategories = 10,
321
322 // Indicates load or store.
323 Store_ = 1 << 31
324 };
325
326 static_assert((1 << NumCategories) - 1 == Any,
327 "NumCategories must include all flags present in Any");
328
AliasSet(uint32_t flags)329 explicit AliasSet(uint32_t flags)
330 : flags_(flags)
331 {
332 }
333
334 public:
isNone()335 inline bool isNone() const {
336 return flags_ == None_;
337 }
flags()338 uint32_t flags() const {
339 return flags_ & Any;
340 }
isStore()341 inline bool isStore() const {
342 return !!(flags_ & Store_);
343 }
isLoad()344 inline bool isLoad() const {
345 return !isStore() && !isNone();
346 }
347 inline AliasSet operator |(const AliasSet& other) const {
348 return AliasSet(flags_ | other.flags_);
349 }
350 inline AliasSet operator&(const AliasSet& other) const {
351 return AliasSet(flags_ & other.flags_);
352 }
None()353 static AliasSet None() {
354 return AliasSet(None_);
355 }
Load(uint32_t flags)356 static AliasSet Load(uint32_t flags) {
357 MOZ_ASSERT(flags && !(flags & Store_));
358 return AliasSet(flags);
359 }
Store(uint32_t flags)360 static AliasSet Store(uint32_t flags) {
361 MOZ_ASSERT(flags && !(flags & Store_));
362 return AliasSet(flags | Store_);
363 }
BoxedOrUnboxedElements(JSValueType type)364 static uint32_t BoxedOrUnboxedElements(JSValueType type) {
365 return (type == JSVAL_TYPE_MAGIC) ? Element : UnboxedElement;
366 }
367 };
368
369 // An MDefinition is an SSA name.
370 class MDefinition : public MNode
371 {
372 friend class MBasicBlock;
373
374 public:
375 enum Opcode {
376 # define DEFINE_OPCODES(op) Op_##op,
377 MIR_OPCODE_LIST(DEFINE_OPCODES)
378 # undef DEFINE_OPCODES
379 Op_Invalid
380 };
381
382 private:
383 InlineList<MUse> uses_; // Use chain.
384 uint32_t id_; // Instruction ID, which after block re-ordering
385 // is sorted within a basic block.
386 uint32_t flags_; // Bit flags.
387 Range* range_; // Any computed range for this def.
388 MIRType resultType_; // Representation of result type.
389 TemporaryTypeSet* resultTypeSet_; // Optional refinement of the result type.
390 union {
391 MInstruction* dependency_; // Implicit dependency (store, call, etc.) of this instruction.
392 // Used by alias analysis, GVN and LICM.
393 uint32_t virtualRegister_; // Used by lowering to map definitions to virtual registers.
394 };
395
396 // Track bailouts by storing the current pc in MIR instruction. Also used
397 // for profiling and keeping track of what the last known pc was.
398 const BytecodeSite* trackedSite_;
399
400 private:
401 enum Flag {
402 None = 0,
403 # define DEFINE_FLAG(flag) flag,
404 MIR_FLAG_LIST(DEFINE_FLAG)
405 # undef DEFINE_FLAG
406 Total
407 };
408
hasFlags(uint32_t flags)409 bool hasFlags(uint32_t flags) const {
410 return (flags_ & flags) == flags;
411 }
removeFlags(uint32_t flags)412 void removeFlags(uint32_t flags) {
413 flags_ &= ~flags;
414 }
setFlags(uint32_t flags)415 void setFlags(uint32_t flags) {
416 flags_ |= flags;
417 }
418
419 protected:
setBlock(MBasicBlock * block)420 virtual void setBlock(MBasicBlock* block) {
421 block_ = block;
422 }
423
424 static HashNumber addU32ToHash(HashNumber hash, uint32_t data);
425
426 public:
MDefinition()427 MDefinition()
428 : id_(0),
429 flags_(0),
430 range_(nullptr),
431 resultType_(MIRType_None),
432 resultTypeSet_(nullptr),
433 dependency_(nullptr),
434 trackedSite_(nullptr)
435 { }
436
437 // Copying a definition leaves the list of uses and the block empty.
MDefinition(const MDefinition & other)438 explicit MDefinition(const MDefinition& other)
439 : id_(0),
440 flags_(other.flags_),
441 range_(other.range_),
442 resultType_(other.resultType_),
443 resultTypeSet_(other.resultTypeSet_),
444 dependency_(other.dependency_),
445 trackedSite_(other.trackedSite_)
446 { }
447
448 virtual Opcode op() const = 0;
449 virtual const char* opName() const = 0;
450 virtual void accept(MDefinitionVisitor* visitor) = 0;
451
452 void printName(GenericPrinter& out) const;
453 static void PrintOpcodeName(GenericPrinter& out, Opcode op);
454 virtual void printOpcode(GenericPrinter& out) const;
455 void dump(GenericPrinter& out) const override;
456 void dump() const override;
457 void dumpLocation(GenericPrinter& out) const;
458 void dumpLocation() const;
459
460 // For LICM.
neverHoist()461 virtual bool neverHoist() const { return false; }
462
463 // Also for LICM. Test whether this definition is likely to be a call, which
464 // would clobber all or many of the floating-point registers, such that
465 // hoisting floating-point constants out of containing loops isn't likely to
466 // be worthwhile.
possiblyCalls()467 virtual bool possiblyCalls() const { return false; }
468
setTrackedSite(const BytecodeSite * site)469 void setTrackedSite(const BytecodeSite* site) {
470 MOZ_ASSERT(site);
471 trackedSite_ = site;
472 }
trackedSite()473 const BytecodeSite* trackedSite() const {
474 return trackedSite_;
475 }
trackedPc()476 jsbytecode* trackedPc() const {
477 return trackedSite_ ? trackedSite_->pc() : nullptr;
478 }
trackedTree()479 InlineScriptTree* trackedTree() const {
480 return trackedSite_ ? trackedSite_->tree() : nullptr;
481 }
trackedOptimizations()482 TrackedOptimizations* trackedOptimizations() const {
483 return trackedSite_ && trackedSite_->hasOptimizations()
484 ? trackedSite_->optimizations()
485 : nullptr;
486 }
487
profilerLeaveScript()488 JSScript* profilerLeaveScript() const {
489 return trackedTree()->outermostCaller()->script();
490 }
491
profilerLeavePc()492 jsbytecode* profilerLeavePc() const {
493 // If this is in a top-level function, use the pc directly.
494 if (trackedTree()->isOutermostCaller())
495 return trackedPc();
496
497 // Walk up the InlineScriptTree chain to find the top-most callPC
498 InlineScriptTree* curTree = trackedTree();
499 InlineScriptTree* callerTree = curTree->caller();
500 while (!callerTree->isOutermostCaller()) {
501 curTree = callerTree;
502 callerTree = curTree->caller();
503 }
504
505 // Return the callPc of the topmost inlined script.
506 return curTree->callerPc();
507 }
508
509 // Return the range of this value, *before* any bailout checks. Contrast
510 // this with the type() method, and the Range constructor which takes an
511 // MDefinition*, which describe the value *after* any bailout checks.
512 //
513 // Warning: Range analysis is removing the bit-operations such as '| 0' at
514 // the end of the transformations. Using this function to analyse any
515 // operands after the truncate phase of the range analysis will lead to
516 // errors. Instead, one should define the collectRangeInfoPreTrunc() to set
517 // the right set of flags which are dependent on the range of the inputs.
range()518 Range* range() const {
519 MOZ_ASSERT(type() != MIRType_None);
520 return range_;
521 }
setRange(Range * range)522 void setRange(Range* range) {
523 MOZ_ASSERT(type() != MIRType_None);
524 range_ = range;
525 }
526
527 virtual HashNumber valueHash() const;
congruentTo(const MDefinition * ins)528 virtual bool congruentTo(const MDefinition* ins) const {
529 return false;
530 }
531 bool congruentIfOperandsEqual(const MDefinition* ins) const;
532 virtual MDefinition* foldsTo(TempAllocator& alloc);
533 virtual void analyzeEdgeCasesForward();
534 virtual void analyzeEdgeCasesBackward();
535
536 // When a floating-point value is used by nodes which would prefer to
537 // recieve integer inputs, we may be able to help by computing our result
538 // into an integer directly.
539 //
540 // A value can be truncated in 4 differents ways:
541 // 1. Ignore Infinities (x / 0 --> 0).
542 // 2. Ignore overflow (INT_MIN / -1 == (INT_MAX + 1) --> INT_MIN)
543 // 3. Ignore negative zeros. (-0 --> 0)
544 // 4. Ignore remainder. (3 / 4 --> 0)
545 //
546 // Indirect truncation is used to represent that we are interested in the
547 // truncated result, but only if it can safely flow into operations which
548 // are computed modulo 2^32, such as (2) and (3). Infinities are not safe,
549 // as they would have absorbed other math operations. Remainders are not
550 // safe, as fractions can be scaled up by multiplication.
551 //
552 // Division is a particularly interesting node here because it covers all 4
553 // cases even when its own operands are integers.
554 //
555 // Note that these enum values are ordered from least value-modifying to
556 // most value-modifying, and code relies on this ordering.
557 enum TruncateKind {
558 // No correction.
559 NoTruncate = 0,
560 // An integer is desired, but we can't skip bailout checks.
561 TruncateAfterBailouts = 1,
562 // The value will be truncated after some arithmetic (see above).
563 IndirectTruncate = 2,
564 // Direct and infallible truncation to int32.
565 Truncate = 3
566 };
567
568 // |needTruncation| records the truncation kind of the results, such that it
569 // can be used to truncate the operands of this instruction. If
570 // |needTruncation| function returns true, then the |truncate| function is
571 // called on the same instruction to mutate the instruction, such as
572 // updating the return type, the range and the specialization of the
573 // instruction.
574 virtual bool needTruncation(TruncateKind kind);
575 virtual void truncate();
576
577 // Determine what kind of truncate this node prefers for the operand at the
578 // given index.
579 virtual TruncateKind operandTruncateKind(size_t index) const;
580
581 // Compute an absolute or symbolic range for the value of this node.
computeRange(TempAllocator & alloc)582 virtual void computeRange(TempAllocator& alloc) {
583 }
584
585 // Collect information from the pre-truncated ranges.
collectRangeInfoPreTrunc()586 virtual void collectRangeInfoPreTrunc() {
587 }
588
kind()589 MNode::Kind kind() const override {
590 return MNode::Definition;
591 }
592
id()593 uint32_t id() const {
594 MOZ_ASSERT(block_);
595 return id_;
596 }
setId(uint32_t id)597 void setId(uint32_t id) {
598 id_ = id;
599 }
600
601 #define FLAG_ACCESSOR(flag) \
602 bool is##flag() const {\
603 return hasFlags(1 << flag);\
604 }\
605 void set##flag() {\
606 MOZ_ASSERT(!hasFlags(1 << flag));\
607 setFlags(1 << flag);\
608 }\
609 void setNot##flag() {\
610 MOZ_ASSERT(hasFlags(1 << flag));\
611 removeFlags(1 << flag);\
612 }\
613 void set##flag##Unchecked() {\
614 setFlags(1 << flag);\
615 } \
616 void setNot##flag##Unchecked() {\
617 removeFlags(1 << flag);\
618 }
619
MIR_FLAG_LIST(FLAG_ACCESSOR)620 MIR_FLAG_LIST(FLAG_ACCESSOR)
621 #undef FLAG_ACCESSOR
622
623 // Return the type of this value. This may be speculative, and enforced
624 // dynamically with the use of bailout checks. If all the bailout checks
625 // pass, the value will have this type.
626 //
627 // Unless this is an MUrsh that has bailouts disabled, which, as a special
628 // case, may return a value in (INT32_MAX,UINT32_MAX] even when its type()
629 // is MIRType_Int32.
630 MIRType type() const {
631 return resultType_;
632 }
633
resultTypeSet()634 TemporaryTypeSet* resultTypeSet() const {
635 return resultTypeSet_;
636 }
637 bool emptyResultTypeSet() const;
638
mightBeType(MIRType type)639 bool mightBeType(MIRType type) const {
640 MOZ_ASSERT(type != MIRType_Value);
641 MOZ_ASSERT(type != MIRType_ObjectOrNull);
642
643 if (type == this->type())
644 return true;
645
646 if (this->type() == MIRType_ObjectOrNull)
647 return type == MIRType_Object || type == MIRType_Null;
648
649 if (this->type() == MIRType_Value)
650 return !resultTypeSet() || resultTypeSet()->mightBeMIRType(type);
651
652 return false;
653 }
654
655 bool mightBeMagicType() const;
656
657 bool maybeEmulatesUndefined(CompilerConstraintList* constraints);
658
659 // Float32 specialization operations (see big comment in IonAnalysis before the Float32
660 // specialization algorithm).
isFloat32Commutative()661 virtual bool isFloat32Commutative() const { return false; }
canProduceFloat32()662 virtual bool canProduceFloat32() const { return false; }
canConsumeFloat32(MUse * use)663 virtual bool canConsumeFloat32(MUse* use) const { return false; }
trySpecializeFloat32(TempAllocator & alloc)664 virtual void trySpecializeFloat32(TempAllocator& alloc) {}
665 #ifdef DEBUG
666 // Used during the pass that checks that Float32 flow into valid MDefinitions
isConsistentFloat32Use(MUse * use)667 virtual bool isConsistentFloat32Use(MUse* use) const {
668 return type() == MIRType_Float32 || canConsumeFloat32(use);
669 }
670 #endif
671
672 // Returns the beginning of this definition's use chain.
usesBegin()673 MUseIterator usesBegin() const {
674 return uses_.begin();
675 }
676
677 // Returns the end of this definition's use chain.
usesEnd()678 MUseIterator usesEnd() const {
679 return uses_.end();
680 }
681
canEmitAtUses()682 bool canEmitAtUses() const {
683 return !isEmittedAtUses();
684 }
685
686 // Removes a use at the given position
removeUse(MUse * use)687 void removeUse(MUse* use) {
688 uses_.remove(use);
689 }
690
691 #if defined(DEBUG) || defined(JS_JITSPEW)
692 // Number of uses of this instruction. This function is only available
693 // in DEBUG mode since it requires traversing the list. Most users should
694 // use hasUses() or hasOneUse() instead.
695 size_t useCount() const;
696
697 // Number of uses of this instruction (only counting MDefinitions, ignoring
698 // MResumePoints). This function is only available in DEBUG mode since it
699 // requires traversing the list. Most users should use hasUses() or
700 // hasOneUse() instead.
701 size_t defUseCount() const;
702 #endif
703
704 // Test whether this MDefinition has exactly one use.
705 bool hasOneUse() const;
706
707 // Test whether this MDefinition has exactly one use.
708 // (only counting MDefinitions, ignoring MResumePoints)
709 bool hasOneDefUse() const;
710
711 // Test whether this MDefinition has at least one use.
712 // (only counting MDefinitions, ignoring MResumePoints)
713 bool hasDefUses() const;
714
715 // Test whether this MDefinition has at least one non-recovered use.
716 // (only counting MDefinitions, ignoring MResumePoints)
717 bool hasLiveDefUses() const;
718
hasUses()719 bool hasUses() const {
720 return !uses_.empty();
721 }
722
addUse(MUse * use)723 void addUse(MUse* use) {
724 MOZ_ASSERT(use->producer() == this);
725 uses_.pushFront(use);
726 }
addUseUnchecked(MUse * use)727 void addUseUnchecked(MUse* use) {
728 MOZ_ASSERT(use->producer() == this);
729 uses_.pushFrontUnchecked(use);
730 }
replaceUse(MUse * old,MUse * now)731 void replaceUse(MUse* old, MUse* now) {
732 MOZ_ASSERT(now->producer() == this);
733 uses_.replace(old, now);
734 }
735
736 // Replace the current instruction by a dominating instruction |dom| in all
737 // uses of the current instruction.
738 void replaceAllUsesWith(MDefinition* dom);
739
740 // Like replaceAllUsesWith, but doesn't set UseRemoved on |this|'s operands.
741 void justReplaceAllUsesWith(MDefinition* dom);
742
743 // Like justReplaceAllUsesWith, but doesn't replace its own use to the
744 // dominating instruction (which would introduce a circular dependency).
745 void justReplaceAllUsesWithExcept(MDefinition* dom);
746
747 // Replace the current instruction by an optimized-out constant in all uses
748 // of the current instruction. Note, that optimized-out constant should not
749 // be observed, and thus they should not flow in any computation.
750 void optimizeOutAllUses(TempAllocator& alloc);
751
752 // Replace the current instruction by a dominating instruction |dom| in all
753 // instruction, but keep the current instruction for resume point and
754 // instruction which are recovered on bailouts.
755 void replaceAllLiveUsesWith(MDefinition* dom);
756
757 // Mark this instruction as having replaced all uses of ins, as during GVN,
758 // returning false if the replacement should not be performed. For use when
759 // GVN eliminates instructions which are not equivalent to one another.
updateForReplacement(MDefinition * ins)760 virtual bool updateForReplacement(MDefinition* ins) {
761 return true;
762 }
763
setVirtualRegister(uint32_t vreg)764 void setVirtualRegister(uint32_t vreg) {
765 virtualRegister_ = vreg;
766 setLoweredUnchecked();
767 }
virtualRegister()768 uint32_t virtualRegister() const {
769 MOZ_ASSERT(isLowered());
770 return virtualRegister_;
771 }
772
773 public:
774 // Opcode testing and casts.
is()775 template<typename MIRType> bool is() const {
776 return op() == MIRType::classOpcode;
777 }
to()778 template<typename MIRType> MIRType* to() {
779 MOZ_ASSERT(this->is<MIRType>());
780 return static_cast<MIRType*>(this);
781 }
to()782 template<typename MIRType> const MIRType* to() const {
783 MOZ_ASSERT(this->is<MIRType>());
784 return static_cast<const MIRType*>(this);
785 }
786 # define OPCODE_CASTS(opcode) \
787 bool is##opcode() const { \
788 return this->is<M##opcode>(); \
789 } \
790 M##opcode* to##opcode() { \
791 return this->to<M##opcode>(); \
792 } \
793 const M##opcode* to##opcode() const { \
794 return this->to<M##opcode>(); \
795 }
MIR_OPCODE_LIST(OPCODE_CASTS)796 MIR_OPCODE_LIST(OPCODE_CASTS)
797 # undef OPCODE_CASTS
798
799 bool isConstantValue() const {
800 return isConstant() || (isBox() && getOperand(0)->isConstant());
801 }
802 const Value& constantValue();
803 const Value* constantVp();
804 bool constantToBoolean();
805
806 inline MInstruction* toInstruction();
807 inline const MInstruction* toInstruction() const;
isInstruction()808 bool isInstruction() const {
809 return !isPhi();
810 }
811
isControlInstruction()812 virtual bool isControlInstruction() const {
813 return false;
814 }
815 inline MControlInstruction* toControlInstruction();
816
setResultType(MIRType type)817 void setResultType(MIRType type) {
818 resultType_ = type;
819 }
setResultTypeSet(TemporaryTypeSet * types)820 void setResultTypeSet(TemporaryTypeSet* types) {
821 resultTypeSet_ = types;
822 }
823
dependency()824 MInstruction* dependency() const {
825 return dependency_;
826 }
setDependency(MInstruction * dependency)827 void setDependency(MInstruction* dependency) {
828 dependency_ = dependency;
829 }
getAliasSet()830 virtual AliasSet getAliasSet() const {
831 // Instructions are effectful by default.
832 return AliasSet::Store(AliasSet::Any);
833 }
isEffectful()834 bool isEffectful() const {
835 return getAliasSet().isStore();
836 }
837
838 #ifdef DEBUG
needsResumePoint()839 virtual bool needsResumePoint() const {
840 // Return whether this instruction should have its own resume point.
841 return isEffectful();
842 }
843 #endif
mightAlias(const MDefinition * store)844 virtual bool mightAlias(const MDefinition* store) const {
845 // Return whether this load may depend on the specified store, given
846 // that the alias sets intersect. This may be refined to exclude
847 // possible aliasing in cases where alias set flags are too imprecise.
848 MOZ_ASSERT(!isEffectful() && store->isEffectful());
849 MOZ_ASSERT(getAliasSet().flags() & store->getAliasSet().flags());
850 return true;
851 }
852
canRecoverOnBailout()853 virtual bool canRecoverOnBailout() const {
854 return false;
855 }
856 };
857
858 // An MUseDefIterator walks over uses in a definition, skipping any use that is
859 // not a definition. Items from the use list must not be deleted during
860 // iteration.
861 class MUseDefIterator
862 {
863 const MDefinition* def_;
864 MUseIterator current_;
865
search(MUseIterator start)866 MUseIterator search(MUseIterator start) {
867 MUseIterator i(start);
868 for (; i != def_->usesEnd(); i++) {
869 if (i->consumer()->isDefinition())
870 return i;
871 }
872 return def_->usesEnd();
873 }
874
875 public:
MUseDefIterator(const MDefinition * def)876 explicit MUseDefIterator(const MDefinition* def)
877 : def_(def),
878 current_(search(def->usesBegin()))
879 { }
880
881 explicit operator bool() const {
882 return current_ != def_->usesEnd();
883 }
884 MUseDefIterator operator ++() {
885 MOZ_ASSERT(current_ != def_->usesEnd());
886 ++current_;
887 current_ = search(current_);
888 return *this;
889 }
890 MUseDefIterator operator ++(int) {
891 MUseDefIterator old(*this);
892 operator++();
893 return old;
894 }
use()895 MUse* use() const {
896 return *current_;
897 }
def()898 MDefinition* def() const {
899 return current_->consumer()->toDefinition();
900 }
901 };
902
903 typedef Vector<MDefinition*, 8, JitAllocPolicy> MDefinitionVector;
904 typedef Vector<MInstruction*, 6, JitAllocPolicy> MInstructionVector;
905
906 class MRootList : public TempObject
907 {
908 private:
909 Vector<JSScript*, 0, JitAllocPolicy> roots_;
910
911 MRootList(const MRootList&) = delete;
912 void operator=(const MRootList&) = delete;
913
914 public:
915 explicit MRootList(TempAllocator& alloc);
916
917 void trace(JSTracer* trc);
918
append(JSScript * script)919 MOZ_MUST_USE bool append(JSScript* script) {
920 if (script)
921 return roots_.append(script);
922 return true;
923 }
924 };
925
926 // An instruction is an SSA name that is inserted into a basic block's IR
927 // stream.
928 class MInstruction
929 : public MDefinition,
930 public InlineListNode<MInstruction>
931 {
932 MResumePoint* resumePoint_;
933
934 public:
MInstruction()935 MInstruction()
936 : resumePoint_(nullptr)
937 { }
938
939 // Copying an instruction leaves the block and resume point as empty.
MInstruction(const MInstruction & other)940 explicit MInstruction(const MInstruction& other)
941 : MDefinition(other),
942 resumePoint_(nullptr)
943 { }
944
945 // Convenient function used for replacing a load by the value of the store
946 // if the types are match, and boxing the value if they do not match.
947 //
948 // Note: There is no need for such function in AsmJS functions as they do
949 // not use any MIRType_Value.
950 MDefinition* foldsToStoredValue(TempAllocator& alloc, MDefinition* loaded);
951
952 void setResumePoint(MResumePoint* resumePoint);
953
954 // Used to transfer the resume point to the rewritten instruction.
955 void stealResumePoint(MInstruction* ins);
956 void moveResumePointAsEntry();
957 void clearResumePoint();
resumePoint()958 MResumePoint* resumePoint() const {
959 return resumePoint_;
960 }
961
962 // For instructions which can be cloned with new inputs, with all other
963 // information being the same. clone() implementations do not need to worry
964 // about cloning generic MInstruction/MDefinition state like flags and
965 // resume points.
canClone()966 virtual bool canClone() const {
967 return false;
968 }
clone(TempAllocator & alloc,const MDefinitionVector & inputs)969 virtual MInstruction* clone(TempAllocator& alloc, const MDefinitionVector& inputs) const {
970 MOZ_CRASH();
971 }
972
973 // Instructions needing to hook into type analysis should return a
974 // TypePolicy.
975 virtual TypePolicy* typePolicy() = 0;
976 virtual MIRType typePolicySpecialization() = 0;
977 };
978
979 #define INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode) \
980 static const Opcode classOpcode = MDefinition::Op_##opcode; \
981 Opcode op() const override { \
982 return classOpcode; \
983 } \
984 const char* opName() const override { \
985 return #opcode; \
986 } \
987 void accept(MDefinitionVisitor* visitor) override { \
988 visitor->visit##opcode(this); \
989 }
990
991 #define INSTRUCTION_HEADER(opcode) \
992 INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(opcode) \
993 virtual TypePolicy* typePolicy() override; \
994 virtual MIRType typePolicySpecialization() override;
995
996 #define ALLOW_CLONE(typename) \
997 bool canClone() const override { \
998 return true; \
999 } \
1000 MInstruction* clone(TempAllocator& alloc, \
1001 const MDefinitionVector& inputs) const override { \
1002 MInstruction* res = new(alloc) typename(*this); \
1003 for (size_t i = 0; i < numOperands(); i++) \
1004 res->replaceOperand(i, inputs[i]); \
1005 return res; \
1006 }
1007
1008 template <size_t Arity>
1009 class MAryInstruction : public MInstruction
1010 {
1011 mozilla::Array<MUse, Arity> operands_;
1012
1013 protected:
getUseFor(size_t index)1014 MUse* getUseFor(size_t index) final override {
1015 return &operands_[index];
1016 }
getUseFor(size_t index)1017 const MUse* getUseFor(size_t index) const final override {
1018 return &operands_[index];
1019 }
initOperand(size_t index,MDefinition * operand)1020 void initOperand(size_t index, MDefinition* operand) {
1021 operands_[index].init(operand, this);
1022 }
1023
1024 public:
getOperand(size_t index)1025 MDefinition* getOperand(size_t index) const final override {
1026 return operands_[index].producer();
1027 }
numOperands()1028 size_t numOperands() const final override {
1029 return Arity;
1030 }
indexOf(const MUse * u)1031 size_t indexOf(const MUse* u) const final override {
1032 MOZ_ASSERT(u >= &operands_[0]);
1033 MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
1034 return u - &operands_[0];
1035 }
replaceOperand(size_t index,MDefinition * operand)1036 void replaceOperand(size_t index, MDefinition* operand) final override {
1037 operands_[index].replaceProducer(operand);
1038 }
1039
MAryInstruction()1040 MAryInstruction() { }
1041
MAryInstruction(const MAryInstruction<Arity> & other)1042 explicit MAryInstruction(const MAryInstruction<Arity>& other)
1043 : MInstruction(other)
1044 {
1045 for (int i = 0; i < (int) Arity; i++) // N.B. use |int| to avoid warnings when Arity == 0
1046 operands_[i].init(other.operands_[i].producer(), this);
1047 }
1048 };
1049
1050 class MNullaryInstruction
1051 : public MAryInstruction<0>,
1052 public NoTypePolicy::Data
1053 { };
1054
1055 class MUnaryInstruction : public MAryInstruction<1>
1056 {
1057 protected:
MUnaryInstruction(MDefinition * ins)1058 explicit MUnaryInstruction(MDefinition* ins)
1059 {
1060 initOperand(0, ins);
1061 }
1062
1063 public:
input()1064 MDefinition* input() const {
1065 return getOperand(0);
1066 }
1067 };
1068
1069 class MBinaryInstruction : public MAryInstruction<2>
1070 {
1071 protected:
MBinaryInstruction(MDefinition * left,MDefinition * right)1072 MBinaryInstruction(MDefinition* left, MDefinition* right)
1073 {
1074 initOperand(0, left);
1075 initOperand(1, right);
1076 }
1077
1078 public:
lhs()1079 MDefinition* lhs() const {
1080 return getOperand(0);
1081 }
rhs()1082 MDefinition* rhs() const {
1083 return getOperand(1);
1084 }
swapOperands()1085 void swapOperands() {
1086 MDefinition* temp = getOperand(0);
1087 replaceOperand(0, getOperand(1));
1088 replaceOperand(1, temp);
1089 }
1090
1091 protected:
valueHash()1092 HashNumber valueHash() const
1093 {
1094 MDefinition* lhs = getOperand(0);
1095 MDefinition* rhs = getOperand(1);
1096
1097 return op() + lhs->id() + rhs->id();
1098 }
binaryCongruentTo(const MDefinition * ins)1099 bool binaryCongruentTo(const MDefinition* ins) const
1100 {
1101 if (op() != ins->op())
1102 return false;
1103
1104 if (type() != ins->type())
1105 return false;
1106
1107 if (isEffectful() || ins->isEffectful())
1108 return false;
1109
1110 const MDefinition* left = getOperand(0);
1111 const MDefinition* right = getOperand(1);
1112 const MDefinition* tmp;
1113
1114 if (isCommutative() && left->id() > right->id()) {
1115 tmp = right;
1116 right = left;
1117 left = tmp;
1118 }
1119
1120 const MBinaryInstruction* bi = static_cast<const MBinaryInstruction*>(ins);
1121 const MDefinition* insLeft = bi->getOperand(0);
1122 const MDefinition* insRight = bi->getOperand(1);
1123 if (isCommutative() && insLeft->id() > insRight->id()) {
1124 tmp = insRight;
1125 insRight = insLeft;
1126 insLeft = tmp;
1127 }
1128
1129 return left == insLeft &&
1130 right == insRight;
1131 }
1132
1133 public:
1134 // Return if the operands to this instruction are both unsigned.
1135 static bool unsignedOperands(MDefinition* left, MDefinition* right);
1136 bool unsignedOperands();
1137
1138 // Replace any wrapping operands with the underlying int32 operands
1139 // in case of unsigned operands.
1140 void replaceWithUnsignedOperands();
1141 };
1142
1143 class MTernaryInstruction : public MAryInstruction<3>
1144 {
1145 protected:
MTernaryInstruction(MDefinition * first,MDefinition * second,MDefinition * third)1146 MTernaryInstruction(MDefinition* first, MDefinition* second, MDefinition* third)
1147 {
1148 initOperand(0, first);
1149 initOperand(1, second);
1150 initOperand(2, third);
1151 }
1152
1153 protected:
valueHash()1154 HashNumber valueHash() const
1155 {
1156 MDefinition* first = getOperand(0);
1157 MDefinition* second = getOperand(1);
1158 MDefinition* third = getOperand(2);
1159
1160 return op() + first->id() + second->id() + third->id();
1161 }
1162 };
1163
1164 class MQuaternaryInstruction : public MAryInstruction<4>
1165 {
1166 protected:
MQuaternaryInstruction(MDefinition * first,MDefinition * second,MDefinition * third,MDefinition * fourth)1167 MQuaternaryInstruction(MDefinition* first, MDefinition* second,
1168 MDefinition* third, MDefinition* fourth)
1169 {
1170 initOperand(0, first);
1171 initOperand(1, second);
1172 initOperand(2, third);
1173 initOperand(3, fourth);
1174 }
1175
1176 protected:
valueHash()1177 HashNumber valueHash() const
1178 {
1179 MDefinition* first = getOperand(0);
1180 MDefinition* second = getOperand(1);
1181 MDefinition* third = getOperand(2);
1182 MDefinition* fourth = getOperand(3);
1183
1184 return op() + first->id() + second->id() +
1185 third->id() + fourth->id();
1186 }
1187 };
1188
1189 template <class T>
1190 class MVariadicT : public T
1191 {
1192 FixedList<MUse> operands_;
1193
1194 protected:
init(TempAllocator & alloc,size_t length)1195 bool init(TempAllocator& alloc, size_t length) {
1196 return operands_.init(alloc, length);
1197 }
initOperand(size_t index,MDefinition * operand)1198 void initOperand(size_t index, MDefinition* operand) {
1199 // FixedList doesn't initialize its elements, so do an unchecked init.
1200 operands_[index].initUnchecked(operand, this);
1201 }
getUseFor(size_t index)1202 MUse* getUseFor(size_t index) final override {
1203 return &operands_[index];
1204 }
getUseFor(size_t index)1205 const MUse* getUseFor(size_t index) const final override {
1206 return &operands_[index];
1207 }
1208
1209 public:
1210 // Will assert if called before initialization.
getOperand(size_t index)1211 MDefinition* getOperand(size_t index) const final override {
1212 return operands_[index].producer();
1213 }
numOperands()1214 size_t numOperands() const final override {
1215 return operands_.length();
1216 }
indexOf(const MUse * u)1217 size_t indexOf(const MUse* u) const final override {
1218 MOZ_ASSERT(u >= &operands_[0]);
1219 MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
1220 return u - &operands_[0];
1221 }
replaceOperand(size_t index,MDefinition * operand)1222 void replaceOperand(size_t index, MDefinition* operand) final override {
1223 operands_[index].replaceProducer(operand);
1224 }
1225 };
1226
1227 typedef MVariadicT<MInstruction> MVariadicInstruction;
1228
1229 // Generates an LSnapshot without further effect.
1230 class MStart : public MNullaryInstruction
1231 {
1232 public:
1233 enum StartType {
1234 StartType_Default,
1235 StartType_Osr
1236 };
1237
1238 private:
1239 StartType startType_;
1240
1241 private:
MStart(StartType startType)1242 explicit MStart(StartType startType)
1243 : startType_(startType)
1244 { }
1245
1246 public:
INSTRUCTION_HEADER(Start)1247 INSTRUCTION_HEADER(Start)
1248 static MStart* New(TempAllocator& alloc, StartType startType) {
1249 return new(alloc) MStart(startType);
1250 }
1251
startType()1252 StartType startType() {
1253 return startType_;
1254 }
1255 };
1256
1257 // Instruction marking on entrypoint for on-stack replacement.
1258 // OSR may occur at loop headers (at JSOP_TRACE).
1259 // There is at most one MOsrEntry per MIRGraph.
1260 class MOsrEntry : public MNullaryInstruction
1261 {
1262 protected:
MOsrEntry()1263 MOsrEntry() {
1264 setResultType(MIRType_Pointer);
1265 }
1266
1267 public:
INSTRUCTION_HEADER(OsrEntry)1268 INSTRUCTION_HEADER(OsrEntry)
1269 static MOsrEntry* New(TempAllocator& alloc) {
1270 return new(alloc) MOsrEntry;
1271 }
1272 };
1273
1274 // No-op instruction. This cannot be moved or eliminated, and is intended for
1275 // anchoring resume points at arbitrary points in a block.
1276 class MNop : public MNullaryInstruction
1277 {
1278 protected:
MNop()1279 MNop() {
1280 }
1281
1282 public:
INSTRUCTION_HEADER(Nop)1283 INSTRUCTION_HEADER(Nop)
1284 static MNop* New(TempAllocator& alloc) {
1285 return new(alloc) MNop();
1286 }
1287
getAliasSet()1288 AliasSet getAliasSet() const override {
1289 return AliasSet::None();
1290 }
1291
1292 ALLOW_CLONE(MNop)
1293 };
1294
1295 // Truncation barrier. This is intended for protecting its input against
1296 // follow-up truncation optimizations.
1297 class MLimitedTruncate
1298 : public MUnaryInstruction,
1299 public ConvertToInt32Policy<0>::Data
1300 {
1301 public:
1302 TruncateKind truncate_;
1303 TruncateKind truncateLimit_;
1304
1305 protected:
MLimitedTruncate(MDefinition * input,TruncateKind limit)1306 MLimitedTruncate(MDefinition* input, TruncateKind limit)
1307 : MUnaryInstruction(input),
1308 truncate_(NoTruncate),
1309 truncateLimit_(limit)
1310 {
1311 setResultType(MIRType_Int32);
1312 setResultTypeSet(input->resultTypeSet());
1313 setMovable();
1314 }
1315
1316 public:
INSTRUCTION_HEADER(LimitedTruncate)1317 INSTRUCTION_HEADER(LimitedTruncate)
1318 static MLimitedTruncate* New(TempAllocator& alloc, MDefinition* input, TruncateKind kind) {
1319 return new(alloc) MLimitedTruncate(input, kind);
1320 }
1321
getAliasSet()1322 AliasSet getAliasSet() const override {
1323 return AliasSet::None();
1324 }
1325
1326 void computeRange(TempAllocator& alloc) override;
1327 bool needTruncation(TruncateKind kind) override;
1328 TruncateKind operandTruncateKind(size_t index) const override;
truncateKind()1329 TruncateKind truncateKind() const {
1330 return truncate_;
1331 }
setTruncateKind(TruncateKind kind)1332 void setTruncateKind(TruncateKind kind) {
1333 truncate_ = kind;
1334 }
1335 };
1336
1337 // A constant js::Value.
1338 class MConstant : public MNullaryInstruction
1339 {
1340 Value value_;
1341
1342 protected:
1343 MConstant(const Value& v, CompilerConstraintList* constraints);
1344 explicit MConstant(JSObject* obj);
1345
1346 public:
1347 INSTRUCTION_HEADER(Constant)
1348 static MConstant* New(TempAllocator& alloc, const Value& v,
1349 CompilerConstraintList* constraints = nullptr);
1350 static MConstant* NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type,
1351 CompilerConstraintList* constraints = nullptr);
1352 static MConstant* NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type);
1353 static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v);
1354
value()1355 const js::Value& value() const {
1356 return value_;
1357 }
vp()1358 const js::Value* vp() const {
1359 return &value_;
1360 }
valueToBoolean()1361 bool valueToBoolean() const {
1362 // A hack to avoid this wordy pattern everywhere in the JIT.
1363 return ToBoolean(HandleValue::fromMarkedLocation(&value_));
1364 }
1365
1366 void printOpcode(GenericPrinter& out) const override;
1367
1368 HashNumber valueHash() const override;
1369 bool congruentTo(const MDefinition* ins) const override;
1370
getAliasSet()1371 AliasSet getAliasSet() const override {
1372 return AliasSet::None();
1373 }
1374
updateForReplacement(MDefinition * def)1375 bool updateForReplacement(MDefinition* def) override {
1376 MConstant* c = def->toConstant();
1377 // During constant folding, we don't want to replace a float32
1378 // value by a double value.
1379 if (type() == MIRType_Float32)
1380 return c->type() == MIRType_Float32;
1381 if (type() == MIRType_Double)
1382 return c->type() != MIRType_Float32;
1383 return true;
1384 }
1385
1386 void computeRange(TempAllocator& alloc) override;
1387 bool needTruncation(TruncateKind kind) override;
1388 void truncate() override;
1389
1390 bool canProduceFloat32() const override;
1391
1392 ALLOW_CLONE(MConstant)
1393 };
1394
1395 // Generic constructor of SIMD valuesX4.
1396 class MSimdValueX4
1397 : public MQuaternaryInstruction,
1398 public Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>,
1399 SimdScalarPolicy<2>, SimdScalarPolicy<3> >::Data
1400 {
1401 protected:
MSimdValueX4(MIRType type,MDefinition * x,MDefinition * y,MDefinition * z,MDefinition * w)1402 MSimdValueX4(MIRType type, MDefinition* x, MDefinition* y, MDefinition* z, MDefinition* w)
1403 : MQuaternaryInstruction(x, y, z, w)
1404 {
1405 MOZ_ASSERT(IsSimdType(type));
1406 MOZ_ASSERT(SimdTypeToLength(type) == 4);
1407
1408 setMovable();
1409 setResultType(type);
1410 }
1411
1412 public:
INSTRUCTION_HEADER(SimdValueX4)1413 INSTRUCTION_HEADER(SimdValueX4)
1414
1415 static MSimdValueX4* New(TempAllocator& alloc, MIRType type, MDefinition* x,
1416 MDefinition* y, MDefinition* z, MDefinition* w)
1417 {
1418 return new(alloc) MSimdValueX4(type, x, y, z, w);
1419 }
1420
NewAsmJS(TempAllocator & alloc,MIRType type,MDefinition * x,MDefinition * y,MDefinition * z,MDefinition * w)1421 static MSimdValueX4* NewAsmJS(TempAllocator& alloc, MIRType type, MDefinition* x,
1422 MDefinition* y, MDefinition* z, MDefinition* w)
1423 {
1424 mozilla::DebugOnly<MIRType> laneType = SimdTypeToLaneType(type);
1425 MOZ_ASSERT(laneType == x->type());
1426 MOZ_ASSERT(laneType == y->type());
1427 MOZ_ASSERT(laneType == z->type());
1428 MOZ_ASSERT(laneType == w->type());
1429 return MSimdValueX4::New(alloc, type, x, y, z, w);
1430 }
1431
canConsumeFloat32(MUse * use)1432 bool canConsumeFloat32(MUse* use) const override {
1433 return SimdTypeToLaneType(type()) == MIRType_Float32;
1434 }
1435
getAliasSet()1436 AliasSet getAliasSet() const override {
1437 return AliasSet::None();
1438 }
1439
congruentTo(const MDefinition * ins)1440 bool congruentTo(const MDefinition* ins) const override {
1441 return congruentIfOperandsEqual(ins);
1442 }
1443
1444 MDefinition* foldsTo(TempAllocator& alloc) override;
1445
1446 ALLOW_CLONE(MSimdValueX4)
1447 };
1448
1449 // Generic constructor of SIMD valuesX4.
1450 class MSimdSplatX4
1451 : public MUnaryInstruction,
1452 public SimdScalarPolicy<0>::Data
1453 {
1454 protected:
MSimdSplatX4(MIRType type,MDefinition * v)1455 MSimdSplatX4(MIRType type, MDefinition* v)
1456 : MUnaryInstruction(v)
1457 {
1458 MOZ_ASSERT(IsSimdType(type));
1459 setMovable();
1460 setResultType(type);
1461 }
1462
1463 public:
INSTRUCTION_HEADER(SimdSplatX4)1464 INSTRUCTION_HEADER(SimdSplatX4)
1465
1466 static MSimdSplatX4* NewAsmJS(TempAllocator& alloc, MDefinition* v, MIRType type)
1467 {
1468 MOZ_ASSERT(SimdTypeToLaneType(type) == v->type());
1469 return new(alloc) MSimdSplatX4(type, v);
1470 }
1471
New(TempAllocator & alloc,MDefinition * v,MIRType type)1472 static MSimdSplatX4* New(TempAllocator& alloc, MDefinition* v, MIRType type)
1473 {
1474 return new(alloc) MSimdSplatX4(type, v);
1475 }
1476
canConsumeFloat32(MUse * use)1477 bool canConsumeFloat32(MUse* use) const override {
1478 return SimdTypeToLaneType(type()) == MIRType_Float32;
1479 }
1480
getAliasSet()1481 AliasSet getAliasSet() const override {
1482 return AliasSet::None();
1483 }
1484
congruentTo(const MDefinition * ins)1485 bool congruentTo(const MDefinition* ins) const override {
1486 return congruentIfOperandsEqual(ins);
1487 }
1488
1489 MDefinition* foldsTo(TempAllocator& alloc) override;
1490
1491 ALLOW_CLONE(MSimdSplatX4)
1492 };
1493
1494 // A constant SIMD value.
1495 class MSimdConstant
1496 : public MNullaryInstruction
1497 {
1498 SimdConstant value_;
1499
1500 protected:
MSimdConstant(const SimdConstant & v,MIRType type)1501 MSimdConstant(const SimdConstant& v, MIRType type) : value_(v) {
1502 MOZ_ASSERT(IsSimdType(type));
1503 setMovable();
1504 setResultType(type);
1505 }
1506
1507 public:
INSTRUCTION_HEADER(SimdConstant)1508 INSTRUCTION_HEADER(SimdConstant)
1509 static MSimdConstant* New(TempAllocator& alloc, const SimdConstant& v, MIRType type) {
1510 return new(alloc) MSimdConstant(v, type);
1511 }
1512
congruentTo(const MDefinition * ins)1513 bool congruentTo(const MDefinition* ins) const override {
1514 if (!ins->isSimdConstant())
1515 return false;
1516 return value() == ins->toSimdConstant()->value();
1517 }
1518
value()1519 const SimdConstant& value() const {
1520 return value_;
1521 }
1522
getAliasSet()1523 AliasSet getAliasSet() const override {
1524 return AliasSet::None();
1525 }
1526
1527 ALLOW_CLONE(MSimdConstant)
1528 };
1529
1530 // Converts all lanes of a given vector into the type of another vector
1531 class MSimdConvert
1532 : public MUnaryInstruction,
1533 public SimdPolicy<0>::Data
1534 {
MSimdConvert(MDefinition * obj,MIRType fromType,MIRType toType)1535 MSimdConvert(MDefinition* obj, MIRType fromType, MIRType toType)
1536 : MUnaryInstruction(obj)
1537 {
1538 MOZ_ASSERT(IsSimdType(toType));
1539 setResultType(toType);
1540 specialization_ = fromType; // expects fromType as input
1541
1542 setMovable();
1543 if (IsFloatingPointSimdType(fromType) && IsIntegerSimdType(toType)) {
1544 // Does the extra range check => do not remove
1545 setGuard();
1546 }
1547 }
1548
1549 public:
INSTRUCTION_HEADER(SimdConvert)1550 INSTRUCTION_HEADER(SimdConvert)
1551 static MSimdConvert* NewAsmJS(TempAllocator& alloc, MDefinition* obj, MIRType fromType,
1552 MIRType toType)
1553 {
1554 MOZ_ASSERT(IsSimdType(obj->type()) && fromType == obj->type());
1555 return new(alloc) MSimdConvert(obj, fromType, toType);
1556 }
1557
New(TempAllocator & alloc,MDefinition * obj,MIRType fromType,MIRType toType)1558 static MSimdConvert* New(TempAllocator& alloc, MDefinition* obj, MIRType fromType,
1559 MIRType toType)
1560 {
1561 return new(alloc) MSimdConvert(obj, fromType, toType);
1562 }
1563
getAliasSet()1564 AliasSet getAliasSet() const override {
1565 return AliasSet::None();
1566 }
congruentTo(const MDefinition * ins)1567 bool congruentTo(const MDefinition* ins) const override {
1568 return congruentIfOperandsEqual(ins);
1569 }
1570 ALLOW_CLONE(MSimdConvert)
1571 };
1572
1573 // Casts bits of a vector input to another SIMD type (doesn't generate code).
1574 class MSimdReinterpretCast
1575 : public MUnaryInstruction,
1576 public SimdPolicy<0>::Data
1577 {
MSimdReinterpretCast(MDefinition * obj,MIRType fromType,MIRType toType)1578 MSimdReinterpretCast(MDefinition* obj, MIRType fromType, MIRType toType)
1579 : MUnaryInstruction(obj)
1580 {
1581 MOZ_ASSERT(IsSimdType(toType));
1582 setMovable();
1583 setResultType(toType);
1584 specialization_ = fromType; // expects fromType as input
1585 }
1586
1587 public:
INSTRUCTION_HEADER(SimdReinterpretCast)1588 INSTRUCTION_HEADER(SimdReinterpretCast)
1589 static MSimdReinterpretCast* NewAsmJS(TempAllocator& alloc, MDefinition* obj, MIRType fromType,
1590 MIRType toType)
1591 {
1592 MOZ_ASSERT(IsSimdType(obj->type()) && fromType == obj->type());
1593 return new(alloc) MSimdReinterpretCast(obj, fromType, toType);
1594 }
1595
New(TempAllocator & alloc,MDefinition * obj,MIRType fromType,MIRType toType)1596 static MSimdReinterpretCast* New(TempAllocator& alloc, MDefinition* obj, MIRType fromType,
1597 MIRType toType)
1598 {
1599 return new(alloc) MSimdReinterpretCast(obj, fromType, toType);
1600 }
1601
getAliasSet()1602 AliasSet getAliasSet() const override {
1603 return AliasSet::None();
1604 }
congruentTo(const MDefinition * ins)1605 bool congruentTo(const MDefinition* ins) const override {
1606 return congruentIfOperandsEqual(ins);
1607 }
1608 ALLOW_CLONE(MSimdReinterpretCast)
1609 };
1610
1611 // Extracts a lane element from a given vector type, given by its lane symbol.
1612 class MSimdExtractElement
1613 : public MUnaryInstruction,
1614 public SimdPolicy<0>::Data
1615 {
1616 protected:
1617 SimdLane lane_;
1618
MSimdExtractElement(MDefinition * obj,MIRType vecType,MIRType laneType,SimdLane lane)1619 MSimdExtractElement(MDefinition* obj, MIRType vecType, MIRType laneType, SimdLane lane)
1620 : MUnaryInstruction(obj), lane_(lane)
1621 {
1622 MOZ_ASSERT(IsSimdType(vecType));
1623 MOZ_ASSERT(uint32_t(lane) < SimdTypeToLength(vecType));
1624 MOZ_ASSERT(!IsSimdType(laneType));
1625 MOZ_ASSERT(SimdTypeToLaneType(vecType) == laneType);
1626
1627 setMovable();
1628 specialization_ = vecType;
1629 setResultType(laneType);
1630 }
1631
1632 public:
INSTRUCTION_HEADER(SimdExtractElement)1633 INSTRUCTION_HEADER(SimdExtractElement)
1634
1635 static MSimdExtractElement* NewAsmJS(TempAllocator& alloc, MDefinition* obj, MIRType type,
1636 SimdLane lane)
1637 {
1638 return new(alloc) MSimdExtractElement(obj, obj->type(), type, lane);
1639 }
1640
New(TempAllocator & alloc,MDefinition * obj,MIRType vecType,MIRType scalarType,SimdLane lane)1641 static MSimdExtractElement* New(TempAllocator& alloc, MDefinition* obj, MIRType vecType,
1642 MIRType scalarType, SimdLane lane)
1643 {
1644 return new(alloc) MSimdExtractElement(obj, vecType, scalarType, lane);
1645 }
1646
lane()1647 SimdLane lane() const {
1648 return lane_;
1649 }
1650
getAliasSet()1651 AliasSet getAliasSet() const override {
1652 return AliasSet::None();
1653 }
congruentTo(const MDefinition * ins)1654 bool congruentTo(const MDefinition* ins) const override {
1655 if (!ins->isSimdExtractElement())
1656 return false;
1657 const MSimdExtractElement* other = ins->toSimdExtractElement();
1658 if (other->lane_ != lane_)
1659 return false;
1660 return congruentIfOperandsEqual(other);
1661 }
1662 ALLOW_CLONE(MSimdExtractElement)
1663 };
1664
1665 // Replaces the datum in the given lane by a scalar value of the same type.
1666 class MSimdInsertElement
1667 : public MBinaryInstruction,
1668 public MixPolicy< SimdSameAsReturnedTypePolicy<0>, SimdScalarPolicy<1> >::Data
1669 {
1670 private:
1671 SimdLane lane_;
1672
MSimdInsertElement(MDefinition * vec,MDefinition * val,MIRType type,SimdLane lane)1673 MSimdInsertElement(MDefinition* vec, MDefinition* val, MIRType type, SimdLane lane)
1674 : MBinaryInstruction(vec, val), lane_(lane)
1675 {
1676 MOZ_ASSERT(IsSimdType(type));
1677 setMovable();
1678 setResultType(type);
1679 }
1680
1681 public:
INSTRUCTION_HEADER(SimdInsertElement)1682 INSTRUCTION_HEADER(SimdInsertElement)
1683
1684 static MSimdInsertElement* NewAsmJS(TempAllocator& alloc, MDefinition* vec, MDefinition* val,
1685 MIRType type, SimdLane lane)
1686 {
1687 MOZ_ASSERT(vec->type() == type);
1688 MOZ_ASSERT(SimdTypeToLaneType(type) == val->type());
1689 return new(alloc) MSimdInsertElement(vec, val, type, lane);
1690 }
1691
New(TempAllocator & alloc,MDefinition * vec,MDefinition * val,MIRType type,SimdLane lane)1692 static MSimdInsertElement* New(TempAllocator& alloc, MDefinition* vec, MDefinition* val,
1693 MIRType type, SimdLane lane)
1694 {
1695 return new(alloc) MSimdInsertElement(vec, val, type, lane);
1696 }
1697
vector()1698 MDefinition* vector() {
1699 return getOperand(0);
1700 }
value()1701 MDefinition* value() {
1702 return getOperand(1);
1703 }
lane()1704 SimdLane lane() const {
1705 return lane_;
1706 }
1707
LaneName(SimdLane lane)1708 static const char* LaneName(SimdLane lane) {
1709 switch (lane) {
1710 case LaneX: return "lane x";
1711 case LaneY: return "lane y";
1712 case LaneZ: return "lane z";
1713 case LaneW: return "lane w";
1714 }
1715 MOZ_CRASH("unknown lane");
1716 }
1717
canConsumeFloat32(MUse * use)1718 bool canConsumeFloat32(MUse* use) const override {
1719 return use == getUseFor(1) && SimdTypeToLaneType(type()) == MIRType_Float32;
1720 }
1721
getAliasSet()1722 AliasSet getAliasSet() const override {
1723 return AliasSet::None();
1724 }
1725
congruentTo(const MDefinition * ins)1726 bool congruentTo(const MDefinition* ins) const override {
1727 return binaryCongruentTo(ins) && lane_ == ins->toSimdInsertElement()->lane();
1728 }
1729
1730 void printOpcode(GenericPrinter& out) const override;
1731
1732 ALLOW_CLONE(MSimdInsertElement)
1733 };
1734
1735 // Extracts the sign bits from a given vector, returning an MIRType_Int32.
1736 class MSimdSignMask
1737 : public MUnaryInstruction,
1738 public SimdPolicy<0>::Data
1739 {
1740 protected:
MSimdSignMask(MDefinition * obj,MIRType type)1741 explicit MSimdSignMask(MDefinition* obj, MIRType type)
1742 : MUnaryInstruction(obj)
1743 {
1744 setResultType(MIRType_Int32);
1745 specialization_ = type;
1746 setMovable();
1747 }
1748
1749 public:
INSTRUCTION_HEADER(SimdSignMask)1750 INSTRUCTION_HEADER(SimdSignMask)
1751
1752 static MSimdSignMask* NewAsmJS(TempAllocator& alloc, MDefinition* obj)
1753 {
1754 MOZ_ASSERT(IsSimdType(obj->type()));
1755 return new(alloc) MSimdSignMask(obj, obj->type());
1756 }
1757
New(TempAllocator & alloc,MDefinition * obj,MIRType type)1758 static MSimdSignMask* New(TempAllocator& alloc, MDefinition* obj, MIRType type)
1759 {
1760 return new(alloc) MSimdSignMask(obj, type);
1761 }
1762
getAliasSet()1763 AliasSet getAliasSet() const override {
1764 return AliasSet::None();
1765 }
congruentTo(const MDefinition * ins)1766 bool congruentTo(const MDefinition* ins) const override {
1767 if (!ins->isSimdSignMask())
1768 return false;
1769 return congruentIfOperandsEqual(ins);
1770 }
1771
1772 ALLOW_CLONE(MSimdSignMask)
1773 };
1774
1775 // Base for the MSimdSwizzle and MSimdShuffle classes.
1776 class MSimdShuffleBase
1777 {
1778 protected:
1779 // As of now, there are at most 4 lanes. For each lane, we need to know
1780 // which input we choose and which of the 4 lanes we choose; that can be
1781 // packed in 3 bits for each lane, so 12 bits in total.
1782 uint32_t laneMask_;
1783 uint32_t arity_;
1784
MSimdShuffleBase(uint32_t laneX,uint32_t laneY,uint32_t laneZ,uint32_t laneW,MIRType type)1785 MSimdShuffleBase(uint32_t laneX, uint32_t laneY, uint32_t laneZ, uint32_t laneW, MIRType type)
1786 {
1787 MOZ_ASSERT(SimdTypeToLength(type) == 4);
1788 MOZ_ASSERT(IsSimdType(type));
1789 laneMask_ = (laneX << 0) | (laneY << 3) | (laneZ << 6) | (laneW << 9);
1790 arity_ = 4;
1791 }
1792
sameLanes(const MSimdShuffleBase * other)1793 bool sameLanes(const MSimdShuffleBase* other) const {
1794 return laneMask_ == other->laneMask_;
1795 }
1796
1797 public:
1798 // For now, these formulas are fine for x4 types. They'll need to be
1799 // generalized for other SIMD type lengths.
laneX()1800 uint32_t laneX() const { MOZ_ASSERT(arity_ == 4); return laneMask_ & 7; }
laneY()1801 uint32_t laneY() const { MOZ_ASSERT(arity_ == 4); return (laneMask_ >> 3) & 7; }
laneZ()1802 uint32_t laneZ() const { MOZ_ASSERT(arity_ == 4); return (laneMask_ >> 6) & 7; }
laneW()1803 uint32_t laneW() const { MOZ_ASSERT(arity_ == 4); return (laneMask_ >> 9) & 7; }
1804
lanesMatch(uint32_t x,uint32_t y,uint32_t z,uint32_t w)1805 bool lanesMatch(uint32_t x, uint32_t y, uint32_t z, uint32_t w) const {
1806 return ((x << 0) | (y << 3) | (z << 6) | (w << 9)) == laneMask_;
1807 }
1808 };
1809
1810 // Applies a shuffle operation to the input, putting the input lanes as
1811 // indicated in the output register's lanes. This implements the SIMD.js
1812 // "shuffle" function, that takes one vector and one mask.
1813 class MSimdSwizzle
1814 : public MUnaryInstruction,
1815 public MSimdShuffleBase,
1816 public NoTypePolicy::Data
1817 {
1818 protected:
MSimdSwizzle(MDefinition * obj,MIRType type,uint32_t laneX,uint32_t laneY,uint32_t laneZ,uint32_t laneW)1819 MSimdSwizzle(MDefinition* obj, MIRType type,
1820 uint32_t laneX, uint32_t laneY, uint32_t laneZ, uint32_t laneW)
1821 : MUnaryInstruction(obj), MSimdShuffleBase(laneX, laneY, laneZ, laneW, type)
1822 {
1823 MOZ_ASSERT(laneX < 4 && laneY < 4 && laneZ < 4 && laneW < 4);
1824 MOZ_ASSERT(IsSimdType(obj->type()));
1825 MOZ_ASSERT(IsSimdType(type));
1826 MOZ_ASSERT(obj->type() == type);
1827 setResultType(type);
1828 setMovable();
1829 }
1830
1831 public:
INSTRUCTION_HEADER(SimdSwizzle)1832 INSTRUCTION_HEADER(SimdSwizzle)
1833
1834 static MSimdSwizzle* New(TempAllocator& alloc, MDefinition* obj, MIRType type,
1835 uint32_t laneX, uint32_t laneY, uint32_t laneZ, uint32_t laneW)
1836 {
1837 return new(alloc) MSimdSwizzle(obj, type, laneX, laneY, laneZ, laneW);
1838 }
1839
congruentTo(const MDefinition * ins)1840 bool congruentTo(const MDefinition* ins) const override {
1841 if (!ins->isSimdSwizzle())
1842 return false;
1843 const MSimdSwizzle* other = ins->toSimdSwizzle();
1844 return sameLanes(other) && congruentIfOperandsEqual(other);
1845 }
1846
getAliasSet()1847 AliasSet getAliasSet() const override {
1848 return AliasSet::None();
1849 }
1850
1851 MDefinition* foldsTo(TempAllocator& alloc) override;
1852
1853 ALLOW_CLONE(MSimdSwizzle)
1854 };
1855
1856 // A "general swizzle" is a swizzle or a shuffle with non-constant lane
1857 // indices. This is the one that Ion inlines and it can be folded into a
1858 // MSimdSwizzle/MSimdShuffle if lane indices are constant. Performance of
1859 // general swizzle/shuffle does not really matter, as we expect to get
1860 // constant indices most of the time.
1861 class MSimdGeneralShuffle :
1862 public MVariadicInstruction,
1863 public SimdShufflePolicy::Data
1864 {
1865 unsigned numVectors_;
1866 unsigned numLanes_;
1867
1868 protected:
MSimdGeneralShuffle(unsigned numVectors,unsigned numLanes,MIRType type)1869 MSimdGeneralShuffle(unsigned numVectors, unsigned numLanes, MIRType type)
1870 : numVectors_(numVectors), numLanes_(numLanes)
1871 {
1872 MOZ_ASSERT(IsSimdType(type));
1873 MOZ_ASSERT(SimdTypeToLength(type) == numLanes_);
1874
1875 setResultType(type);
1876 specialization_ = type;
1877 setGuard(); // throws if lane index is out of bounds
1878 setMovable();
1879 }
1880
1881 public:
1882 INSTRUCTION_HEADER(SimdGeneralShuffle);
1883
New(TempAllocator & alloc,unsigned numVectors,unsigned numLanes,MIRType type)1884 static MSimdGeneralShuffle* New(TempAllocator& alloc, unsigned numVectors, unsigned numLanes,
1885 MIRType type)
1886 {
1887 return new(alloc) MSimdGeneralShuffle(numVectors, numLanes, type);
1888 }
1889
init(TempAllocator & alloc)1890 bool init(TempAllocator& alloc) {
1891 return MVariadicInstruction::init(alloc, numVectors_ + numLanes_);
1892 }
setVector(unsigned i,MDefinition * vec)1893 void setVector(unsigned i, MDefinition* vec) {
1894 MOZ_ASSERT(i < numVectors_);
1895 initOperand(i, vec);
1896 }
setLane(unsigned i,MDefinition * laneIndex)1897 void setLane(unsigned i, MDefinition* laneIndex) {
1898 MOZ_ASSERT(i < numLanes_);
1899 initOperand(numVectors_ + i, laneIndex);
1900 }
1901
numVectors()1902 unsigned numVectors() const {
1903 return numVectors_;
1904 }
numLanes()1905 unsigned numLanes() const {
1906 return numLanes_;
1907 }
vector(unsigned i)1908 MDefinition* vector(unsigned i) const {
1909 MOZ_ASSERT(i < numVectors_);
1910 return getOperand(i);
1911 }
lane(unsigned i)1912 MDefinition* lane(unsigned i) const {
1913 MOZ_ASSERT(i < numLanes_);
1914 return getOperand(numVectors_ + i);
1915 }
1916
congruentTo(const MDefinition * ins)1917 bool congruentTo(const MDefinition* ins) const override {
1918 if (!ins->isSimdGeneralShuffle())
1919 return false;
1920 const MSimdGeneralShuffle* other = ins->toSimdGeneralShuffle();
1921 return numVectors_ == other->numVectors() &&
1922 numLanes_ == other->numLanes() &&
1923 congruentIfOperandsEqual(other);
1924 }
1925
1926 MDefinition* foldsTo(TempAllocator& alloc) override;
1927
getAliasSet()1928 AliasSet getAliasSet() const override {
1929 return AliasSet::None();
1930 }
1931 };
1932
1933 // Applies a shuffle operation to the inputs, selecting the 2 first lanes of the
1934 // output from lanes of the first input, and the 2 last lanes of the output from
1935 // lanes of the second input.
1936 class MSimdShuffle
1937 : public MBinaryInstruction,
1938 public MSimdShuffleBase,
1939 public NoTypePolicy::Data
1940 {
MSimdShuffle(MDefinition * lhs,MDefinition * rhs,MIRType type,uint32_t laneX,uint32_t laneY,uint32_t laneZ,uint32_t laneW)1941 MSimdShuffle(MDefinition* lhs, MDefinition* rhs, MIRType type,
1942 uint32_t laneX, uint32_t laneY, uint32_t laneZ, uint32_t laneW)
1943 : MBinaryInstruction(lhs, rhs), MSimdShuffleBase(laneX, laneY, laneZ, laneW, lhs->type())
1944 {
1945 MOZ_ASSERT(laneX < 8 && laneY < 8 && laneZ < 8 && laneW < 8);
1946 MOZ_ASSERT(IsSimdType(lhs->type()));
1947 MOZ_ASSERT(IsSimdType(rhs->type()));
1948 MOZ_ASSERT(lhs->type() == rhs->type());
1949 MOZ_ASSERT(IsSimdType(type));
1950 MOZ_ASSERT(lhs->type() == type);
1951 setResultType(type);
1952 setMovable();
1953 }
1954
1955 public:
INSTRUCTION_HEADER(SimdShuffle)1956 INSTRUCTION_HEADER(SimdShuffle)
1957
1958 static MInstruction* New(TempAllocator& alloc, MDefinition* lhs, MDefinition* rhs,
1959 MIRType type, uint32_t laneX, uint32_t laneY, uint32_t laneZ,
1960 uint32_t laneW)
1961 {
1962 // Swap operands so that new lanes come from LHS in majority.
1963 // In the balanced case, swap operands if needs be, in order to be able
1964 // to do only one vshufps on x86.
1965 unsigned lanesFromLHS = (laneX < 4) + (laneY < 4) + (laneZ < 4) + (laneW < 4);
1966 if (lanesFromLHS < 2 || (lanesFromLHS == 2 && laneX >= 4 && laneY >=4)) {
1967 laneX = (laneX + 4) % 8;
1968 laneY = (laneY + 4) % 8;
1969 laneZ = (laneZ + 4) % 8;
1970 laneW = (laneW + 4) % 8;
1971 mozilla::Swap(lhs, rhs);
1972 }
1973
1974 // If all lanes come from the same vector, just use swizzle instead.
1975 if (laneX < 4 && laneY < 4 && laneZ < 4 && laneW < 4)
1976 return MSimdSwizzle::New(alloc, lhs, type, laneX, laneY, laneZ, laneW);
1977
1978 return new(alloc) MSimdShuffle(lhs, rhs, type, laneX, laneY, laneZ, laneW);
1979 }
1980
congruentTo(const MDefinition * ins)1981 bool congruentTo(const MDefinition* ins) const override {
1982 if (!ins->isSimdShuffle())
1983 return false;
1984 const MSimdShuffle* other = ins->toSimdShuffle();
1985 return sameLanes(other) && binaryCongruentTo(other);
1986 }
1987
getAliasSet()1988 AliasSet getAliasSet() const override {
1989 return AliasSet::None();
1990 }
1991
1992 ALLOW_CLONE(MSimdShuffle)
1993 };
1994
1995 class MSimdUnaryArith
1996 : public MUnaryInstruction,
1997 public SimdSameAsReturnedTypePolicy<0>::Data
1998 {
1999 public:
2000 enum Operation {
2001 #define OP_LIST_(OP) OP,
2002 UNARY_ARITH_FLOAT32X4_SIMD_OP(OP_LIST_)
2003 neg,
2004 not_
2005 #undef OP_LIST_
2006 };
2007
OperationName(Operation op)2008 static const char* OperationName(Operation op) {
2009 switch (op) {
2010 case abs: return "abs";
2011 case neg: return "neg";
2012 case not_: return "not";
2013 case reciprocalApproximation: return "reciprocalApproximation";
2014 case reciprocalSqrtApproximation: return "reciprocalSqrtApproximation";
2015 case sqrt: return "sqrt";
2016 }
2017 MOZ_CRASH("unexpected operation");
2018 }
2019
2020 private:
2021 Operation operation_;
2022
MSimdUnaryArith(MDefinition * def,Operation op,MIRType type)2023 MSimdUnaryArith(MDefinition* def, Operation op, MIRType type)
2024 : MUnaryInstruction(def), operation_(op)
2025 {
2026 MOZ_ASSERT_IF(type == MIRType_Int32x4, op == neg || op == not_);
2027 setResultType(type);
2028 setMovable();
2029 }
2030
2031 public:
INSTRUCTION_HEADER(SimdUnaryArith)2032 INSTRUCTION_HEADER(SimdUnaryArith)
2033
2034 static MSimdUnaryArith* New(TempAllocator& alloc, MDefinition* def, Operation op, MIRType t)
2035 {
2036 return new(alloc) MSimdUnaryArith(def, op, t);
2037 }
2038
NewAsmJS(TempAllocator & alloc,MDefinition * def,Operation op,MIRType t)2039 static MSimdUnaryArith* NewAsmJS(TempAllocator& alloc, MDefinition* def,
2040 Operation op, MIRType t)
2041 {
2042 MOZ_ASSERT(IsSimdType(t));
2043 MOZ_ASSERT(def->type() == t);
2044 return new(alloc) MSimdUnaryArith(def, op, t);
2045 }
2046
operation()2047 Operation operation() const { return operation_; }
2048
getAliasSet()2049 AliasSet getAliasSet() const override {
2050 return AliasSet::None();
2051 }
2052
congruentTo(const MDefinition * ins)2053 bool congruentTo(const MDefinition* ins) const override {
2054 return congruentIfOperandsEqual(ins) && ins->toSimdUnaryArith()->operation() == operation();
2055 }
2056
2057 void printOpcode(GenericPrinter& out) const override;
2058
2059 ALLOW_CLONE(MSimdUnaryArith);
2060 };
2061
2062 // Compares each value of a SIMD vector to each corresponding lane's value of
2063 // another SIMD vector, and returns a int32x4 vector containing the results of
2064 // the comparison: all bits are set to 1 if the comparison is true, 0 otherwise.
2065 class MSimdBinaryComp
2066 : public MBinaryInstruction,
2067 public SimdAllPolicy::Data
2068 {
2069 public:
2070 enum Operation {
2071 #define NAME_(x) x,
2072 COMP_COMMONX4_TO_INT32X4_SIMD_OP(NAME_)
2073 #undef NAME_
2074 };
2075
OperationName(Operation op)2076 static const char* OperationName(Operation op) {
2077 switch (op) {
2078 #define NAME_(x) case x: return #x;
2079 COMP_COMMONX4_TO_INT32X4_SIMD_OP(NAME_)
2080 #undef NAME_
2081 }
2082 MOZ_CRASH("unexpected operation");
2083 }
2084
2085 private:
2086 Operation operation_;
2087
MSimdBinaryComp(MDefinition * left,MDefinition * right,Operation op,MIRType opType)2088 MSimdBinaryComp(MDefinition* left, MDefinition* right, Operation op, MIRType opType)
2089 : MBinaryInstruction(left, right), operation_(op)
2090 {
2091 setResultType(MIRType_Int32x4);
2092 specialization_ = opType;
2093 setMovable();
2094 if (op == equal || op == notEqual)
2095 setCommutative();
2096 }
2097
2098 public:
INSTRUCTION_HEADER(SimdBinaryComp)2099 INSTRUCTION_HEADER(SimdBinaryComp)
2100 static MSimdBinaryComp* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
2101 Operation op)
2102 {
2103 MOZ_ASSERT(IsSimdType(left->type()));
2104 MOZ_ASSERT(left->type() == right->type());
2105 return new(alloc) MSimdBinaryComp(left, right, op, left->type());
2106 }
2107
New(TempAllocator & alloc,MDefinition * left,MDefinition * right,Operation op,MIRType opType)2108 static MSimdBinaryComp* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
2109 Operation op, MIRType opType)
2110 {
2111 return new(alloc) MSimdBinaryComp(left, right, op, opType);
2112 }
2113
getAliasSet()2114 AliasSet getAliasSet() const override {
2115 return AliasSet::None();
2116 }
2117
operation()2118 Operation operation() const { return operation_; }
specialization()2119 MIRType specialization() const { return specialization_; }
2120
2121 // Swap the operands and reverse the comparison predicate.
reverse()2122 void reverse() {
2123 switch (operation()) {
2124 case greaterThan: operation_ = lessThan; break;
2125 case greaterThanOrEqual: operation_ = lessThanOrEqual; break;
2126 case lessThan: operation_ = greaterThan; break;
2127 case lessThanOrEqual: operation_ = greaterThanOrEqual; break;
2128 case equal:
2129 case notEqual:
2130 break;
2131 default: MOZ_CRASH("Unexpected compare operation");
2132 }
2133 swapOperands();
2134 }
2135
congruentTo(const MDefinition * ins)2136 bool congruentTo(const MDefinition* ins) const override {
2137 if (!binaryCongruentTo(ins))
2138 return false;
2139 const MSimdBinaryComp* other = ins->toSimdBinaryComp();
2140 return specialization_ == other->specialization() &&
2141 operation_ == other->operation();
2142 }
2143
2144 void printOpcode(GenericPrinter& out) const override;
2145
2146 ALLOW_CLONE(MSimdBinaryComp)
2147 };
2148
2149 class MSimdBinaryArith
2150 : public MBinaryInstruction,
2151 public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
2152 {
2153 public:
2154 enum Operation {
2155 #define OP_LIST_(OP) Op_##OP,
2156 ARITH_COMMONX4_SIMD_OP(OP_LIST_)
2157 BINARY_ARITH_FLOAT32X4_SIMD_OP(OP_LIST_)
2158 #undef OP_LIST_
2159 };
2160
OperationName(Operation op)2161 static const char* OperationName(Operation op) {
2162 switch (op) {
2163 #define OP_CASE_LIST_(OP) case Op_##OP: return #OP;
2164 ARITH_COMMONX4_SIMD_OP(OP_CASE_LIST_)
2165 BINARY_ARITH_FLOAT32X4_SIMD_OP(OP_CASE_LIST_)
2166 #undef OP_CASE_LIST_
2167 }
2168 MOZ_CRASH("unexpected operation");
2169 }
2170
2171 private:
2172 Operation operation_;
2173
MSimdBinaryArith(MDefinition * left,MDefinition * right,Operation op,MIRType type)2174 MSimdBinaryArith(MDefinition* left, MDefinition* right, Operation op, MIRType type)
2175 : MBinaryInstruction(left, right), operation_(op)
2176 {
2177 MOZ_ASSERT_IF(type == MIRType_Int32x4, op == Op_add || op == Op_sub || op == Op_mul);
2178 MOZ_ASSERT(IsSimdType(type));
2179 setResultType(type);
2180 setMovable();
2181 if (op == Op_add || op == Op_mul || op == Op_min || op == Op_max)
2182 setCommutative();
2183 }
2184
2185 public:
INSTRUCTION_HEADER(SimdBinaryArith)2186 INSTRUCTION_HEADER(SimdBinaryArith)
2187 static MSimdBinaryArith* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
2188 Operation op, MIRType t)
2189 {
2190 return new(alloc) MSimdBinaryArith(left, right, op, t);
2191 }
2192
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right,Operation op,MIRType t)2193 static MSimdBinaryArith* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
2194 Operation op, MIRType t)
2195 {
2196 MOZ_ASSERT(left->type() == right->type());
2197 MOZ_ASSERT(left->type() == t);
2198 return New(alloc, left, right, op, t);
2199 }
2200
getAliasSet()2201 AliasSet getAliasSet() const override {
2202 return AliasSet::None();
2203 }
2204
operation()2205 Operation operation() const { return operation_; }
2206
congruentTo(const MDefinition * ins)2207 bool congruentTo(const MDefinition* ins) const override {
2208 if (!binaryCongruentTo(ins))
2209 return false;
2210 return operation_ == ins->toSimdBinaryArith()->operation();
2211 }
2212
2213 void printOpcode(GenericPrinter& out) const override;
2214
2215 ALLOW_CLONE(MSimdBinaryArith)
2216 };
2217
2218 class MSimdBinaryBitwise
2219 : public MBinaryInstruction,
2220 public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
2221 {
2222 public:
2223 enum Operation {
2224 and_,
2225 or_,
2226 xor_
2227 };
2228
OperationName(Operation op)2229 static const char* OperationName(Operation op) {
2230 switch (op) {
2231 case and_: return "and";
2232 case or_: return "or";
2233 case xor_: return "xor";
2234 }
2235 MOZ_CRASH("unexpected operation");
2236 }
2237
2238 private:
2239 Operation operation_;
2240
MSimdBinaryBitwise(MDefinition * left,MDefinition * right,Operation op,MIRType type)2241 MSimdBinaryBitwise(MDefinition* left, MDefinition* right, Operation op, MIRType type)
2242 : MBinaryInstruction(left, right), operation_(op)
2243 {
2244 MOZ_ASSERT(IsSimdType(type));
2245 setResultType(type);
2246 setMovable();
2247 setCommutative();
2248 }
2249
2250 public:
INSTRUCTION_HEADER(SimdBinaryBitwise)2251 INSTRUCTION_HEADER(SimdBinaryBitwise)
2252 static MSimdBinaryBitwise* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
2253 Operation op, MIRType t)
2254 {
2255 return new(alloc) MSimdBinaryBitwise(left, right, op, t);
2256 }
2257
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right,Operation op,MIRType t)2258 static MSimdBinaryBitwise* NewAsmJS(TempAllocator& alloc, MDefinition* left,
2259 MDefinition* right, Operation op, MIRType t)
2260 {
2261 MOZ_ASSERT(left->type() == right->type());
2262 MOZ_ASSERT(left->type() == t);
2263 return new(alloc) MSimdBinaryBitwise(left, right, op, t);
2264 }
2265
getAliasSet()2266 AliasSet getAliasSet() const override {
2267 return AliasSet::None();
2268 }
2269
operation()2270 Operation operation() const { return operation_; }
2271
congruentTo(const MDefinition * ins)2272 bool congruentTo(const MDefinition* ins) const override {
2273 if (!binaryCongruentTo(ins))
2274 return false;
2275 return operation_ == ins->toSimdBinaryBitwise()->operation();
2276 }
2277
2278 void printOpcode(GenericPrinter& out) const override;
2279
2280 ALLOW_CLONE(MSimdBinaryBitwise)
2281 };
2282
2283 class MSimdShift
2284 : public MBinaryInstruction,
2285 public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdScalarPolicy<1> >::Data
2286 {
2287 public:
2288 enum Operation {
2289 lsh,
2290 rsh,
2291 ursh
2292 };
2293
2294 private:
2295 Operation operation_;
2296
MSimdShift(MDefinition * left,MDefinition * right,Operation op)2297 MSimdShift(MDefinition* left, MDefinition* right, Operation op)
2298 : MBinaryInstruction(left, right), operation_(op)
2299 {
2300 setResultType(MIRType_Int32x4);
2301 setMovable();
2302 }
2303
2304 public:
INSTRUCTION_HEADER(SimdShift)2305 INSTRUCTION_HEADER(SimdShift)
2306 static MSimdShift* NewAsmJS(TempAllocator& alloc, MDefinition* left,
2307 MDefinition* right, Operation op)
2308 {
2309 MOZ_ASSERT(left->type() == MIRType_Int32x4 && right->type() == MIRType_Int32);
2310 return new(alloc) MSimdShift(left, right, op);
2311 }
2312
New(TempAllocator & alloc,MDefinition * left,MDefinition * right,Operation op,MIRType type)2313 static MSimdShift* New(TempAllocator& alloc, MDefinition* left, MDefinition* right,
2314 Operation op, MIRType type)
2315 {
2316 MOZ_ASSERT(type == MIRType_Int32x4);
2317 return new(alloc) MSimdShift(left, right, op);
2318 }
2319
getAliasSet()2320 AliasSet getAliasSet() const override {
2321 return AliasSet::None();
2322 }
2323
operation()2324 Operation operation() const { return operation_; }
2325
OperationName(Operation op)2326 static const char* OperationName(Operation op) {
2327 switch (op) {
2328 case lsh: return "lsh";
2329 case rsh: return "rsh-arithmetic";
2330 case ursh: return "rhs-logical";
2331 }
2332 MOZ_CRASH("unexpected operation");
2333 }
2334
2335 void printOpcode(GenericPrinter& out) const override;
2336
congruentTo(const MDefinition * ins)2337 bool congruentTo(const MDefinition* ins) const override {
2338 if (!binaryCongruentTo(ins))
2339 return false;
2340 return operation_ == ins->toSimdShift()->operation();
2341 }
2342
2343 ALLOW_CLONE(MSimdShift)
2344 };
2345
2346 class MSimdSelect
2347 : public MTernaryInstruction,
2348 public SimdSelectPolicy::Data
2349 {
2350 bool isElementWise_;
2351
MSimdSelect(MDefinition * mask,MDefinition * lhs,MDefinition * rhs,MIRType type,bool isElementWise)2352 MSimdSelect(MDefinition* mask, MDefinition* lhs, MDefinition* rhs, MIRType type,
2353 bool isElementWise)
2354 : MTernaryInstruction(mask, lhs, rhs), isElementWise_(isElementWise)
2355 {
2356 MOZ_ASSERT(IsSimdType(type));
2357 setResultType(type);
2358 specialization_ = type;
2359 setMovable();
2360 }
2361
2362 public:
INSTRUCTION_HEADER(SimdSelect)2363 INSTRUCTION_HEADER(SimdSelect)
2364 static MSimdSelect* NewAsmJS(TempAllocator& alloc, MDefinition* mask, MDefinition* lhs,
2365 MDefinition* rhs, MIRType t, bool isElementWise)
2366 {
2367 MOZ_ASSERT(mask->type() == MIRType_Int32x4);
2368 MOZ_ASSERT(lhs->type() == rhs->type());
2369 MOZ_ASSERT(lhs->type() == t);
2370 return new(alloc) MSimdSelect(mask, lhs, rhs, t, isElementWise);
2371 }
2372
New(TempAllocator & alloc,MDefinition * mask,MDefinition * lhs,MDefinition * rhs,MIRType t,bool isElementWise)2373 static MSimdSelect* New(TempAllocator& alloc, MDefinition* mask, MDefinition* lhs,
2374 MDefinition* rhs, MIRType t, bool isElementWise)
2375 {
2376 return new(alloc) MSimdSelect(mask, lhs, rhs, t, isElementWise);
2377 }
2378
mask()2379 MDefinition* mask() const {
2380 return getOperand(0);
2381 }
2382
getAliasSet()2383 AliasSet getAliasSet() const override {
2384 return AliasSet::None();
2385 }
2386
isElementWise()2387 bool isElementWise() const {
2388 return isElementWise_;
2389 }
2390
congruentTo(const MDefinition * ins)2391 bool congruentTo(const MDefinition* ins) const override {
2392 if (!congruentIfOperandsEqual(ins))
2393 return false;
2394 return isElementWise_ == ins->toSimdSelect()->isElementWise();
2395 }
2396
2397 ALLOW_CLONE(MSimdSelect)
2398 };
2399
2400 // Deep clone a constant JSObject.
2401 class MCloneLiteral
2402 : public MUnaryInstruction,
2403 public ObjectPolicy<0>::Data
2404 {
2405 protected:
MCloneLiteral(MDefinition * obj)2406 explicit MCloneLiteral(MDefinition* obj)
2407 : MUnaryInstruction(obj)
2408 {
2409 setResultType(MIRType_Object);
2410 }
2411
2412 public:
2413 INSTRUCTION_HEADER(CloneLiteral)
2414 static MCloneLiteral* New(TempAllocator& alloc, MDefinition* obj);
2415 };
2416
2417 class MParameter : public MNullaryInstruction
2418 {
2419 int32_t index_;
2420
2421 public:
2422 static const int32_t THIS_SLOT = -1;
2423
MParameter(int32_t index,TemporaryTypeSet * types)2424 MParameter(int32_t index, TemporaryTypeSet* types)
2425 : index_(index)
2426 {
2427 setResultType(MIRType_Value);
2428 setResultTypeSet(types);
2429 }
2430
2431 public:
2432 INSTRUCTION_HEADER(Parameter)
2433 static MParameter* New(TempAllocator& alloc, int32_t index, TemporaryTypeSet* types);
2434
index()2435 int32_t index() const {
2436 return index_;
2437 }
2438 void printOpcode(GenericPrinter& out) const override;
2439
2440 HashNumber valueHash() const override;
2441 bool congruentTo(const MDefinition* ins) const override;
2442 };
2443
2444 class MCallee : public MNullaryInstruction
2445 {
2446 public:
MCallee()2447 MCallee()
2448 {
2449 setResultType(MIRType_Object);
2450 setMovable();
2451 }
2452
2453 public:
INSTRUCTION_HEADER(Callee)2454 INSTRUCTION_HEADER(Callee)
2455
2456 bool congruentTo(const MDefinition* ins) const override {
2457 return congruentIfOperandsEqual(ins);
2458 }
2459
New(TempAllocator & alloc)2460 static MCallee* New(TempAllocator& alloc) {
2461 return new(alloc) MCallee();
2462 }
getAliasSet()2463 AliasSet getAliasSet() const override {
2464 return AliasSet::None();
2465 }
2466 };
2467
2468 class MIsConstructing : public MNullaryInstruction
2469 {
2470 public:
MIsConstructing()2471 MIsConstructing() {
2472 setResultType(MIRType_Boolean);
2473 setMovable();
2474 }
2475
2476 public:
INSTRUCTION_HEADER(IsConstructing)2477 INSTRUCTION_HEADER(IsConstructing)
2478
2479 bool congruentTo(const MDefinition* ins) const override {
2480 return congruentIfOperandsEqual(ins);
2481 }
New(TempAllocator & alloc)2482 static MIsConstructing* New(TempAllocator& alloc) {
2483 return new(alloc) MIsConstructing();
2484 }
getAliasSet()2485 AliasSet getAliasSet() const override {
2486 return AliasSet::None();
2487 }
2488 };
2489
2490 class MControlInstruction : public MInstruction
2491 {
2492 public:
MControlInstruction()2493 MControlInstruction()
2494 { }
2495
2496 virtual size_t numSuccessors() const = 0;
2497 virtual MBasicBlock* getSuccessor(size_t i) const = 0;
2498 virtual void replaceSuccessor(size_t i, MBasicBlock* successor) = 0;
2499
isControlInstruction()2500 bool isControlInstruction() const override {
2501 return true;
2502 }
2503
2504 void printOpcode(GenericPrinter& out) const override;
2505 };
2506
2507 class MTableSwitch final
2508 : public MControlInstruction,
2509 public NoFloatPolicy<0>::Data
2510 {
2511 // The successors of the tableswitch
2512 // - First successor = the default case
2513 // - Successor 2 and higher = the cases sorted on case index.
2514 Vector<MBasicBlock*, 0, JitAllocPolicy> successors_;
2515 Vector<size_t, 0, JitAllocPolicy> cases_;
2516
2517 // Contains the blocks/cases that still need to get build
2518 Vector<MBasicBlock*, 0, JitAllocPolicy> blocks_;
2519
2520 MUse operand_;
2521 int32_t low_;
2522 int32_t high_;
2523
initOperand(size_t index,MDefinition * operand)2524 void initOperand(size_t index, MDefinition* operand) {
2525 MOZ_ASSERT(index == 0);
2526 operand_.init(operand, this);
2527 }
2528
MTableSwitch(TempAllocator & alloc,MDefinition * ins,int32_t low,int32_t high)2529 MTableSwitch(TempAllocator& alloc, MDefinition* ins,
2530 int32_t low, int32_t high)
2531 : successors_(alloc),
2532 cases_(alloc),
2533 blocks_(alloc),
2534 low_(low),
2535 high_(high)
2536 {
2537 initOperand(0, ins);
2538 }
2539
2540 protected:
getUseFor(size_t index)2541 MUse* getUseFor(size_t index) override {
2542 MOZ_ASSERT(index == 0);
2543 return &operand_;
2544 }
2545
getUseFor(size_t index)2546 const MUse* getUseFor(size_t index) const override {
2547 MOZ_ASSERT(index == 0);
2548 return &operand_;
2549 }
2550
2551 public:
2552 INSTRUCTION_HEADER(TableSwitch)
2553 static MTableSwitch* New(TempAllocator& alloc, MDefinition* ins, int32_t low, int32_t high);
2554
numSuccessors()2555 size_t numSuccessors() const override {
2556 return successors_.length();
2557 }
2558
addSuccessor(MBasicBlock * successor,size_t * index)2559 bool addSuccessor(MBasicBlock* successor, size_t* index) {
2560 MOZ_ASSERT(successors_.length() < (size_t)(high_ - low_ + 2));
2561 MOZ_ASSERT(!successors_.empty());
2562 *index = successors_.length();
2563 return successors_.append(successor);
2564 }
2565
getSuccessor(size_t i)2566 MBasicBlock* getSuccessor(size_t i) const override {
2567 MOZ_ASSERT(i < numSuccessors());
2568 return successors_[i];
2569 }
2570
replaceSuccessor(size_t i,MBasicBlock * successor)2571 void replaceSuccessor(size_t i, MBasicBlock* successor) override {
2572 MOZ_ASSERT(i < numSuccessors());
2573 successors_[i] = successor;
2574 }
2575
blocks()2576 MBasicBlock** blocks() {
2577 return &blocks_[0];
2578 }
2579
numBlocks()2580 size_t numBlocks() const {
2581 return blocks_.length();
2582 }
2583
low()2584 int32_t low() const {
2585 return low_;
2586 }
2587
high()2588 int32_t high() const {
2589 return high_;
2590 }
2591
getDefault()2592 MBasicBlock* getDefault() const {
2593 return getSuccessor(0);
2594 }
2595
getCase(size_t i)2596 MBasicBlock* getCase(size_t i) const {
2597 return getSuccessor(cases_[i]);
2598 }
2599
numCases()2600 size_t numCases() const {
2601 return high() - low() + 1;
2602 }
2603
2604 bool addDefault(MBasicBlock* block, size_t* index = nullptr) {
2605 MOZ_ASSERT(successors_.empty());
2606 if (index)
2607 *index = 0;
2608 return successors_.append(block);
2609 }
2610
addCase(size_t successorIndex)2611 bool addCase(size_t successorIndex) {
2612 return cases_.append(successorIndex);
2613 }
2614
getBlock(size_t i)2615 MBasicBlock* getBlock(size_t i) const {
2616 MOZ_ASSERT(i < numBlocks());
2617 return blocks_[i];
2618 }
2619
addBlock(MBasicBlock * block)2620 bool addBlock(MBasicBlock* block) {
2621 return blocks_.append(block);
2622 }
2623
getOperand(size_t index)2624 MDefinition* getOperand(size_t index) const override {
2625 MOZ_ASSERT(index == 0);
2626 return operand_.producer();
2627 }
2628
numOperands()2629 size_t numOperands() const override {
2630 return 1;
2631 }
2632
indexOf(const MUse * u)2633 size_t indexOf(const MUse* u) const final override {
2634 MOZ_ASSERT(u == getUseFor(0));
2635 return 0;
2636 }
2637
replaceOperand(size_t index,MDefinition * operand)2638 void replaceOperand(size_t index, MDefinition* operand) final override {
2639 MOZ_ASSERT(index == 0);
2640 operand_.replaceProducer(operand);
2641 }
2642
2643 MDefinition* foldsTo(TempAllocator& alloc) override;
2644 };
2645
2646 template <size_t Arity, size_t Successors>
2647 class MAryControlInstruction : public MControlInstruction
2648 {
2649 mozilla::Array<MUse, Arity> operands_;
2650 mozilla::Array<MBasicBlock*, Successors> successors_;
2651
2652 protected:
setSuccessor(size_t index,MBasicBlock * successor)2653 void setSuccessor(size_t index, MBasicBlock* successor) {
2654 successors_[index] = successor;
2655 }
2656
getUseFor(size_t index)2657 MUse* getUseFor(size_t index) final override {
2658 return &operands_[index];
2659 }
getUseFor(size_t index)2660 const MUse* getUseFor(size_t index) const final override {
2661 return &operands_[index];
2662 }
initOperand(size_t index,MDefinition * operand)2663 void initOperand(size_t index, MDefinition* operand) {
2664 operands_[index].init(operand, this);
2665 }
2666
2667 public:
getOperand(size_t index)2668 MDefinition* getOperand(size_t index) const final override {
2669 return operands_[index].producer();
2670 }
numOperands()2671 size_t numOperands() const final override {
2672 return Arity;
2673 }
indexOf(const MUse * u)2674 size_t indexOf(const MUse* u) const final override {
2675 MOZ_ASSERT(u >= &operands_[0]);
2676 MOZ_ASSERT(u <= &operands_[numOperands() - 1]);
2677 return u - &operands_[0];
2678 }
replaceOperand(size_t index,MDefinition * operand)2679 void replaceOperand(size_t index, MDefinition* operand) final override {
2680 operands_[index].replaceProducer(operand);
2681 }
numSuccessors()2682 size_t numSuccessors() const final override {
2683 return Successors;
2684 }
getSuccessor(size_t i)2685 MBasicBlock* getSuccessor(size_t i) const final override {
2686 return successors_[i];
2687 }
replaceSuccessor(size_t i,MBasicBlock * succ)2688 void replaceSuccessor(size_t i, MBasicBlock* succ) final override {
2689 successors_[i] = succ;
2690 }
2691 };
2692
2693 // Jump to the start of another basic block.
2694 class MGoto
2695 : public MAryControlInstruction<0, 1>,
2696 public NoTypePolicy::Data
2697 {
MGoto(MBasicBlock * target)2698 explicit MGoto(MBasicBlock* target) {
2699 setSuccessor(0, target);
2700 }
2701
2702 public:
2703 INSTRUCTION_HEADER(Goto)
2704 static MGoto* New(TempAllocator& alloc, MBasicBlock* target);
2705
target()2706 MBasicBlock* target() {
2707 return getSuccessor(0);
2708 }
getAliasSet()2709 AliasSet getAliasSet() const override {
2710 return AliasSet::None();
2711 }
2712 };
2713
2714 enum BranchDirection {
2715 FALSE_BRANCH,
2716 TRUE_BRANCH
2717 };
2718
2719 static inline BranchDirection
NegateBranchDirection(BranchDirection dir)2720 NegateBranchDirection(BranchDirection dir)
2721 {
2722 return (dir == FALSE_BRANCH) ? TRUE_BRANCH : FALSE_BRANCH;
2723 }
2724
2725 // Tests if the input instruction evaluates to true or false, and jumps to the
2726 // start of a corresponding basic block.
2727 class MTest
2728 : public MAryControlInstruction<1, 2>,
2729 public TestPolicy::Data
2730 {
2731 bool operandMightEmulateUndefined_;
2732
MTest(MDefinition * ins,MBasicBlock * if_true,MBasicBlock * if_false)2733 MTest(MDefinition* ins, MBasicBlock* if_true, MBasicBlock* if_false)
2734 : operandMightEmulateUndefined_(true)
2735 {
2736 initOperand(0, ins);
2737 setSuccessor(0, if_true);
2738 setSuccessor(1, if_false);
2739 }
2740
2741 public:
2742 INSTRUCTION_HEADER(Test)
2743 static MTest* New(TempAllocator& alloc, MDefinition* ins,
2744 MBasicBlock* ifTrue, MBasicBlock* ifFalse);
2745
input()2746 MDefinition* input() const {
2747 return getOperand(0);
2748 }
ifTrue()2749 MBasicBlock* ifTrue() const {
2750 return getSuccessor(0);
2751 }
ifFalse()2752 MBasicBlock* ifFalse() const {
2753 return getSuccessor(1);
2754 }
branchSuccessor(BranchDirection dir)2755 MBasicBlock* branchSuccessor(BranchDirection dir) const {
2756 return (dir == TRUE_BRANCH) ? ifTrue() : ifFalse();
2757 }
2758
getAliasSet()2759 AliasSet getAliasSet() const override {
2760 return AliasSet::None();
2761 }
2762
2763 // We cache whether our operand might emulate undefined, but we don't want
2764 // to do that from New() or the constructor, since those can be called on
2765 // background threads. So make callers explicitly call it if they want us
2766 // to check whether the operand might do this. If this method is never
2767 // called, we'll assume our operand can emulate undefined.
2768 void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
2769 MDefinition* foldsTo(TempAllocator& alloc) override;
2770 void filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
2771 bool* filtersNull);
2772
markNoOperandEmulatesUndefined()2773 void markNoOperandEmulatesUndefined() {
2774 operandMightEmulateUndefined_ = false;
2775 }
operandMightEmulateUndefined()2776 bool operandMightEmulateUndefined() const {
2777 return operandMightEmulateUndefined_;
2778 }
2779 #ifdef DEBUG
isConsistentFloat32Use(MUse * use)2780 bool isConsistentFloat32Use(MUse* use) const override {
2781 return true;
2782 }
2783 #endif
2784 };
2785
2786 // Equivalent to MTest(true, successor, fake), except without the foldsTo
2787 // method. This allows IonBuilder to insert fake CFG edges to magically protect
2788 // control flow for try-catch blocks.
2789 class MGotoWithFake
2790 : public MAryControlInstruction<0, 2>,
2791 public NoTypePolicy::Data
2792 {
MGotoWithFake(MBasicBlock * successor,MBasicBlock * fake)2793 MGotoWithFake(MBasicBlock* successor, MBasicBlock* fake)
2794 {
2795 setSuccessor(0, successor);
2796 setSuccessor(1, fake);
2797 }
2798
2799 public:
INSTRUCTION_HEADER(GotoWithFake)2800 INSTRUCTION_HEADER(GotoWithFake)
2801 static MGotoWithFake* New(TempAllocator& alloc, MBasicBlock* successor, MBasicBlock* fake) {
2802 return new(alloc) MGotoWithFake(successor, fake);
2803 }
2804
target()2805 MBasicBlock* target() const {
2806 return getSuccessor(0);
2807 }
2808
getAliasSet()2809 AliasSet getAliasSet() const override {
2810 return AliasSet::None();
2811 }
2812 };
2813
2814 // Returns from this function to the previous caller.
2815 class MReturn
2816 : public MAryControlInstruction<1, 0>,
2817 public BoxInputsPolicy::Data
2818 {
MReturn(MDefinition * ins)2819 explicit MReturn(MDefinition* ins) {
2820 initOperand(0, ins);
2821 }
2822
2823 public:
INSTRUCTION_HEADER(Return)2824 INSTRUCTION_HEADER(Return)
2825 static MReturn* New(TempAllocator& alloc, MDefinition* ins) {
2826 return new(alloc) MReturn(ins);
2827 }
2828
input()2829 MDefinition* input() const {
2830 return getOperand(0);
2831 }
getAliasSet()2832 AliasSet getAliasSet() const override {
2833 return AliasSet::None();
2834 }
2835 };
2836
2837 class MThrow
2838 : public MAryControlInstruction<1, 0>,
2839 public BoxInputsPolicy::Data
2840 {
MThrow(MDefinition * ins)2841 explicit MThrow(MDefinition* ins) {
2842 initOperand(0, ins);
2843 }
2844
2845 public:
INSTRUCTION_HEADER(Throw)2846 INSTRUCTION_HEADER(Throw)
2847 static MThrow* New(TempAllocator& alloc, MDefinition* ins) {
2848 return new(alloc) MThrow(ins);
2849 }
2850
getAliasSet()2851 virtual AliasSet getAliasSet() const override {
2852 return AliasSet::None();
2853 }
possiblyCalls()2854 bool possiblyCalls() const override {
2855 return true;
2856 }
2857 };
2858
2859 // Fabricate a type set containing only the type of the specified object.
2860 TemporaryTypeSet*
2861 MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj);
2862
2863 TemporaryTypeSet*
2864 MakeSingletonTypeSet(CompilerConstraintList* constraints, ObjectGroup* obj);
2865
2866 bool
2867 MergeTypes(MIRType* ptype, TemporaryTypeSet** ptypeSet,
2868 MIRType newType, TemporaryTypeSet* newTypeSet);
2869
2870 bool
2871 TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes);
2872
2873 bool
2874 EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
2875 MIRType type2, TemporaryTypeSet* typeset2);
2876
2877 bool
2878 CanStoreUnboxedType(TempAllocator& alloc,
2879 JSValueType unboxedType, MIRType input, TypeSet* inputTypes);
2880
2881 #ifdef DEBUG
2882 bool
2883 IonCompilationCanUseNurseryPointers();
2884 #endif
2885
2886 // Helper class to check that GC pointers embedded in MIR instructions are in
2887 // in the nursery only when the store buffer has been marked as needing to
2888 // cancel all ion compilations. Otherwise, off-thread Ion compilation and
2889 // nursery GCs can happen in parallel, so it's invalid to store pointers to
2890 // nursery things. There's no need to root these pointers, as GC is suppressed
2891 // during compilation and off-thread compilations are canceled on major GCs.
2892 template <typename T>
2893 class CompilerGCPointer
2894 {
2895 js::gc::Cell* ptr_;
2896
2897 public:
CompilerGCPointer(T ptr)2898 explicit CompilerGCPointer(T ptr)
2899 : ptr_(ptr)
2900 {
2901 MOZ_ASSERT_IF(IsInsideNursery(ptr), IonCompilationCanUseNurseryPointers());
2902 #ifdef DEBUG
2903 PerThreadData* pt = TlsPerThreadData.get();
2904 MOZ_ASSERT_IF(pt->runtimeIfOnOwnerThread(), pt->suppressGC);
2905 #endif
2906 }
2907
T()2908 operator T() const { return static_cast<T>(ptr_); }
2909 T operator->() const { return static_cast<T>(ptr_); }
2910
2911 private:
2912 CompilerGCPointer() = delete;
2913 CompilerGCPointer(const CompilerGCPointer<T>&) = delete;
2914 CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete;
2915 };
2916
2917 typedef CompilerGCPointer<JSObject*> CompilerObject;
2918 typedef CompilerGCPointer<NativeObject*> CompilerNativeObject;
2919 typedef CompilerGCPointer<JSFunction*> CompilerFunction;
2920 typedef CompilerGCPointer<JSScript*> CompilerScript;
2921 typedef CompilerGCPointer<PropertyName*> CompilerPropertyName;
2922 typedef CompilerGCPointer<Shape*> CompilerShape;
2923 typedef CompilerGCPointer<ObjectGroup*> CompilerObjectGroup;
2924
2925 class MNewArray
2926 : public MUnaryInstruction,
2927 public NoTypePolicy::Data
2928 {
2929 private:
2930 // Number of elements to allocate for the array.
2931 uint32_t length_;
2932
2933 // Heap where the array should be allocated.
2934 gc::InitialHeap initialHeap_;
2935
2936 // Whether values written to this array should be converted to double first.
2937 bool convertDoubleElements_;
2938
2939 jsbytecode* pc_;
2940
2941 MNewArray(CompilerConstraintList* constraints, uint32_t length, MConstant* templateConst,
2942 gc::InitialHeap initialHeap, jsbytecode* pc);
2943
2944 public:
INSTRUCTION_HEADER(NewArray)2945 INSTRUCTION_HEADER(NewArray)
2946
2947 static MNewArray* New(TempAllocator& alloc, CompilerConstraintList* constraints,
2948 uint32_t length, MConstant* templateConst,
2949 gc::InitialHeap initialHeap, jsbytecode* pc)
2950 {
2951 return new(alloc) MNewArray(constraints, length, templateConst, initialHeap, pc);
2952 }
2953
length()2954 uint32_t length() const {
2955 return length_;
2956 }
2957
templateObject()2958 JSObject* templateObject() const {
2959 return getOperand(0)->toConstant()->value().toObjectOrNull();
2960 }
2961
initialHeap()2962 gc::InitialHeap initialHeap() const {
2963 return initialHeap_;
2964 }
2965
pc()2966 jsbytecode* pc() const {
2967 return pc_;
2968 }
2969
convertDoubleElements()2970 bool convertDoubleElements() const {
2971 return convertDoubleElements_;
2972 }
2973
2974 // Returns true if the code generator should call through to the
2975 // VM rather than the fast path.
2976 bool shouldUseVM() const;
2977
2978 // NewArray is marked as non-effectful because all our allocations are
2979 // either lazy when we are using "new Array(length)" or bounded by the
2980 // script or the stack size when we are using "new Array(...)" or "[...]"
2981 // notations. So we might have to allocate the array twice if we bail
2982 // during the computation of the first element of the square braket
2983 // notation.
getAliasSet()2984 virtual AliasSet getAliasSet() const override {
2985 return AliasSet::None();
2986 }
2987
2988 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()2989 bool canRecoverOnBailout() const override {
2990 // The template object can safely be used in the recover instruction
2991 // because it can never be mutated by any other function execution.
2992 return templateObject() != nullptr;
2993 }
2994 };
2995
2996 class MNewArrayCopyOnWrite : public MNullaryInstruction
2997 {
2998 CompilerGCPointer<ArrayObject*> templateObject_;
2999 gc::InitialHeap initialHeap_;
3000
MNewArrayCopyOnWrite(CompilerConstraintList * constraints,ArrayObject * templateObject,gc::InitialHeap initialHeap)3001 MNewArrayCopyOnWrite(CompilerConstraintList* constraints, ArrayObject* templateObject,
3002 gc::InitialHeap initialHeap)
3003 : templateObject_(templateObject),
3004 initialHeap_(initialHeap)
3005 {
3006 MOZ_ASSERT(!templateObject->isSingleton());
3007 setResultType(MIRType_Object);
3008 setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
3009 }
3010
3011 public:
INSTRUCTION_HEADER(NewArrayCopyOnWrite)3012 INSTRUCTION_HEADER(NewArrayCopyOnWrite)
3013
3014 static MNewArrayCopyOnWrite* New(TempAllocator& alloc,
3015 CompilerConstraintList* constraints,
3016 ArrayObject* templateObject,
3017 gc::InitialHeap initialHeap)
3018 {
3019 return new(alloc) MNewArrayCopyOnWrite(constraints, templateObject, initialHeap);
3020 }
3021
templateObject()3022 ArrayObject* templateObject() const {
3023 return templateObject_;
3024 }
3025
initialHeap()3026 gc::InitialHeap initialHeap() const {
3027 return initialHeap_;
3028 }
3029
getAliasSet()3030 virtual AliasSet getAliasSet() const override {
3031 return AliasSet::None();
3032 }
3033 };
3034
3035 class MNewArrayDynamicLength
3036 : public MUnaryInstruction,
3037 public IntPolicy<0>::Data
3038 {
3039 CompilerObject templateObject_;
3040 gc::InitialHeap initialHeap_;
3041
MNewArrayDynamicLength(CompilerConstraintList * constraints,JSObject * templateObject,gc::InitialHeap initialHeap,MDefinition * length)3042 MNewArrayDynamicLength(CompilerConstraintList* constraints, JSObject* templateObject,
3043 gc::InitialHeap initialHeap, MDefinition* length)
3044 : MUnaryInstruction(length),
3045 templateObject_(templateObject),
3046 initialHeap_(initialHeap)
3047 {
3048 setGuard(); // Need to throw if length is negative.
3049 setResultType(MIRType_Object);
3050 if (!templateObject->isSingleton())
3051 setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
3052 }
3053
3054 public:
INSTRUCTION_HEADER(NewArrayDynamicLength)3055 INSTRUCTION_HEADER(NewArrayDynamicLength)
3056
3057 static MNewArrayDynamicLength* New(TempAllocator& alloc, CompilerConstraintList* constraints,
3058 JSObject* templateObject, gc::InitialHeap initialHeap,
3059 MDefinition* length)
3060 {
3061 return new(alloc) MNewArrayDynamicLength(constraints, templateObject, initialHeap, length);
3062 }
3063
length()3064 MDefinition* length() const {
3065 return getOperand(0);
3066 }
templateObject()3067 JSObject* templateObject() const {
3068 return templateObject_;
3069 }
initialHeap()3070 gc::InitialHeap initialHeap() const {
3071 return initialHeap_;
3072 }
3073
getAliasSet()3074 virtual AliasSet getAliasSet() const override {
3075 return AliasSet::None();
3076 }
3077 };
3078
3079 class MNewObject
3080 : public MUnaryInstruction,
3081 public NoTypePolicy::Data
3082 {
3083 public:
3084 enum Mode { ObjectLiteral, ObjectCreate };
3085
3086 private:
3087 gc::InitialHeap initialHeap_;
3088 Mode mode_;
3089
MNewObject(CompilerConstraintList * constraints,MConstant * templateConst,gc::InitialHeap initialHeap,Mode mode)3090 MNewObject(CompilerConstraintList* constraints, MConstant* templateConst,
3091 gc::InitialHeap initialHeap, Mode mode)
3092 : MUnaryInstruction(templateConst),
3093 initialHeap_(initialHeap),
3094 mode_(mode)
3095 {
3096 MOZ_ASSERT_IF(mode != ObjectLiteral, !shouldUseVM());
3097 setResultType(MIRType_Object);
3098
3099 if (JSObject* obj = templateObject())
3100 setResultTypeSet(MakeSingletonTypeSet(constraints, obj));
3101
3102 // The constant is kept separated in a MConstant, this way we can safely
3103 // mark it during GC if we recover the object allocation. Otherwise, by
3104 // making it emittedAtUses, we do not produce register allocations for
3105 // it and inline its content inside the code produced by the
3106 // CodeGenerator.
3107 if (templateConst->toConstant()->value().isObject())
3108 templateConst->setEmittedAtUses();
3109 }
3110
3111 public:
INSTRUCTION_HEADER(NewObject)3112 INSTRUCTION_HEADER(NewObject)
3113
3114 static MNewObject* New(TempAllocator& alloc, CompilerConstraintList* constraints,
3115 MConstant* templateConst, gc::InitialHeap initialHeap,
3116 Mode mode)
3117 {
3118 return new(alloc) MNewObject(constraints, templateConst, initialHeap, mode);
3119 }
3120
3121 // Returns true if the code generator should call through to the
3122 // VM rather than the fast path.
3123 bool shouldUseVM() const;
3124
mode()3125 Mode mode() const {
3126 return mode_;
3127 }
3128
templateObject()3129 JSObject* templateObject() const {
3130 return getOperand(0)->toConstant()->value().toObjectOrNull();
3131 }
3132
initialHeap()3133 gc::InitialHeap initialHeap() const {
3134 return initialHeap_;
3135 }
3136
3137 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()3138 bool canRecoverOnBailout() const override {
3139 // The template object can safely be used in the recover instruction
3140 // because it can never be mutated by any other function execution.
3141 return templateObject() != nullptr;
3142 }
3143 };
3144
3145 class MNewTypedObject : public MNullaryInstruction
3146 {
3147 CompilerGCPointer<InlineTypedObject*> templateObject_;
3148 gc::InitialHeap initialHeap_;
3149
MNewTypedObject(CompilerConstraintList * constraints,InlineTypedObject * templateObject,gc::InitialHeap initialHeap)3150 MNewTypedObject(CompilerConstraintList* constraints,
3151 InlineTypedObject* templateObject,
3152 gc::InitialHeap initialHeap)
3153 : templateObject_(templateObject),
3154 initialHeap_(initialHeap)
3155 {
3156 setResultType(MIRType_Object);
3157 setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
3158 }
3159
3160 public:
INSTRUCTION_HEADER(NewTypedObject)3161 INSTRUCTION_HEADER(NewTypedObject)
3162
3163 static MNewTypedObject* New(TempAllocator& alloc,
3164 CompilerConstraintList* constraints,
3165 InlineTypedObject* templateObject,
3166 gc::InitialHeap initialHeap)
3167 {
3168 return new(alloc) MNewTypedObject(constraints, templateObject, initialHeap);
3169 }
3170
templateObject()3171 InlineTypedObject* templateObject() const {
3172 return templateObject_;
3173 }
3174
initialHeap()3175 gc::InitialHeap initialHeap() const {
3176 return initialHeap_;
3177 }
3178
getAliasSet()3179 virtual AliasSet getAliasSet() const override {
3180 return AliasSet::None();
3181 }
3182 };
3183
3184 class MTypedObjectDescr
3185 : public MUnaryInstruction,
3186 public SingleObjectPolicy::Data
3187 {
3188 private:
MTypedObjectDescr(MDefinition * object)3189 explicit MTypedObjectDescr(MDefinition* object)
3190 : MUnaryInstruction(object)
3191 {
3192 setResultType(MIRType_Object);
3193 setMovable();
3194 }
3195
3196 public:
INSTRUCTION_HEADER(TypedObjectDescr)3197 INSTRUCTION_HEADER(TypedObjectDescr)
3198
3199 static MTypedObjectDescr* New(TempAllocator& alloc, MDefinition* object) {
3200 return new(alloc) MTypedObjectDescr(object);
3201 }
3202
object()3203 MDefinition* object() const {
3204 return getOperand(0);
3205 }
congruentTo(const MDefinition * ins)3206 bool congruentTo(const MDefinition* ins) const override {
3207 return congruentIfOperandsEqual(ins);
3208 }
getAliasSet()3209 AliasSet getAliasSet() const override {
3210 return AliasSet::Load(AliasSet::ObjectFields);
3211 }
3212 };
3213
3214 // Generic way for constructing a SIMD object in IonMonkey, this instruction
3215 // takes as argument a SIMD instruction and returns a new SIMD object which
3216 // corresponds to the MIRType of its operand.
3217 class MSimdBox
3218 : public MUnaryInstruction,
3219 public NoTypePolicy::Data
3220 {
3221 protected:
3222 CompilerGCPointer<InlineTypedObject*> templateObject_;
3223 gc::InitialHeap initialHeap_;
3224
MSimdBox(CompilerConstraintList * constraints,MDefinition * op,InlineTypedObject * templateObject,gc::InitialHeap initialHeap)3225 MSimdBox(CompilerConstraintList* constraints,
3226 MDefinition* op,
3227 InlineTypedObject* templateObject,
3228 gc::InitialHeap initialHeap)
3229 : MUnaryInstruction(op),
3230 templateObject_(templateObject),
3231 initialHeap_(initialHeap)
3232 {
3233 MOZ_ASSERT(IsSimdType(op->type()));
3234 setMovable();
3235 setResultType(MIRType_Object);
3236 if (constraints)
3237 setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
3238 }
3239
3240 public:
INSTRUCTION_HEADER(SimdBox)3241 INSTRUCTION_HEADER(SimdBox)
3242
3243 static MSimdBox* New(TempAllocator& alloc,
3244 CompilerConstraintList* constraints,
3245 MDefinition* op,
3246 InlineTypedObject* templateObject,
3247 gc::InitialHeap initialHeap)
3248 {
3249 return new(alloc) MSimdBox(constraints, op, templateObject, initialHeap);
3250 }
3251
templateObject()3252 InlineTypedObject* templateObject() const {
3253 return templateObject_;
3254 }
3255
initialHeap()3256 gc::InitialHeap initialHeap() const {
3257 return initialHeap_;
3258 }
3259
congruentTo(const MDefinition * ins)3260 bool congruentTo(const MDefinition* ins) const override {
3261 if (congruentIfOperandsEqual(ins)) {
3262 MOZ_ASSERT(ins->toSimdBox()->initialHeap() == initialHeap());
3263 return true;
3264 }
3265
3266 return false;
3267 }
3268
getAliasSet()3269 AliasSet getAliasSet() const override {
3270 return AliasSet::None();
3271 }
3272
3273 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()3274 bool canRecoverOnBailout() const override {
3275 return true;
3276 }
3277 };
3278
3279 class MSimdUnbox
3280 : public MUnaryInstruction,
3281 public SingleObjectPolicy::Data
3282 {
3283 protected:
MSimdUnbox(MDefinition * op,MIRType type)3284 MSimdUnbox(MDefinition* op, MIRType type)
3285 : MUnaryInstruction(op)
3286 {
3287 MOZ_ASSERT(IsSimdType(type));
3288 setGuard();
3289 setMovable();
3290 setResultType(type);
3291 }
3292
3293 public:
INSTRUCTION_HEADER(SimdUnbox)3294 INSTRUCTION_HEADER(SimdUnbox)
3295 ALLOW_CLONE(MSimdUnbox)
3296
3297 static MSimdUnbox* New(TempAllocator& alloc, MDefinition* op, MIRType type)
3298 {
3299 return new(alloc) MSimdUnbox(op, type);
3300 }
3301
3302 MDefinition* foldsTo(TempAllocator& alloc) override;
congruentTo(const MDefinition * ins)3303 bool congruentTo(const MDefinition* ins) const override {
3304 return congruentIfOperandsEqual(ins);
3305 }
3306
getAliasSet()3307 AliasSet getAliasSet() const override {
3308 return AliasSet::None();
3309 }
3310 };
3311
3312 // Creates a new derived type object. At runtime, this is just a call
3313 // to `BinaryBlock::createDerived()`. That is, the MIR itself does not
3314 // compile to particularly optimized code. However, using a distinct
3315 // MIR for creating derived type objects allows the compiler to
3316 // optimize ephemeral typed objects as would be created for a
3317 // reference like `a.b.c` -- here, the `a.b` will create an ephemeral
3318 // derived type object that aliases the memory of `a` itself. The
3319 // specific nature of `a.b` is revealed by using
3320 // `MNewDerivedTypedObject` rather than `MGetProperty` or what have
3321 // you. Moreover, the compiler knows that there are no side-effects,
3322 // so `MNewDerivedTypedObject` instructions can be reordered or pruned
3323 // as dead code.
3324 class MNewDerivedTypedObject
3325 : public MTernaryInstruction,
3326 public Mix3Policy<ObjectPolicy<0>,
3327 ObjectPolicy<1>,
3328 IntPolicy<2> >::Data
3329 {
3330 private:
3331 TypedObjectPrediction prediction_;
3332
MNewDerivedTypedObject(TypedObjectPrediction prediction,MDefinition * type,MDefinition * owner,MDefinition * offset)3333 MNewDerivedTypedObject(TypedObjectPrediction prediction,
3334 MDefinition* type,
3335 MDefinition* owner,
3336 MDefinition* offset)
3337 : MTernaryInstruction(type, owner, offset),
3338 prediction_(prediction)
3339 {
3340 setMovable();
3341 setResultType(MIRType_Object);
3342 }
3343
3344 public:
INSTRUCTION_HEADER(NewDerivedTypedObject)3345 INSTRUCTION_HEADER(NewDerivedTypedObject)
3346
3347 static MNewDerivedTypedObject* New(TempAllocator& alloc, TypedObjectPrediction prediction,
3348 MDefinition* type, MDefinition* owner, MDefinition* offset)
3349 {
3350 return new(alloc) MNewDerivedTypedObject(prediction, type, owner, offset);
3351 }
3352
prediction()3353 TypedObjectPrediction prediction() const {
3354 return prediction_;
3355 }
3356
type()3357 MDefinition* type() const {
3358 return getOperand(0);
3359 }
3360
owner()3361 MDefinition* owner() const {
3362 return getOperand(1);
3363 }
3364
offset()3365 MDefinition* offset() const {
3366 return getOperand(2);
3367 }
3368
getAliasSet()3369 virtual AliasSet getAliasSet() const override {
3370 return AliasSet::None();
3371 }
3372
3373 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()3374 bool canRecoverOnBailout() const override {
3375 return true;
3376 }
3377 };
3378
3379 // This vector is used when the recovered object is kept unboxed. We map the
3380 // offset of each property to the index of the corresponding operands in the
3381 // object state.
3382 struct OperandIndexMap : public TempObject
3383 {
3384 // The number of properties is limited by scalar replacement. Thus we cannot
3385 // have any large number of properties.
3386 FixedList<uint8_t> map;
3387
3388 bool init(TempAllocator& alloc, JSObject* templateObject);
3389 };
3390
3391 // Represent the content of all slots of an object. This instruction is not
3392 // lowered and is not used to generate code.
3393 class MObjectState
3394 : public MVariadicInstruction,
3395 public NoFloatPolicyAfter<1>::Data
3396 {
3397 private:
3398 uint32_t numSlots_;
3399 uint32_t numFixedSlots_; // valid if isUnboxed() == false.
3400 OperandIndexMap* operandIndex_; // valid if isUnboxed() == true.
3401
isUnboxed()3402 bool isUnboxed() const {
3403 return operandIndex_ != nullptr;
3404 }
3405
3406 MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex);
3407 explicit MObjectState(MObjectState* state);
3408
3409 bool init(TempAllocator& alloc, MDefinition* obj);
3410
initSlot(uint32_t slot,MDefinition * def)3411 void initSlot(uint32_t slot, MDefinition* def) {
3412 initOperand(slot + 1, def);
3413 }
3414
3415 public:
3416 INSTRUCTION_HEADER(ObjectState)
3417
3418 // Return the template object of any object creation which can be recovered
3419 // on bailout.
3420 static JSObject* templateObjectOf(MDefinition* obj);
3421
3422 static MObjectState* New(TempAllocator& alloc, MDefinition* obj, MDefinition* undefinedVal);
3423 static MObjectState* Copy(TempAllocator& alloc, MObjectState* state);
3424
object()3425 MDefinition* object() const {
3426 return getOperand(0);
3427 }
3428
numFixedSlots()3429 size_t numFixedSlots() const {
3430 MOZ_ASSERT(!isUnboxed());
3431 return numFixedSlots_;
3432 }
numSlots()3433 size_t numSlots() const {
3434 return numSlots_;
3435 }
3436
getSlot(uint32_t slot)3437 MDefinition* getSlot(uint32_t slot) const {
3438 return getOperand(slot + 1);
3439 }
setSlot(uint32_t slot,MDefinition * def)3440 void setSlot(uint32_t slot, MDefinition* def) {
3441 replaceOperand(slot + 1, def);
3442 }
3443
hasFixedSlot(uint32_t slot)3444 bool hasFixedSlot(uint32_t slot) const {
3445 return slot < numSlots() && slot < numFixedSlots();
3446 }
getFixedSlot(uint32_t slot)3447 MDefinition* getFixedSlot(uint32_t slot) const {
3448 MOZ_ASSERT(slot < numFixedSlots());
3449 return getSlot(slot);
3450 }
setFixedSlot(uint32_t slot,MDefinition * def)3451 void setFixedSlot(uint32_t slot, MDefinition* def) {
3452 MOZ_ASSERT(slot < numFixedSlots());
3453 setSlot(slot, def);
3454 }
3455
hasDynamicSlot(uint32_t slot)3456 bool hasDynamicSlot(uint32_t slot) const {
3457 return numFixedSlots() < numSlots() && slot < numSlots() - numFixedSlots();
3458 }
getDynamicSlot(uint32_t slot)3459 MDefinition* getDynamicSlot(uint32_t slot) const {
3460 return getSlot(slot + numFixedSlots());
3461 }
setDynamicSlot(uint32_t slot,MDefinition * def)3462 void setDynamicSlot(uint32_t slot, MDefinition* def) {
3463 setSlot(slot + numFixedSlots(), def);
3464 }
3465
3466 // Interface reserved for unboxed objects.
hasOffset(uint32_t offset)3467 bool hasOffset(uint32_t offset) const {
3468 MOZ_ASSERT(isUnboxed());
3469 return offset < operandIndex_->map.length() && operandIndex_->map[offset] != 0;
3470 }
getOffset(uint32_t offset)3471 MDefinition* getOffset(uint32_t offset) const {
3472 return getOperand(operandIndex_->map[offset]);
3473 }
setOffset(uint32_t offset,MDefinition * def)3474 void setOffset(uint32_t offset, MDefinition* def) {
3475 replaceOperand(operandIndex_->map[offset], def);
3476 }
3477
3478 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()3479 bool canRecoverOnBailout() const override {
3480 return true;
3481 }
3482 };
3483
3484 // Represent the contents of all elements of an array. This instruction is not
3485 // lowered and is not used to generate code.
3486 class MArrayState
3487 : public MVariadicInstruction,
3488 public NoFloatPolicyAfter<2>::Data
3489 {
3490 private:
3491 uint32_t numElements_;
3492
3493 explicit MArrayState(MDefinition* arr);
3494
3495 bool init(TempAllocator& alloc, MDefinition* obj, MDefinition* len);
3496
initElement(uint32_t index,MDefinition * def)3497 void initElement(uint32_t index, MDefinition* def) {
3498 initOperand(index + 2, def);
3499 }
3500
3501 public:
3502 INSTRUCTION_HEADER(ArrayState)
3503
3504 static MArrayState* New(TempAllocator& alloc, MDefinition* arr, MDefinition* undefinedVal,
3505 MDefinition* initLength);
3506 static MArrayState* Copy(TempAllocator& alloc, MArrayState* state);
3507
array()3508 MDefinition* array() const {
3509 return getOperand(0);
3510 }
3511
initializedLength()3512 MDefinition* initializedLength() const {
3513 return getOperand(1);
3514 }
setInitializedLength(MDefinition * def)3515 void setInitializedLength(MDefinition* def) {
3516 replaceOperand(1, def);
3517 }
3518
3519
numElements()3520 size_t numElements() const {
3521 return numElements_;
3522 }
3523
getElement(uint32_t index)3524 MDefinition* getElement(uint32_t index) const {
3525 return getOperand(index + 2);
3526 }
setElement(uint32_t index,MDefinition * def)3527 void setElement(uint32_t index, MDefinition* def) {
3528 replaceOperand(index + 2, def);
3529 }
3530
3531 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()3532 bool canRecoverOnBailout() const override {
3533 return true;
3534 }
3535 };
3536
3537 // Setting __proto__ in an object literal.
3538 class MMutateProto
3539 : public MAryInstruction<2>,
3540 public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
3541 {
3542 protected:
MMutateProto(MDefinition * obj,MDefinition * value)3543 MMutateProto(MDefinition* obj, MDefinition* value)
3544 {
3545 initOperand(0, obj);
3546 initOperand(1, value);
3547 setResultType(MIRType_None);
3548 }
3549
3550 public:
INSTRUCTION_HEADER(MutateProto)3551 INSTRUCTION_HEADER(MutateProto)
3552
3553 static MMutateProto* New(TempAllocator& alloc, MDefinition* obj, MDefinition* value)
3554 {
3555 return new(alloc) MMutateProto(obj, value);
3556 }
3557
getObject()3558 MDefinition* getObject() const {
3559 return getOperand(0);
3560 }
getValue()3561 MDefinition* getValue() const {
3562 return getOperand(1);
3563 }
3564
possiblyCalls()3565 bool possiblyCalls() const override {
3566 return true;
3567 }
3568 };
3569
3570 // Slow path for adding a property to an object without a known base.
3571 class MInitProp
3572 : public MAryInstruction<2>,
3573 public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
3574 {
3575 CompilerPropertyName name_;
3576
3577 protected:
MInitProp(MDefinition * obj,PropertyName * name,MDefinition * value)3578 MInitProp(MDefinition* obj, PropertyName* name, MDefinition* value)
3579 : name_(name)
3580 {
3581 initOperand(0, obj);
3582 initOperand(1, value);
3583 setResultType(MIRType_None);
3584 }
3585
3586 public:
INSTRUCTION_HEADER(InitProp)3587 INSTRUCTION_HEADER(InitProp)
3588
3589 static MInitProp* New(TempAllocator& alloc, MDefinition* obj, PropertyName* name,
3590 MDefinition* value)
3591 {
3592 return new(alloc) MInitProp(obj, name, value);
3593 }
3594
getObject()3595 MDefinition* getObject() const {
3596 return getOperand(0);
3597 }
getValue()3598 MDefinition* getValue() const {
3599 return getOperand(1);
3600 }
3601
propertyName()3602 PropertyName* propertyName() const {
3603 return name_;
3604 }
3605
possiblyCalls()3606 bool possiblyCalls() const override {
3607 return true;
3608 }
3609 };
3610
3611 class MInitPropGetterSetter
3612 : public MBinaryInstruction,
3613 public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
3614 {
3615 CompilerPropertyName name_;
3616
MInitPropGetterSetter(MDefinition * obj,PropertyName * name,MDefinition * value)3617 MInitPropGetterSetter(MDefinition* obj, PropertyName* name, MDefinition* value)
3618 : MBinaryInstruction(obj, value),
3619 name_(name)
3620 { }
3621
3622 public:
INSTRUCTION_HEADER(InitPropGetterSetter)3623 INSTRUCTION_HEADER(InitPropGetterSetter)
3624
3625 static MInitPropGetterSetter* New(TempAllocator& alloc, MDefinition* obj, PropertyName* name,
3626 MDefinition* value)
3627 {
3628 return new(alloc) MInitPropGetterSetter(obj, name, value);
3629 }
3630
object()3631 MDefinition* object() const {
3632 return getOperand(0);
3633 }
value()3634 MDefinition* value() const {
3635 return getOperand(1);
3636 }
name()3637 PropertyName* name() const {
3638 return name_;
3639 }
3640 };
3641
3642 class MInitElem
3643 : public MAryInstruction<3>,
3644 public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, BoxPolicy<2> >::Data
3645 {
MInitElem(MDefinition * obj,MDefinition * id,MDefinition * value)3646 MInitElem(MDefinition* obj, MDefinition* id, MDefinition* value)
3647 {
3648 initOperand(0, obj);
3649 initOperand(1, id);
3650 initOperand(2, value);
3651 setResultType(MIRType_None);
3652 }
3653
3654 public:
INSTRUCTION_HEADER(InitElem)3655 INSTRUCTION_HEADER(InitElem)
3656
3657 static MInitElem* New(TempAllocator& alloc, MDefinition* obj, MDefinition* id,
3658 MDefinition* value)
3659 {
3660 return new(alloc) MInitElem(obj, id, value);
3661 }
3662
getObject()3663 MDefinition* getObject() const {
3664 return getOperand(0);
3665 }
getId()3666 MDefinition* getId() const {
3667 return getOperand(1);
3668 }
getValue()3669 MDefinition* getValue() const {
3670 return getOperand(2);
3671 }
possiblyCalls()3672 bool possiblyCalls() const override {
3673 return true;
3674 }
3675 };
3676
3677 class MInitElemGetterSetter
3678 : public MTernaryInstruction,
3679 public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2> >::Data
3680 {
MInitElemGetterSetter(MDefinition * obj,MDefinition * id,MDefinition * value)3681 MInitElemGetterSetter(MDefinition* obj, MDefinition* id, MDefinition* value)
3682 : MTernaryInstruction(obj, id, value)
3683 { }
3684
3685 public:
INSTRUCTION_HEADER(InitElemGetterSetter)3686 INSTRUCTION_HEADER(InitElemGetterSetter)
3687
3688 static MInitElemGetterSetter* New(TempAllocator& alloc, MDefinition* obj, MDefinition* id,
3689 MDefinition* value)
3690 {
3691 return new(alloc) MInitElemGetterSetter(obj, id, value);
3692 }
3693
object()3694 MDefinition* object() const {
3695 return getOperand(0);
3696 }
idValue()3697 MDefinition* idValue() const {
3698 return getOperand(1);
3699 }
value()3700 MDefinition* value() const {
3701 return getOperand(2);
3702 }
3703 };
3704
3705 class MCall
3706 : public MVariadicInstruction,
3707 public CallPolicy::Data
3708 {
3709 private:
3710 // An MCall uses the MPrepareCall, MDefinition for the function, and
3711 // MPassArg instructions. They are stored in the same list.
3712 static const size_t FunctionOperandIndex = 0;
3713 static const size_t NumNonArgumentOperands = 1;
3714
3715 protected:
3716 // Monomorphic cache of single target from TI, or nullptr.
3717 CompilerFunction target_;
3718
3719 // Original value of argc from the bytecode.
3720 uint32_t numActualArgs_;
3721
3722 // True if the call is for JSOP_NEW.
3723 bool construct_;
3724
3725 bool needsArgCheck_;
3726
MCall(JSFunction * target,uint32_t numActualArgs,bool construct)3727 MCall(JSFunction* target, uint32_t numActualArgs, bool construct)
3728 : target_(target),
3729 numActualArgs_(numActualArgs),
3730 construct_(construct),
3731 needsArgCheck_(true)
3732 {
3733 setResultType(MIRType_Value);
3734 }
3735
3736 public:
3737 INSTRUCTION_HEADER(Call)
3738 static MCall* New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
3739 bool construct, bool isDOMCall);
3740
initFunction(MDefinition * func)3741 void initFunction(MDefinition* func) {
3742 initOperand(FunctionOperandIndex, func);
3743 }
3744
needsArgCheck()3745 bool needsArgCheck() const {
3746 return needsArgCheck_;
3747 }
3748
disableArgCheck()3749 void disableArgCheck() {
3750 needsArgCheck_ = false;
3751 }
getFunction()3752 MDefinition* getFunction() const {
3753 return getOperand(FunctionOperandIndex);
3754 }
replaceFunction(MInstruction * newfunc)3755 void replaceFunction(MInstruction* newfunc) {
3756 replaceOperand(FunctionOperandIndex, newfunc);
3757 }
3758
3759 void addArg(size_t argnum, MDefinition* arg);
3760
getArg(uint32_t index)3761 MDefinition* getArg(uint32_t index) const {
3762 return getOperand(NumNonArgumentOperands + index);
3763 }
3764
IndexOfThis()3765 static size_t IndexOfThis() {
3766 return NumNonArgumentOperands;
3767 }
IndexOfArgument(size_t index)3768 static size_t IndexOfArgument(size_t index) {
3769 return NumNonArgumentOperands + index + 1; // +1 to skip |this|.
3770 }
IndexOfStackArg(size_t index)3771 static size_t IndexOfStackArg(size_t index) {
3772 return NumNonArgumentOperands + index;
3773 }
3774
3775 // For TI-informed monomorphic callsites.
getSingleTarget()3776 JSFunction* getSingleTarget() const {
3777 return target_;
3778 }
3779
isConstructing()3780 bool isConstructing() const {
3781 return construct_;
3782 }
3783
3784 // The number of stack arguments is the max between the number of formal
3785 // arguments and the number of actual arguments. The number of stack
3786 // argument includes the |undefined| padding added in case of underflow.
3787 // Includes |this|.
numStackArgs()3788 uint32_t numStackArgs() const {
3789 return numOperands() - NumNonArgumentOperands;
3790 }
3791
3792 // Does not include |this|.
numActualArgs()3793 uint32_t numActualArgs() const {
3794 return numActualArgs_;
3795 }
3796
possiblyCalls()3797 bool possiblyCalls() const override {
3798 return true;
3799 }
3800
isCallDOMNative()3801 virtual bool isCallDOMNative() const {
3802 return false;
3803 }
3804
3805 // A method that can be called to tell the MCall to figure out whether it's
3806 // movable or not. This can't be done in the constructor, because it
3807 // depends on the arguments to the call, and those aren't passed to the
3808 // constructor but are set up later via addArg.
computeMovable()3809 virtual void computeMovable() {
3810 }
3811 };
3812
3813 class MCallDOMNative : public MCall
3814 {
3815 // A helper class for MCalls for DOM natives. Note that this is NOT
3816 // actually a separate MIR op from MCall, because all sorts of places use
3817 // isCall() to check for calls and all we really want is to overload a few
3818 // virtual things from MCall.
3819 protected:
MCallDOMNative(JSFunction * target,uint32_t numActualArgs)3820 MCallDOMNative(JSFunction* target, uint32_t numActualArgs)
3821 : MCall(target, numActualArgs, false)
3822 {
3823 MOZ_ASSERT(getJitInfo()->type() != JSJitInfo::InlinableNative);
3824
3825 // If our jitinfo is not marked eliminatable, that means that our C++
3826 // implementation is fallible or that it never wants to be eliminated or
3827 // that we have no hope of ever doing the sort of argument analysis that
3828 // would allow us to detemine that we're side-effect-free. In the
3829 // latter case we wouldn't get DCEd no matter what, but for the former
3830 // two cases we have to explicitly say that we can't be DCEd.
3831 if (!getJitInfo()->isEliminatable)
3832 setGuard();
3833 }
3834
3835 friend MCall* MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc,
3836 size_t numActualArgs, bool construct, bool isDOMCall);
3837
3838 const JSJitInfo* getJitInfo() const;
3839 public:
3840 virtual AliasSet getAliasSet() const override;
3841
3842 virtual bool congruentTo(const MDefinition* ins) const override;
3843
isCallDOMNative()3844 virtual bool isCallDOMNative() const override {
3845 return true;
3846 }
3847
3848 virtual void computeMovable() override;
3849 };
3850
3851 // arr.splice(start, deleteCount) with unused return value.
3852 class MArraySplice
3853 : public MTernaryInstruction,
3854 public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2> >::Data
3855 {
3856 private:
3857
MArraySplice(MDefinition * object,MDefinition * start,MDefinition * deleteCount)3858 MArraySplice(MDefinition* object, MDefinition* start, MDefinition* deleteCount)
3859 : MTernaryInstruction(object, start, deleteCount)
3860 { }
3861
3862 public:
INSTRUCTION_HEADER(ArraySplice)3863 INSTRUCTION_HEADER(ArraySplice)
3864 static MArraySplice* New(TempAllocator& alloc, MDefinition* object,
3865 MDefinition* start, MDefinition* deleteCount)
3866 {
3867 return new(alloc) MArraySplice(object, start, deleteCount);
3868 }
3869
object()3870 MDefinition* object() const {
3871 return getOperand(0);
3872 }
3873
start()3874 MDefinition* start() const {
3875 return getOperand(1);
3876 }
3877
deleteCount()3878 MDefinition* deleteCount() const {
3879 return getOperand(2);
3880 }
3881
possiblyCalls()3882 bool possiblyCalls() const override {
3883 return true;
3884 }
3885 };
3886
3887 // fun.apply(self, arguments)
3888 class MApplyArgs
3889 : public MAryInstruction<3>,
3890 public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >::Data
3891 {
3892 protected:
3893 // Monomorphic cache of single target from TI, or nullptr.
3894 CompilerFunction target_;
3895
MApplyArgs(JSFunction * target,MDefinition * fun,MDefinition * argc,MDefinition * self)3896 MApplyArgs(JSFunction* target, MDefinition* fun, MDefinition* argc, MDefinition* self)
3897 : target_(target)
3898 {
3899 initOperand(0, fun);
3900 initOperand(1, argc);
3901 initOperand(2, self);
3902 setResultType(MIRType_Value);
3903 }
3904
3905 public:
3906 INSTRUCTION_HEADER(ApplyArgs)
3907 static MApplyArgs* New(TempAllocator& alloc, JSFunction* target, MDefinition* fun,
3908 MDefinition* argc, MDefinition* self);
3909
getFunction()3910 MDefinition* getFunction() const {
3911 return getOperand(0);
3912 }
3913
3914 // For TI-informed monomorphic callsites.
getSingleTarget()3915 JSFunction* getSingleTarget() const {
3916 return target_;
3917 }
3918
getArgc()3919 MDefinition* getArgc() const {
3920 return getOperand(1);
3921 }
getThis()3922 MDefinition* getThis() const {
3923 return getOperand(2);
3924 }
possiblyCalls()3925 bool possiblyCalls() const override {
3926 return true;
3927 }
3928 };
3929
3930 // fun.apply(fn, array)
3931 class MApplyArray
3932 : public MAryInstruction<3>,
3933 public Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, BoxPolicy<2> >::Data
3934 {
3935 protected:
3936 // Monomorphic cache of single target from TI, or nullptr.
3937 CompilerFunction target_;
3938
MApplyArray(JSFunction * target,MDefinition * fun,MDefinition * elements,MDefinition * self)3939 MApplyArray(JSFunction* target, MDefinition* fun, MDefinition* elements, MDefinition* self)
3940 : target_(target)
3941 {
3942 initOperand(0, fun);
3943 initOperand(1, elements);
3944 initOperand(2, self);
3945 setResultType(MIRType_Value);
3946 }
3947
3948 public:
3949 INSTRUCTION_HEADER(ApplyArray)
3950 static MApplyArray* New(TempAllocator& alloc, JSFunction* target, MDefinition* fun,
3951 MDefinition* elements, MDefinition* self);
3952
getFunction()3953 MDefinition* getFunction() const {
3954 return getOperand(0);
3955 }
3956
3957 // For TI-informed monomorphic callsites.
getSingleTarget()3958 JSFunction* getSingleTarget() const {
3959 return target_;
3960 }
3961
getElements()3962 MDefinition* getElements() const {
3963 return getOperand(1);
3964 }
getThis()3965 MDefinition* getThis() const {
3966 return getOperand(2);
3967 }
possiblyCalls()3968 bool possiblyCalls() const override {
3969 return true;
3970 }
3971 };
3972
3973 class MBail : public MNullaryInstruction
3974 {
3975 protected:
MBail(BailoutKind kind)3976 explicit MBail(BailoutKind kind)
3977 : MNullaryInstruction()
3978 {
3979 bailoutKind_ = kind;
3980 setGuard();
3981 }
3982
3983 private:
3984 BailoutKind bailoutKind_;
3985
3986 public:
INSTRUCTION_HEADER(Bail)3987 INSTRUCTION_HEADER(Bail)
3988
3989 static MBail*
3990 New(TempAllocator& alloc, BailoutKind kind) {
3991 return new(alloc) MBail(kind);
3992 }
3993 static MBail*
New(TempAllocator & alloc)3994 New(TempAllocator& alloc) {
3995 return new(alloc) MBail(Bailout_Inevitable);
3996 }
3997
getAliasSet()3998 AliasSet getAliasSet() const override {
3999 return AliasSet::None();
4000 }
4001
bailoutKind()4002 BailoutKind bailoutKind() const {
4003 return bailoutKind_;
4004 }
4005 };
4006
4007 class MUnreachable
4008 : public MAryControlInstruction<0, 0>,
4009 public NoTypePolicy::Data
4010 {
4011 public:
INSTRUCTION_HEADER(Unreachable)4012 INSTRUCTION_HEADER(Unreachable)
4013
4014 static MUnreachable* New(TempAllocator& alloc) {
4015 return new(alloc) MUnreachable();
4016 }
4017
getAliasSet()4018 AliasSet getAliasSet() const override {
4019 return AliasSet::None();
4020 }
4021 };
4022
4023 // This class serve as a way to force the encoding of a snapshot, even if there
4024 // is no resume point using it. This is useful to run MAssertRecoveredOnBailout
4025 // assertions.
4026 class MEncodeSnapshot : public MNullaryInstruction
4027 {
4028 protected:
MEncodeSnapshot()4029 MEncodeSnapshot()
4030 : MNullaryInstruction()
4031 {
4032 setGuard();
4033 }
4034
4035 public:
INSTRUCTION_HEADER(EncodeSnapshot)4036 INSTRUCTION_HEADER(EncodeSnapshot)
4037
4038 static MEncodeSnapshot*
4039 New(TempAllocator& alloc) {
4040 return new(alloc) MEncodeSnapshot();
4041 }
4042 };
4043
4044 class MAssertRecoveredOnBailout
4045 : public MUnaryInstruction,
4046 public NoTypePolicy::Data
4047 {
4048 protected:
4049 bool mustBeRecovered_;
4050
MAssertRecoveredOnBailout(MDefinition * ins,bool mustBeRecovered)4051 MAssertRecoveredOnBailout(MDefinition* ins, bool mustBeRecovered)
4052 : MUnaryInstruction(ins), mustBeRecovered_(mustBeRecovered)
4053 {
4054 setResultType(MIRType_Value);
4055 setRecoveredOnBailout();
4056 setGuard();
4057 }
4058
4059 public:
INSTRUCTION_HEADER(AssertRecoveredOnBailout)4060 INSTRUCTION_HEADER(AssertRecoveredOnBailout)
4061
4062 static MAssertRecoveredOnBailout* New(TempAllocator& alloc, MDefinition* ins,
4063 bool mustBeRecovered)
4064 {
4065 return new(alloc) MAssertRecoveredOnBailout(ins, mustBeRecovered);
4066 }
4067
4068 // Needed to assert that float32 instructions are correctly recovered.
canConsumeFloat32(MUse * use)4069 bool canConsumeFloat32(MUse* use) const override { return true; }
4070
4071 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()4072 bool canRecoverOnBailout() const override {
4073 return true;
4074 }
4075 };
4076
4077 class MAssertFloat32
4078 : public MUnaryInstruction,
4079 public NoTypePolicy::Data
4080 {
4081 protected:
4082 bool mustBeFloat32_;
4083
MAssertFloat32(MDefinition * value,bool mustBeFloat32)4084 MAssertFloat32(MDefinition* value, bool mustBeFloat32)
4085 : MUnaryInstruction(value), mustBeFloat32_(mustBeFloat32)
4086 {
4087 }
4088
4089 public:
INSTRUCTION_HEADER(AssertFloat32)4090 INSTRUCTION_HEADER(AssertFloat32)
4091
4092 static MAssertFloat32* New(TempAllocator& alloc, MDefinition* value, bool mustBeFloat32) {
4093 return new(alloc) MAssertFloat32(value, mustBeFloat32);
4094 }
4095
canConsumeFloat32(MUse * use)4096 bool canConsumeFloat32(MUse* use) const override { return true; }
4097
mustBeFloat32()4098 bool mustBeFloat32() const { return mustBeFloat32_; }
4099 };
4100
4101 class MGetDynamicName
4102 : public MAryInstruction<2>,
4103 public MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<1> >::Data
4104 {
4105 protected:
MGetDynamicName(MDefinition * scopeChain,MDefinition * name)4106 MGetDynamicName(MDefinition* scopeChain, MDefinition* name)
4107 {
4108 initOperand(0, scopeChain);
4109 initOperand(1, name);
4110 setResultType(MIRType_Value);
4111 }
4112
4113 public:
INSTRUCTION_HEADER(GetDynamicName)4114 INSTRUCTION_HEADER(GetDynamicName)
4115
4116 static MGetDynamicName*
4117 New(TempAllocator& alloc, MDefinition* scopeChain, MDefinition* name) {
4118 return new(alloc) MGetDynamicName(scopeChain, name);
4119 }
4120
getScopeChain()4121 MDefinition* getScopeChain() const {
4122 return getOperand(0);
4123 }
getName()4124 MDefinition* getName() const {
4125 return getOperand(1);
4126 }
possiblyCalls()4127 bool possiblyCalls() const override {
4128 return true;
4129 }
4130 };
4131
4132 class MCallDirectEval
4133 : public MAryInstruction<3>,
4134 public Mix3Policy<ObjectPolicy<0>,
4135 StringPolicy<1>,
4136 BoxPolicy<2> >::Data
4137 {
4138 protected:
MCallDirectEval(MDefinition * scopeChain,MDefinition * string,MDefinition * newTargetValue,jsbytecode * pc)4139 MCallDirectEval(MDefinition* scopeChain, MDefinition* string,
4140 MDefinition* newTargetValue, jsbytecode* pc)
4141 : pc_(pc)
4142 {
4143 initOperand(0, scopeChain);
4144 initOperand(1, string);
4145 initOperand(2, newTargetValue);
4146 setResultType(MIRType_Value);
4147 }
4148
4149 public:
INSTRUCTION_HEADER(CallDirectEval)4150 INSTRUCTION_HEADER(CallDirectEval)
4151
4152 static MCallDirectEval*
4153 New(TempAllocator& alloc, MDefinition* scopeChain, MDefinition* string,
4154 MDefinition* newTargetValue, jsbytecode* pc)
4155 {
4156 return new(alloc) MCallDirectEval(scopeChain, string, newTargetValue, pc);
4157 }
4158
getScopeChain()4159 MDefinition* getScopeChain() const {
4160 return getOperand(0);
4161 }
getString()4162 MDefinition* getString() const {
4163 return getOperand(1);
4164 }
getNewTargetValue()4165 MDefinition* getNewTargetValue() const {
4166 return getOperand(2);
4167 }
4168
pc()4169 jsbytecode* pc() const {
4170 return pc_;
4171 }
4172
possiblyCalls()4173 bool possiblyCalls() const override {
4174 return true;
4175 }
4176
4177 private:
4178 jsbytecode* pc_;
4179 };
4180
4181 class MCompare
4182 : public MBinaryInstruction,
4183 public ComparePolicy::Data
4184 {
4185 public:
4186 enum CompareType {
4187
4188 // Anything compared to Undefined
4189 Compare_Undefined,
4190
4191 // Anything compared to Null
4192 Compare_Null,
4193
4194 // Undefined compared to Boolean
4195 // Null compared to Boolean
4196 // Double compared to Boolean
4197 // String compared to Boolean
4198 // Symbol compared to Boolean
4199 // Object compared to Boolean
4200 // Value compared to Boolean
4201 Compare_Boolean,
4202
4203 // Int32 compared to Int32
4204 // Boolean compared to Boolean
4205 Compare_Int32,
4206 Compare_Int32MaybeCoerceBoth,
4207 Compare_Int32MaybeCoerceLHS,
4208 Compare_Int32MaybeCoerceRHS,
4209
4210 // Int32 compared as unsigneds
4211 Compare_UInt32,
4212
4213 // Double compared to Double
4214 Compare_Double,
4215
4216 Compare_DoubleMaybeCoerceLHS,
4217 Compare_DoubleMaybeCoerceRHS,
4218
4219 // Float compared to Float
4220 Compare_Float32,
4221
4222 // String compared to String
4223 Compare_String,
4224
4225 // Undefined compared to String
4226 // Null compared to String
4227 // Boolean compared to String
4228 // Int32 compared to String
4229 // Double compared to String
4230 // Object compared to String
4231 // Value compared to String
4232 Compare_StrictString,
4233
4234 // Object compared to Object
4235 Compare_Object,
4236
4237 // Compare 2 values bitwise
4238 Compare_Bitwise,
4239
4240 // All other possible compares
4241 Compare_Unknown
4242 };
4243
4244 private:
4245 CompareType compareType_;
4246 JSOp jsop_;
4247 bool operandMightEmulateUndefined_;
4248 bool operandsAreNeverNaN_;
4249
4250 // When a floating-point comparison is converted to an integer comparison
4251 // (when range analysis proves it safe), we need to convert the operands
4252 // to integer as well.
4253 bool truncateOperands_;
4254
MCompare(MDefinition * left,MDefinition * right,JSOp jsop)4255 MCompare(MDefinition* left, MDefinition* right, JSOp jsop)
4256 : MBinaryInstruction(left, right),
4257 compareType_(Compare_Unknown),
4258 jsop_(jsop),
4259 operandMightEmulateUndefined_(true),
4260 operandsAreNeverNaN_(false),
4261 truncateOperands_(false)
4262 {
4263 setResultType(MIRType_Boolean);
4264 setMovable();
4265 }
4266
4267 public:
4268 INSTRUCTION_HEADER(Compare)
4269 static MCompare* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, JSOp op);
4270 static MCompare* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, JSOp op,
4271 CompareType compareType);
4272
4273 bool tryFold(bool* result);
4274 bool evaluateConstantOperands(TempAllocator& alloc, bool* result);
4275 MDefinition* foldsTo(TempAllocator& alloc) override;
4276 void filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
4277 bool* filtersNull);
4278
compareType()4279 CompareType compareType() const {
4280 return compareType_;
4281 }
isInt32Comparison()4282 bool isInt32Comparison() const {
4283 return compareType() == Compare_Int32 ||
4284 compareType() == Compare_Int32MaybeCoerceBoth ||
4285 compareType() == Compare_Int32MaybeCoerceLHS ||
4286 compareType() == Compare_Int32MaybeCoerceRHS;
4287 }
isDoubleComparison()4288 bool isDoubleComparison() const {
4289 return compareType() == Compare_Double ||
4290 compareType() == Compare_DoubleMaybeCoerceLHS ||
4291 compareType() == Compare_DoubleMaybeCoerceRHS;
4292 }
isFloat32Comparison()4293 bool isFloat32Comparison() const {
4294 return compareType() == Compare_Float32;
4295 }
isNumericComparison()4296 bool isNumericComparison() const {
4297 return isInt32Comparison() ||
4298 isDoubleComparison() ||
4299 isFloat32Comparison();
4300 }
setCompareType(CompareType type)4301 void setCompareType(CompareType type) {
4302 compareType_ = type;
4303 }
4304 MIRType inputType();
4305
jsop()4306 JSOp jsop() const {
4307 return jsop_;
4308 }
markNoOperandEmulatesUndefined()4309 void markNoOperandEmulatesUndefined() {
4310 operandMightEmulateUndefined_ = false;
4311 }
operandMightEmulateUndefined()4312 bool operandMightEmulateUndefined() const {
4313 return operandMightEmulateUndefined_;
4314 }
operandsAreNeverNaN()4315 bool operandsAreNeverNaN() const {
4316 return operandsAreNeverNaN_;
4317 }
getAliasSet()4318 AliasSet getAliasSet() const override {
4319 // Strict equality is never effectful.
4320 if (jsop_ == JSOP_STRICTEQ || jsop_ == JSOP_STRICTNE)
4321 return AliasSet::None();
4322 if (compareType_ == Compare_Unknown)
4323 return AliasSet::Store(AliasSet::Any);
4324 MOZ_ASSERT(compareType_ <= Compare_Bitwise);
4325 return AliasSet::None();
4326 }
4327
4328 void printOpcode(GenericPrinter& out) const override;
4329 void collectRangeInfoPreTrunc() override;
4330
4331 void trySpecializeFloat32(TempAllocator& alloc) override;
isFloat32Commutative()4332 bool isFloat32Commutative() const override { return true; }
4333 bool needTruncation(TruncateKind kind) override;
4334 void truncate() override;
4335 TruncateKind operandTruncateKind(size_t index) const override;
4336
4337 static CompareType determineCompareType(JSOp op, MDefinition* left, MDefinition* right);
4338 void cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints);
4339
4340 # ifdef DEBUG
isConsistentFloat32Use(MUse * use)4341 bool isConsistentFloat32Use(MUse* use) const override {
4342 // Both sides of the compare can be Float32
4343 return compareType_ == Compare_Float32;
4344 }
4345 # endif
4346
4347 ALLOW_CLONE(MCompare)
4348
4349 protected:
4350 bool tryFoldEqualOperands(bool* result);
4351 bool tryFoldTypeOf(bool* result);
4352
congruentTo(const MDefinition * ins)4353 bool congruentTo(const MDefinition* ins) const override {
4354 if (!binaryCongruentTo(ins))
4355 return false;
4356 return compareType() == ins->toCompare()->compareType() &&
4357 jsop() == ins->toCompare()->jsop();
4358 }
4359 };
4360
4361 // Takes a typed value and returns an untyped value.
4362 class MBox
4363 : public MUnaryInstruction,
4364 public NoTypePolicy::Data
4365 {
MBox(TempAllocator & alloc,MDefinition * ins)4366 MBox(TempAllocator& alloc, MDefinition* ins)
4367 : MUnaryInstruction(ins)
4368 {
4369 setResultType(MIRType_Value);
4370 if (ins->resultTypeSet()) {
4371 setResultTypeSet(ins->resultTypeSet());
4372 } else if (ins->type() != MIRType_Value) {
4373 TypeSet::Type ntype = ins->type() == MIRType_Object
4374 ? TypeSet::AnyObjectType()
4375 : TypeSet::PrimitiveType(ValueTypeFromMIRType(ins->type()));
4376 setResultTypeSet(alloc.lifoAlloc()->new_<TemporaryTypeSet>(alloc.lifoAlloc(), ntype));
4377 }
4378 setMovable();
4379 }
4380
4381 public:
INSTRUCTION_HEADER(Box)4382 INSTRUCTION_HEADER(Box)
4383 static MBox* New(TempAllocator& alloc, MDefinition* ins)
4384 {
4385 // Cannot box a box.
4386 MOZ_ASSERT(ins->type() != MIRType_Value);
4387
4388 return new(alloc) MBox(alloc, ins);
4389 }
4390
congruentTo(const MDefinition * ins)4391 bool congruentTo(const MDefinition* ins) const override {
4392 return congruentIfOperandsEqual(ins);
4393 }
getAliasSet()4394 AliasSet getAliasSet() const override {
4395 return AliasSet::None();
4396 }
4397
4398 ALLOW_CLONE(MBox)
4399 };
4400
4401 // Note: the op may have been inverted during lowering (to put constants in a
4402 // position where they can be immediates), so it is important to use the
4403 // lir->jsop() instead of the mir->jsop() when it is present.
4404 static inline Assembler::Condition
JSOpToCondition(MCompare::CompareType compareType,JSOp op)4405 JSOpToCondition(MCompare::CompareType compareType, JSOp op)
4406 {
4407 bool isSigned = (compareType != MCompare::Compare_UInt32);
4408 return JSOpToCondition(op, isSigned);
4409 }
4410
4411 // Takes a typed value and checks if it is a certain type. If so, the payload
4412 // is unpacked and returned as that type. Otherwise, it is considered a
4413 // deoptimization.
4414 class MUnbox final : public MUnaryInstruction, public BoxInputsPolicy::Data
4415 {
4416 public:
4417 enum Mode {
4418 Fallible, // Check the type, and deoptimize if unexpected.
4419 Infallible, // Type guard is not necessary.
4420 TypeBarrier // Guard on the type, and act like a TypeBarrier on failure.
4421 };
4422
4423 private:
4424 Mode mode_;
4425 BailoutKind bailoutKind_;
4426
MUnbox(MDefinition * ins,MIRType type,Mode mode,BailoutKind kind,TempAllocator & alloc)4427 MUnbox(MDefinition* ins, MIRType type, Mode mode, BailoutKind kind, TempAllocator& alloc)
4428 : MUnaryInstruction(ins),
4429 mode_(mode)
4430 {
4431 // Only allow unboxing a non MIRType_Value when input and output types
4432 // don't match. This is often used to force a bailout. Boxing happens
4433 // during type analysis.
4434 MOZ_ASSERT_IF(ins->type() != MIRType_Value, type != ins->type());
4435
4436 MOZ_ASSERT(type == MIRType_Boolean ||
4437 type == MIRType_Int32 ||
4438 type == MIRType_Double ||
4439 type == MIRType_String ||
4440 type == MIRType_Symbol ||
4441 type == MIRType_Object);
4442
4443 TemporaryTypeSet* resultSet = ins->resultTypeSet();
4444 if (resultSet && type == MIRType_Object)
4445 resultSet = resultSet->cloneObjectsOnly(alloc.lifoAlloc());
4446
4447 setResultType(type);
4448 setResultTypeSet(resultSet);
4449 setMovable();
4450
4451 if (mode_ == TypeBarrier || mode_ == Fallible)
4452 setGuard();
4453
4454 bailoutKind_ = kind;
4455 }
4456 public:
INSTRUCTION_HEADER(Unbox)4457 INSTRUCTION_HEADER(Unbox)
4458 static MUnbox* New(TempAllocator& alloc, MDefinition* ins, MIRType type, Mode mode)
4459 {
4460 // Unless we were given a specific BailoutKind, pick a default based on
4461 // the type we expect.
4462 BailoutKind kind;
4463 switch (type) {
4464 case MIRType_Boolean:
4465 kind = Bailout_NonBooleanInput;
4466 break;
4467 case MIRType_Int32:
4468 kind = Bailout_NonInt32Input;
4469 break;
4470 case MIRType_Double:
4471 kind = Bailout_NonNumericInput; // Int32s are fine too
4472 break;
4473 case MIRType_String:
4474 kind = Bailout_NonStringInput;
4475 break;
4476 case MIRType_Symbol:
4477 kind = Bailout_NonSymbolInput;
4478 break;
4479 case MIRType_Object:
4480 kind = Bailout_NonObjectInput;
4481 break;
4482 default:
4483 MOZ_CRASH("Given MIRType cannot be unboxed.");
4484 }
4485
4486 return new(alloc) MUnbox(ins, type, mode, kind, alloc);
4487 }
4488
New(TempAllocator & alloc,MDefinition * ins,MIRType type,Mode mode,BailoutKind kind)4489 static MUnbox* New(TempAllocator& alloc, MDefinition* ins, MIRType type, Mode mode,
4490 BailoutKind kind)
4491 {
4492 return new(alloc) MUnbox(ins, type, mode, kind, alloc);
4493 }
4494
mode()4495 Mode mode() const {
4496 return mode_;
4497 }
bailoutKind()4498 BailoutKind bailoutKind() const {
4499 // If infallible, no bailout should be generated.
4500 MOZ_ASSERT(fallible());
4501 return bailoutKind_;
4502 }
fallible()4503 bool fallible() const {
4504 return mode() != Infallible;
4505 }
congruentTo(const MDefinition * ins)4506 bool congruentTo(const MDefinition* ins) const override {
4507 if (!ins->isUnbox() || ins->toUnbox()->mode() != mode())
4508 return false;
4509 return congruentIfOperandsEqual(ins);
4510 }
4511
4512 MDefinition* foldsTo(TempAllocator& alloc) override;
4513
getAliasSet()4514 AliasSet getAliasSet() const override {
4515 return AliasSet::None();
4516 }
4517 void printOpcode(GenericPrinter& out) const override;
makeInfallible()4518 void makeInfallible() {
4519 // Should only be called if we're already Infallible or TypeBarrier
4520 MOZ_ASSERT(mode() != Fallible);
4521 mode_ = Infallible;
4522 }
4523
4524 ALLOW_CLONE(MUnbox)
4525 };
4526
4527 class MGuardObject
4528 : public MUnaryInstruction,
4529 public SingleObjectPolicy::Data
4530 {
MGuardObject(MDefinition * ins)4531 explicit MGuardObject(MDefinition* ins)
4532 : MUnaryInstruction(ins)
4533 {
4534 setGuard();
4535 setMovable();
4536 setResultType(MIRType_Object);
4537 setResultTypeSet(ins->resultTypeSet());
4538 }
4539
4540 public:
INSTRUCTION_HEADER(GuardObject)4541 INSTRUCTION_HEADER(GuardObject)
4542
4543 static MGuardObject* New(TempAllocator& alloc, MDefinition* ins) {
4544 return new(alloc) MGuardObject(ins);
4545 }
getAliasSet()4546 AliasSet getAliasSet() const override {
4547 return AliasSet::None();
4548 }
4549 };
4550
4551 class MGuardString
4552 : public MUnaryInstruction,
4553 public StringPolicy<0>::Data
4554 {
MGuardString(MDefinition * ins)4555 explicit MGuardString(MDefinition* ins)
4556 : MUnaryInstruction(ins)
4557 {
4558 setGuard();
4559 setMovable();
4560 setResultType(MIRType_String);
4561 }
4562
4563 public:
INSTRUCTION_HEADER(GuardString)4564 INSTRUCTION_HEADER(GuardString)
4565
4566 static MGuardString* New(TempAllocator& alloc, MDefinition* ins) {
4567 return new(alloc) MGuardString(ins);
4568 }
4569
getAliasSet()4570 AliasSet getAliasSet() const override {
4571 return AliasSet::None();
4572 }
4573 };
4574
4575 class MPolyInlineGuard
4576 : public MUnaryInstruction,
4577 public SingleObjectPolicy::Data
4578 {
MPolyInlineGuard(MDefinition * ins)4579 explicit MPolyInlineGuard(MDefinition* ins)
4580 : MUnaryInstruction(ins)
4581 {
4582 setGuard();
4583 setResultType(MIRType_Object);
4584 setResultTypeSet(ins->resultTypeSet());
4585 }
4586
4587 public:
INSTRUCTION_HEADER(PolyInlineGuard)4588 INSTRUCTION_HEADER(PolyInlineGuard)
4589
4590 static MPolyInlineGuard* New(TempAllocator& alloc, MDefinition* ins) {
4591 return new(alloc) MPolyInlineGuard(ins);
4592 }
getAliasSet()4593 AliasSet getAliasSet() const override {
4594 return AliasSet::None();
4595 }
4596 };
4597
4598 class MAssertRange
4599 : public MUnaryInstruction,
4600 public NoTypePolicy::Data
4601 {
4602 // This is the range checked by the assertion. Don't confuse this with the
4603 // range_ member or the range() accessor. Since MAssertRange doesn't return
4604 // a value, it doesn't use those.
4605 const Range* assertedRange_;
4606
MAssertRange(MDefinition * ins,const Range * assertedRange)4607 MAssertRange(MDefinition* ins, const Range* assertedRange)
4608 : MUnaryInstruction(ins), assertedRange_(assertedRange)
4609 {
4610 setGuard();
4611 setResultType(MIRType_None);
4612 }
4613
4614 public:
INSTRUCTION_HEADER(AssertRange)4615 INSTRUCTION_HEADER(AssertRange)
4616
4617 static MAssertRange* New(TempAllocator& alloc, MDefinition* ins, const Range* assertedRange) {
4618 return new(alloc) MAssertRange(ins, assertedRange);
4619 }
4620
assertedRange()4621 const Range* assertedRange() const {
4622 return assertedRange_;
4623 }
4624
getAliasSet()4625 AliasSet getAliasSet() const override {
4626 return AliasSet::None();
4627 }
4628
4629 void printOpcode(GenericPrinter& out) const override;
4630 };
4631
4632 // Caller-side allocation of |this| for |new|:
4633 // Given a templateobject, construct |this| for JSOP_NEW
4634 class MCreateThisWithTemplate
4635 : public MUnaryInstruction,
4636 public NoTypePolicy::Data
4637 {
4638 gc::InitialHeap initialHeap_;
4639
MCreateThisWithTemplate(CompilerConstraintList * constraints,MConstant * templateConst,gc::InitialHeap initialHeap)4640 MCreateThisWithTemplate(CompilerConstraintList* constraints, MConstant* templateConst,
4641 gc::InitialHeap initialHeap)
4642 : MUnaryInstruction(templateConst),
4643 initialHeap_(initialHeap)
4644 {
4645 setResultType(MIRType_Object);
4646 setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject()));
4647 }
4648
4649 public:
INSTRUCTION_HEADER(CreateThisWithTemplate)4650 INSTRUCTION_HEADER(CreateThisWithTemplate)
4651 static MCreateThisWithTemplate* New(TempAllocator& alloc, CompilerConstraintList* constraints,
4652 MConstant* templateConst, gc::InitialHeap initialHeap)
4653 {
4654 return new(alloc) MCreateThisWithTemplate(constraints, templateConst, initialHeap);
4655 }
4656
4657 // Template for |this|, provided by TI.
templateObject()4658 JSObject* templateObject() const {
4659 return &getOperand(0)->toConstant()->value().toObject();
4660 }
4661
initialHeap()4662 gc::InitialHeap initialHeap() const {
4663 return initialHeap_;
4664 }
4665
4666 // Although creation of |this| modifies global state, it is safely repeatable.
getAliasSet()4667 AliasSet getAliasSet() const override {
4668 return AliasSet::None();
4669 }
4670
4671 bool writeRecoverData(CompactBufferWriter& writer) const override;
4672 bool canRecoverOnBailout() const override;
4673 };
4674
4675 // Caller-side allocation of |this| for |new|:
4676 // Given a prototype operand, construct |this| for JSOP_NEW.
4677 class MCreateThisWithProto
4678 : public MTernaryInstruction,
4679 public Mix3Policy<ObjectPolicy<0>, ObjectPolicy<1>, ObjectPolicy<2> >::Data
4680 {
MCreateThisWithProto(MDefinition * callee,MDefinition * newTarget,MDefinition * prototype)4681 MCreateThisWithProto(MDefinition* callee, MDefinition* newTarget, MDefinition* prototype)
4682 : MTernaryInstruction(callee, newTarget, prototype)
4683 {
4684 setResultType(MIRType_Object);
4685 }
4686
4687 public:
INSTRUCTION_HEADER(CreateThisWithProto)4688 INSTRUCTION_HEADER(CreateThisWithProto)
4689 static MCreateThisWithProto* New(TempAllocator& alloc, MDefinition* callee,
4690 MDefinition* newTarget, MDefinition* prototype)
4691 {
4692 return new(alloc) MCreateThisWithProto(callee, newTarget, prototype);
4693 }
4694
getCallee()4695 MDefinition* getCallee() const {
4696 return getOperand(0);
4697 }
getNewTarget()4698 MDefinition* getNewTarget() const {
4699 return getOperand(1);
4700 }
getPrototype()4701 MDefinition* getPrototype() const {
4702 return getOperand(2);
4703 }
4704
4705 // Although creation of |this| modifies global state, it is safely repeatable.
getAliasSet()4706 AliasSet getAliasSet() const override {
4707 return AliasSet::None();
4708 }
possiblyCalls()4709 bool possiblyCalls() const override {
4710 return true;
4711 }
4712 };
4713
4714 // Caller-side allocation of |this| for |new|:
4715 // Constructs |this| when possible, else MagicValue(JS_IS_CONSTRUCTING).
4716 class MCreateThis
4717 : public MBinaryInstruction,
4718 public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
4719 {
MCreateThis(MDefinition * callee,MDefinition * newTarget)4720 explicit MCreateThis(MDefinition* callee, MDefinition* newTarget)
4721 : MBinaryInstruction(callee, newTarget)
4722 {
4723 setResultType(MIRType_Value);
4724 }
4725
4726 public:
INSTRUCTION_HEADER(CreateThis)4727 INSTRUCTION_HEADER(CreateThis)
4728 static MCreateThis* New(TempAllocator& alloc, MDefinition* callee, MDefinition* newTarget)
4729 {
4730 return new(alloc) MCreateThis(callee, newTarget);
4731 }
4732
getCallee()4733 MDefinition* getCallee() const {
4734 return getOperand(0);
4735 }
getNewTarget()4736 MDefinition* getNewTarget() const {
4737 return getOperand(0);
4738 }
4739
4740 // Although creation of |this| modifies global state, it is safely repeatable.
getAliasSet()4741 AliasSet getAliasSet() const override {
4742 return AliasSet::None();
4743 }
possiblyCalls()4744 bool possiblyCalls() const override {
4745 return true;
4746 }
4747 };
4748
4749 // Eager initialization of arguments object.
4750 class MCreateArgumentsObject
4751 : public MUnaryInstruction,
4752 public ObjectPolicy<0>::Data
4753 {
MCreateArgumentsObject(MDefinition * callObj)4754 explicit MCreateArgumentsObject(MDefinition* callObj)
4755 : MUnaryInstruction(callObj)
4756 {
4757 setResultType(MIRType_Object);
4758 setGuard();
4759 }
4760
4761 public:
INSTRUCTION_HEADER(CreateArgumentsObject)4762 INSTRUCTION_HEADER(CreateArgumentsObject)
4763 static MCreateArgumentsObject* New(TempAllocator& alloc, MDefinition* callObj) {
4764 return new(alloc) MCreateArgumentsObject(callObj);
4765 }
4766
getCallObject()4767 MDefinition* getCallObject() const {
4768 return getOperand(0);
4769 }
4770
getAliasSet()4771 AliasSet getAliasSet() const override {
4772 return AliasSet::None();
4773 }
4774
possiblyCalls()4775 bool possiblyCalls() const override {
4776 return true;
4777 }
4778 };
4779
4780 class MGetArgumentsObjectArg
4781 : public MUnaryInstruction,
4782 public ObjectPolicy<0>::Data
4783 {
4784 size_t argno_;
4785
MGetArgumentsObjectArg(MDefinition * argsObject,size_t argno)4786 MGetArgumentsObjectArg(MDefinition* argsObject, size_t argno)
4787 : MUnaryInstruction(argsObject),
4788 argno_(argno)
4789 {
4790 setResultType(MIRType_Value);
4791 }
4792
4793 public:
INSTRUCTION_HEADER(GetArgumentsObjectArg)4794 INSTRUCTION_HEADER(GetArgumentsObjectArg)
4795 static MGetArgumentsObjectArg* New(TempAllocator& alloc, MDefinition* argsObj, size_t argno)
4796 {
4797 return new(alloc) MGetArgumentsObjectArg(argsObj, argno);
4798 }
4799
getArgsObject()4800 MDefinition* getArgsObject() const {
4801 return getOperand(0);
4802 }
4803
argno()4804 size_t argno() const {
4805 return argno_;
4806 }
4807
getAliasSet()4808 AliasSet getAliasSet() const override {
4809 return AliasSet::Load(AliasSet::Any);
4810 }
4811 };
4812
4813 class MSetArgumentsObjectArg
4814 : public MBinaryInstruction,
4815 public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
4816 {
4817 size_t argno_;
4818
MSetArgumentsObjectArg(MDefinition * argsObj,size_t argno,MDefinition * value)4819 MSetArgumentsObjectArg(MDefinition* argsObj, size_t argno, MDefinition* value)
4820 : MBinaryInstruction(argsObj, value),
4821 argno_(argno)
4822 {
4823 }
4824
4825 public:
INSTRUCTION_HEADER(SetArgumentsObjectArg)4826 INSTRUCTION_HEADER(SetArgumentsObjectArg)
4827 static MSetArgumentsObjectArg* New(TempAllocator& alloc, MDefinition* argsObj, size_t argno,
4828 MDefinition* value)
4829 {
4830 return new(alloc) MSetArgumentsObjectArg(argsObj, argno, value);
4831 }
4832
getArgsObject()4833 MDefinition* getArgsObject() const {
4834 return getOperand(0);
4835 }
4836
argno()4837 size_t argno() const {
4838 return argno_;
4839 }
4840
getValue()4841 MDefinition* getValue() const {
4842 return getOperand(1);
4843 }
4844
getAliasSet()4845 AliasSet getAliasSet() const override {
4846 return AliasSet::Store(AliasSet::Any);
4847 }
4848 };
4849
4850 class MRunOncePrologue
4851 : public MNullaryInstruction
4852 {
4853 protected:
MRunOncePrologue()4854 MRunOncePrologue()
4855 {
4856 setGuard();
4857 }
4858
4859 public:
INSTRUCTION_HEADER(RunOncePrologue)4860 INSTRUCTION_HEADER(RunOncePrologue)
4861
4862 static MRunOncePrologue* New(TempAllocator& alloc) {
4863 return new(alloc) MRunOncePrologue();
4864 }
possiblyCalls()4865 bool possiblyCalls() const override {
4866 return true;
4867 }
4868 };
4869
4870 // Given a MIRType_Value A and a MIRType_Object B:
4871 // If the Value may be safely unboxed to an Object, return Object(A).
4872 // Otherwise, return B.
4873 // Used to implement return behavior for inlined constructors.
4874 class MReturnFromCtor
4875 : public MAryInstruction<2>,
4876 public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >::Data
4877 {
MReturnFromCtor(MDefinition * value,MDefinition * object)4878 MReturnFromCtor(MDefinition* value, MDefinition* object) {
4879 initOperand(0, value);
4880 initOperand(1, object);
4881 setResultType(MIRType_Object);
4882 }
4883
4884 public:
INSTRUCTION_HEADER(ReturnFromCtor)4885 INSTRUCTION_HEADER(ReturnFromCtor)
4886 static MReturnFromCtor* New(TempAllocator& alloc, MDefinition* value, MDefinition* object)
4887 {
4888 return new(alloc) MReturnFromCtor(value, object);
4889 }
4890
getValue()4891 MDefinition* getValue() const {
4892 return getOperand(0);
4893 }
getObject()4894 MDefinition* getObject() const {
4895 return getOperand(1);
4896 }
4897
getAliasSet()4898 AliasSet getAliasSet() const override {
4899 return AliasSet::None();
4900 }
4901 };
4902
4903 class MToFPInstruction
4904 : public MUnaryInstruction,
4905 public ToDoublePolicy::Data
4906 {
4907 public:
4908 // Types of values which can be converted.
4909 enum ConversionKind {
4910 NonStringPrimitives,
4911 NonNullNonStringPrimitives,
4912 NumbersOnly
4913 };
4914
4915 private:
4916 ConversionKind conversion_;
4917
4918 protected:
4919 explicit MToFPInstruction(MDefinition* def, ConversionKind conversion = NonStringPrimitives)
MUnaryInstruction(def)4920 : MUnaryInstruction(def), conversion_(conversion)
4921 { }
4922
4923 public:
conversion()4924 ConversionKind conversion() const {
4925 return conversion_;
4926 }
4927 };
4928
4929 // Converts a primitive (either typed or untyped) to a double. If the input is
4930 // not primitive at runtime, a bailout occurs.
4931 class MToDouble
4932 : public MToFPInstruction
4933 {
4934 private:
4935 TruncateKind implicitTruncate_;
4936
4937 explicit MToDouble(MDefinition* def, ConversionKind conversion = NonStringPrimitives)
MToFPInstruction(def,conversion)4938 : MToFPInstruction(def, conversion), implicitTruncate_(NoTruncate)
4939 {
4940 setResultType(MIRType_Double);
4941 setMovable();
4942
4943 // An object might have "valueOf", which means it is effectful.
4944 // ToNumber(symbol) throws.
4945 if (def->mightBeType(MIRType_Object) || def->mightBeType(MIRType_Symbol))
4946 setGuard();
4947 }
4948
4949 public:
INSTRUCTION_HEADER(ToDouble)4950 INSTRUCTION_HEADER(ToDouble)
4951 static MToDouble* New(TempAllocator& alloc, MDefinition* def,
4952 ConversionKind conversion = NonStringPrimitives)
4953 {
4954 return new(alloc) MToDouble(def, conversion);
4955 }
NewAsmJS(TempAllocator & alloc,MDefinition * def)4956 static MToDouble* NewAsmJS(TempAllocator& alloc, MDefinition* def) {
4957 return new(alloc) MToDouble(def);
4958 }
4959
4960 MDefinition* foldsTo(TempAllocator& alloc) override;
congruentTo(const MDefinition * ins)4961 bool congruentTo(const MDefinition* ins) const override {
4962 if (!ins->isToDouble() || ins->toToDouble()->conversion() != conversion())
4963 return false;
4964 return congruentIfOperandsEqual(ins);
4965 }
getAliasSet()4966 AliasSet getAliasSet() const override {
4967 return AliasSet::None();
4968 }
4969
4970 void computeRange(TempAllocator& alloc) override;
4971 bool needTruncation(TruncateKind kind) override;
4972 void truncate() override;
4973 TruncateKind operandTruncateKind(size_t index) const override;
4974
4975 #ifdef DEBUG
isConsistentFloat32Use(MUse * use)4976 bool isConsistentFloat32Use(MUse* use) const override { return true; }
4977 #endif
4978
truncateKind()4979 TruncateKind truncateKind() const {
4980 return implicitTruncate_;
4981 }
setTruncateKind(TruncateKind kind)4982 void setTruncateKind(TruncateKind kind) {
4983 implicitTruncate_ = Max(implicitTruncate_, kind);
4984 }
4985
4986 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()4987 bool canRecoverOnBailout() const override {
4988 if (input()->type() == MIRType_Value)
4989 return false;
4990 if (input()->type() == MIRType_Symbol)
4991 return false;
4992
4993 return true;
4994 }
4995
4996 ALLOW_CLONE(MToDouble)
4997 };
4998
4999 // Converts a primitive (either typed or untyped) to a float32. If the input is
5000 // not primitive at runtime, a bailout occurs.
5001 class MToFloat32
5002 : public MToFPInstruction
5003 {
5004 protected:
MToFloat32(MDefinition * def,ConversionKind conversion)5005 MToFloat32(MDefinition* def, ConversionKind conversion)
5006 : MToFPInstruction(def, conversion)
5007 {
5008 setResultType(MIRType_Float32);
5009 setMovable();
5010
5011 // An object might have "valueOf", which means it is effectful.
5012 // ToNumber(symbol) throws.
5013 if (def->mightBeType(MIRType_Object) || def->mightBeType(MIRType_Symbol))
5014 setGuard();
5015 }
5016
5017 public:
INSTRUCTION_HEADER(ToFloat32)5018 INSTRUCTION_HEADER(ToFloat32)
5019 static MToFloat32* New(TempAllocator& alloc, MDefinition* def,
5020 ConversionKind conversion = NonStringPrimitives)
5021 {
5022 return new(alloc) MToFloat32(def, conversion);
5023 }
NewAsmJS(TempAllocator & alloc,MDefinition * def)5024 static MToFloat32* NewAsmJS(TempAllocator& alloc, MDefinition* def) {
5025 return new(alloc) MToFloat32(def, NonStringPrimitives);
5026 }
5027
5028 virtual MDefinition* foldsTo(TempAllocator& alloc) override;
congruentTo(const MDefinition * ins)5029 bool congruentTo(const MDefinition* ins) const override {
5030 if (!ins->isToFloat32() || ins->toToFloat32()->conversion() != conversion())
5031 return false;
5032 return congruentIfOperandsEqual(ins);
5033 }
getAliasSet()5034 AliasSet getAliasSet() const override {
5035 return AliasSet::None();
5036 }
5037
5038 void computeRange(TempAllocator& alloc) override;
5039
canConsumeFloat32(MUse * use)5040 bool canConsumeFloat32(MUse* use) const override { return true; }
canProduceFloat32()5041 bool canProduceFloat32() const override { return true; }
5042
5043 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5044 bool canRecoverOnBailout() const override {
5045 return true;
5046 }
5047
5048 ALLOW_CLONE(MToFloat32)
5049 };
5050
5051 // Converts a uint32 to a double (coming from asm.js).
5052 class MAsmJSUnsignedToDouble
5053 : public MUnaryInstruction,
5054 public NoTypePolicy::Data
5055 {
MAsmJSUnsignedToDouble(MDefinition * def)5056 explicit MAsmJSUnsignedToDouble(MDefinition* def)
5057 : MUnaryInstruction(def)
5058 {
5059 setResultType(MIRType_Double);
5060 setMovable();
5061 }
5062
5063 public:
INSTRUCTION_HEADER(AsmJSUnsignedToDouble)5064 INSTRUCTION_HEADER(AsmJSUnsignedToDouble)
5065 static MAsmJSUnsignedToDouble* NewAsmJS(TempAllocator& alloc, MDefinition* def) {
5066 return new(alloc) MAsmJSUnsignedToDouble(def);
5067 }
5068
5069 MDefinition* foldsTo(TempAllocator& alloc) override;
congruentTo(const MDefinition * ins)5070 bool congruentTo(const MDefinition* ins) const override {
5071 return congruentIfOperandsEqual(ins);
5072 }
getAliasSet()5073 AliasSet getAliasSet() const override {
5074 return AliasSet::None();
5075 }
5076 };
5077
5078 // Converts a uint32 to a float32 (coming from asm.js).
5079 class MAsmJSUnsignedToFloat32
5080 : public MUnaryInstruction,
5081 public NoTypePolicy::Data
5082 {
MAsmJSUnsignedToFloat32(MDefinition * def)5083 explicit MAsmJSUnsignedToFloat32(MDefinition* def)
5084 : MUnaryInstruction(def)
5085 {
5086 setResultType(MIRType_Float32);
5087 setMovable();
5088 }
5089
5090 public:
INSTRUCTION_HEADER(AsmJSUnsignedToFloat32)5091 INSTRUCTION_HEADER(AsmJSUnsignedToFloat32)
5092 static MAsmJSUnsignedToFloat32* NewAsmJS(TempAllocator& alloc, MDefinition* def) {
5093 return new(alloc) MAsmJSUnsignedToFloat32(def);
5094 }
5095
5096 MDefinition* foldsTo(TempAllocator& alloc) override;
congruentTo(const MDefinition * ins)5097 bool congruentTo(const MDefinition* ins) const override {
5098 return congruentIfOperandsEqual(ins);
5099 }
getAliasSet()5100 AliasSet getAliasSet() const override {
5101 return AliasSet::None();
5102 }
5103
canProduceFloat32()5104 bool canProduceFloat32() const override { return true; }
5105 };
5106
5107 // Converts a primitive (either typed or untyped) to an int32. If the input is
5108 // not primitive at runtime, a bailout occurs. If the input cannot be converted
5109 // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
5110 class MToInt32
5111 : public MUnaryInstruction,
5112 public ToInt32Policy::Data
5113 {
5114 bool canBeNegativeZero_;
5115 MacroAssembler::IntConversionInputKind conversion_;
5116
MToInt32(MDefinition * def,MacroAssembler::IntConversionInputKind conversion)5117 MToInt32(MDefinition* def, MacroAssembler::IntConversionInputKind conversion)
5118 : MUnaryInstruction(def),
5119 canBeNegativeZero_(true),
5120 conversion_(conversion)
5121 {
5122 setResultType(MIRType_Int32);
5123 setMovable();
5124
5125 // An object might have "valueOf", which means it is effectful.
5126 // ToNumber(symbol) throws.
5127 if (def->mightBeType(MIRType_Object) || def->mightBeType(MIRType_Symbol))
5128 setGuard();
5129 }
5130
5131 public:
INSTRUCTION_HEADER(ToInt32)5132 INSTRUCTION_HEADER(ToInt32)
5133 static MToInt32* New(TempAllocator& alloc, MDefinition* def,
5134 MacroAssembler::IntConversionInputKind conversion =
5135 MacroAssembler::IntConversion_Any)
5136 {
5137 return new(alloc) MToInt32(def, conversion);
5138 }
5139
5140 MDefinition* foldsTo(TempAllocator& alloc) override;
5141
5142 // this only has backwards information flow.
5143 void analyzeEdgeCasesBackward() override;
5144
canBeNegativeZero()5145 bool canBeNegativeZero() const {
5146 return canBeNegativeZero_;
5147 }
setCanBeNegativeZero(bool negativeZero)5148 void setCanBeNegativeZero(bool negativeZero) {
5149 canBeNegativeZero_ = negativeZero;
5150 }
5151
conversion()5152 MacroAssembler::IntConversionInputKind conversion() const {
5153 return conversion_;
5154 }
5155
congruentTo(const MDefinition * ins)5156 bool congruentTo(const MDefinition* ins) const override {
5157 if (!ins->isToInt32() || ins->toToInt32()->conversion() != conversion())
5158 return false;
5159 return congruentIfOperandsEqual(ins);
5160 }
5161
getAliasSet()5162 AliasSet getAliasSet() const override {
5163 return AliasSet::None();
5164 }
5165 void computeRange(TempAllocator& alloc) override;
5166 void collectRangeInfoPreTrunc() override;
5167
5168 #ifdef DEBUG
isConsistentFloat32Use(MUse * use)5169 bool isConsistentFloat32Use(MUse* use) const override { return true; }
5170 #endif
5171
5172 ALLOW_CLONE(MToInt32)
5173 };
5174
5175 // Converts a value or typed input to a truncated int32, for use with bitwise
5176 // operations. This is an infallible ValueToECMAInt32.
5177 class MTruncateToInt32
5178 : public MUnaryInstruction,
5179 public ToInt32Policy::Data
5180 {
MTruncateToInt32(MDefinition * def)5181 explicit MTruncateToInt32(MDefinition* def)
5182 : MUnaryInstruction(def)
5183 {
5184 setResultType(MIRType_Int32);
5185 setMovable();
5186
5187 // An object might have "valueOf", which means it is effectful.
5188 // ToInt32(symbol) throws.
5189 if (def->mightBeType(MIRType_Object) || def->mightBeType(MIRType_Symbol))
5190 setGuard();
5191 }
5192
5193 public:
INSTRUCTION_HEADER(TruncateToInt32)5194 INSTRUCTION_HEADER(TruncateToInt32)
5195 static MTruncateToInt32* New(TempAllocator& alloc, MDefinition* def) {
5196 return new(alloc) MTruncateToInt32(def);
5197 }
NewAsmJS(TempAllocator & alloc,MDefinition * def)5198 static MTruncateToInt32* NewAsmJS(TempAllocator& alloc, MDefinition* def) {
5199 return new(alloc) MTruncateToInt32(def);
5200 }
5201
5202 MDefinition* foldsTo(TempAllocator& alloc) override;
5203
congruentTo(const MDefinition * ins)5204 bool congruentTo(const MDefinition* ins) const override {
5205 return congruentIfOperandsEqual(ins);
5206 }
getAliasSet()5207 AliasSet getAliasSet() const override {
5208 return AliasSet::None();
5209 }
5210
5211 void computeRange(TempAllocator& alloc) override;
5212 TruncateKind operandTruncateKind(size_t index) const override;
5213 # ifdef DEBUG
isConsistentFloat32Use(MUse * use)5214 bool isConsistentFloat32Use(MUse* use) const override {
5215 return true;
5216 }
5217 #endif
5218
5219 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5220 bool canRecoverOnBailout() const override {
5221 return input()->type() < MIRType_Symbol;
5222 }
5223
5224 ALLOW_CLONE(MTruncateToInt32)
5225 };
5226
5227 // Converts any type to a string
5228 class MToString :
5229 public MUnaryInstruction,
5230 public ToStringPolicy::Data
5231 {
MToString(MDefinition * def)5232 explicit MToString(MDefinition* def)
5233 : MUnaryInstruction(def)
5234 {
5235 setResultType(MIRType_String);
5236 setMovable();
5237 }
5238
5239 public:
INSTRUCTION_HEADER(ToString)5240 INSTRUCTION_HEADER(ToString)
5241 static MToString* New(TempAllocator& alloc, MDefinition* def)
5242 {
5243 return new(alloc) MToString(def);
5244 }
5245
5246 MDefinition* foldsTo(TempAllocator& alloc) override;
5247
congruentTo(const MDefinition * ins)5248 bool congruentTo(const MDefinition* ins) const override {
5249 return congruentIfOperandsEqual(ins);
5250 }
5251
getAliasSet()5252 AliasSet getAliasSet() const override {
5253 return AliasSet::None();
5254 }
5255
fallible()5256 bool fallible() const {
5257 return input()->mightBeType(MIRType_Object);
5258 }
5259
5260 ALLOW_CLONE(MToString)
5261 };
5262
5263 // Converts any type to an object or null value, throwing on undefined.
5264 class MToObjectOrNull :
5265 public MUnaryInstruction,
5266 public BoxInputsPolicy::Data
5267 {
MToObjectOrNull(MDefinition * def)5268 explicit MToObjectOrNull(MDefinition* def)
5269 : MUnaryInstruction(def)
5270 {
5271 setResultType(MIRType_ObjectOrNull);
5272 setMovable();
5273 }
5274
5275 public:
INSTRUCTION_HEADER(ToObjectOrNull)5276 INSTRUCTION_HEADER(ToObjectOrNull)
5277 static MToObjectOrNull* New(TempAllocator& alloc, MDefinition* def)
5278 {
5279 return new(alloc) MToObjectOrNull(def);
5280 }
5281
congruentTo(const MDefinition * ins)5282 bool congruentTo(const MDefinition* ins) const override {
5283 return congruentIfOperandsEqual(ins);
5284 }
5285
getAliasSet()5286 AliasSet getAliasSet() const override {
5287 return AliasSet::None();
5288 }
5289
5290 ALLOW_CLONE(MToObjectOrNull)
5291 };
5292
5293 class MBitNot
5294 : public MUnaryInstruction,
5295 public BitwisePolicy::Data
5296 {
5297 protected:
MBitNot(MDefinition * input)5298 explicit MBitNot(MDefinition* input)
5299 : MUnaryInstruction(input)
5300 {
5301 specialization_ = MIRType_None;
5302 setResultType(MIRType_Int32);
5303 setMovable();
5304 }
5305
5306 public:
5307 INSTRUCTION_HEADER(BitNot)
5308 static MBitNot* New(TempAllocator& alloc, MDefinition* input);
5309 static MBitNot* NewAsmJS(TempAllocator& alloc, MDefinition* input);
5310
5311 MDefinition* foldsTo(TempAllocator& alloc) override;
setSpecialization(MIRType type)5312 void setSpecialization(MIRType type) {
5313 specialization_ = type;
5314 setResultType(type);
5315 }
5316
congruentTo(const MDefinition * ins)5317 bool congruentTo(const MDefinition* ins) const override {
5318 return congruentIfOperandsEqual(ins);
5319 }
getAliasSet()5320 AliasSet getAliasSet() const override {
5321 if (specialization_ == MIRType_None)
5322 return AliasSet::Store(AliasSet::Any);
5323 return AliasSet::None();
5324 }
5325 void computeRange(TempAllocator& alloc) override;
5326
5327 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5328 bool canRecoverOnBailout() const override {
5329 return specialization_ != MIRType_None;
5330 }
5331
5332 ALLOW_CLONE(MBitNot)
5333 };
5334
5335 class MTypeOf
5336 : public MUnaryInstruction,
5337 public BoxInputsPolicy::Data
5338 {
5339 MIRType inputType_;
5340 bool inputMaybeCallableOrEmulatesUndefined_;
5341
MTypeOf(MDefinition * def,MIRType inputType)5342 MTypeOf(MDefinition* def, MIRType inputType)
5343 : MUnaryInstruction(def), inputType_(inputType),
5344 inputMaybeCallableOrEmulatesUndefined_(true)
5345 {
5346 setResultType(MIRType_String);
5347 setMovable();
5348 }
5349
5350 public:
INSTRUCTION_HEADER(TypeOf)5351 INSTRUCTION_HEADER(TypeOf)
5352
5353 static MTypeOf* New(TempAllocator& alloc, MDefinition* def, MIRType inputType) {
5354 return new(alloc) MTypeOf(def, inputType);
5355 }
5356
inputType()5357 MIRType inputType() const {
5358 return inputType_;
5359 }
5360
5361 MDefinition* foldsTo(TempAllocator& alloc) override;
5362 void cacheInputMaybeCallableOrEmulatesUndefined(CompilerConstraintList* constraints);
5363
inputMaybeCallableOrEmulatesUndefined()5364 bool inputMaybeCallableOrEmulatesUndefined() const {
5365 return inputMaybeCallableOrEmulatesUndefined_;
5366 }
markInputNotCallableOrEmulatesUndefined()5367 void markInputNotCallableOrEmulatesUndefined() {
5368 inputMaybeCallableOrEmulatesUndefined_ = false;
5369 }
5370
getAliasSet()5371 AliasSet getAliasSet() const override {
5372 return AliasSet::None();
5373 }
5374
congruentTo(const MDefinition * ins)5375 bool congruentTo(const MDefinition* ins) const override {
5376 if (!ins->isTypeOf())
5377 return false;
5378 if (inputType() != ins->toTypeOf()->inputType())
5379 return false;
5380 if (inputMaybeCallableOrEmulatesUndefined() !=
5381 ins->toTypeOf()->inputMaybeCallableOrEmulatesUndefined())
5382 {
5383 return false;
5384 }
5385 return congruentIfOperandsEqual(ins);
5386 }
5387
5388 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5389 bool canRecoverOnBailout() const override {
5390 return true;
5391 }
5392 };
5393
5394 class MToId
5395 : public MUnaryInstruction,
5396 public BoxInputsPolicy::Data
5397 {
MToId(MDefinition * index)5398 explicit MToId(MDefinition* index)
5399 : MUnaryInstruction(index)
5400 {
5401 setResultType(MIRType_Value);
5402 }
5403
5404 public:
INSTRUCTION_HEADER(ToId)5405 INSTRUCTION_HEADER(ToId)
5406
5407 static MToId* New(TempAllocator& alloc, MDefinition* index) {
5408 return new(alloc) MToId(index);
5409 }
5410 };
5411
5412 class MBinaryBitwiseInstruction
5413 : public MBinaryInstruction,
5414 public BitwisePolicy::Data
5415 {
5416 protected:
MBinaryBitwiseInstruction(MDefinition * left,MDefinition * right)5417 MBinaryBitwiseInstruction(MDefinition* left, MDefinition* right)
5418 : MBinaryInstruction(left, right)
5419 {
5420 setResultType(MIRType_Int32);
5421 setMovable();
5422 }
5423
5424 void specializeAsInt32();
5425
5426 public:
5427 MDefinition* foldsTo(TempAllocator& alloc) override;
5428 MDefinition* foldUnnecessaryBitop();
5429 virtual MDefinition* foldIfZero(size_t operand) = 0;
5430 virtual MDefinition* foldIfNegOne(size_t operand) = 0;
5431 virtual MDefinition* foldIfEqual() = 0;
5432 virtual void infer(BaselineInspector* inspector, jsbytecode* pc);
5433
congruentTo(const MDefinition * ins)5434 bool congruentTo(const MDefinition* ins) const override {
5435 return binaryCongruentTo(ins);
5436 }
getAliasSet()5437 AliasSet getAliasSet() const override {
5438 if (specialization_ >= MIRType_Object)
5439 return AliasSet::Store(AliasSet::Any);
5440 return AliasSet::None();
5441 }
5442
5443 TruncateKind operandTruncateKind(size_t index) const override;
5444 };
5445
5446 class MBitAnd : public MBinaryBitwiseInstruction
5447 {
MBitAnd(MDefinition * left,MDefinition * right)5448 MBitAnd(MDefinition* left, MDefinition* right)
5449 : MBinaryBitwiseInstruction(left, right)
5450 { }
5451
5452 public:
5453 INSTRUCTION_HEADER(BitAnd)
5454 static MBitAnd* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5455 static MBitAnd* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5456
foldIfZero(size_t operand)5457 MDefinition* foldIfZero(size_t operand) override {
5458 return getOperand(operand); // 0 & x => 0;
5459 }
foldIfNegOne(size_t operand)5460 MDefinition* foldIfNegOne(size_t operand) override {
5461 return getOperand(1 - operand); // x & -1 => x
5462 }
foldIfEqual()5463 MDefinition* foldIfEqual() override {
5464 return getOperand(0); // x & x => x;
5465 }
5466 void computeRange(TempAllocator& alloc) override;
5467
5468 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5469 bool canRecoverOnBailout() const override {
5470 return specialization_ != MIRType_None;
5471 }
5472
5473 ALLOW_CLONE(MBitAnd)
5474 };
5475
5476 class MBitOr : public MBinaryBitwiseInstruction
5477 {
MBitOr(MDefinition * left,MDefinition * right)5478 MBitOr(MDefinition* left, MDefinition* right)
5479 : MBinaryBitwiseInstruction(left, right)
5480 { }
5481
5482 public:
5483 INSTRUCTION_HEADER(BitOr)
5484 static MBitOr* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5485 static MBitOr* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5486
foldIfZero(size_t operand)5487 MDefinition* foldIfZero(size_t operand) override {
5488 return getOperand(1 - operand); // 0 | x => x, so if ith is 0, return (1-i)th
5489 }
foldIfNegOne(size_t operand)5490 MDefinition* foldIfNegOne(size_t operand) override {
5491 return getOperand(operand); // x | -1 => -1
5492 }
foldIfEqual()5493 MDefinition* foldIfEqual() override {
5494 return getOperand(0); // x | x => x
5495 }
5496 void computeRange(TempAllocator& alloc) override;
5497 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5498 bool canRecoverOnBailout() const override {
5499 return specialization_ != MIRType_None;
5500 }
5501
5502 ALLOW_CLONE(MBitOr)
5503 };
5504
5505 class MBitXor : public MBinaryBitwiseInstruction
5506 {
MBitXor(MDefinition * left,MDefinition * right)5507 MBitXor(MDefinition* left, MDefinition* right)
5508 : MBinaryBitwiseInstruction(left, right)
5509 { }
5510
5511 public:
5512 INSTRUCTION_HEADER(BitXor)
5513 static MBitXor* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5514 static MBitXor* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5515
foldIfZero(size_t operand)5516 MDefinition* foldIfZero(size_t operand) override {
5517 return getOperand(1 - operand); // 0 ^ x => x
5518 }
foldIfNegOne(size_t operand)5519 MDefinition* foldIfNegOne(size_t operand) override {
5520 return this;
5521 }
foldIfEqual()5522 MDefinition* foldIfEqual() override {
5523 return this;
5524 }
5525 void computeRange(TempAllocator& alloc) override;
5526
5527 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5528 bool canRecoverOnBailout() const override {
5529 return specialization_ < MIRType_Object;
5530 }
5531
5532 ALLOW_CLONE(MBitXor)
5533 };
5534
5535 class MShiftInstruction
5536 : public MBinaryBitwiseInstruction
5537 {
5538 protected:
MShiftInstruction(MDefinition * left,MDefinition * right)5539 MShiftInstruction(MDefinition* left, MDefinition* right)
5540 : MBinaryBitwiseInstruction(left, right)
5541 { }
5542
5543 public:
foldIfNegOne(size_t operand)5544 MDefinition* foldIfNegOne(size_t operand) override {
5545 return this;
5546 }
foldIfEqual()5547 MDefinition* foldIfEqual() override {
5548 return this;
5549 }
5550 virtual void infer(BaselineInspector* inspector, jsbytecode* pc) override;
5551 };
5552
5553 class MLsh : public MShiftInstruction
5554 {
MLsh(MDefinition * left,MDefinition * right)5555 MLsh(MDefinition* left, MDefinition* right)
5556 : MShiftInstruction(left, right)
5557 { }
5558
5559 public:
5560 INSTRUCTION_HEADER(Lsh)
5561 static MLsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5562 static MLsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5563
foldIfZero(size_t operand)5564 MDefinition* foldIfZero(size_t operand) override {
5565 // 0 << x => 0
5566 // x << 0 => x
5567 return getOperand(0);
5568 }
5569
5570 void computeRange(TempAllocator& alloc) override;
5571 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5572 bool canRecoverOnBailout() const override {
5573 return specialization_ != MIRType_None;
5574 }
5575
5576 ALLOW_CLONE(MLsh)
5577 };
5578
5579 class MRsh : public MShiftInstruction
5580 {
MRsh(MDefinition * left,MDefinition * right)5581 MRsh(MDefinition* left, MDefinition* right)
5582 : MShiftInstruction(left, right)
5583 { }
5584
5585 public:
5586 INSTRUCTION_HEADER(Rsh)
5587 static MRsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5588 static MRsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5589
foldIfZero(size_t operand)5590 MDefinition* foldIfZero(size_t operand) override {
5591 // 0 >> x => 0
5592 // x >> 0 => x
5593 return getOperand(0);
5594 }
5595 void computeRange(TempAllocator& alloc) override;
5596
5597 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5598 bool canRecoverOnBailout() const override {
5599 return specialization_ < MIRType_Object;
5600 }
5601
5602 ALLOW_CLONE(MRsh)
5603 };
5604
5605 class MUrsh : public MShiftInstruction
5606 {
5607 bool bailoutsDisabled_;
5608
MUrsh(MDefinition * left,MDefinition * right)5609 MUrsh(MDefinition* left, MDefinition* right)
5610 : MShiftInstruction(left, right),
5611 bailoutsDisabled_(false)
5612 { }
5613
5614 public:
5615 INSTRUCTION_HEADER(Ursh)
5616 static MUrsh* New(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5617 static MUrsh* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right);
5618
foldIfZero(size_t operand)5619 MDefinition* foldIfZero(size_t operand) override {
5620 // 0 >>> x => 0
5621 if (operand == 0)
5622 return getOperand(0);
5623
5624 return this;
5625 }
5626
5627 void infer(BaselineInspector* inspector, jsbytecode* pc) override;
5628
bailoutsDisabled()5629 bool bailoutsDisabled() const {
5630 return bailoutsDisabled_;
5631 }
5632
5633 bool fallible() const;
5634
5635 void computeRange(TempAllocator& alloc) override;
5636 void collectRangeInfoPreTrunc() override;
5637
5638 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5639 bool canRecoverOnBailout() const override {
5640 return specialization_ < MIRType_Object;
5641 }
5642
5643 ALLOW_CLONE(MUrsh)
5644 };
5645
5646 class MBinaryArithInstruction
5647 : public MBinaryInstruction,
5648 public ArithPolicy::Data
5649 {
5650 // Implicit truncate flag is set by the truncate backward range analysis
5651 // optimization phase, and by asm.js pre-processing. It is used in
5652 // NeedNegativeZeroCheck to check if the result of a multiplication needs to
5653 // produce -0 double value, and for avoiding overflow checks.
5654
5655 // This optimization happens when the multiplication cannot be truncated
5656 // even if all uses are truncating its result, such as when the range
5657 // analysis detect a precision loss in the multiplication.
5658 TruncateKind implicitTruncate_;
5659
5660 public:
MBinaryArithInstruction(MDefinition * left,MDefinition * right)5661 MBinaryArithInstruction(MDefinition* left, MDefinition* right)
5662 : MBinaryInstruction(left, right),
5663 implicitTruncate_(NoTruncate)
5664 {
5665 specialization_ = MIRType_None;
5666 setMovable();
5667 }
5668
5669 static MBinaryArithInstruction* New(TempAllocator& alloc, Opcode op,
5670 MDefinition* left, MDefinition* right);
5671
5672 bool constantDoubleResult(TempAllocator& alloc);
5673
5674 MDefinition* foldsTo(TempAllocator& alloc) override;
5675
5676 virtual double getIdentity() = 0;
5677
setSpecialization(MIRType type)5678 void setSpecialization(MIRType type) {
5679 specialization_ = type;
5680 setResultType(type);
5681 }
setInt32Specialization()5682 void setInt32Specialization() {
5683 specialization_ = MIRType_Int32;
5684 setResultType(MIRType_Int32);
5685 }
5686 void setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector, jsbytecode* pc);
5687
5688 virtual void trySpecializeFloat32(TempAllocator& alloc) override;
5689
congruentTo(const MDefinition * ins)5690 bool congruentTo(const MDefinition* ins) const override {
5691 return binaryCongruentTo(ins);
5692 }
getAliasSet()5693 AliasSet getAliasSet() const override {
5694 if (specialization_ >= MIRType_Object)
5695 return AliasSet::Store(AliasSet::Any);
5696 return AliasSet::None();
5697 }
5698
isTruncated()5699 bool isTruncated() const {
5700 return implicitTruncate_ == Truncate;
5701 }
truncateKind()5702 TruncateKind truncateKind() const {
5703 return implicitTruncate_;
5704 }
setTruncateKind(TruncateKind kind)5705 void setTruncateKind(TruncateKind kind) {
5706 implicitTruncate_ = Max(implicitTruncate_, kind);
5707 }
5708 };
5709
5710 class MMinMax
5711 : public MBinaryInstruction,
5712 public ArithPolicy::Data
5713 {
5714 bool isMax_;
5715
MMinMax(MDefinition * left,MDefinition * right,MIRType type,bool isMax)5716 MMinMax(MDefinition* left, MDefinition* right, MIRType type, bool isMax)
5717 : MBinaryInstruction(left, right),
5718 isMax_(isMax)
5719 {
5720 MOZ_ASSERT(IsNumberType(type));
5721 setResultType(type);
5722 setMovable();
5723 specialization_ = type;
5724 }
5725
5726 public:
INSTRUCTION_HEADER(MinMax)5727 INSTRUCTION_HEADER(MinMax)
5728 static MMinMax* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type,
5729 bool isMax)
5730 {
5731 return new(alloc) MMinMax(left, right, type, isMax);
5732 }
5733
isMax()5734 bool isMax() const {
5735 return isMax_;
5736 }
5737
congruentTo(const MDefinition * ins)5738 bool congruentTo(const MDefinition* ins) const override {
5739 if (!ins->isMinMax())
5740 return false;
5741 if (isMax() != ins->toMinMax()->isMax())
5742 return false;
5743 return congruentIfOperandsEqual(ins);
5744 }
5745
getAliasSet()5746 AliasSet getAliasSet() const override {
5747 return AliasSet::None();
5748 }
5749 MDefinition* foldsTo(TempAllocator& alloc) override;
5750 void computeRange(TempAllocator& alloc) override;
5751 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5752 bool canRecoverOnBailout() const override {
5753 return true;
5754 }
5755
isFloat32Commutative()5756 bool isFloat32Commutative() const override { return true; }
5757 void trySpecializeFloat32(TempAllocator& alloc) override;
5758
5759 ALLOW_CLONE(MMinMax)
5760 };
5761
5762 class MAbs
5763 : public MUnaryInstruction,
5764 public ArithPolicy::Data
5765 {
5766 bool implicitTruncate_;
5767
MAbs(MDefinition * num,MIRType type)5768 MAbs(MDefinition* num, MIRType type)
5769 : MUnaryInstruction(num),
5770 implicitTruncate_(false)
5771 {
5772 MOZ_ASSERT(IsNumberType(type));
5773 setResultType(type);
5774 setMovable();
5775 specialization_ = type;
5776 }
5777
5778 public:
INSTRUCTION_HEADER(Abs)5779 INSTRUCTION_HEADER(Abs)
5780 static MAbs* New(TempAllocator& alloc, MDefinition* num, MIRType type) {
5781 return new(alloc) MAbs(num, type);
5782 }
NewAsmJS(TempAllocator & alloc,MDefinition * num,MIRType type)5783 static MAbs* NewAsmJS(TempAllocator& alloc, MDefinition* num, MIRType type) {
5784 MAbs* ins = new(alloc) MAbs(num, type);
5785 if (type == MIRType_Int32)
5786 ins->implicitTruncate_ = true;
5787 return ins;
5788 }
congruentTo(const MDefinition * ins)5789 bool congruentTo(const MDefinition* ins) const override {
5790 return congruentIfOperandsEqual(ins);
5791 }
5792 bool fallible() const;
5793
getAliasSet()5794 AliasSet getAliasSet() const override {
5795 return AliasSet::None();
5796 }
5797 void computeRange(TempAllocator& alloc) override;
isFloat32Commutative()5798 bool isFloat32Commutative() const override { return true; }
5799 void trySpecializeFloat32(TempAllocator& alloc) override;
5800
5801 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5802 bool canRecoverOnBailout() const override {
5803 return true;
5804 }
5805
5806 ALLOW_CLONE(MAbs)
5807 };
5808
5809 class MClz
5810 : public MUnaryInstruction
5811 , public BitwisePolicy::Data
5812 {
5813 bool operandIsNeverZero_;
5814
MClz(MDefinition * num)5815 explicit MClz(MDefinition* num)
5816 : MUnaryInstruction(num),
5817 operandIsNeverZero_(false)
5818 {
5819 MOZ_ASSERT(IsNumberType(num->type()));
5820 specialization_ = MIRType_Int32;
5821 setResultType(MIRType_Int32);
5822 setMovable();
5823 }
5824
5825 public:
INSTRUCTION_HEADER(Clz)5826 INSTRUCTION_HEADER(Clz)
5827 static MClz* New(TempAllocator& alloc, MDefinition* num) {
5828 return new(alloc) MClz(num);
5829 }
NewAsmJS(TempAllocator & alloc,MDefinition * num)5830 static MClz* NewAsmJS(TempAllocator& alloc, MDefinition* num) {
5831 return new(alloc) MClz(num);
5832 }
num()5833 MDefinition* num() const {
5834 return getOperand(0);
5835 }
congruentTo(const MDefinition * ins)5836 bool congruentTo(const MDefinition* ins) const override {
5837 return congruentIfOperandsEqual(ins);
5838 }
5839
getAliasSet()5840 AliasSet getAliasSet() const override {
5841 return AliasSet::None();
5842 }
5843
operandIsNeverZero()5844 bool operandIsNeverZero() const {
5845 return operandIsNeverZero_;
5846 }
5847
5848 MDefinition* foldsTo(TempAllocator& alloc) override;
5849 void computeRange(TempAllocator& alloc) override;
5850 void collectRangeInfoPreTrunc() override;
5851 };
5852
5853 // Inline implementation of Math.sqrt().
5854 class MSqrt
5855 : public MUnaryInstruction,
5856 public FloatingPointPolicy<0>::Data
5857 {
MSqrt(MDefinition * num,MIRType type)5858 MSqrt(MDefinition* num, MIRType type)
5859 : MUnaryInstruction(num)
5860 {
5861 setResultType(type);
5862 specialization_ = type;
5863 setMovable();
5864 }
5865
5866 public:
INSTRUCTION_HEADER(Sqrt)5867 INSTRUCTION_HEADER(Sqrt)
5868 static MSqrt* New(TempAllocator& alloc, MDefinition* num) {
5869 return new(alloc) MSqrt(num, MIRType_Double);
5870 }
NewAsmJS(TempAllocator & alloc,MDefinition * num,MIRType type)5871 static MSqrt* NewAsmJS(TempAllocator& alloc, MDefinition* num, MIRType type) {
5872 MOZ_ASSERT(IsFloatingPointType(type));
5873 return new(alloc) MSqrt(num, type);
5874 }
congruentTo(const MDefinition * ins)5875 bool congruentTo(const MDefinition* ins) const override {
5876 return congruentIfOperandsEqual(ins);
5877 }
5878
getAliasSet()5879 AliasSet getAliasSet() const override {
5880 return AliasSet::None();
5881 }
5882 void computeRange(TempAllocator& alloc) override;
5883
isFloat32Commutative()5884 bool isFloat32Commutative() const override { return true; }
5885 void trySpecializeFloat32(TempAllocator& alloc) override;
5886
5887 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5888 bool canRecoverOnBailout() const override {
5889 return true;
5890 }
5891
5892 ALLOW_CLONE(MSqrt)
5893 };
5894
5895 // Inline implementation of atan2 (arctangent of y/x).
5896 class MAtan2
5897 : public MBinaryInstruction,
5898 public MixPolicy<DoublePolicy<0>, DoublePolicy<1> >::Data
5899 {
MAtan2(MDefinition * y,MDefinition * x)5900 MAtan2(MDefinition* y, MDefinition* x)
5901 : MBinaryInstruction(y, x)
5902 {
5903 setResultType(MIRType_Double);
5904 setMovable();
5905 }
5906
5907 public:
INSTRUCTION_HEADER(Atan2)5908 INSTRUCTION_HEADER(Atan2)
5909 static MAtan2* New(TempAllocator& alloc, MDefinition* y, MDefinition* x) {
5910 return new(alloc) MAtan2(y, x);
5911 }
5912
y()5913 MDefinition* y() const {
5914 return getOperand(0);
5915 }
5916
x()5917 MDefinition* x() const {
5918 return getOperand(1);
5919 }
5920
congruentTo(const MDefinition * ins)5921 bool congruentTo(const MDefinition* ins) const override {
5922 return congruentIfOperandsEqual(ins);
5923 }
5924
getAliasSet()5925 AliasSet getAliasSet() const override {
5926 return AliasSet::None();
5927 }
5928
possiblyCalls()5929 bool possiblyCalls() const override {
5930 return true;
5931 }
5932
5933 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5934 bool canRecoverOnBailout() const override {
5935 return true;
5936 }
5937
5938 ALLOW_CLONE(MAtan2)
5939 };
5940
5941 // Inline implementation of Math.hypot().
5942 class MHypot
5943 : public MVariadicInstruction,
5944 public AllDoublePolicy::Data
5945 {
MHypot()5946 MHypot()
5947 {
5948 setResultType(MIRType_Double);
5949 setMovable();
5950 }
5951
5952 public:
5953 INSTRUCTION_HEADER(Hypot)
5954 static MHypot* New(TempAllocator& alloc, const MDefinitionVector& vector);
5955
congruentTo(const MDefinition * ins)5956 bool congruentTo(const MDefinition* ins) const override {
5957 return congruentIfOperandsEqual(ins);
5958 }
5959
getAliasSet()5960 AliasSet getAliasSet() const override {
5961 return AliasSet::None();
5962 }
5963
possiblyCalls()5964 bool possiblyCalls() const override {
5965 return true;
5966 }
5967
5968 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()5969 bool canRecoverOnBailout() const override {
5970 return true;
5971 }
5972
canClone()5973 bool canClone() const override {
5974 return true;
5975 }
5976
clone(TempAllocator & alloc,const MDefinitionVector & inputs)5977 MInstruction* clone(TempAllocator& alloc,
5978 const MDefinitionVector& inputs) const override {
5979 return MHypot::New(alloc, inputs);
5980 }
5981 };
5982
5983 // Inline implementation of Math.pow().
5984 class MPow
5985 : public MBinaryInstruction,
5986 public PowPolicy::Data
5987 {
MPow(MDefinition * input,MDefinition * power,MIRType powerType)5988 MPow(MDefinition* input, MDefinition* power, MIRType powerType)
5989 : MBinaryInstruction(input, power)
5990 {
5991 specialization_ = powerType;
5992 setResultType(MIRType_Double);
5993 setMovable();
5994 }
5995
5996 public:
INSTRUCTION_HEADER(Pow)5997 INSTRUCTION_HEADER(Pow)
5998 static MPow* New(TempAllocator& alloc, MDefinition* input, MDefinition* power,
5999 MIRType powerType)
6000 {
6001 MOZ_ASSERT(powerType == MIRType_Double || powerType == MIRType_Int32);
6002 return new(alloc) MPow(input, power, powerType);
6003 }
6004
input()6005 MDefinition* input() const {
6006 return lhs();
6007 }
power()6008 MDefinition* power() const {
6009 return rhs();
6010 }
congruentTo(const MDefinition * ins)6011 bool congruentTo(const MDefinition* ins) const override {
6012 return congruentIfOperandsEqual(ins);
6013 }
getAliasSet()6014 AliasSet getAliasSet() const override {
6015 return AliasSet::None();
6016 }
possiblyCalls()6017 bool possiblyCalls() const override {
6018 return true;
6019 }
6020 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6021 bool canRecoverOnBailout() const override {
6022 // Temporarily disable recovery to relieve fuzzer pressure. See bug 1188586.
6023 return false;
6024 }
6025
6026 ALLOW_CLONE(MPow)
6027 };
6028
6029 // Inline implementation of Math.pow(x, 0.5), which subtly differs from Math.sqrt(x).
6030 class MPowHalf
6031 : public MUnaryInstruction,
6032 public DoublePolicy<0>::Data
6033 {
6034 bool operandIsNeverNegativeInfinity_;
6035 bool operandIsNeverNegativeZero_;
6036 bool operandIsNeverNaN_;
6037
MPowHalf(MDefinition * input)6038 explicit MPowHalf(MDefinition* input)
6039 : MUnaryInstruction(input),
6040 operandIsNeverNegativeInfinity_(false),
6041 operandIsNeverNegativeZero_(false),
6042 operandIsNeverNaN_(false)
6043 {
6044 setResultType(MIRType_Double);
6045 setMovable();
6046 }
6047
6048 public:
INSTRUCTION_HEADER(PowHalf)6049 INSTRUCTION_HEADER(PowHalf)
6050 static MPowHalf* New(TempAllocator& alloc, MDefinition* input) {
6051 return new(alloc) MPowHalf(input);
6052 }
congruentTo(const MDefinition * ins)6053 bool congruentTo(const MDefinition* ins) const override {
6054 return congruentIfOperandsEqual(ins);
6055 }
operandIsNeverNegativeInfinity()6056 bool operandIsNeverNegativeInfinity() const {
6057 return operandIsNeverNegativeInfinity_;
6058 }
operandIsNeverNegativeZero()6059 bool operandIsNeverNegativeZero() const {
6060 return operandIsNeverNegativeZero_;
6061 }
operandIsNeverNaN()6062 bool operandIsNeverNaN() const {
6063 return operandIsNeverNaN_;
6064 }
getAliasSet()6065 AliasSet getAliasSet() const override {
6066 return AliasSet::None();
6067 }
6068 void collectRangeInfoPreTrunc() override;
6069 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6070 bool canRecoverOnBailout() const override {
6071 return true;
6072 }
6073
6074 ALLOW_CLONE(MPowHalf)
6075 };
6076
6077 // Inline implementation of Math.random().
6078 class MRandom : public MNullaryInstruction
6079 {
MRandom()6080 MRandom()
6081 {
6082 setResultType(MIRType_Double);
6083 }
6084
6085 public:
INSTRUCTION_HEADER(Random)6086 INSTRUCTION_HEADER(Random)
6087 static MRandom* New(TempAllocator& alloc) {
6088 return new(alloc) MRandom;
6089 }
6090
getAliasSet()6091 AliasSet getAliasSet() const override {
6092 return AliasSet::None();
6093 }
6094
possiblyCalls()6095 bool possiblyCalls() const override {
6096 return true;
6097 }
6098
6099 void computeRange(TempAllocator& alloc) override;
6100
6101 ALLOW_CLONE(MRandom)
6102 };
6103
6104 class MMathFunction
6105 : public MUnaryInstruction,
6106 public FloatingPointPolicy<0>::Data
6107 {
6108 public:
6109 enum Function {
6110 Log,
6111 Sin,
6112 Cos,
6113 Exp,
6114 Tan,
6115 ACos,
6116 ASin,
6117 ATan,
6118 Log10,
6119 Log2,
6120 Log1P,
6121 ExpM1,
6122 CosH,
6123 SinH,
6124 TanH,
6125 ACosH,
6126 ASinH,
6127 ATanH,
6128 Sign,
6129 Trunc,
6130 Cbrt,
6131 Floor,
6132 Ceil,
6133 Round
6134 };
6135
6136 private:
6137 Function function_;
6138 const MathCache* cache_;
6139
MMathFunction(MDefinition * input,Function function,const MathCache * cache)6140 MMathFunction(MDefinition* input, Function function, const MathCache* cache)
6141 : MUnaryInstruction(input), function_(function), cache_(cache)
6142 {
6143 setResultType(MIRType_Double);
6144 specialization_ = MIRType_Double;
6145 setMovable();
6146 }
6147
6148 public:
INSTRUCTION_HEADER(MathFunction)6149 INSTRUCTION_HEADER(MathFunction)
6150
6151 // A nullptr cache means this function will neither access nor update the cache.
6152 static MMathFunction* New(TempAllocator& alloc, MDefinition* input, Function function,
6153 const MathCache* cache)
6154 {
6155 return new(alloc) MMathFunction(input, function, cache);
6156 }
function()6157 Function function() const {
6158 return function_;
6159 }
cache()6160 const MathCache* cache() const {
6161 return cache_;
6162 }
congruentTo(const MDefinition * ins)6163 bool congruentTo(const MDefinition* ins) const override {
6164 if (!ins->isMathFunction())
6165 return false;
6166 if (ins->toMathFunction()->function() != function())
6167 return false;
6168 return congruentIfOperandsEqual(ins);
6169 }
6170
getAliasSet()6171 AliasSet getAliasSet() const override {
6172 return AliasSet::None();
6173 }
6174
possiblyCalls()6175 bool possiblyCalls() const override {
6176 return true;
6177 }
6178
6179 MDefinition* foldsTo(TempAllocator& alloc) override;
6180
6181 void printOpcode(GenericPrinter& out) const override;
6182
6183 static const char* FunctionName(Function function);
6184
isFloat32Commutative()6185 bool isFloat32Commutative() const override {
6186 return function_ == Floor || function_ == Ceil || function_ == Round;
6187 }
6188 void trySpecializeFloat32(TempAllocator& alloc) override;
6189 void computeRange(TempAllocator& alloc) override;
6190 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6191 bool canRecoverOnBailout() const override {
6192 if (input()->type() == MIRType_SinCosDouble)
6193 return false;
6194 switch(function_) {
6195 case Sin:
6196 case Log:
6197 case Round:
6198 return true;
6199 default:
6200 return false;
6201 }
6202 }
6203
6204 ALLOW_CLONE(MMathFunction)
6205 };
6206
6207 class MAdd : public MBinaryArithInstruction
6208 {
6209 // Is this instruction really an int at heart?
MAdd(MDefinition * left,MDefinition * right)6210 MAdd(MDefinition* left, MDefinition* right)
6211 : MBinaryArithInstruction(left, right)
6212 {
6213 setResultType(MIRType_Value);
6214 }
6215
6216 public:
INSTRUCTION_HEADER(Add)6217 INSTRUCTION_HEADER(Add)
6218 static MAdd* New(TempAllocator& alloc, MDefinition* left, MDefinition* right) {
6219 return new(alloc) MAdd(left, right);
6220 }
6221
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right,MIRType type)6222 static MAdd* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
6223 MIRType type)
6224 {
6225 MAdd* add = new(alloc) MAdd(left, right);
6226 add->specialization_ = type;
6227 add->setResultType(type);
6228 if (type == MIRType_Int32) {
6229 add->setTruncateKind(Truncate);
6230 add->setCommutative();
6231 }
6232 return add;
6233 }
6234
isFloat32Commutative()6235 bool isFloat32Commutative() const override { return true; }
6236
getIdentity()6237 double getIdentity() override {
6238 return 0;
6239 }
6240
6241 bool fallible() const;
6242 void computeRange(TempAllocator& alloc) override;
6243 bool needTruncation(TruncateKind kind) override;
6244 void truncate() override;
6245 TruncateKind operandTruncateKind(size_t index) const override;
6246
6247 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6248 bool canRecoverOnBailout() const override {
6249 return specialization_ < MIRType_Object;
6250 }
6251
6252 ALLOW_CLONE(MAdd)
6253 };
6254
6255 class MSub : public MBinaryArithInstruction
6256 {
MSub(MDefinition * left,MDefinition * right)6257 MSub(MDefinition* left, MDefinition* right)
6258 : MBinaryArithInstruction(left, right)
6259 {
6260 setResultType(MIRType_Value);
6261 }
6262
6263 public:
INSTRUCTION_HEADER(Sub)6264 INSTRUCTION_HEADER(Sub)
6265 static MSub* New(TempAllocator& alloc, MDefinition* left, MDefinition* right) {
6266 return new(alloc) MSub(left, right);
6267 }
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right,MIRType type)6268 static MSub* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
6269 MIRType type)
6270 {
6271 MSub* sub = new(alloc) MSub(left, right);
6272 sub->specialization_ = type;
6273 sub->setResultType(type);
6274 if (type == MIRType_Int32)
6275 sub->setTruncateKind(Truncate);
6276 return sub;
6277 }
6278
getIdentity()6279 double getIdentity() override {
6280 return 0;
6281 }
6282
isFloat32Commutative()6283 bool isFloat32Commutative() const override { return true; }
6284
6285 bool fallible() const;
6286 void computeRange(TempAllocator& alloc) override;
6287 bool needTruncation(TruncateKind kind) override;
6288 void truncate() override;
6289 TruncateKind operandTruncateKind(size_t index) const override;
6290
6291 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6292 bool canRecoverOnBailout() const override {
6293 return specialization_ < MIRType_Object;
6294 }
6295
6296 ALLOW_CLONE(MSub)
6297 };
6298
6299 class MMul : public MBinaryArithInstruction
6300 {
6301 public:
6302 enum Mode {
6303 Normal,
6304 Integer
6305 };
6306
6307 private:
6308 // Annotation the result could be a negative zero
6309 // and we need to guard this during execution.
6310 bool canBeNegativeZero_;
6311
6312 Mode mode_;
6313
MMul(MDefinition * left,MDefinition * right,MIRType type,Mode mode)6314 MMul(MDefinition* left, MDefinition* right, MIRType type, Mode mode)
6315 : MBinaryArithInstruction(left, right),
6316 canBeNegativeZero_(true),
6317 mode_(mode)
6318 {
6319 if (mode == Integer) {
6320 // This implements the required behavior for Math.imul, which
6321 // can never fail and always truncates its output to int32.
6322 canBeNegativeZero_ = false;
6323 setTruncateKind(Truncate);
6324 setCommutative();
6325 }
6326 MOZ_ASSERT_IF(mode != Integer, mode == Normal);
6327
6328 if (type != MIRType_Value)
6329 specialization_ = type;
6330 setResultType(type);
6331 }
6332
6333 public:
INSTRUCTION_HEADER(Mul)6334 INSTRUCTION_HEADER(Mul)
6335 static MMul* New(TempAllocator& alloc, MDefinition* left, MDefinition* right) {
6336 return new(alloc) MMul(left, right, MIRType_Value, MMul::Normal);
6337 }
6338 static MMul* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type,
6339 Mode mode = Normal)
6340 {
6341 return new(alloc) MMul(left, right, type, mode);
6342 }
6343
6344 MDefinition* foldsTo(TempAllocator& alloc) override;
6345 void analyzeEdgeCasesForward() override;
6346 void analyzeEdgeCasesBackward() override;
6347 void collectRangeInfoPreTrunc() override;
6348
getIdentity()6349 double getIdentity() override {
6350 return 1;
6351 }
6352
congruentTo(const MDefinition * ins)6353 bool congruentTo(const MDefinition* ins) const override {
6354 if (!ins->isMul())
6355 return false;
6356
6357 const MMul* mul = ins->toMul();
6358 if (canBeNegativeZero_ != mul->canBeNegativeZero())
6359 return false;
6360
6361 if (mode_ != mul->mode())
6362 return false;
6363
6364 return binaryCongruentTo(ins);
6365 }
6366
6367 bool canOverflow() const;
6368
canBeNegativeZero()6369 bool canBeNegativeZero() const {
6370 return canBeNegativeZero_;
6371 }
setCanBeNegativeZero(bool negativeZero)6372 void setCanBeNegativeZero(bool negativeZero) {
6373 canBeNegativeZero_ = negativeZero;
6374 }
6375
6376 bool updateForReplacement(MDefinition* ins) override;
6377
fallible()6378 bool fallible() const {
6379 return canBeNegativeZero_ || canOverflow();
6380 }
6381
setSpecialization(MIRType type)6382 void setSpecialization(MIRType type) {
6383 specialization_ = type;
6384 }
6385
isFloat32Commutative()6386 bool isFloat32Commutative() const override { return true; }
6387
6388 void computeRange(TempAllocator& alloc) override;
6389 bool needTruncation(TruncateKind kind) override;
6390 void truncate() override;
6391 TruncateKind operandTruncateKind(size_t index) const override;
6392
mode()6393 Mode mode() const { return mode_; }
6394
6395 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6396 bool canRecoverOnBailout() const override {
6397 return specialization_ < MIRType_Object;
6398 }
6399
6400 ALLOW_CLONE(MMul)
6401 };
6402
6403 class MDiv : public MBinaryArithInstruction
6404 {
6405 bool canBeNegativeZero_;
6406 bool canBeNegativeOverflow_;
6407 bool canBeDivideByZero_;
6408 bool canBeNegativeDividend_;
6409 bool unsigned_;
6410
MDiv(MDefinition * left,MDefinition * right,MIRType type)6411 MDiv(MDefinition* left, MDefinition* right, MIRType type)
6412 : MBinaryArithInstruction(left, right),
6413 canBeNegativeZero_(true),
6414 canBeNegativeOverflow_(true),
6415 canBeDivideByZero_(true),
6416 canBeNegativeDividend_(true),
6417 unsigned_(false)
6418 {
6419 if (type != MIRType_Value)
6420 specialization_ = type;
6421 setResultType(type);
6422 }
6423
6424 public:
INSTRUCTION_HEADER(Div)6425 INSTRUCTION_HEADER(Div)
6426 static MDiv* New(TempAllocator& alloc, MDefinition* left, MDefinition* right) {
6427 return new(alloc) MDiv(left, right, MIRType_Value);
6428 }
New(TempAllocator & alloc,MDefinition * left,MDefinition * right,MIRType type)6429 static MDiv* New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type) {
6430 return new(alloc) MDiv(left, right, type);
6431 }
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right,MIRType type,bool unsignd)6432 static MDiv* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
6433 MIRType type, bool unsignd)
6434 {
6435 MDiv* div = new(alloc) MDiv(left, right, type);
6436 div->unsigned_ = unsignd;
6437 if (type == MIRType_Int32)
6438 div->setTruncateKind(Truncate);
6439 return div;
6440 }
6441
6442 MDefinition* foldsTo(TempAllocator& alloc) override;
6443 void analyzeEdgeCasesForward() override;
6444 void analyzeEdgeCasesBackward() override;
6445
getIdentity()6446 double getIdentity() override {
6447 MOZ_CRASH("not used");
6448 }
6449
canBeNegativeZero()6450 bool canBeNegativeZero() const {
6451 return canBeNegativeZero_;
6452 }
setCanBeNegativeZero(bool negativeZero)6453 void setCanBeNegativeZero(bool negativeZero) {
6454 canBeNegativeZero_ = negativeZero;
6455 }
6456
canBeNegativeOverflow()6457 bool canBeNegativeOverflow() const {
6458 return canBeNegativeOverflow_;
6459 }
6460
canBeDivideByZero()6461 bool canBeDivideByZero() const {
6462 return canBeDivideByZero_;
6463 }
6464
canBeNegativeDividend()6465 bool canBeNegativeDividend() const {
6466 // "Dividend" is an ambiguous concept for unsigned truncated
6467 // division, because of the truncation procedure:
6468 // ((x>>>0)/2)|0, for example, gets transformed in
6469 // MDiv::truncate into a node with lhs representing x (not
6470 // x>>>0) and rhs representing the constant 2; in other words,
6471 // the MIR node corresponds to "cast operands to unsigned and
6472 // divide" operation. In this case, is the dividend x or is it
6473 // x>>>0? In order to resolve such ambiguities, we disallow
6474 // the usage of this method for unsigned division.
6475 MOZ_ASSERT(!unsigned_);
6476 return canBeNegativeDividend_;
6477 }
6478
isUnsigned()6479 bool isUnsigned() const {
6480 return unsigned_;
6481 }
6482
isTruncatedIndirectly()6483 bool isTruncatedIndirectly() const {
6484 return truncateKind() >= IndirectTruncate;
6485 }
6486
canTruncateInfinities()6487 bool canTruncateInfinities() const {
6488 return isTruncated();
6489 }
canTruncateRemainder()6490 bool canTruncateRemainder() const {
6491 return isTruncated();
6492 }
canTruncateOverflow()6493 bool canTruncateOverflow() const {
6494 return isTruncated() || isTruncatedIndirectly();
6495 }
canTruncateNegativeZero()6496 bool canTruncateNegativeZero() const {
6497 return isTruncated() || isTruncatedIndirectly();
6498 }
6499
isFloat32Commutative()6500 bool isFloat32Commutative() const override { return true; }
6501
6502 void computeRange(TempAllocator& alloc) override;
6503 bool fallible() const;
6504 bool needTruncation(TruncateKind kind) override;
6505 void truncate() override;
6506 void collectRangeInfoPreTrunc() override;
6507 TruncateKind operandTruncateKind(size_t index) const override;
6508
6509 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6510 bool canRecoverOnBailout() const override {
6511 return specialization_ < MIRType_Object;
6512 }
6513
congruentTo(const MDefinition * ins)6514 bool congruentTo(const MDefinition* ins) const override {
6515 return MBinaryArithInstruction::congruentTo(ins) &&
6516 unsigned_ == ins->toDiv()->isUnsigned();
6517 }
6518
6519 ALLOW_CLONE(MDiv)
6520 };
6521
6522 class MMod : public MBinaryArithInstruction
6523 {
6524 bool unsigned_;
6525 bool canBeNegativeDividend_;
6526 bool canBePowerOfTwoDivisor_;
6527 bool canBeDivideByZero_;
6528
MMod(MDefinition * left,MDefinition * right,MIRType type)6529 MMod(MDefinition* left, MDefinition* right, MIRType type)
6530 : MBinaryArithInstruction(left, right),
6531 unsigned_(false),
6532 canBeNegativeDividend_(true),
6533 canBePowerOfTwoDivisor_(true),
6534 canBeDivideByZero_(true)
6535 {
6536 if (type != MIRType_Value)
6537 specialization_ = type;
6538 setResultType(type);
6539 }
6540
6541 public:
INSTRUCTION_HEADER(Mod)6542 INSTRUCTION_HEADER(Mod)
6543 static MMod* New(TempAllocator& alloc, MDefinition* left, MDefinition* right) {
6544 return new(alloc) MMod(left, right, MIRType_Value);
6545 }
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right,MIRType type,bool unsignd)6546 static MMod* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
6547 MIRType type, bool unsignd)
6548 {
6549 MMod* mod = new(alloc) MMod(left, right, type);
6550 mod->unsigned_ = unsignd;
6551 if (type == MIRType_Int32)
6552 mod->setTruncateKind(Truncate);
6553 return mod;
6554 }
6555
6556 MDefinition* foldsTo(TempAllocator& alloc) override;
6557
getIdentity()6558 double getIdentity() override {
6559 MOZ_CRASH("not used");
6560 }
6561
canBeNegativeDividend()6562 bool canBeNegativeDividend() const {
6563 MOZ_ASSERT(specialization_ == MIRType_Int32);
6564 MOZ_ASSERT(!unsigned_);
6565 return canBeNegativeDividend_;
6566 }
6567
canBeDivideByZero()6568 bool canBeDivideByZero() const {
6569 MOZ_ASSERT(specialization_ == MIRType_Int32);
6570 return canBeDivideByZero_;
6571 }
6572
canBePowerOfTwoDivisor()6573 bool canBePowerOfTwoDivisor() const {
6574 MOZ_ASSERT(specialization_ == MIRType_Int32);
6575 return canBePowerOfTwoDivisor_;
6576 }
6577
6578 void analyzeEdgeCasesForward() override;
6579
isUnsigned()6580 bool isUnsigned() const {
6581 return unsigned_;
6582 }
6583
6584 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6585 bool canRecoverOnBailout() const override {
6586 return specialization_ < MIRType_Object;
6587 }
6588
6589 bool fallible() const;
6590
6591 void computeRange(TempAllocator& alloc) override;
6592 bool needTruncation(TruncateKind kind) override;
6593 void truncate() override;
6594 void collectRangeInfoPreTrunc() override;
6595 TruncateKind operandTruncateKind(size_t index) const override;
6596
congruentTo(const MDefinition * ins)6597 bool congruentTo(const MDefinition* ins) const override {
6598 return MBinaryArithInstruction::congruentTo(ins) &&
6599 unsigned_ == ins->toMod()->isUnsigned();
6600 }
6601
6602 ALLOW_CLONE(MMod)
6603 };
6604
6605 class MConcat
6606 : public MBinaryInstruction,
6607 public MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >::Data
6608 {
MConcat(MDefinition * left,MDefinition * right)6609 MConcat(MDefinition* left, MDefinition* right)
6610 : MBinaryInstruction(left, right)
6611 {
6612 // At least one input should be definitely string
6613 MOZ_ASSERT(left->type() == MIRType_String || right->type() == MIRType_String);
6614
6615 setMovable();
6616 setResultType(MIRType_String);
6617 }
6618
6619 public:
INSTRUCTION_HEADER(Concat)6620 INSTRUCTION_HEADER(Concat)
6621 static MConcat* New(TempAllocator& alloc, MDefinition* left, MDefinition* right) {
6622 return new(alloc) MConcat(left, right);
6623 }
6624
6625 MDefinition* foldsTo(TempAllocator& alloc) override;
congruentTo(const MDefinition * ins)6626 bool congruentTo(const MDefinition* ins) const override {
6627 return congruentIfOperandsEqual(ins);
6628 }
getAliasSet()6629 AliasSet getAliasSet() const override {
6630 return AliasSet::None();
6631 }
6632
6633 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6634 bool canRecoverOnBailout() const override {
6635 return true;
6636 }
6637
6638 ALLOW_CLONE(MConcat)
6639 };
6640
6641 class MCharCodeAt
6642 : public MBinaryInstruction,
6643 public MixPolicy<StringPolicy<0>, IntPolicy<1> >::Data
6644 {
MCharCodeAt(MDefinition * str,MDefinition * index)6645 MCharCodeAt(MDefinition* str, MDefinition* index)
6646 : MBinaryInstruction(str, index)
6647 {
6648 setMovable();
6649 setResultType(MIRType_Int32);
6650 }
6651
6652 public:
INSTRUCTION_HEADER(CharCodeAt)6653 INSTRUCTION_HEADER(CharCodeAt)
6654
6655 static MCharCodeAt* New(TempAllocator& alloc, MDefinition* str, MDefinition* index) {
6656 return new(alloc) MCharCodeAt(str, index);
6657 }
6658
congruentTo(const MDefinition * ins)6659 bool congruentTo(const MDefinition* ins) const override {
6660 return congruentIfOperandsEqual(ins);
6661 }
6662
getAliasSet()6663 virtual AliasSet getAliasSet() const override {
6664 // Strings are immutable, so there is no implicit dependency.
6665 return AliasSet::None();
6666 }
6667
6668 void computeRange(TempAllocator& alloc) override;
6669
6670 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6671 bool canRecoverOnBailout() const override {
6672 return true;
6673 }
6674
6675 ALLOW_CLONE(MCharCodeAt)
6676 };
6677
6678 class MFromCharCode
6679 : public MUnaryInstruction,
6680 public IntPolicy<0>::Data
6681 {
MFromCharCode(MDefinition * code)6682 explicit MFromCharCode(MDefinition* code)
6683 : MUnaryInstruction(code)
6684 {
6685 setMovable();
6686 setResultType(MIRType_String);
6687 }
6688
6689 public:
INSTRUCTION_HEADER(FromCharCode)6690 INSTRUCTION_HEADER(FromCharCode)
6691
6692 static MFromCharCode* New(TempAllocator& alloc, MDefinition* code) {
6693 return new(alloc) MFromCharCode(code);
6694 }
6695
getAliasSet()6696 virtual AliasSet getAliasSet() const override {
6697 return AliasSet::None();
6698 }
congruentTo(const MDefinition * ins)6699 bool congruentTo(const MDefinition* ins) const override {
6700 return congruentIfOperandsEqual(ins);
6701 }
6702
6703 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6704 bool canRecoverOnBailout() const override {
6705 return true;
6706 }
6707
6708 ALLOW_CLONE(MFromCharCode)
6709 };
6710
6711 class MSinCos
6712 : public MUnaryInstruction,
6713 public FloatingPointPolicy<0>::Data
6714 {
6715 const MathCache* cache_;
6716
MSinCos(MDefinition * input,const MathCache * cache)6717 MSinCos(MDefinition *input, const MathCache *cache) : MUnaryInstruction(input), cache_(cache)
6718 {
6719 setResultType(MIRType_SinCosDouble);
6720 specialization_ = MIRType_Double;
6721 setMovable();
6722 }
6723
6724 public:
INSTRUCTION_HEADER(SinCos)6725 INSTRUCTION_HEADER(SinCos)
6726
6727 static MSinCos *New(TempAllocator &alloc, MDefinition *input, const MathCache *cache)
6728 {
6729 return new (alloc) MSinCos(input, cache);
6730 }
getAliasSet()6731 AliasSet getAliasSet() const override {
6732 return AliasSet::None();
6733 }
congruentTo(const MDefinition * ins)6734 bool congruentTo(const MDefinition *ins) const override {
6735 return congruentIfOperandsEqual(ins);
6736 }
possiblyCalls()6737 bool possiblyCalls() const override {
6738 return true;
6739 }
cache()6740 const MathCache* cache() const {
6741 return cache_;
6742 }
6743 };
6744
6745 class MStringSplit
6746 : public MTernaryInstruction,
6747 public MixPolicy<StringPolicy<0>, StringPolicy<1> >::Data
6748 {
MStringSplit(CompilerConstraintList * constraints,MDefinition * string,MDefinition * sep,MConstant * templateObject)6749 MStringSplit(CompilerConstraintList* constraints, MDefinition* string, MDefinition* sep,
6750 MConstant* templateObject)
6751 : MTernaryInstruction(string, sep, templateObject)
6752 {
6753 setResultType(MIRType_Object);
6754 setResultTypeSet(templateObject->resultTypeSet());
6755 }
6756
6757 public:
INSTRUCTION_HEADER(StringSplit)6758 INSTRUCTION_HEADER(StringSplit)
6759
6760 static MStringSplit* New(TempAllocator& alloc, CompilerConstraintList* constraints,
6761 MDefinition* string, MDefinition* sep,
6762 MConstant* templateObject)
6763 {
6764 return new(alloc) MStringSplit(constraints, string, sep, templateObject);
6765 }
string()6766 MDefinition* string() const {
6767 return getOperand(0);
6768 }
separator()6769 MDefinition* separator() const {
6770 return getOperand(1);
6771 }
templateObject()6772 JSObject* templateObject() const {
6773 return &getOperand(2)->toConstant()->value().toObject();
6774 }
group()6775 ObjectGroup* group() const {
6776 return templateObject()->group();
6777 }
possiblyCalls()6778 bool possiblyCalls() const override {
6779 return true;
6780 }
getAliasSet()6781 virtual AliasSet getAliasSet() const override {
6782 // Although this instruction returns a new array, we don't have to mark
6783 // it as store instruction, see also MNewArray.
6784 return AliasSet::None();
6785 }
6786 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()6787 bool canRecoverOnBailout() const override {
6788 return true;
6789 }
6790 };
6791
6792 // Returns the value to use as |this| value. See also ComputeThis and
6793 // BoxNonStrictThis in Interpreter.h.
6794 class MComputeThis
6795 : public MUnaryInstruction,
6796 public BoxPolicy<0>::Data
6797 {
MComputeThis(MDefinition * def)6798 explicit MComputeThis(MDefinition* def)
6799 : MUnaryInstruction(def)
6800 {
6801 setResultType(MIRType_Value);
6802 }
6803
6804 public:
INSTRUCTION_HEADER(ComputeThis)6805 INSTRUCTION_HEADER(ComputeThis)
6806
6807 static MComputeThis* New(TempAllocator& alloc, MDefinition* def) {
6808 return new(alloc) MComputeThis(def);
6809 }
6810
possiblyCalls()6811 bool possiblyCalls() const override {
6812 return true;
6813 }
6814
6815 // Note: don't override getAliasSet: the thisValue hook can be effectful.
6816 };
6817
6818 // Load an arrow function's |new.target| value.
6819 class MArrowNewTarget
6820 : public MUnaryInstruction,
6821 public SingleObjectPolicy::Data
6822 {
MArrowNewTarget(MDefinition * callee)6823 explicit MArrowNewTarget(MDefinition* callee)
6824 : MUnaryInstruction(callee)
6825 {
6826 setResultType(MIRType_Value);
6827 setMovable();
6828 }
6829
6830 public:
INSTRUCTION_HEADER(ArrowNewTarget)6831 INSTRUCTION_HEADER(ArrowNewTarget)
6832
6833 static MArrowNewTarget* New(TempAllocator& alloc, MDefinition* callee) {
6834 return new(alloc) MArrowNewTarget(callee);
6835 }
callee()6836 MDefinition* callee() const {
6837 return getOperand(0);
6838 }
congruentTo(const MDefinition * ins)6839 bool congruentTo(const MDefinition* ins) const override {
6840 return congruentIfOperandsEqual(ins);
6841 }
getAliasSet()6842 AliasSet getAliasSet() const override {
6843 // An arrow function's lexical |this| value is immutable.
6844 return AliasSet::None();
6845 }
6846 };
6847
6848 class MPhi final
6849 : public MDefinition,
6850 public InlineListNode<MPhi>,
6851 public NoTypePolicy::Data
6852 {
6853 js::Vector<MUse, 2, JitAllocPolicy> inputs_;
6854
6855 TruncateKind truncateKind_;
6856 bool hasBackedgeType_;
6857 bool triedToSpecialize_;
6858 bool isIterator_;
6859 bool canProduceFloat32_;
6860 bool canConsumeFloat32_;
6861
6862 #if DEBUG
6863 bool specialized_;
6864 #endif
6865
6866 protected:
getUseFor(size_t index)6867 MUse* getUseFor(size_t index) override {
6868 // Note: after the initial IonBuilder pass, it is OK to change phi
6869 // operands such that they do not include the type sets of their
6870 // operands. This can arise during e.g. value numbering, where
6871 // definitions producing the same value may have different type sets.
6872 MOZ_ASSERT(index < numOperands());
6873 return &inputs_[index];
6874 }
getUseFor(size_t index)6875 const MUse* getUseFor(size_t index) const override {
6876 return &inputs_[index];
6877 }
6878
6879 public:
6880 INSTRUCTION_HEADER_WITHOUT_TYPEPOLICY(Phi)
6881 virtual TypePolicy* typePolicy();
6882 virtual MIRType typePolicySpecialization();
6883
MPhi(TempAllocator & alloc,MIRType resultType)6884 MPhi(TempAllocator& alloc, MIRType resultType)
6885 : inputs_(alloc),
6886 truncateKind_(NoTruncate),
6887 hasBackedgeType_(false),
6888 triedToSpecialize_(false),
6889 isIterator_(false),
6890 canProduceFloat32_(false),
6891 canConsumeFloat32_(false)
6892 #if DEBUG
6893 , specialized_(false)
6894 #endif
6895 {
6896 setResultType(resultType);
6897 }
6898
6899 static MPhi* New(TempAllocator& alloc, MIRType resultType = MIRType_Value) {
6900 return new(alloc) MPhi(alloc, resultType);
6901 }
6902
6903 void removeOperand(size_t index);
6904 void removeAllOperands();
6905
getOperand(size_t index)6906 MDefinition* getOperand(size_t index) const override {
6907 return inputs_[index].producer();
6908 }
numOperands()6909 size_t numOperands() const override {
6910 return inputs_.length();
6911 }
indexOf(const MUse * u)6912 size_t indexOf(const MUse* u) const final override {
6913 MOZ_ASSERT(u >= &inputs_[0]);
6914 MOZ_ASSERT(u <= &inputs_[numOperands() - 1]);
6915 return u - &inputs_[0];
6916 }
replaceOperand(size_t index,MDefinition * operand)6917 void replaceOperand(size_t index, MDefinition* operand) final override {
6918 inputs_[index].replaceProducer(operand);
6919 }
hasBackedgeType()6920 bool hasBackedgeType() const {
6921 return hasBackedgeType_;
6922 }
triedToSpecialize()6923 bool triedToSpecialize() const {
6924 return triedToSpecialize_;
6925 }
specialize(MIRType type)6926 void specialize(MIRType type) {
6927 triedToSpecialize_ = true;
6928 setResultType(type);
6929 }
6930 bool specializeType();
6931
6932 #ifdef DEBUG
6933 // Assert that this is a phi in a loop header with a unique predecessor and
6934 // a unique backedge.
6935 void assertLoopPhi() const;
6936 #else
assertLoopPhi()6937 void assertLoopPhi() const {}
6938 #endif
6939
6940 // Assuming this phi is in a loop header with a unique loop entry, return
6941 // the phi operand along the loop entry.
getLoopPredecessorOperand()6942 MDefinition* getLoopPredecessorOperand() const {
6943 assertLoopPhi();
6944 return getOperand(0);
6945 }
6946
6947 // Assuming this phi is in a loop header with a unique loop entry, return
6948 // the phi operand along the loop backedge.
getLoopBackedgeOperand()6949 MDefinition* getLoopBackedgeOperand() const {
6950 assertLoopPhi();
6951 return getOperand(1);
6952 }
6953
6954 // Whether this phi's type already includes information for def.
6955 bool typeIncludes(MDefinition* def);
6956
6957 // Add types for this phi which speculate about new inputs that may come in
6958 // via a loop backedge.
6959 bool addBackedgeType(MIRType type, TemporaryTypeSet* typeSet);
6960
6961 // Initializes the operands vector to the given capacity,
6962 // permitting use of addInput() instead of addInputSlow().
reserveLength(size_t length)6963 bool reserveLength(size_t length) {
6964 return inputs_.reserve(length);
6965 }
6966
6967 // Use only if capacity has been reserved by reserveLength
addInput(MDefinition * ins)6968 void addInput(MDefinition* ins) {
6969 // Use infallibleGrowByUninitialized and placement-new instead of just
6970 // infallibleAppend to avoid creating a temporary MUse which will get
6971 // linked into |ins|'s use list and then unlinked in favor of the
6972 // MUse in the Vector. We'd ideally like to use an emplace method here,
6973 // once Vector supports that.
6974 inputs_.infallibleGrowByUninitialized(1);
6975 new (&inputs_.back()) MUse(ins, this);
6976 }
6977
6978 // Appends a new input to the input vector. May perform reallocation.
6979 // Prefer reserveLength() and addInput() instead, where possible.
addInputSlow(MDefinition * ins)6980 bool addInputSlow(MDefinition* ins) {
6981 return inputs_.emplaceBack(ins, this);
6982 }
6983
6984 // Update the type of this phi after adding |ins| as an input. Set
6985 // |*ptypeChange| to true if the type changed.
6986 bool checkForTypeChange(MDefinition* ins, bool* ptypeChange);
6987
6988 MDefinition* foldsTo(TempAllocator& alloc) override;
6989 MDefinition* foldsTernary();
6990 MDefinition* foldsFilterTypeSet();
6991
6992 bool congruentTo(const MDefinition* ins) const override;
6993
isIterator()6994 bool isIterator() const {
6995 return isIterator_;
6996 }
setIterator()6997 void setIterator() {
6998 isIterator_ = true;
6999 }
7000
getAliasSet()7001 AliasSet getAliasSet() const override {
7002 return AliasSet::None();
7003 }
7004 void computeRange(TempAllocator& alloc) override;
7005
7006 MDefinition* operandIfRedundant();
7007
canProduceFloat32()7008 bool canProduceFloat32() const override {
7009 return canProduceFloat32_;
7010 }
7011
setCanProduceFloat32(bool can)7012 void setCanProduceFloat32(bool can) {
7013 canProduceFloat32_ = can;
7014 }
7015
canConsumeFloat32(MUse * use)7016 bool canConsumeFloat32(MUse* use) const override {
7017 return canConsumeFloat32_;
7018 }
7019
setCanConsumeFloat32(bool can)7020 void setCanConsumeFloat32(bool can) {
7021 canConsumeFloat32_ = can;
7022 }
7023
7024 TruncateKind operandTruncateKind(size_t index) const override;
7025 bool needTruncation(TruncateKind kind) override;
7026 void truncate() override;
7027 };
7028
7029 // The goal of a Beta node is to split a def at a conditionally taken
7030 // branch, so that uses dominated by it have a different name.
7031 class MBeta
7032 : public MUnaryInstruction,
7033 public NoTypePolicy::Data
7034 {
7035 private:
7036 // This is the range induced by a comparison and branch in a preceding
7037 // block. Note that this does not reflect any range constraints from
7038 // the input value itself, so this value may differ from the range()
7039 // range after it is computed.
7040 const Range* comparison_;
7041
MBeta(MDefinition * val,const Range * comp)7042 MBeta(MDefinition* val, const Range* comp)
7043 : MUnaryInstruction(val),
7044 comparison_(comp)
7045 {
7046 setResultType(val->type());
7047 setResultTypeSet(val->resultTypeSet());
7048 }
7049
7050 public:
7051 INSTRUCTION_HEADER(Beta)
7052 void printOpcode(GenericPrinter& out) const override;
New(TempAllocator & alloc,MDefinition * val,const Range * comp)7053 static MBeta* New(TempAllocator& alloc, MDefinition* val, const Range* comp)
7054 {
7055 return new(alloc) MBeta(val, comp);
7056 }
7057
getAliasSet()7058 AliasSet getAliasSet() const override {
7059 return AliasSet::None();
7060 }
7061
7062 void computeRange(TempAllocator& alloc) override;
7063 };
7064
7065 // MIR representation of a Value on the OSR BaselineFrame.
7066 // The Value is indexed off of OsrFrameReg.
7067 class MOsrValue
7068 : public MUnaryInstruction,
7069 public NoTypePolicy::Data
7070 {
7071 private:
7072 ptrdiff_t frameOffset_;
7073
MOsrValue(MOsrEntry * entry,ptrdiff_t frameOffset)7074 MOsrValue(MOsrEntry* entry, ptrdiff_t frameOffset)
7075 : MUnaryInstruction(entry),
7076 frameOffset_(frameOffset)
7077 {
7078 setResultType(MIRType_Value);
7079 }
7080
7081 public:
INSTRUCTION_HEADER(OsrValue)7082 INSTRUCTION_HEADER(OsrValue)
7083 static MOsrValue* New(TempAllocator& alloc, MOsrEntry* entry, ptrdiff_t frameOffset) {
7084 return new(alloc) MOsrValue(entry, frameOffset);
7085 }
7086
frameOffset()7087 ptrdiff_t frameOffset() const {
7088 return frameOffset_;
7089 }
7090
entry()7091 MOsrEntry* entry() {
7092 return getOperand(0)->toOsrEntry();
7093 }
7094
getAliasSet()7095 AliasSet getAliasSet() const override {
7096 return AliasSet::None();
7097 }
7098 };
7099
7100 // MIR representation of a JSObject scope chain pointer on the OSR BaselineFrame.
7101 // The pointer is indexed off of OsrFrameReg.
7102 class MOsrScopeChain
7103 : public MUnaryInstruction,
7104 public NoTypePolicy::Data
7105 {
7106 private:
MOsrScopeChain(MOsrEntry * entry)7107 explicit MOsrScopeChain(MOsrEntry* entry)
7108 : MUnaryInstruction(entry)
7109 {
7110 setResultType(MIRType_Object);
7111 }
7112
7113 public:
INSTRUCTION_HEADER(OsrScopeChain)7114 INSTRUCTION_HEADER(OsrScopeChain)
7115 static MOsrScopeChain* New(TempAllocator& alloc, MOsrEntry* entry) {
7116 return new(alloc) MOsrScopeChain(entry);
7117 }
7118
entry()7119 MOsrEntry* entry() {
7120 return getOperand(0)->toOsrEntry();
7121 }
7122 };
7123
7124 // MIR representation of a JSObject ArgumentsObject pointer on the OSR BaselineFrame.
7125 // The pointer is indexed off of OsrFrameReg.
7126 class MOsrArgumentsObject
7127 : public MUnaryInstruction,
7128 public NoTypePolicy::Data
7129 {
7130 private:
MOsrArgumentsObject(MOsrEntry * entry)7131 explicit MOsrArgumentsObject(MOsrEntry* entry)
7132 : MUnaryInstruction(entry)
7133 {
7134 setResultType(MIRType_Object);
7135 }
7136
7137 public:
INSTRUCTION_HEADER(OsrArgumentsObject)7138 INSTRUCTION_HEADER(OsrArgumentsObject)
7139 static MOsrArgumentsObject* New(TempAllocator& alloc, MOsrEntry* entry) {
7140 return new(alloc) MOsrArgumentsObject(entry);
7141 }
7142
entry()7143 MOsrEntry* entry() {
7144 return getOperand(0)->toOsrEntry();
7145 }
7146 };
7147
7148 // MIR representation of the return value on the OSR BaselineFrame.
7149 // The Value is indexed off of OsrFrameReg.
7150 class MOsrReturnValue
7151 : public MUnaryInstruction,
7152 public NoTypePolicy::Data
7153 {
7154 private:
MOsrReturnValue(MOsrEntry * entry)7155 explicit MOsrReturnValue(MOsrEntry* entry)
7156 : MUnaryInstruction(entry)
7157 {
7158 setResultType(MIRType_Value);
7159 }
7160
7161 public:
INSTRUCTION_HEADER(OsrReturnValue)7162 INSTRUCTION_HEADER(OsrReturnValue)
7163 static MOsrReturnValue* New(TempAllocator& alloc, MOsrEntry* entry) {
7164 return new(alloc) MOsrReturnValue(entry);
7165 }
7166
entry()7167 MOsrEntry* entry() {
7168 return getOperand(0)->toOsrEntry();
7169 }
7170 };
7171
7172 class MBinarySharedStub
7173 : public MBinaryInstruction,
7174 public MixPolicy<BoxPolicy<0>, BoxPolicy<1> >::Data
7175 {
7176 protected:
MBinarySharedStub(MDefinition * left,MDefinition * right)7177 explicit MBinarySharedStub(MDefinition* left, MDefinition* right)
7178 : MBinaryInstruction(left, right)
7179 {
7180 setResultType(MIRType_Value);
7181 }
7182
7183 public:
INSTRUCTION_HEADER(BinarySharedStub)7184 INSTRUCTION_HEADER(BinarySharedStub)
7185
7186 static MBinarySharedStub* New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
7187 {
7188 return new(alloc) MBinarySharedStub(left, right);
7189 }
7190
7191 };
7192
7193 class MUnarySharedStub
7194 : public MUnaryInstruction,
7195 public BoxPolicy<0>::Data
7196 {
MUnarySharedStub(MDefinition * input)7197 explicit MUnarySharedStub(MDefinition* input)
7198 : MUnaryInstruction(input)
7199 {
7200 setResultType(MIRType_Value);
7201 }
7202
7203 public:
INSTRUCTION_HEADER(UnarySharedStub)7204 INSTRUCTION_HEADER(UnarySharedStub)
7205
7206 static MUnarySharedStub* New(TempAllocator& alloc, MDefinition* input)
7207 {
7208 return new(alloc) MUnarySharedStub(input);
7209 }
7210 };
7211
7212 // Check the current frame for over-recursion past the global stack limit.
7213 class MCheckOverRecursed
7214 : public MNullaryInstruction
7215 {
7216 public:
INSTRUCTION_HEADER(CheckOverRecursed)7217 INSTRUCTION_HEADER(CheckOverRecursed)
7218
7219 static MCheckOverRecursed* New(TempAllocator& alloc) {
7220 return new(alloc) MCheckOverRecursed();
7221 }
getAliasSet()7222 AliasSet getAliasSet() const override {
7223 return AliasSet::None();
7224 }
7225 };
7226
7227 // Check whether we need to fire the interrupt handler.
7228 class MInterruptCheck : public MNullaryInstruction
7229 {
MInterruptCheck()7230 MInterruptCheck() {
7231 setGuard();
7232 }
7233
7234 public:
INSTRUCTION_HEADER(InterruptCheck)7235 INSTRUCTION_HEADER(InterruptCheck)
7236
7237 static MInterruptCheck* New(TempAllocator& alloc) {
7238 return new(alloc) MInterruptCheck();
7239 }
getAliasSet()7240 AliasSet getAliasSet() const override {
7241 return AliasSet::None();
7242 }
7243 };
7244
7245 // Check whether we need to fire the interrupt handler at loop headers and
7246 // function prologues in asm.js. Generated only if we can't use implicit
7247 // interrupt checks with signal handlers.
7248 class MAsmJSInterruptCheck
7249 : public MNullaryInstruction
7250 {
7251 Label* interruptExit_;
7252 wasm::CallSiteDesc funcDesc_;
7253
MAsmJSInterruptCheck(Label * interruptExit,const wasm::CallSiteDesc & funcDesc)7254 MAsmJSInterruptCheck(Label* interruptExit, const wasm::CallSiteDesc& funcDesc)
7255 : interruptExit_(interruptExit), funcDesc_(funcDesc)
7256 {}
7257
7258 public:
INSTRUCTION_HEADER(AsmJSInterruptCheck)7259 INSTRUCTION_HEADER(AsmJSInterruptCheck)
7260
7261 static MAsmJSInterruptCheck* New(TempAllocator& alloc, Label* interruptExit,
7262 const wasm::CallSiteDesc& funcDesc)
7263 {
7264 return new(alloc) MAsmJSInterruptCheck(interruptExit, funcDesc);
7265 }
interruptExit()7266 Label* interruptExit() const {
7267 return interruptExit_;
7268 }
funcDesc()7269 const wasm::CallSiteDesc& funcDesc() const {
7270 return funcDesc_;
7271 }
7272 };
7273
7274 // Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
7275 // it to baseline to throw at the correct pc.
7276 class MLexicalCheck
7277 : public MUnaryInstruction,
7278 public BoxPolicy<0>::Data
7279 {
7280 BailoutKind kind_;
MLexicalCheck(MDefinition * input,BailoutKind kind)7281 explicit MLexicalCheck(MDefinition* input, BailoutKind kind)
7282 : MUnaryInstruction(input),
7283 kind_(kind)
7284 {
7285 setResultType(MIRType_Value);
7286 setResultTypeSet(input->resultTypeSet());
7287 setMovable();
7288 setGuard();
7289 }
7290
7291 public:
INSTRUCTION_HEADER(LexicalCheck)7292 INSTRUCTION_HEADER(LexicalCheck)
7293
7294 static MLexicalCheck* New(TempAllocator& alloc, MDefinition* input,
7295 BailoutKind kind = Bailout_UninitializedLexical) {
7296 return new(alloc) MLexicalCheck(input, kind);
7297 }
7298
getAliasSet()7299 AliasSet getAliasSet() const override {
7300 return AliasSet::None();
7301 }
7302
input()7303 MDefinition* input() const {
7304 return getOperand(0);
7305 }
7306
bailoutKind()7307 BailoutKind bailoutKind() const {
7308 return kind_;
7309 }
7310
congruentTo(const MDefinition * ins)7311 bool congruentTo(const MDefinition* ins) const override {
7312 return congruentIfOperandsEqual(ins);
7313 }
7314 };
7315
7316 // Unconditionally throw an uninitialized let error.
7317 class MThrowRuntimeLexicalError : public MNullaryInstruction
7318 {
7319 unsigned errorNumber_;
7320
MThrowRuntimeLexicalError(unsigned errorNumber)7321 explicit MThrowRuntimeLexicalError(unsigned errorNumber)
7322 : errorNumber_(errorNumber)
7323 {
7324 setGuard();
7325 setResultType(MIRType_None);
7326 }
7327
7328 public:
INSTRUCTION_HEADER(ThrowRuntimeLexicalError)7329 INSTRUCTION_HEADER(ThrowRuntimeLexicalError)
7330
7331 static MThrowRuntimeLexicalError* New(TempAllocator& alloc, unsigned errorNumber) {
7332 return new(alloc) MThrowRuntimeLexicalError(errorNumber);
7333 }
7334
errorNumber()7335 unsigned errorNumber() const {
7336 return errorNumber_;
7337 }
7338
getAliasSet()7339 AliasSet getAliasSet() const override {
7340 return AliasSet::None();
7341 }
7342 };
7343
7344 // In the prologues of global and eval scripts, check for redeclarations.
7345 class MGlobalNameConflictsCheck : public MNullaryInstruction
7346 {
MGlobalNameConflictsCheck()7347 MGlobalNameConflictsCheck() {
7348 setGuard();
7349 }
7350
7351 public:
INSTRUCTION_HEADER(GlobalNameConflictsCheck)7352 INSTRUCTION_HEADER(GlobalNameConflictsCheck)
7353
7354 static MGlobalNameConflictsCheck* New(TempAllocator& alloc) {
7355 return new(alloc) MGlobalNameConflictsCheck();
7356 }
7357 };
7358
7359 // If not defined, set a global variable to |undefined|.
7360 class MDefVar
7361 : public MUnaryInstruction,
7362 public NoTypePolicy::Data
7363 {
7364 CompilerPropertyName name_; // Target name to be defined.
7365 unsigned attrs_; // Attributes to be set.
7366
7367 private:
MDefVar(PropertyName * name,unsigned attrs,MDefinition * scopeChain)7368 MDefVar(PropertyName* name, unsigned attrs, MDefinition* scopeChain)
7369 : MUnaryInstruction(scopeChain),
7370 name_(name),
7371 attrs_(attrs)
7372 {
7373 }
7374
7375 public:
INSTRUCTION_HEADER(DefVar)7376 INSTRUCTION_HEADER(DefVar)
7377
7378 static MDefVar* New(TempAllocator& alloc, PropertyName* name, unsigned attrs,
7379 MDefinition* scopeChain)
7380 {
7381 return new(alloc) MDefVar(name, attrs, scopeChain);
7382 }
7383
name()7384 PropertyName* name() const {
7385 return name_;
7386 }
attrs()7387 unsigned attrs() const {
7388 return attrs_;
7389 }
scopeChain()7390 MDefinition* scopeChain() const {
7391 return getOperand(0);
7392 }
possiblyCalls()7393 bool possiblyCalls() const override {
7394 return true;
7395 }
7396 };
7397
7398 class MDefLexical
7399 : public MNullaryInstruction
7400 {
7401 CompilerPropertyName name_; // Target name to be defined.
7402 unsigned attrs_; // Attributes to be set.
7403
7404 private:
MDefLexical(PropertyName * name,unsigned attrs)7405 MDefLexical(PropertyName* name, unsigned attrs)
7406 : name_(name),
7407 attrs_(attrs)
7408 { }
7409
7410 public:
INSTRUCTION_HEADER(DefLexical)7411 INSTRUCTION_HEADER(DefLexical)
7412
7413 static MDefLexical* New(TempAllocator& alloc, PropertyName* name, unsigned attrs) {
7414 return new(alloc) MDefLexical(name, attrs);
7415 }
7416
name()7417 PropertyName* name() const {
7418 return name_;
7419 }
attrs()7420 unsigned attrs() const {
7421 return attrs_;
7422 }
7423 };
7424
7425 class MDefFun
7426 : public MUnaryInstruction,
7427 public NoTypePolicy::Data
7428 {
7429 CompilerFunction fun_;
7430
7431 private:
MDefFun(JSFunction * fun,MDefinition * scopeChain)7432 MDefFun(JSFunction* fun, MDefinition* scopeChain)
7433 : MUnaryInstruction(scopeChain),
7434 fun_(fun)
7435 {}
7436
7437 public:
INSTRUCTION_HEADER(DefFun)7438 INSTRUCTION_HEADER(DefFun)
7439
7440 static MDefFun* New(TempAllocator& alloc, JSFunction* fun, MDefinition* scopeChain) {
7441 return new(alloc) MDefFun(fun, scopeChain);
7442 }
7443
fun()7444 JSFunction* fun() const {
7445 return fun_;
7446 }
scopeChain()7447 MDefinition* scopeChain() const {
7448 return getOperand(0);
7449 }
possiblyCalls()7450 bool possiblyCalls() const override {
7451 return true;
7452 }
7453 };
7454
7455 class MRegExp : public MNullaryInstruction
7456 {
7457 CompilerGCPointer<RegExpObject*> source_;
7458 bool mustClone_;
7459
MRegExp(CompilerConstraintList * constraints,RegExpObject * source,bool mustClone)7460 MRegExp(CompilerConstraintList* constraints, RegExpObject* source, bool mustClone)
7461 : source_(source),
7462 mustClone_(mustClone)
7463 {
7464 setResultType(MIRType_Object);
7465 setResultTypeSet(MakeSingletonTypeSet(constraints, source));
7466 }
7467
7468 public:
INSTRUCTION_HEADER(RegExp)7469 INSTRUCTION_HEADER(RegExp)
7470
7471 static MRegExp* New(TempAllocator& alloc, CompilerConstraintList* constraints,
7472 RegExpObject* source, bool mustClone)
7473 {
7474 return new(alloc) MRegExp(constraints, source, mustClone);
7475 }
7476
mustClone()7477 bool mustClone() const {
7478 return mustClone_;
7479 }
source()7480 RegExpObject* source() const {
7481 return source_;
7482 }
getAliasSet()7483 AliasSet getAliasSet() const override {
7484 return AliasSet::None();
7485 }
possiblyCalls()7486 bool possiblyCalls() const override {
7487 return true;
7488 }
7489 };
7490
7491 class MRegExpExec
7492 : public MBinaryInstruction,
7493 public MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1> >::Data
7494 {
7495 private:
7496
MRegExpExec(MDefinition * regexp,MDefinition * string)7497 MRegExpExec(MDefinition* regexp, MDefinition* string)
7498 : MBinaryInstruction(string, regexp)
7499 {
7500 // May be object or null.
7501 setResultType(MIRType_Value);
7502 }
7503
7504 public:
INSTRUCTION_HEADER(RegExpExec)7505 INSTRUCTION_HEADER(RegExpExec)
7506
7507 static MRegExpExec* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string) {
7508 return new(alloc) MRegExpExec(regexp, string);
7509 }
7510
string()7511 MDefinition* string() const {
7512 return getOperand(0);
7513 }
7514
regexp()7515 MDefinition* regexp() const {
7516 return getOperand(1);
7517 }
7518
7519 bool writeRecoverData(CompactBufferWriter& writer) const override;
7520
canRecoverOnBailout()7521 bool canRecoverOnBailout() const override {
7522 // XXX: always return false for now, to work around bug 1132128.
7523 if (false && regexp()->isRegExp())
7524 return !regexp()->toRegExp()->source()->needUpdateLastIndex();
7525 return false;
7526 }
7527
possiblyCalls()7528 bool possiblyCalls() const override {
7529 return true;
7530 }
7531 };
7532
7533 class MRegExpTest
7534 : public MBinaryInstruction,
7535 public MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0> >::Data
7536 {
7537 private:
7538
MRegExpTest(MDefinition * regexp,MDefinition * string)7539 MRegExpTest(MDefinition* regexp, MDefinition* string)
7540 : MBinaryInstruction(string, regexp)
7541 {
7542 setResultType(MIRType_Boolean);
7543 }
7544
7545 public:
INSTRUCTION_HEADER(RegExpTest)7546 INSTRUCTION_HEADER(RegExpTest)
7547
7548 static MRegExpTest* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string) {
7549 return new(alloc) MRegExpTest(regexp, string);
7550 }
7551
string()7552 MDefinition* string() const {
7553 return getOperand(0);
7554 }
regexp()7555 MDefinition* regexp() const {
7556 return getOperand(1);
7557 }
7558
possiblyCalls()7559 bool possiblyCalls() const override {
7560 return true;
7561 }
7562
7563 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()7564 bool canRecoverOnBailout() const override {
7565 // RegExpTest has a side-effect on the regexp object's lastIndex
7566 // when sticky or global flags are set.
7567 // Return false unless we are sure it's not the case.
7568 // XXX: always return false for now, to work around bug 1132128.
7569 if (false && regexp()->isRegExp())
7570 return !regexp()->toRegExp()->source()->needUpdateLastIndex();
7571 return false;
7572 }
7573 };
7574
7575 template <class Policy1>
7576 class MStrReplace
7577 : public MTernaryInstruction,
7578 public Mix3Policy<StringPolicy<0>, Policy1, StringPolicy<2> >::Data
7579 {
7580 protected:
7581
MStrReplace(MDefinition * string,MDefinition * pattern,MDefinition * replacement)7582 MStrReplace(MDefinition* string, MDefinition* pattern, MDefinition* replacement)
7583 : MTernaryInstruction(string, pattern, replacement)
7584 {
7585 setMovable();
7586 setResultType(MIRType_String);
7587 }
7588
7589 public:
7590
string()7591 MDefinition* string() const {
7592 return getOperand(0);
7593 }
pattern()7594 MDefinition* pattern() const {
7595 return getOperand(1);
7596 }
replacement()7597 MDefinition* replacement() const {
7598 return getOperand(2);
7599 }
7600
possiblyCalls()7601 bool possiblyCalls() const override {
7602 return true;
7603 }
7604 };
7605
7606 class MRegExpReplace
7607 : public MStrReplace< ObjectPolicy<1> >
7608 {
7609 private:
7610
MRegExpReplace(MDefinition * string,MDefinition * pattern,MDefinition * replacement)7611 MRegExpReplace(MDefinition* string, MDefinition* pattern, MDefinition* replacement)
7612 : MStrReplace< ObjectPolicy<1> >(string, pattern, replacement)
7613 {
7614 }
7615
7616 public:
INSTRUCTION_HEADER(RegExpReplace)7617 INSTRUCTION_HEADER(RegExpReplace)
7618
7619 static MRegExpReplace* New(TempAllocator& alloc, MDefinition* string, MDefinition* pattern, MDefinition* replacement) {
7620 return new(alloc) MRegExpReplace(string, pattern, replacement);
7621 }
7622
7623 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()7624 bool canRecoverOnBailout() const override {
7625 // RegExpReplace will zero the lastIndex field when global flag is set.
7626 // So we can only remove this if it's non-global.
7627 // XXX: always return false for now, to work around bug 1132128.
7628 if (false && pattern()->isRegExp())
7629 return !pattern()->toRegExp()->source()->global();
7630 return false;
7631 }
7632 };
7633
7634 class MStringReplace
7635 : public MStrReplace< StringPolicy<1> >
7636 {
7637 private:
7638
MStringReplace(MDefinition * string,MDefinition * pattern,MDefinition * replacement)7639 MStringReplace(MDefinition* string, MDefinition* pattern, MDefinition* replacement)
7640 : MStrReplace< StringPolicy<1> >(string, pattern, replacement)
7641 {
7642 }
7643
7644 public:
INSTRUCTION_HEADER(StringReplace)7645 INSTRUCTION_HEADER(StringReplace)
7646
7647 static MStringReplace* New(TempAllocator& alloc, MDefinition* string, MDefinition* pattern, MDefinition* replacement) {
7648 return new(alloc) MStringReplace(string, pattern, replacement);
7649 }
7650
congruentTo(const MDefinition * ins)7651 bool congruentTo(const MDefinition* ins) const override {
7652 return congruentIfOperandsEqual(ins);
7653 }
7654
getAliasSet()7655 AliasSet getAliasSet() const override {
7656 return AliasSet::None();
7657 }
7658
7659 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()7660 bool canRecoverOnBailout() const override {
7661 if (pattern()->isRegExp())
7662 return !pattern()->toRegExp()->source()->global();
7663 return false;
7664 }
7665 };
7666
7667 class MSubstr
7668 : public MTernaryInstruction,
7669 public Mix3Policy<StringPolicy<0>, IntPolicy<1>, IntPolicy<2>>::Data
7670 {
7671 private:
7672
MSubstr(MDefinition * string,MDefinition * begin,MDefinition * length)7673 MSubstr(MDefinition* string, MDefinition* begin, MDefinition* length)
7674 : MTernaryInstruction(string, begin, length)
7675 {
7676 setResultType(MIRType_String);
7677 }
7678
7679 public:
INSTRUCTION_HEADER(Substr)7680 INSTRUCTION_HEADER(Substr)
7681
7682 static MSubstr* New(TempAllocator& alloc, MDefinition* string, MDefinition* begin,
7683 MDefinition* length)
7684 {
7685 return new(alloc) MSubstr(string, begin, length);
7686 }
7687
string()7688 MDefinition* string() {
7689 return getOperand(0);
7690 }
7691
begin()7692 MDefinition* begin() {
7693 return getOperand(1);
7694 }
7695
length()7696 MDefinition* length() {
7697 return getOperand(2);
7698 }
7699
congruentTo(const MDefinition * ins)7700 bool congruentTo(const MDefinition* ins) const override {
7701 return congruentIfOperandsEqual(ins);
7702 }
getAliasSet()7703 AliasSet getAliasSet() const override {
7704 return AliasSet::None();
7705 }
7706 };
7707
7708 struct LambdaFunctionInfo
7709 {
7710 // The functions used in lambdas are the canonical original function in
7711 // the script, and are immutable except for delazification. Record this
7712 // information while still on the main thread to avoid races.
7713 CompilerFunction fun;
7714 uint16_t flags;
7715 uint16_t nargs;
7716 gc::Cell* scriptOrLazyScript;
7717 bool singletonType;
7718 bool useSingletonForClone;
7719
LambdaFunctionInfoLambdaFunctionInfo7720 explicit LambdaFunctionInfo(JSFunction* fun)
7721 : fun(fun), flags(fun->flags()), nargs(fun->nargs()),
7722 scriptOrLazyScript(fun->hasScript()
7723 ? (gc::Cell*) fun->nonLazyScript()
7724 : (gc::Cell*) fun->lazyScript()),
7725 singletonType(fun->isSingleton()),
7726 useSingletonForClone(ObjectGroup::useSingletonForClone(fun))
7727 {}
7728
7729 private:
7730 LambdaFunctionInfo(const LambdaFunctionInfo&) = delete;
7731 void operator=(const LambdaFunctionInfo&) = delete;
7732 };
7733
7734 class MLambda
7735 : public MBinaryInstruction,
7736 public SingleObjectPolicy::Data
7737 {
7738 const LambdaFunctionInfo info_;
7739
MLambda(CompilerConstraintList * constraints,MDefinition * scopeChain,MConstant * cst)7740 MLambda(CompilerConstraintList* constraints, MDefinition* scopeChain, MConstant* cst)
7741 : MBinaryInstruction(scopeChain, cst), info_(&cst->value().toObject().as<JSFunction>())
7742 {
7743 setResultType(MIRType_Object);
7744 if (!info().fun->isSingleton() && !ObjectGroup::useSingletonForClone(info().fun))
7745 setResultTypeSet(MakeSingletonTypeSet(constraints, info().fun));
7746 }
7747
7748 public:
INSTRUCTION_HEADER(Lambda)7749 INSTRUCTION_HEADER(Lambda)
7750
7751 static MLambda* New(TempAllocator& alloc, CompilerConstraintList* constraints,
7752 MDefinition* scopeChain, MConstant* fun)
7753 {
7754 return new(alloc) MLambda(constraints, scopeChain, fun);
7755 }
scopeChain()7756 MDefinition* scopeChain() const {
7757 return getOperand(0);
7758 }
functionOperand()7759 MConstant* functionOperand() const {
7760 return getOperand(1)->toConstant();
7761 }
info()7762 const LambdaFunctionInfo& info() const {
7763 return info_;
7764 }
7765 bool writeRecoverData(CompactBufferWriter& writer) const override;
canRecoverOnBailout()7766 bool canRecoverOnBailout() const override {
7767 return true;
7768 }
7769 };
7770
7771 class MLambdaArrow
7772 : public MBinaryInstruction,
7773 public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data
7774 {
7775 const LambdaFunctionInfo info_;
7776
MLambdaArrow(CompilerConstraintList * constraints,MDefinition * scopeChain,MDefinition * newTarget_,JSFunction * fun)7777 MLambdaArrow(CompilerConstraintList* constraints, MDefinition* scopeChain,
7778 MDefinition* newTarget_, JSFunction* fun)
7779 : MBinaryInstruction(scopeChain, newTarget_), info_(fun)
7780 {
7781 setResultType(MIRType_Object);
7782 MOZ_ASSERT(!ObjectGroup::useSingletonForClone(fun));
7783 if (!fun->isSingleton())
7784 setResultTypeSet(MakeSingletonTypeSet(constraints, fun));
7785 }
7786
7787 public:
INSTRUCTION_HEADER(LambdaArrow)7788 INSTRUCTION_HEADER(LambdaArrow)
7789
7790 static MLambdaArrow* New(TempAllocator& alloc, CompilerConstraintList* constraints,
7791 MDefinition* scopeChain, MDefinition* newTarget_, JSFunction* fun)
7792 {
7793 return new(alloc) MLambdaArrow(constraints, scopeChain, newTarget_, fun);
7794 }
scopeChain()7795 MDefinition* scopeChain() const {
7796 return getOperand(0);
7797 }
newTargetDef()7798 MDefinition* newTargetDef() const {
7799 return getOperand(1);
7800 }
info()7801 const LambdaFunctionInfo& info() const {
7802 return info_;
7803 }
7804 };
7805
7806 // Returns obj->slots.
7807 class MSlots
7808 : public MUnaryInstruction,
7809 public SingleObjectPolicy::Data
7810 {
MSlots(MDefinition * object)7811 explicit MSlots(MDefinition* object)
7812 : MUnaryInstruction(object)
7813 {
7814 setResultType(MIRType_Slots);
7815 setMovable();
7816 }
7817
7818 public:
INSTRUCTION_HEADER(Slots)7819 INSTRUCTION_HEADER(Slots)
7820
7821 static MSlots* New(TempAllocator& alloc, MDefinition* object) {
7822 return new(alloc) MSlots(object);
7823 }
7824
object()7825 MDefinition* object() const {
7826 return getOperand(0);
7827 }
congruentTo(const MDefinition * ins)7828 bool congruentTo(const MDefinition* ins) const override {
7829 return congruentIfOperandsEqual(ins);
7830 }
getAliasSet()7831 AliasSet getAliasSet() const override {
7832 return AliasSet::Load(AliasSet::ObjectFields);
7833 }
7834
7835 ALLOW_CLONE(MSlots)
7836 };
7837
7838 // Returns obj->elements.
7839 class MElements
7840 : public MUnaryInstruction,
7841 public SingleObjectPolicy::Data
7842 {
7843 bool unboxed_;
7844
MElements(MDefinition * object,bool unboxed)7845 explicit MElements(MDefinition* object, bool unboxed)
7846 : MUnaryInstruction(object), unboxed_(unboxed)
7847 {
7848 setResultType(MIRType_Elements);
7849 setMovable();
7850 }
7851
7852 public:
INSTRUCTION_HEADER(Elements)7853 INSTRUCTION_HEADER(Elements)
7854
7855 static MElements* New(TempAllocator& alloc, MDefinition* object, bool unboxed = false) {
7856 return new(alloc) MElements(object, unboxed);
7857 }
7858
object()7859 MDefinition* object() const {
7860 return getOperand(0);
7861 }
unboxed()7862 bool unboxed() const {
7863 return unboxed_;
7864 }
congruentTo(const MDefinition * ins)7865 bool congruentTo(const MDefinition* ins) const override {
7866 return congruentIfOperandsEqual(ins) &&
7867 ins->toElements()->unboxed() == unboxed();
7868 }
getAliasSet()7869 AliasSet getAliasSet() const override {
7870 return AliasSet::Load(AliasSet::ObjectFields);
7871 }
7872 bool mightAlias(const MDefinition* store) const override;
7873
7874 ALLOW_CLONE(MElements)
7875 };
7876
7877 // A constant value for some object's typed array elements.
7878 class MConstantElements : public MNullaryInstruction
7879 {
7880 SharedMem<void*> value_;
7881
7882 protected:
MConstantElements(SharedMem<void * > v)7883 explicit MConstantElements(SharedMem<void*> v)
7884 : value_(v)
7885 {
7886 setResultType(MIRType_Elements);
7887 setMovable();
7888 }
7889
7890 public:
INSTRUCTION_HEADER(ConstantElements)7891 INSTRUCTION_HEADER(ConstantElements)
7892 static MConstantElements* New(TempAllocator& alloc, SharedMem<void*> v) {
7893 return new(alloc) MConstantElements(v);
7894 }
7895
value()7896 SharedMem<void*> value() const {
7897 return value_;
7898 }
7899
7900 void printOpcode(GenericPrinter& out) const override;
7901
valueHash()7902 HashNumber valueHash() const override {
7903 return (HashNumber)(size_t) value_.asValue();
7904 }
7905
congruentTo(const MDefinition * ins)7906 bool congruentTo(const MDefinition* ins) const override {
7907 return ins->isConstantElements() && ins->toConstantElements()->value() == value();
7908 }
7909
getAliasSet()7910 AliasSet getAliasSet() const override {
7911 return AliasSet::None();
7912 }
7913
7914 ALLOW_CLONE(MConstantElements)
7915 };
7916
7917 // Passes through an object's elements, after ensuring it is entirely doubles.
7918 class MConvertElementsToDoubles
7919 : public MUnaryInstruction,
7920 public NoTypePolicy::Data
7921 {
MConvertElementsToDoubles(MDefinition * elements)7922 explicit MConvertElementsToDoubles(MDefinition* elements)
7923 : MUnaryInstruction(elements)
7924 {
7925 setGuard();
7926 setMovable();
7927 setResultType(MIRType_Elements);
7928 }
7929
7930 public:
INSTRUCTION_HEADER(ConvertElementsToDoubles)7931 INSTRUCTION_HEADER(ConvertElementsToDoubles)
7932
7933 static MConvertElementsToDoubles* New(TempAllocator& alloc, MDefinition* elements) {
7934 return new(alloc) MConvertElementsToDoubles(elements);
7935 }
7936
elements()7937 MDefinition* elements() const {
7938 return getOperand(0);
7939 }
congruentTo(const MDefinition * ins)7940 bool congruentTo(const MDefinition* ins) const override {
7941 return congruentIfOperandsEqual(ins);
7942 }
getAliasSet()7943 AliasSet getAliasSet() const override {
7944 // This instruction can read and write to the elements' contents.
7945 // However, it is alright to hoist this from loops which explicitly
7946 // read or write to the elements: such reads and writes will use double
7947 // values and can be reordered freely wrt this conversion, except that
7948 // definite double loads must follow the conversion. The latter
7949 // property is ensured by chaining this instruction with the elements
7950 // themselves, in the same manner as MBoundsCheck.
7951 return AliasSet::None();
7952 }
7953 };
7954
7955 // If |elements| has the CONVERT_DOUBLE_ELEMENTS flag, convert value to
7956 // double. Else return the original value.
7957 class MMaybeToDoubleElement
7958 : public MBinaryInstruction,
7959 public IntPolicy<1>::Data
7960 {
MMaybeToDoubleElement(MDefinition * elements,MDefinition * value)7961 MMaybeToDoubleElement(MDefinition* elements, MDefinition* value)
7962 : MBinaryInstruction(elements, value)
7963 {
7964 MOZ_ASSERT(elements->type() == MIRType_Elements);
7965 setMovable();
7966 setResultType(MIRType_Value);
7967 }
7968
7969 public:
INSTRUCTION_HEADER(MaybeToDoubleElement)7970 INSTRUCTION_HEADER(MaybeToDoubleElement)
7971
7972 static MMaybeToDoubleElement* New(TempAllocator& alloc, MDefinition* elements,
7973 MDefinition* value)
7974 {
7975 return new(alloc) MMaybeToDoubleElement(elements, value);
7976 }
7977
elements()7978 MDefinition* elements() const {
7979 return getOperand(0);
7980 }
value()7981 MDefinition* value() const {
7982 return getOperand(1);
7983 }
congruentTo(const MDefinition * ins)7984 bool congruentTo(const MDefinition* ins) const override {
7985 return congruentIfOperandsEqual(ins);
7986 }
getAliasSet()7987