1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "mozilla/EndianUtils.h"
10 #include "mozilla/FloatingPoint.h"
11 #include "mozilla/MathAlgorithms.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/ScopeExit.h"
18 #include "jslibmath.h"
22 #include "builtin/RegExp.h"
23 #include "jit/AtomicOperations.h"
24 #include "jit/CompileInfo.h"
25 #include "jit/KnownClass.h"
26 #include "jit/MIR-wasm.h"
27 #include "jit/MIRGraph.h"
28 #include "jit/RangeAnalysis.h"
29 #include "jit/VMFunctions.h"
30 #include "jit/WarpBuilderShared.h"
31 #include "js/Conversions.h"
32 #include "js/experimental/JitInfo.h" // JSJitInfo, JSTypedMethodJitInfo
33 #include "js/ScalarType.h" // js::Scalar::Type
34 #include "util/Text.h"
35 #include "util/Unicode.h"
36 #include "vm/BigIntType.h"
37 #include "vm/Float16.h"
38 #include "vm/Iteration.h" // js::NativeIterator
39 #include "vm/PlainObject.h" // js::PlainObject
40 #include "vm/Uint8Clamped.h"
42 #include "vm/BytecodeUtil-inl.h"
43 #include "vm/JSAtomUtils-inl.h" // TypeName
46 using namespace js::jit
;
50 using mozilla::IsFloat32Representable
;
51 using mozilla::IsPowerOfTwo
;
52 using mozilla::NumbersAreIdentical
;
54 NON_GC_POINTER_TYPE_ASSERTIONS_GENERATED
57 size_t MUse::index() const { return consumer()->indexOf(this); }
61 static void ConvertDefinitionToDouble(TempAllocator
& alloc
, MDefinition
* def
,
62 MInstruction
* consumer
) {
63 MInstruction
* replace
= MToDouble::New(alloc
, def
);
64 consumer
->replaceOperand(Op
, replace
);
65 consumer
->block()->insertBefore(consumer
, replace
);
68 template <size_t Arity
, size_t Index
>
69 static void ConvertOperandToDouble(MAryInstruction
<Arity
>* def
,
70 TempAllocator
& alloc
) {
71 static_assert(Index
< Arity
);
72 auto* operand
= def
->getOperand(Index
);
73 if (operand
->type() == MIRType::Float32
) {
74 ConvertDefinitionToDouble
<Index
>(alloc
, operand
, def
);
78 template <size_t Arity
, size_t... ISeq
>
79 static void ConvertOperandsToDouble(MAryInstruction
<Arity
>* def
,
81 std::index_sequence
<ISeq
...>) {
82 (ConvertOperandToDouble
<Arity
, ISeq
>(def
, alloc
), ...);
85 template <size_t Arity
>
86 static void ConvertOperandsToDouble(MAryInstruction
<Arity
>* def
,
87 TempAllocator
& alloc
) {
88 ConvertOperandsToDouble
<Arity
>(def
, alloc
, std::make_index_sequence
<Arity
>{});
91 template <size_t Arity
, size_t... ISeq
>
92 static bool AllOperandsCanProduceFloat32(MAryInstruction
<Arity
>* def
,
93 std::index_sequence
<ISeq
...>) {
94 return (def
->getOperand(ISeq
)->canProduceFloat32() && ...);
97 template <size_t Arity
>
98 static bool AllOperandsCanProduceFloat32(MAryInstruction
<Arity
>* def
) {
99 return AllOperandsCanProduceFloat32
<Arity
>(def
,
100 std::make_index_sequence
<Arity
>{});
103 static bool CheckUsesAreFloat32Consumers(const MInstruction
* ins
) {
104 if (ins
->isImplicitlyUsed()) {
107 bool allConsumerUses
= true;
108 for (MUseDefIterator
use(ins
); allConsumerUses
&& use
; use
++) {
109 allConsumerUses
&= use
.def()->canConsumeFloat32(use
.use());
111 return allConsumerUses
;
115 static const char* OpcodeName(MDefinition::Opcode op
) {
116 static const char* const names
[] = {
118 MIR_OPCODE_LIST(NAME
)
121 return names
[unsigned(op
)];
124 void MDefinition::PrintOpcodeName(GenericPrinter
& out
, Opcode op
) {
125 const char* name
= OpcodeName(op
);
126 size_t len
= strlen(name
);
127 for (size_t i
= 0; i
< len
; i
++) {
128 out
.printf("%c", unicode::ToLowerCase(name
[i
]));
132 uint32_t js::jit::GetMBasicBlockId(const MBasicBlock
* block
) {
137 static MConstant
* EvaluateInt64ConstantOperands(TempAllocator
& alloc
,
138 MBinaryInstruction
* ins
) {
139 MDefinition
* left
= ins
->getOperand(0);
140 MDefinition
* right
= ins
->getOperand(1);
142 if (!left
->isConstant() || !right
->isConstant()) {
146 MOZ_ASSERT(left
->type() == MIRType::Int64
);
147 MOZ_ASSERT(right
->type() == MIRType::Int64
);
149 int64_t lhs
= left
->toConstant()->toInt64();
150 int64_t rhs
= right
->toConstant()->toInt64();
154 case MDefinition::Opcode::BitAnd
:
157 case MDefinition::Opcode::BitOr
:
160 case MDefinition::Opcode::BitXor
:
163 case MDefinition::Opcode::Lsh
:
164 ret
= lhs
<< (rhs
& 0x3F);
166 case MDefinition::Opcode::Rsh
:
167 ret
= lhs
>> (rhs
& 0x3F);
169 case MDefinition::Opcode::Ursh
:
170 ret
= uint64_t(lhs
) >> (uint64_t(rhs
) & 0x3F);
172 case MDefinition::Opcode::Add
:
175 case MDefinition::Opcode::Sub
:
178 case MDefinition::Opcode::Mul
:
181 case MDefinition::Opcode::Div
:
183 // Division by zero will trap at runtime.
186 if (ins
->toDiv()->isUnsigned()) {
187 ret
= int64_t(uint64_t(lhs
) / uint64_t(rhs
));
188 } else if (lhs
== INT64_MIN
|| rhs
== -1) {
189 // Overflow will trap at runtime.
195 case MDefinition::Opcode::Mod
:
197 // Division by zero will trap at runtime.
200 if (!ins
->toMod()->isUnsigned() && (lhs
< 0 || rhs
< 0)) {
201 // Handle all negative values at runtime, for simplicity.
204 ret
= int64_t(uint64_t(lhs
) % uint64_t(rhs
));
210 return MConstant::NewInt64(alloc
, ret
);
213 static MConstant
* EvaluateConstantOperands(TempAllocator
& alloc
,
214 MBinaryInstruction
* ins
,
215 bool* ptypeChange
= nullptr) {
216 MDefinition
* left
= ins
->getOperand(0);
217 MDefinition
* right
= ins
->getOperand(1);
219 MOZ_ASSERT(IsTypeRepresentableAsDouble(left
->type()));
220 MOZ_ASSERT(IsTypeRepresentableAsDouble(right
->type()));
222 if (!left
->isConstant() || !right
->isConstant()) {
226 MConstant
* lhs
= left
->toConstant();
227 MConstant
* rhs
= right
->toConstant();
228 double ret
= JS::GenericNaN();
231 case MDefinition::Opcode::BitAnd
:
232 ret
= double(lhs
->toInt32() & rhs
->toInt32());
234 case MDefinition::Opcode::BitOr
:
235 ret
= double(lhs
->toInt32() | rhs
->toInt32());
237 case MDefinition::Opcode::BitXor
:
238 ret
= double(lhs
->toInt32() ^ rhs
->toInt32());
240 case MDefinition::Opcode::Lsh
:
241 ret
= double(uint32_t(lhs
->toInt32()) << (rhs
->toInt32() & 0x1F));
243 case MDefinition::Opcode::Rsh
:
244 ret
= double(lhs
->toInt32() >> (rhs
->toInt32() & 0x1F));
246 case MDefinition::Opcode::Ursh
:
247 ret
= double(uint32_t(lhs
->toInt32()) >> (rhs
->toInt32() & 0x1F));
249 case MDefinition::Opcode::Add
:
250 ret
= lhs
->numberToDouble() + rhs
->numberToDouble();
252 case MDefinition::Opcode::Sub
:
253 ret
= lhs
->numberToDouble() - rhs
->numberToDouble();
255 case MDefinition::Opcode::Mul
:
256 ret
= lhs
->numberToDouble() * rhs
->numberToDouble();
258 case MDefinition::Opcode::Div
:
259 if (ins
->toDiv()->isUnsigned()) {
260 if (rhs
->isInt32(0)) {
261 if (ins
->toDiv()->trapOnError()) {
266 ret
= double(uint32_t(lhs
->toInt32()) / uint32_t(rhs
->toInt32()));
269 ret
= NumberDiv(lhs
->numberToDouble(), rhs
->numberToDouble());
272 case MDefinition::Opcode::Mod
:
273 if (ins
->toMod()->isUnsigned()) {
274 if (rhs
->isInt32(0)) {
275 if (ins
->toMod()->trapOnError()) {
280 ret
= double(uint32_t(lhs
->toInt32()) % uint32_t(rhs
->toInt32()));
283 ret
= NumberMod(lhs
->numberToDouble(), rhs
->numberToDouble());
290 if (ins
->type() == MIRType::Float32
) {
291 return MConstant::NewFloat32(alloc
, float(ret
));
293 if (ins
->type() == MIRType::Double
) {
294 return MConstant::New(alloc
, DoubleValue(ret
));
298 retVal
.setNumber(JS::CanonicalizeNaN(ret
));
300 // If this was an int32 operation but the result isn't an int32 (for
301 // example, a division where the numerator isn't evenly divisible by the
302 // denominator), decline folding.
303 MOZ_ASSERT(ins
->type() == MIRType::Int32
);
304 if (!retVal
.isInt32()) {
311 return MConstant::New(alloc
, retVal
);
314 static MConstant
* EvaluateConstantNaNOperand(MBinaryInstruction
* ins
) {
315 auto* left
= ins
->lhs();
316 auto* right
= ins
->rhs();
318 MOZ_ASSERT(IsTypeRepresentableAsDouble(left
->type()));
319 MOZ_ASSERT(IsTypeRepresentableAsDouble(right
->type()));
320 MOZ_ASSERT(left
->type() == ins
->type());
321 MOZ_ASSERT(right
->type() == ins
->type());
323 // Don't fold NaN if we can't return a floating point type.
324 if (!IsFloatingPointType(ins
->type())) {
328 MOZ_ASSERT(!left
->isConstant() || !right
->isConstant(),
329 "EvaluateConstantOperands should have handled this case");
331 // One operand must be a constant NaN.
333 if (left
->isConstant()) {
334 cst
= left
->toConstant();
335 } else if (right
->isConstant()) {
336 cst
= right
->toConstant();
340 if (!std::isnan(cst
->numberToDouble())) {
344 // Fold to constant NaN.
348 static MMul
* EvaluateExactReciprocal(TempAllocator
& alloc
, MDiv
* ins
) {
349 // we should fold only when it is a floating point operation
350 if (!IsFloatingPointType(ins
->type())) {
354 MDefinition
* left
= ins
->getOperand(0);
355 MDefinition
* right
= ins
->getOperand(1);
357 if (!right
->isConstant()) {
362 if (!mozilla::NumberIsInt32(right
->toConstant()->numberToDouble(), &num
)) {
366 // check if rhs is a power of two
367 if (mozilla::Abs(num
) & (mozilla::Abs(num
) - 1)) {
372 ret
.setDouble(1.0 / double(num
));
374 MConstant
* foldedRhs
;
375 if (ins
->type() == MIRType::Float32
) {
376 foldedRhs
= MConstant::NewFloat32(alloc
, ret
.toDouble());
378 foldedRhs
= MConstant::New(alloc
, ret
);
381 MOZ_ASSERT(foldedRhs
->type() == ins
->type());
382 ins
->block()->insertBefore(ins
, foldedRhs
);
384 MMul
* mul
= MMul::New(alloc
, left
, foldedRhs
, ins
->type());
385 mul
->setMustPreserveNaN(ins
->mustPreserveNaN());
390 const char* MDefinition::opName() const { return OpcodeName(op()); }
392 void MDefinition::printName(GenericPrinter
& out
) const {
393 PrintOpcodeName(out
, op());
394 out
.printf("%u", id());
398 HashNumber
MDefinition::valueHash() const {
399 HashNumber out
= HashNumber(op());
400 for (size_t i
= 0, e
= numOperands(); i
< e
; i
++) {
401 out
= addU32ToHash(out
, getOperand(i
)->id());
403 if (MDefinition
* dep
= dependency()) {
404 out
= addU32ToHash(out
, dep
->id());
409 HashNumber
MNullaryInstruction::valueHash() const {
410 HashNumber hash
= HashNumber(op());
411 if (MDefinition
* dep
= dependency()) {
412 hash
= addU32ToHash(hash
, dep
->id());
414 MOZ_ASSERT(hash
== MDefinition::valueHash());
418 HashNumber
MUnaryInstruction::valueHash() const {
419 HashNumber hash
= HashNumber(op());
420 hash
= addU32ToHash(hash
, getOperand(0)->id());
421 if (MDefinition
* dep
= dependency()) {
422 hash
= addU32ToHash(hash
, dep
->id());
424 MOZ_ASSERT(hash
== MDefinition::valueHash());
428 HashNumber
MBinaryInstruction::valueHash() const {
429 HashNumber hash
= HashNumber(op());
430 hash
= addU32ToHash(hash
, getOperand(0)->id());
431 hash
= addU32ToHash(hash
, getOperand(1)->id());
432 if (MDefinition
* dep
= dependency()) {
433 hash
= addU32ToHash(hash
, dep
->id());
435 MOZ_ASSERT(hash
== MDefinition::valueHash());
439 HashNumber
MTernaryInstruction::valueHash() const {
440 HashNumber hash
= HashNumber(op());
441 hash
= addU32ToHash(hash
, getOperand(0)->id());
442 hash
= addU32ToHash(hash
, getOperand(1)->id());
443 hash
= addU32ToHash(hash
, getOperand(2)->id());
444 if (MDefinition
* dep
= dependency()) {
445 hash
= addU32ToHash(hash
, dep
->id());
447 MOZ_ASSERT(hash
== MDefinition::valueHash());
451 HashNumber
MQuaternaryInstruction::valueHash() const {
452 HashNumber hash
= HashNumber(op());
453 hash
= addU32ToHash(hash
, getOperand(0)->id());
454 hash
= addU32ToHash(hash
, getOperand(1)->id());
455 hash
= addU32ToHash(hash
, getOperand(2)->id());
456 hash
= addU32ToHash(hash
, getOperand(3)->id());
457 if (MDefinition
* dep
= dependency()) {
458 hash
= addU32ToHash(hash
, dep
->id());
460 MOZ_ASSERT(hash
== MDefinition::valueHash());
464 const MDefinition
* MDefinition::skipObjectGuards() const {
465 const MDefinition
* result
= this;
466 // These instructions don't modify the object and just guard specific
469 if (result
->isGuardShape()) {
470 result
= result
->toGuardShape()->object();
473 if (result
->isGuardNullProto()) {
474 result
= result
->toGuardNullProto()->object();
477 if (result
->isGuardProto()) {
478 result
= result
->toGuardProto()->object();
488 bool MDefinition::congruentIfOperandsEqual(const MDefinition
* ins
) const {
489 if (op() != ins
->op()) {
493 if (type() != ins
->type()) {
497 if (isEffectful() || ins
->isEffectful()) {
501 if (numOperands() != ins
->numOperands()) {
505 for (size_t i
= 0, e
= numOperands(); i
< e
; i
++) {
506 if (getOperand(i
) != ins
->getOperand(i
)) {
514 MDefinition
* MDefinition::foldsTo(TempAllocator
& alloc
) {
515 // In the default case, there are no constants to fold.
519 bool MDefinition::mightBeMagicType() const {
520 if (IsMagicType(type())) {
524 if (MIRType::Value
!= type()) {
531 bool MDefinition::definitelyType(std::initializer_list
<MIRType
> types
) const {
533 // Only support specialized, non-magic types.
534 auto isSpecializedNonMagic
= [](MIRType type
) {
535 return type
<= MIRType::Object
;
539 MOZ_ASSERT(types
.size() > 0);
540 MOZ_ASSERT(std::all_of(types
.begin(), types
.end(), isSpecializedNonMagic
));
542 if (type() == MIRType::Value
) {
546 return std::find(types
.begin(), types
.end(), type()) != types
.end();
549 MDefinition
* MInstruction::foldsToStore(TempAllocator
& alloc
) {
554 MDefinition
* store
= dependency();
555 if (mightAlias(store
) != AliasType::MustAlias
) {
559 if (!store
->block()->dominates(block())) {
564 switch (store
->op()) {
565 case Opcode::StoreFixedSlot
:
566 value
= store
->toStoreFixedSlot()->value();
568 case Opcode::StoreDynamicSlot
:
569 value
= store
->toStoreDynamicSlot()->value();
571 case Opcode::StoreElement
:
572 value
= store
->toStoreElement()->value();
575 MOZ_CRASH("unknown store");
578 // If the type are matching then we return the value which is used as
579 // argument of the store.
580 if (value
->type() != type()) {
581 // If we expect to read a type which is more generic than the type seen
582 // by the store, then we box the value used by the store.
583 if (type() != MIRType::Value
) {
587 MOZ_ASSERT(value
->type() < MIRType::Value
);
588 MBox
* box
= MBox::New(alloc
, value
);
595 void MDefinition::analyzeEdgeCasesForward() {}
597 void MDefinition::analyzeEdgeCasesBackward() {}
599 void MInstruction::setResumePoint(MResumePoint
* resumePoint
) {
600 MOZ_ASSERT(!resumePoint_
);
601 resumePoint_
= resumePoint
;
602 resumePoint_
->setInstruction(this);
605 void MInstruction::stealResumePoint(MInstruction
* other
) {
606 MResumePoint
* resumePoint
= other
->resumePoint_
;
607 other
->resumePoint_
= nullptr;
609 resumePoint
->resetInstruction();
610 setResumePoint(resumePoint
);
613 void MInstruction::moveResumePointAsEntry() {
615 block()->clearEntryResumePoint();
616 block()->setEntryResumePoint(resumePoint_
);
617 resumePoint_
->resetInstruction();
618 resumePoint_
= nullptr;
621 void MInstruction::clearResumePoint() {
622 resumePoint_
->resetInstruction();
623 block()->discardPreAllocatedResumePoint(resumePoint_
);
624 resumePoint_
= nullptr;
627 MDefinition
* MTest::foldsDoubleNegation(TempAllocator
& alloc
) {
628 MDefinition
* op
= getOperand(0);
631 // If the operand of the Not is itself a Not, they cancel out.
632 MDefinition
* opop
= op
->getOperand(0);
634 return MTest::New(alloc
, opop
->toNot()->input(), ifTrue(), ifFalse());
636 return MTest::New(alloc
, op
->toNot()->input(), ifFalse(), ifTrue());
641 MDefinition
* MTest::foldsConstant(TempAllocator
& alloc
) {
642 MDefinition
* op
= getOperand(0);
643 if (MConstant
* opConst
= op
->maybeConstantValue()) {
645 if (opConst
->valueToBoolean(&b
)) {
646 return MGoto::New(alloc
, b
? ifTrue() : ifFalse());
652 MDefinition
* MTest::foldsTypes(TempAllocator
& alloc
) {
653 MDefinition
* op
= getOperand(0);
655 switch (op
->type()) {
656 case MIRType::Undefined
:
658 return MGoto::New(alloc
, ifFalse());
659 case MIRType::Symbol
:
660 return MGoto::New(alloc
, ifTrue());
671 explicit UsesIterator(MDefinition
* def
) : def_(def
) {}
672 auto begin() const { return def_
->usesBegin(); }
673 auto end() const { return def_
->usesEnd(); }
676 static bool AllInstructionsDeadIfUnused(MBasicBlock
* block
) {
677 for (auto* ins
: *block
) {
678 // Skip trivial instructions.
679 if (ins
->isNop() || ins
->isGoto()) {
683 // All uses must be within the current block.
684 for (auto* use
: UsesIterator(ins
)) {
685 if (use
->consumer()->block() != block
) {
690 // All instructions within this block must be dead if unused.
691 if (!DeadIfUnused(ins
)) {
698 MDefinition
* MTest::foldsNeedlessControlFlow(TempAllocator
& alloc
) {
699 // All instructions within both successors need be dead if unused.
700 if (!AllInstructionsDeadIfUnused(ifTrue()) ||
701 !AllInstructionsDeadIfUnused(ifFalse())) {
705 // Both successors must have the same target successor.
706 if (ifTrue()->numSuccessors() != 1 || ifFalse()->numSuccessors() != 1) {
709 if (ifTrue()->getSuccessor(0) != ifFalse()->getSuccessor(0)) {
713 // The target successor's phis must be redundant. Redundant phis should have
714 // been removed in an earlier pass, so only check if any phis are present,
715 // which is a stronger condition.
716 if (ifTrue()->successorWithPhis()) {
720 return MGoto::New(alloc
, ifTrue());
723 // If a test is dominated by either the true or false path of a previous test of
724 // the same condition, then the test is redundant and can be converted into a
725 // goto true or goto false, respectively.
726 MDefinition
* MTest::foldsRedundantTest(TempAllocator
& alloc
) {
727 MBasicBlock
* myBlock
= this->block();
728 MDefinition
* originalInput
= getOperand(0);
730 // Handle single and double negatives. This ensures that we do not miss a
731 // folding opportunity due to a condition being inverted.
732 MDefinition
* newInput
= input();
733 bool inverted
= false;
734 if (originalInput
->isNot()) {
735 newInput
= originalInput
->toNot()->input();
737 if (originalInput
->toNot()->input()->isNot()) {
738 newInput
= originalInput
->toNot()->input()->toNot()->input();
743 // The specific order of traversal does not matter. If there are multiple
744 // dominating redundant tests, they will either agree on direction (in which
745 // case we will prune the same way regardless of order), or they will
746 // disagree, in which case we will eventually be marked entirely dead by the
747 // folding of the redundant parent.
748 for (MUseIterator
i(newInput
->usesBegin()), e(newInput
->usesEnd()); i
!= e
;
750 if (!i
->consumer()->isDefinition()) {
753 if (!i
->consumer()->toDefinition()->isTest()) {
756 MTest
* otherTest
= i
->consumer()->toDefinition()->toTest();
757 if (otherTest
== this) {
761 if (otherTest
->ifFalse()->dominates(myBlock
)) {
762 // This test cannot be true, so fold to a goto false.
763 return MGoto::New(alloc
, inverted
? ifTrue() : ifFalse());
765 if (otherTest
->ifTrue()->dominates(myBlock
)) {
766 // This test cannot be false, so fold to a goto true.
767 return MGoto::New(alloc
, inverted
? ifFalse() : ifTrue());
774 MDefinition
* MTest::foldsTo(TempAllocator
& alloc
) {
775 if (MDefinition
* def
= foldsRedundantTest(alloc
)) {
779 if (MDefinition
* def
= foldsDoubleNegation(alloc
)) {
783 if (MDefinition
* def
= foldsConstant(alloc
)) {
787 if (MDefinition
* def
= foldsTypes(alloc
)) {
791 if (MDefinition
* def
= foldsNeedlessControlFlow(alloc
)) {
798 AliasSet
MThrow::getAliasSet() const {
799 return AliasSet::Store(AliasSet::ExceptionState
);
802 AliasSet
MThrowWithStack::getAliasSet() const {
803 return AliasSet::Store(AliasSet::ExceptionState
);
806 AliasSet
MNewArrayDynamicLength::getAliasSet() const {
807 return AliasSet::Store(AliasSet::ExceptionState
);
810 AliasSet
MNewTypedArrayDynamicLength::getAliasSet() const {
811 return AliasSet::Store(AliasSet::ExceptionState
);
815 void MDefinition::printOpcode(GenericPrinter
& out
) const {
816 PrintOpcodeName(out
, op());
817 for (size_t j
= 0, e
= numOperands(); j
< e
; j
++) {
819 if (getUseFor(j
)->hasProducer()) {
820 getOperand(j
)->printName(out
);
821 out
.printf(":%s", StringFromMIRType(getOperand(j
)->type()));
823 out
.printf("(null)");
828 void MDefinition::dump(GenericPrinter
& out
) const {
830 out
.printf(":%s", StringFromMIRType(type()));
835 if (isInstruction()) {
836 if (MResumePoint
* resume
= toInstruction()->resumePoint()) {
842 void MDefinition::dump() const {
843 Fprinter
out(stderr
);
848 void MDefinition::dumpLocation(GenericPrinter
& out
) const {
849 MResumePoint
* rp
= nullptr;
850 const char* linkWord
= nullptr;
851 if (isInstruction() && toInstruction()->resumePoint()) {
852 rp
= toInstruction()->resumePoint();
855 rp
= block()->entryResumePoint();
860 JSScript
* script
= rp
->block()->info().script();
861 uint32_t lineno
= PCToLineNumber(rp
->block()->info().script(), rp
->pc());
862 out
.printf(" %s %s:%u\n", linkWord
, script
->filename(), lineno
);
868 void MDefinition::dumpLocation() const {
869 Fprinter
out(stderr
);
875 #if defined(DEBUG) || defined(JS_JITSPEW)
876 size_t MDefinition::useCount() const {
878 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
884 size_t MDefinition::defUseCount() const {
886 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
887 if ((*i
)->consumer()->isDefinition()) {
895 bool MDefinition::hasOneUse() const {
896 MUseIterator
i(uses_
.begin());
897 if (i
== uses_
.end()) {
901 return i
== uses_
.end();
904 bool MDefinition::hasOneDefUse() const {
905 bool hasOneDefUse
= false;
906 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
907 if (!(*i
)->consumer()->isDefinition()) {
911 // We already have a definition use. So 1+
916 // We saw one definition. Loop to test if there is another.
923 bool MDefinition::hasOneLiveDefUse() const {
924 bool hasOneDefUse
= false;
925 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
926 if (!(*i
)->consumer()->isDefinition()) {
930 MDefinition
* def
= (*i
)->consumer()->toDefinition();
931 if (def
->isRecoveredOnBailout()) {
935 // We already have a definition use. So 1+
940 // We saw one definition. Loop to test if there is another.
947 bool MDefinition::hasDefUses() const {
948 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
949 if ((*i
)->consumer()->isDefinition()) {
957 bool MDefinition::hasLiveDefUses() const {
958 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
959 MNode
* ins
= (*i
)->consumer();
960 if (ins
->isDefinition()) {
961 if (!ins
->toDefinition()->isRecoveredOnBailout()) {
965 MOZ_ASSERT(ins
->isResumePoint());
966 if (!ins
->toResumePoint()->isRecoverableOperand(*i
)) {
975 MDefinition
* MDefinition::maybeSingleDefUse() const {
976 MUseDefIterator
use(this);
982 MDefinition
* useDef
= use
.def();
986 // More than one def-use.
993 MDefinition
* MDefinition::maybeMostRecentlyAddedDefUse() const {
994 MUseDefIterator
use(this);
1000 MDefinition
* mostRecentUse
= use
.def();
1003 // This function relies on addUse adding new uses to the front of the list.
1004 // Check this invariant by asserting the next few uses are 'older'. Skip this
1005 // for phis because setBackedge can add a new use for a loop phi even if the
1006 // loop body has a use with an id greater than the loop phi's id.
1007 if (!mostRecentUse
->isPhi()) {
1008 static constexpr size_t NumUsesToCheck
= 3;
1010 for (size_t i
= 0; use
&& i
< NumUsesToCheck
; i
++, use
++) {
1011 MOZ_ASSERT(use
.def()->id() <= mostRecentUse
->id());
1016 return mostRecentUse
;
1019 void MDefinition::replaceAllUsesWith(MDefinition
* dom
) {
1020 for (size_t i
= 0, e
= numOperands(); i
< e
; ++i
) {
1021 getOperand(i
)->setImplicitlyUsedUnchecked();
1024 justReplaceAllUsesWith(dom
);
1027 void MDefinition::justReplaceAllUsesWith(MDefinition
* dom
) {
1028 MOZ_ASSERT(dom
!= nullptr);
1029 MOZ_ASSERT(dom
!= this);
1031 // Carry over the fact the value has uses which are no longer inspectable
1033 if (isImplicitlyUsed()) {
1034 dom
->setImplicitlyUsedUnchecked();
1037 for (MUseIterator
i(usesBegin()), e(usesEnd()); i
!= e
; ++i
) {
1038 i
->setProducerUnchecked(dom
);
1040 dom
->uses_
.takeElements(uses_
);
1043 bool MDefinition::optimizeOutAllUses(TempAllocator
& alloc
) {
1044 for (MUseIterator
i(usesBegin()), e(usesEnd()); i
!= e
;) {
1046 MConstant
* constant
= use
->consumer()->block()->optimizedOutConstant(alloc
);
1047 if (!alloc
.ensureBallast()) {
1051 // Update the resume point operand to use the optimized-out constant.
1052 use
->setProducerUnchecked(constant
);
1053 constant
->addUseUnchecked(use
);
1056 // Remove dangling pointers.
1057 this->uses_
.clear();
1061 void MDefinition::replaceAllLiveUsesWith(MDefinition
* dom
) {
1062 for (MUseIterator
i(usesBegin()), e(usesEnd()); i
!= e
;) {
1064 MNode
* consumer
= use
->consumer();
1065 if (consumer
->isResumePoint()) {
1068 if (consumer
->isDefinition() &&
1069 consumer
->toDefinition()->isRecoveredOnBailout()) {
1073 // Update the operand to use the dominating definition.
1074 use
->replaceProducer(dom
);
1078 MConstant
* MConstant::New(TempAllocator
& alloc
, const Value
& v
) {
1079 return new (alloc
) MConstant(alloc
, v
);
1082 MConstant
* MConstant::New(TempAllocator::Fallible alloc
, const Value
& v
) {
1083 return new (alloc
) MConstant(alloc
.alloc
, v
);
1086 MConstant
* MConstant::NewFloat32(TempAllocator
& alloc
, double d
) {
1087 MOZ_ASSERT(std::isnan(d
) || d
== double(float(d
)));
1088 return new (alloc
) MConstant(float(d
));
1091 MConstant
* MConstant::NewInt64(TempAllocator
& alloc
, int64_t i
) {
1092 return new (alloc
) MConstant(MIRType::Int64
, i
);
1095 MConstant
* MConstant::NewIntPtr(TempAllocator
& alloc
, intptr_t i
) {
1096 return new (alloc
) MConstant(MIRType::IntPtr
, i
);
1099 MConstant
* MConstant::New(TempAllocator
& alloc
, const Value
& v
, MIRType type
) {
1100 if (type
== MIRType::Float32
) {
1101 return NewFloat32(alloc
, v
.toNumber());
1103 MConstant
* res
= New(alloc
, v
);
1104 MOZ_ASSERT(res
->type() == type
);
1108 MConstant
* MConstant::NewObject(TempAllocator
& alloc
, JSObject
* v
) {
1109 return new (alloc
) MConstant(v
);
1112 MConstant
* MConstant::NewShape(TempAllocator
& alloc
, Shape
* s
) {
1113 return new (alloc
) MConstant(s
);
1116 static MIRType
MIRTypeFromValue(const js::Value
& vp
) {
1117 if (vp
.isDouble()) {
1118 return MIRType::Double
;
1121 switch (vp
.whyMagic()) {
1122 case JS_OPTIMIZED_OUT
:
1123 return MIRType::MagicOptimizedOut
;
1124 case JS_ELEMENTS_HOLE
:
1125 return MIRType::MagicHole
;
1126 case JS_IS_CONSTRUCTING
:
1127 return MIRType::MagicIsConstructing
;
1128 case JS_UNINITIALIZED_LEXICAL
:
1129 return MIRType::MagicUninitializedLexical
;
1131 MOZ_ASSERT_UNREACHABLE("Unexpected magic constant");
1134 return MIRTypeFromValueType(vp
.extractNonDoubleType());
1137 MConstant::MConstant(TempAllocator
& alloc
, const js::Value
& vp
)
1138 : MNullaryInstruction(classOpcode
) {
1139 setResultType(MIRTypeFromValue(vp
));
1141 MOZ_ASSERT(payload_
.asBits
== 0);
1144 case MIRType::Undefined
:
1147 case MIRType::Boolean
:
1148 payload_
.b
= vp
.toBoolean();
1150 case MIRType::Int32
:
1151 payload_
.i32
= vp
.toInt32();
1153 case MIRType::Double
:
1154 payload_
.d
= vp
.toDouble();
1156 case MIRType::String
: {
1157 JSString
* str
= vp
.toString();
1158 if (str
->isAtomRef()) {
1161 MOZ_ASSERT(!IsInsideNursery(str
));
1162 MOZ_ASSERT(str
->isAtom());
1163 payload_
.str
= vp
.toString();
1166 case MIRType::Symbol
:
1167 payload_
.sym
= vp
.toSymbol();
1169 case MIRType::BigInt
:
1170 MOZ_ASSERT(!IsInsideNursery(vp
.toBigInt()));
1171 payload_
.bi
= vp
.toBigInt();
1173 case MIRType::Object
:
1174 MOZ_ASSERT(!IsInsideNursery(&vp
.toObject()));
1175 payload_
.obj
= &vp
.toObject();
1177 case MIRType::MagicOptimizedOut
:
1178 case MIRType::MagicHole
:
1179 case MIRType::MagicIsConstructing
:
1180 case MIRType::MagicUninitializedLexical
:
1183 MOZ_CRASH("Unexpected type");
1189 MConstant::MConstant(JSObject
* obj
) : MNullaryInstruction(classOpcode
) {
1190 MOZ_ASSERT(!IsInsideNursery(obj
));
1191 setResultType(MIRType::Object
);
1196 MConstant::MConstant(Shape
* shape
) : MNullaryInstruction(classOpcode
) {
1197 setResultType(MIRType::Shape
);
1198 payload_
.shape
= shape
;
1202 MConstant::MConstant(float f
) : MNullaryInstruction(classOpcode
) {
1203 setResultType(MIRType::Float32
);
1208 MConstant::MConstant(MIRType type
, int64_t i
)
1209 : MNullaryInstruction(classOpcode
) {
1210 MOZ_ASSERT(type
== MIRType::Int64
|| type
== MIRType::IntPtr
);
1211 setResultType(type
);
1212 if (type
== MIRType::Int64
) {
1221 void MConstant::assertInitializedPayload() const {
1222 // valueHash() and equals() expect the unused payload bits to be
1223 // initialized to zero. Assert this in debug builds.
1226 case MIRType::Int32
:
1227 case MIRType::Float32
:
1228 # if MOZ_LITTLE_ENDIAN()
1229 MOZ_ASSERT((payload_
.asBits
>> 32) == 0);
1231 MOZ_ASSERT((payload_
.asBits
<< 32) == 0);
1234 case MIRType::Boolean
:
1235 # if MOZ_LITTLE_ENDIAN()
1236 MOZ_ASSERT((payload_
.asBits
>> 1) == 0);
1238 MOZ_ASSERT((payload_
.asBits
& ~(1ULL << 56)) == 0);
1241 case MIRType::Double
:
1242 case MIRType::Int64
:
1244 case MIRType::String
:
1245 case MIRType::Object
:
1246 case MIRType::Symbol
:
1247 case MIRType::BigInt
:
1248 case MIRType::IntPtr
:
1249 case MIRType::Shape
:
1250 # if MOZ_LITTLE_ENDIAN()
1251 MOZ_ASSERT_IF(JS_BITS_PER_WORD
== 32, (payload_
.asBits
>> 32) == 0);
1253 MOZ_ASSERT_IF(JS_BITS_PER_WORD
== 32, (payload_
.asBits
<< 32) == 0);
1257 MOZ_ASSERT(IsNullOrUndefined(type()) || IsMagicType(type()));
1258 MOZ_ASSERT(payload_
.asBits
== 0);
1264 HashNumber
MConstant::valueHash() const {
1265 static_assert(sizeof(Payload
) == sizeof(uint64_t),
1266 "Code below assumes payload fits in 64 bits");
1268 assertInitializedPayload();
1269 return ConstantValueHash(type(), payload_
.asBits
);
1272 HashNumber
MConstantProto::valueHash() const {
1273 HashNumber hash
= protoObject()->valueHash();
1274 const MDefinition
* receiverObject
= getReceiverObject();
1275 if (receiverObject
) {
1276 hash
= addU32ToHash(hash
, receiverObject
->id());
1281 bool MConstant::congruentTo(const MDefinition
* ins
) const {
1282 return ins
->isConstant() && equals(ins
->toConstant());
1286 void MConstant::printOpcode(GenericPrinter
& out
) const {
1287 PrintOpcodeName(out
, op());
1290 case MIRType::Undefined
:
1291 out
.printf("undefined");
1296 case MIRType::Boolean
:
1297 out
.printf(toBoolean() ? "true" : "false");
1299 case MIRType::Int32
:
1300 out
.printf("0x%x", uint32_t(toInt32()));
1302 case MIRType::Int64
:
1303 out
.printf("0x%" PRIx64
, uint64_t(toInt64()));
1305 case MIRType::IntPtr
:
1306 out
.printf("0x%" PRIxPTR
, uintptr_t(toIntPtr()));
1308 case MIRType::Double
:
1309 out
.printf("%.16g", toDouble());
1311 case MIRType::Float32
: {
1312 float val
= toFloat32();
1313 out
.printf("%.16g", val
);
1316 case MIRType::Object
:
1317 if (toObject().is
<JSFunction
>()) {
1318 JSFunction
* fun
= &toObject().as
<JSFunction
>();
1319 if (fun
->maybePartialDisplayAtom()) {
1320 out
.put("function ");
1321 EscapedStringPrinter(out
, fun
->maybePartialDisplayAtom(), 0);
1323 out
.put("unnamed function");
1325 if (fun
->hasBaseScript()) {
1326 BaseScript
* script
= fun
->baseScript();
1327 out
.printf(" (%s:%u)", script
->filename() ? script
->filename() : "",
1330 out
.printf(" at %p", (void*)fun
);
1333 out
.printf("object %p (%s)", (void*)&toObject(),
1334 toObject().getClass()->name
);
1336 case MIRType::Symbol
:
1337 out
.printf("symbol at %p", (void*)toSymbol());
1339 case MIRType::BigInt
:
1340 out
.printf("BigInt at %p", (void*)toBigInt());
1342 case MIRType::String
:
1343 out
.printf("string %p", (void*)toString());
1345 case MIRType::Shape
:
1346 out
.printf("shape at %p", (void*)toShape());
1348 case MIRType::MagicHole
:
1349 out
.printf("magic hole");
1351 case MIRType::MagicIsConstructing
:
1352 out
.printf("magic is-constructing");
1354 case MIRType::MagicOptimizedOut
:
1355 out
.printf("magic optimized-out");
1357 case MIRType::MagicUninitializedLexical
:
1358 out
.printf("magic uninitialized-lexical");
1361 MOZ_CRASH("unexpected type");
1366 bool MConstant::canProduceFloat32() const {
1367 if (!isTypeRepresentableAsDouble()) {
1371 if (type() == MIRType::Int32
) {
1372 return IsFloat32Representable(static_cast<double>(toInt32()));
1374 if (type() == MIRType::Double
) {
1375 return IsFloat32Representable(toDouble());
1377 MOZ_ASSERT(type() == MIRType::Float32
);
1381 Value
MConstant::toJSValue() const {
1382 // Wasm has types like int64 that cannot be stored as js::Value. It also
1383 // doesn't want the NaN canonicalization enforced by js::Value.
1384 MOZ_ASSERT(!IsCompilingWasm());
1387 case MIRType::Undefined
:
1388 return UndefinedValue();
1391 case MIRType::Boolean
:
1392 return BooleanValue(toBoolean());
1393 case MIRType::Int32
:
1394 return Int32Value(toInt32());
1395 case MIRType::Double
:
1396 return DoubleValue(toDouble());
1397 case MIRType::Float32
:
1398 return Float32Value(toFloat32());
1399 case MIRType::String
:
1400 return StringValue(toString());
1401 case MIRType::Symbol
:
1402 return SymbolValue(toSymbol());
1403 case MIRType::BigInt
:
1404 return BigIntValue(toBigInt());
1405 case MIRType::Object
:
1406 return ObjectValue(toObject());
1407 case MIRType::Shape
:
1408 return PrivateGCThingValue(toShape());
1409 case MIRType::MagicOptimizedOut
:
1410 return MagicValue(JS_OPTIMIZED_OUT
);
1411 case MIRType::MagicHole
:
1412 return MagicValue(JS_ELEMENTS_HOLE
);
1413 case MIRType::MagicIsConstructing
:
1414 return MagicValue(JS_IS_CONSTRUCTING
);
1415 case MIRType::MagicUninitializedLexical
:
1416 return MagicValue(JS_UNINITIALIZED_LEXICAL
);
1418 MOZ_CRASH("Unexpected type");
1422 bool MConstant::valueToBoolean(bool* res
) const {
1424 case MIRType::Boolean
:
1427 case MIRType::Int32
:
1428 *res
= toInt32() != 0;
1430 case MIRType::Int64
:
1431 *res
= toInt64() != 0;
1433 case MIRType::Double
:
1434 *res
= !std::isnan(toDouble()) && toDouble() != 0.0;
1436 case MIRType::Float32
:
1437 *res
= !std::isnan(toFloat32()) && toFloat32() != 0.0f
;
1440 case MIRType::Undefined
:
1443 case MIRType::Symbol
:
1446 case MIRType::BigInt
:
1447 *res
= !toBigInt()->isZero();
1449 case MIRType::String
:
1450 *res
= toString()->length() != 0;
1452 case MIRType::Object
:
1453 // TODO(Warp): Lazy groups have been removed.
1454 // We have to call EmulatesUndefined but that reads obj->group->clasp
1455 // and so it's racy when the object has a lazy group. The main callers
1456 // of this (MTest, MNot) already know how to fold the object case, so
1460 MOZ_ASSERT(IsMagicType(type()));
1466 void MControlInstruction::printOpcode(GenericPrinter
& out
) const {
1467 MDefinition::printOpcode(out
);
1468 for (size_t j
= 0; j
< numSuccessors(); j
++) {
1469 if (getSuccessor(j
)) {
1470 out
.printf(" block%u", getSuccessor(j
)->id());
1472 out
.printf(" (null-to-be-patched)");
1477 void MCompare::printOpcode(GenericPrinter
& out
) const {
1478 MDefinition::printOpcode(out
);
1479 out
.printf(" %s", CodeName(jsop()));
1482 void MTypeOfIs::printOpcode(GenericPrinter
& out
) const {
1483 MDefinition::printOpcode(out
);
1484 out
.printf(" %s", CodeName(jsop()));
1486 const char* name
= "";
1488 case JSTYPE_UNDEFINED
:
1494 case JSTYPE_FUNCTION
:
1503 case JSTYPE_BOOLEAN
:
1512 # ifdef ENABLE_RECORD_TUPLE
1517 MOZ_CRASH("Unexpected type");
1519 out
.printf(" '%s'", name
);
1522 void MLoadUnboxedScalar::printOpcode(GenericPrinter
& out
) const {
1523 MDefinition::printOpcode(out
);
1524 out
.printf(" %s", Scalar::name(storageType()));
1527 void MLoadDataViewElement::printOpcode(GenericPrinter
& out
) const {
1528 MDefinition::printOpcode(out
);
1529 out
.printf(" %s", Scalar::name(storageType()));
1532 void MAssertRange::printOpcode(GenericPrinter
& out
) const {
1533 MDefinition::printOpcode(out
);
1535 assertedRange()->dump(out
);
1538 void MNearbyInt::printOpcode(GenericPrinter
& out
) const {
1539 MDefinition::printOpcode(out
);
1540 const char* roundingModeStr
= nullptr;
1541 switch (roundingMode_
) {
1542 case RoundingMode::Up
:
1543 roundingModeStr
= "(up)";
1545 case RoundingMode::Down
:
1546 roundingModeStr
= "(down)";
1548 case RoundingMode::NearestTiesToEven
:
1549 roundingModeStr
= "(nearest ties even)";
1551 case RoundingMode::TowardsZero
:
1552 roundingModeStr
= "(towards zero)";
1555 out
.printf(" %s", roundingModeStr
);
1559 AliasSet
MRandom::getAliasSet() const { return AliasSet::Store(AliasSet::RNG
); }
1561 MDefinition
* MSign::foldsTo(TempAllocator
& alloc
) {
1562 MDefinition
* input
= getOperand(0);
1563 if (!input
->isConstant() ||
1564 !input
->toConstant()->isTypeRepresentableAsDouble()) {
1568 double in
= input
->toConstant()->numberToDouble();
1569 double out
= js::math_sign_impl(in
);
1571 if (type() == MIRType::Int32
) {
1572 // Decline folding if this is an int32 operation, but the result type
1574 Value outValue
= NumberValue(out
);
1575 if (!outValue
.isInt32()) {
1579 return MConstant::New(alloc
, outValue
);
1582 return MConstant::New(alloc
, DoubleValue(out
));
1585 const char* MMathFunction::FunctionName(UnaryMathFunction function
) {
1586 return GetUnaryMathFunctionName(function
);
1590 void MMathFunction::printOpcode(GenericPrinter
& out
) const {
1591 MDefinition::printOpcode(out
);
1592 out
.printf(" %s", FunctionName(function()));
1596 MDefinition
* MMathFunction::foldsTo(TempAllocator
& alloc
) {
1597 MDefinition
* input
= getOperand(0);
1598 if (!input
->isConstant() ||
1599 !input
->toConstant()->isTypeRepresentableAsDouble()) {
1603 UnaryMathFunctionType funPtr
= GetUnaryMathFunctionPtr(function());
1605 double in
= input
->toConstant()->numberToDouble();
1607 // The function pointer call can't GC.
1608 JS::AutoSuppressGCAnalysis nogc
;
1609 double out
= funPtr(in
);
1611 if (input
->type() == MIRType::Float32
) {
1612 return MConstant::NewFloat32(alloc
, out
);
1614 return MConstant::New(alloc
, DoubleValue(out
));
1617 MDefinition
* MAtomicIsLockFree::foldsTo(TempAllocator
& alloc
) {
1618 MDefinition
* input
= getOperand(0);
1619 if (!input
->isConstant() || input
->type() != MIRType::Int32
) {
1623 int32_t i
= input
->toConstant()->toInt32();
1624 return MConstant::New(alloc
, BooleanValue(AtomicOperations::isLockfreeJS(i
)));
1627 // Define |THIS_SLOT| as part of this translation unit, as it is used to
1628 // specialized the parameterized |New| function calls introduced by
1629 // TRIVIAL_NEW_WRAPPERS.
1630 const int32_t MParameter::THIS_SLOT
;
1633 void MParameter::printOpcode(GenericPrinter
& out
) const {
1634 PrintOpcodeName(out
, op());
1635 if (index() == THIS_SLOT
) {
1636 out
.printf(" THIS_SLOT");
1638 out
.printf(" %d", index());
1643 HashNumber
MParameter::valueHash() const {
1644 HashNumber hash
= MDefinition::valueHash();
1645 hash
= addU32ToHash(hash
, index_
);
1649 bool MParameter::congruentTo(const MDefinition
* ins
) const {
1650 if (!ins
->isParameter()) {
1654 return ins
->toParameter()->index() == index_
;
1657 WrappedFunction::WrappedFunction(JSFunction
* nativeFun
, uint16_t nargs
,
1658 FunctionFlags flags
)
1659 : nativeFun_(nativeFun
), nargs_(nargs
), flags_(flags
) {
1660 MOZ_ASSERT_IF(nativeFun
, isNativeWithoutJitEntry());
1663 // If we are not running off-main thread we can assert that the
1664 // metadata is consistent.
1665 if (!CanUseExtraThreads() && nativeFun
) {
1666 MOZ_ASSERT(nativeFun
->nargs() == nargs
);
1668 MOZ_ASSERT(nativeFun
->isNativeWithoutJitEntry() ==
1669 isNativeWithoutJitEntry());
1670 MOZ_ASSERT(nativeFun
->hasJitEntry() == hasJitEntry());
1671 MOZ_ASSERT(nativeFun
->isConstructor() == isConstructor());
1672 MOZ_ASSERT(nativeFun
->isClassConstructor() == isClassConstructor());
1677 MCall
* MCall::New(TempAllocator
& alloc
, WrappedFunction
* target
, size_t maxArgc
,
1678 size_t numActualArgs
, bool construct
, bool ignoresReturnValue
,
1679 bool isDOMCall
, mozilla::Maybe
<DOMObjectKind
> objectKind
) {
1680 MOZ_ASSERT(isDOMCall
== objectKind
.isSome());
1681 MOZ_ASSERT(maxArgc
>= numActualArgs
);
1684 MOZ_ASSERT(!construct
);
1685 ins
= new (alloc
) MCallDOMNative(target
, numActualArgs
, *objectKind
);
1688 new (alloc
) MCall(target
, numActualArgs
, construct
, ignoresReturnValue
);
1690 if (!ins
->init(alloc
, maxArgc
+ NumNonArgumentOperands
)) {
1696 AliasSet
MCallDOMNative::getAliasSet() const {
1697 const JSJitInfo
* jitInfo
= getJitInfo();
1699 // If we don't know anything about the types of our arguments, we have to
1700 // assume that type-coercions can have side-effects, so we need to alias
1702 if (jitInfo
->aliasSet() == JSJitInfo::AliasEverything
||
1703 !jitInfo
->isTypedMethodJitInfo()) {
1704 return AliasSet::Store(AliasSet::Any
);
1707 uint32_t argIndex
= 0;
1708 const JSTypedMethodJitInfo
* methodInfo
=
1709 reinterpret_cast<const JSTypedMethodJitInfo
*>(jitInfo
);
1710 for (const JSJitInfo::ArgType
* argType
= methodInfo
->argTypes
;
1711 *argType
!= JSJitInfo::ArgTypeListEnd
; ++argType
, ++argIndex
) {
1712 if (argIndex
>= numActualArgs()) {
1713 // Passing through undefined can't have side-effects
1716 // getArg(0) is "this", so skip it
1717 MDefinition
* arg
= getArg(argIndex
+ 1);
1718 MIRType actualType
= arg
->type();
1719 // The only way to reliably avoid side-effects given the information we
1720 // have here is if we're passing in a known primitive value to an
1721 // argument that expects a primitive value.
1723 // XXXbz maybe we need to communicate better information. For example,
1724 // a sequence argument will sort of unavoidably have side effects, while
1725 // a typed array argument won't have any, but both are claimed to be
1726 // JSJitInfo::Object. But if we do that, we need to watch out for our
1727 // movability/DCE-ability bits: if we have an arg type that can reliably
1728 // throw an exception on conversion, that might not affect our alias set
1729 // per se, but it should prevent us being moved or DCE-ed, unless we
1730 // know the incoming things match that arg type and won't throw.
1732 if ((actualType
== MIRType::Value
|| actualType
== MIRType::Object
) ||
1733 (*argType
& JSJitInfo::Object
)) {
1734 return AliasSet::Store(AliasSet::Any
);
1738 // We checked all the args, and they check out. So we only alias DOM
1739 // mutations or alias nothing, depending on the alias set in the jitinfo.
1740 if (jitInfo
->aliasSet() == JSJitInfo::AliasNone
) {
1741 return AliasSet::None();
1744 MOZ_ASSERT(jitInfo
->aliasSet() == JSJitInfo::AliasDOMSets
);
1745 return AliasSet::Load(AliasSet::DOMProperty
);
1748 void MCallDOMNative::computeMovable() {
1749 // We are movable if the jitinfo says we can be and if we're also not
1750 // effectful. The jitinfo can't check for the latter, since it depends on
1751 // the types of our arguments.
1752 const JSJitInfo
* jitInfo
= getJitInfo();
1754 MOZ_ASSERT_IF(jitInfo
->isMovable
,
1755 jitInfo
->aliasSet() != JSJitInfo::AliasEverything
);
1757 if (jitInfo
->isMovable
&& !isEffectful()) {
1762 bool MCallDOMNative::congruentTo(const MDefinition
* ins
) const {
1767 if (!ins
->isCall()) {
1771 const MCall
* call
= ins
->toCall();
1773 if (!call
->isCallDOMNative()) {
1777 if (getSingleTarget() != call
->getSingleTarget()) {
1781 if (isConstructing() != call
->isConstructing()) {
1785 if (numActualArgs() != call
->numActualArgs()) {
1789 if (!congruentIfOperandsEqual(call
)) {
1793 // The other call had better be movable at this point!
1794 MOZ_ASSERT(call
->isMovable());
1799 const JSJitInfo
* MCallDOMNative::getJitInfo() const {
1800 MOZ_ASSERT(getSingleTarget()->hasJitInfo());
1801 return getSingleTarget()->jitInfo();
1804 MCallClassHook
* MCallClassHook::New(TempAllocator
& alloc
, JSNative target
,
1805 uint32_t argc
, bool constructing
) {
1806 auto* ins
= new (alloc
) MCallClassHook(target
, constructing
);
1808 // Add callee + |this| + (if constructing) newTarget.
1809 uint32_t numOperands
= 2 + argc
+ constructing
;
1811 if (!ins
->init(alloc
, numOperands
)) {
1818 MDefinition
* MStringLength::foldsTo(TempAllocator
& alloc
) {
1819 if (string()->isConstant()) {
1820 JSString
* str
= string()->toConstant()->toString();
1821 return MConstant::New(alloc
, Int32Value(str
->length()));
1824 // MFromCharCode returns a one-element string.
1825 if (string()->isFromCharCode()) {
1826 return MConstant::New(alloc
, Int32Value(1));
1832 MDefinition
* MConcat::foldsTo(TempAllocator
& alloc
) {
1833 if (lhs()->isConstant() && lhs()->toConstant()->toString()->empty()) {
1837 if (rhs()->isConstant() && rhs()->toConstant()->toString()->empty()) {
1844 MDefinition
* MStringConvertCase::foldsTo(TempAllocator
& alloc
) {
1845 MDefinition
* string
= this->string();
1847 // Handle the pattern |str[idx].toUpperCase()| and simplify it from
1848 // |StringConvertCase(FromCharCode(CharCodeAt(str, idx)))| to just
1849 // |CharCodeConvertCase(CharCodeAt(str, idx))|.
1850 if (string
->isFromCharCode()) {
1851 auto* charCode
= string
->toFromCharCode()->code();
1852 auto mode
= mode_
== Mode::LowerCase
? MCharCodeConvertCase::LowerCase
1853 : MCharCodeConvertCase::UpperCase
;
1854 return MCharCodeConvertCase::New(alloc
, charCode
, mode
);
1857 // Handle the pattern |num.toString(base).toUpperCase()| and simplify it to
1858 // directly return the string representation in the correct case.
1859 if (string
->isInt32ToStringWithBase()) {
1860 auto* toString
= string
->toInt32ToStringWithBase();
1862 bool lowerCase
= mode_
== Mode::LowerCase
;
1863 if (toString
->lowerCase() == lowerCase
) {
1866 return MInt32ToStringWithBase::New(alloc
, toString
->input(),
1867 toString
->base(), lowerCase
);
1873 static bool IsSubstrTo(MSubstr
* substr
, int32_t len
) {
1874 // We want to match this pattern:
1876 // Substr(string, Constant(0), Min(Constant(length), StringLength(string)))
1878 // which is generated for the self-hosted `String.p.{substring,slice,substr}`
1879 // functions when called with constants `start` and `end` parameters.
1881 auto isConstantZero
= [](auto* def
) {
1882 return def
->isConstant() && def
->toConstant()->isInt32(0);
1885 if (!isConstantZero(substr
->begin())) {
1889 auto* length
= substr
->length();
1890 if (length
->isBitOr()) {
1891 // Unnecessary bit-ops haven't yet been removed.
1892 auto* bitOr
= length
->toBitOr();
1893 if (isConstantZero(bitOr
->lhs())) {
1894 length
= bitOr
->rhs();
1895 } else if (isConstantZero(bitOr
->rhs())) {
1896 length
= bitOr
->lhs();
1899 if (!length
->isMinMax() || length
->toMinMax()->isMax()) {
1903 auto* min
= length
->toMinMax();
1904 if (!min
->lhs()->isConstant() && !min
->rhs()->isConstant()) {
1908 auto* minConstant
= min
->lhs()->isConstant() ? min
->lhs()->toConstant()
1909 : min
->rhs()->toConstant();
1911 auto* minOperand
= min
->lhs()->isConstant() ? min
->rhs() : min
->lhs();
1912 if (!minOperand
->isStringLength() ||
1913 minOperand
->toStringLength()->string() != substr
->string()) {
1917 // Ensure |len| matches the substring's length.
1918 return minConstant
->isInt32(len
);
1921 MDefinition
* MSubstr::foldsTo(TempAllocator
& alloc
) {
1922 // Fold |str.substring(0, 1)| to |str.charAt(0)|.
1923 if (!IsSubstrTo(this, 1)) {
1927 auto* charCode
= MCharCodeAtOrNegative::New(alloc
, string(), begin());
1928 block()->insertBefore(this, charCode
);
1930 return MFromCharCodeEmptyIfNegative::New(alloc
, charCode
);
1933 MDefinition
* MCharCodeAt::foldsTo(TempAllocator
& alloc
) {
1934 MDefinition
* string
= this->string();
1935 if (!string
->isConstant() && !string
->isFromCharCode()) {
1939 MDefinition
* index
= this->index();
1940 if (index
->isSpectreMaskIndex()) {
1941 index
= index
->toSpectreMaskIndex()->index();
1943 if (!index
->isConstant()) {
1946 int32_t idx
= index
->toConstant()->toInt32();
1948 // Handle the pattern |s[idx].charCodeAt(0)|.
1949 if (string
->isFromCharCode()) {
1954 // Simplify |CharCodeAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
1955 // |CharCodeAt(s, idx)|.
1956 auto* charCode
= string
->toFromCharCode()->code();
1957 if (!charCode
->isCharCodeAt()) {
1964 JSLinearString
* str
= &string
->toConstant()->toString()->asLinear();
1965 if (idx
< 0 || uint32_t(idx
) >= str
->length()) {
1969 char16_t ch
= str
->latin1OrTwoByteChar(idx
);
1970 return MConstant::New(alloc
, Int32Value(ch
));
1973 MDefinition
* MCodePointAt::foldsTo(TempAllocator
& alloc
) {
1974 MDefinition
* string
= this->string();
1975 if (!string
->isConstant() && !string
->isFromCharCode()) {
1979 MDefinition
* index
= this->index();
1980 if (index
->isSpectreMaskIndex()) {
1981 index
= index
->toSpectreMaskIndex()->index();
1983 if (!index
->isConstant()) {
1986 int32_t idx
= index
->toConstant()->toInt32();
1988 // Handle the pattern |s[idx].codePointAt(0)|.
1989 if (string
->isFromCharCode()) {
1994 // Simplify |CodePointAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
1995 // |CharCodeAt(s, idx)|.
1996 auto* charCode
= string
->toFromCharCode()->code();
1997 if (!charCode
->isCharCodeAt()) {
2004 JSLinearString
* str
= &string
->toConstant()->toString()->asLinear();
2005 if (idx
< 0 || uint32_t(idx
) >= str
->length()) {
2009 char32_t first
= str
->latin1OrTwoByteChar(idx
);
2010 if (unicode::IsLeadSurrogate(first
) && uint32_t(idx
) + 1 < str
->length()) {
2011 char32_t second
= str
->latin1OrTwoByteChar(idx
+ 1);
2012 if (unicode::IsTrailSurrogate(second
)) {
2013 first
= unicode::UTF16Decode(first
, second
);
2016 return MConstant::New(alloc
, Int32Value(first
));
2019 MDefinition
* MToRelativeStringIndex::foldsTo(TempAllocator
& alloc
) {
2020 MDefinition
* index
= this->index();
2021 MDefinition
* length
= this->length();
2023 if (!index
->isConstant()) {
2026 if (!length
->isStringLength() && !length
->isConstant()) {
2029 MOZ_ASSERT_IF(length
->isConstant(), length
->toConstant()->toInt32() >= 0);
2031 int32_t relativeIndex
= index
->toConstant()->toInt32();
2032 if (relativeIndex
>= 0) {
2036 // Safe to truncate because |length| is never negative.
2037 return MAdd::New(alloc
, index
, length
, TruncateKind::Truncate
);
2040 template <size_t Arity
>
2041 [[nodiscard
]] static bool EnsureFloatInputOrConvert(
2042 MAryInstruction
<Arity
>* owner
, TempAllocator
& alloc
) {
2043 MOZ_ASSERT(!IsFloatingPointType(owner
->type()),
2044 "Floating point types must check consumers");
2046 if (AllOperandsCanProduceFloat32(owner
)) {
2049 ConvertOperandsToDouble(owner
, alloc
);
2053 template <size_t Arity
>
2054 [[nodiscard
]] static bool EnsureFloatConsumersAndInputOrConvert(
2055 MAryInstruction
<Arity
>* owner
, TempAllocator
& alloc
) {
2056 MOZ_ASSERT(IsFloatingPointType(owner
->type()),
2057 "Integer types don't need to check consumers");
2059 if (AllOperandsCanProduceFloat32(owner
) &&
2060 CheckUsesAreFloat32Consumers(owner
)) {
2063 ConvertOperandsToDouble(owner
, alloc
);
2067 void MFloor::trySpecializeFloat32(TempAllocator
& alloc
) {
2068 MOZ_ASSERT(type() == MIRType::Int32
);
2069 if (EnsureFloatInputOrConvert(this, alloc
)) {
2070 specialization_
= MIRType::Float32
;
2074 void MCeil::trySpecializeFloat32(TempAllocator
& alloc
) {
2075 MOZ_ASSERT(type() == MIRType::Int32
);
2076 if (EnsureFloatInputOrConvert(this, alloc
)) {
2077 specialization_
= MIRType::Float32
;
2081 void MRound::trySpecializeFloat32(TempAllocator
& alloc
) {
2082 MOZ_ASSERT(type() == MIRType::Int32
);
2083 if (EnsureFloatInputOrConvert(this, alloc
)) {
2084 specialization_
= MIRType::Float32
;
2088 void MTrunc::trySpecializeFloat32(TempAllocator
& alloc
) {
2089 MOZ_ASSERT(type() == MIRType::Int32
);
2090 if (EnsureFloatInputOrConvert(this, alloc
)) {
2091 specialization_
= MIRType::Float32
;
2095 void MNearbyInt::trySpecializeFloat32(TempAllocator
& alloc
) {
2096 if (EnsureFloatConsumersAndInputOrConvert(this, alloc
)) {
2097 specialization_
= MIRType::Float32
;
2098 setResultType(MIRType::Float32
);
2102 MGoto
* MGoto::New(TempAllocator
& alloc
, MBasicBlock
* target
) {
2103 return new (alloc
) MGoto(target
);
2106 MGoto
* MGoto::New(TempAllocator::Fallible alloc
, MBasicBlock
* target
) {
2108 return new (alloc
) MGoto(target
);
2111 MGoto
* MGoto::New(TempAllocator
& alloc
) { return new (alloc
) MGoto(nullptr); }
2113 MDefinition
* MBox::foldsTo(TempAllocator
& alloc
) {
2114 if (input()->isUnbox()) {
2115 return input()->toUnbox()->input();
2121 void MUnbox::printOpcode(GenericPrinter
& out
) const {
2122 PrintOpcodeName(out
, op());
2124 getOperand(0)->printName(out
);
2128 case MIRType::Int32
:
2129 out
.printf("to Int32");
2131 case MIRType::Double
:
2132 out
.printf("to Double");
2134 case MIRType::Boolean
:
2135 out
.printf("to Boolean");
2137 case MIRType::String
:
2138 out
.printf("to String");
2140 case MIRType::Symbol
:
2141 out
.printf("to Symbol");
2143 case MIRType::BigInt
:
2144 out
.printf("to BigInt");
2146 case MIRType::Object
:
2147 out
.printf("to Object");
2155 out
.printf(" (fallible)");
2158 out
.printf(" (infallible)");
2166 MDefinition
* MUnbox::foldsTo(TempAllocator
& alloc
) {
2167 if (input()->isBox()) {
2168 MDefinition
* unboxed
= input()->toBox()->input();
2170 // Fold MUnbox(MBox(x)) => x if types match.
2171 if (unboxed
->type() == type()) {
2173 unboxed
->setImplicitlyUsedUnchecked();
2178 // Fold MUnbox(MBox(x)) => MToDouble(x) if possible.
2179 if (type() == MIRType::Double
&&
2180 IsTypeRepresentableAsDouble(unboxed
->type())) {
2181 if (unboxed
->isConstant()) {
2182 return MConstant::New(
2183 alloc
, DoubleValue(unboxed
->toConstant()->numberToDouble()));
2186 return MToDouble::New(alloc
, unboxed
);
2189 // MUnbox<Int32>(MBox<Double>(x)) will always fail, even if x can be
2190 // represented as an Int32. Fold to avoid unnecessary bailouts.
2191 if (type() == MIRType::Int32
&& unboxed
->type() == MIRType::Double
) {
2192 auto* folded
= MToNumberInt32::New(alloc
, unboxed
,
2193 IntConversionInputKind::NumbersOnly
);
2203 void MPhi::assertLoopPhi() const {
2204 // getLoopPredecessorOperand and getLoopBackedgeOperand rely on these
2205 // predecessors being at known indices.
2206 if (block()->numPredecessors() == 2) {
2207 MBasicBlock
* pred
= block()->getPredecessor(0);
2208 MBasicBlock
* back
= block()->getPredecessor(1);
2209 MOZ_ASSERT(pred
== block()->loopPredecessor());
2210 MOZ_ASSERT(pred
->successorWithPhis() == block());
2211 MOZ_ASSERT(pred
->positionInPhiSuccessor() == 0);
2212 MOZ_ASSERT(back
== block()->backedge());
2213 MOZ_ASSERT(back
->successorWithPhis() == block());
2214 MOZ_ASSERT(back
->positionInPhiSuccessor() == 1);
2216 // After we remove fake loop predecessors for loop headers that
2217 // are only reachable via OSR, the only predecessor is the
2219 MOZ_ASSERT(block()->numPredecessors() == 1);
2220 MOZ_ASSERT(block()->graph().osrBlock());
2221 MOZ_ASSERT(!block()->graph().canBuildDominators());
2222 MBasicBlock
* back
= block()->getPredecessor(0);
2223 MOZ_ASSERT(back
== block()->backedge());
2224 MOZ_ASSERT(back
->successorWithPhis() == block());
2225 MOZ_ASSERT(back
->positionInPhiSuccessor() == 0);
2230 MDefinition
* MPhi::getLoopPredecessorOperand() const {
2231 // This should not be called after removing fake loop predecessors.
2232 MOZ_ASSERT(block()->numPredecessors() == 2);
2234 return getOperand(0);
2237 MDefinition
* MPhi::getLoopBackedgeOperand() const {
2239 uint32_t idx
= block()->numPredecessors() == 2 ? 1 : 0;
2240 return getOperand(idx
);
2243 void MPhi::removeOperand(size_t index
) {
2244 MOZ_ASSERT(index
< numOperands());
2245 MOZ_ASSERT(getUseFor(index
)->index() == index
);
2246 MOZ_ASSERT(getUseFor(index
)->consumer() == this);
2248 // If we have phi(..., a, b, c, d, ..., z) and we plan
2249 // on removing a, then first shift downward so that we have
2250 // phi(..., b, c, d, ..., z, z):
2251 MUse
* p
= inputs_
.begin() + index
;
2252 MUse
* e
= inputs_
.end();
2253 p
->producer()->removeUse(p
);
2254 for (; p
< e
- 1; ++p
) {
2255 MDefinition
* producer
= (p
+ 1)->producer();
2256 p
->setProducerUnchecked(producer
);
2257 producer
->replaceUse(p
+ 1, p
);
2260 // truncate the inputs_ list:
2264 void MPhi::removeAllOperands() {
2265 for (MUse
& p
: inputs_
) {
2266 p
.producer()->removeUse(&p
);
2271 MDefinition
* MPhi::foldsTernary(TempAllocator
& alloc
) {
2272 /* Look if this MPhi is a ternary construct.
2273 * This is a very loose term as it actually only checks for
2281 * Which we will simply call:
2282 * x ? x : y or x ? y : x
2285 if (numOperands() != 2) {
2289 MOZ_ASSERT(block()->numPredecessors() == 2);
2291 MBasicBlock
* pred
= block()->immediateDominator();
2292 if (!pred
|| !pred
->lastIns()->isTest()) {
2296 MTest
* test
= pred
->lastIns()->toTest();
2298 // True branch may only dominate one edge of MPhi.
2299 if (test
->ifTrue()->dominates(block()->getPredecessor(0)) ==
2300 test
->ifTrue()->dominates(block()->getPredecessor(1))) {
2304 // False branch may only dominate one edge of MPhi.
2305 if (test
->ifFalse()->dominates(block()->getPredecessor(0)) ==
2306 test
->ifFalse()->dominates(block()->getPredecessor(1))) {
2310 // True and false branch must dominate different edges of MPhi.
2311 if (test
->ifTrue()->dominates(block()->getPredecessor(0)) ==
2312 test
->ifFalse()->dominates(block()->getPredecessor(0))) {
2316 // We found a ternary construct.
2317 bool firstIsTrueBranch
=
2318 test
->ifTrue()->dominates(block()->getPredecessor(0));
2319 MDefinition
* trueDef
= firstIsTrueBranch
? getOperand(0) : getOperand(1);
2320 MDefinition
* falseDef
= firstIsTrueBranch
? getOperand(1) : getOperand(0);
2323 // testArg ? testArg : constant or
2324 // testArg ? constant : testArg
2325 if (!trueDef
->isConstant() && !falseDef
->isConstant()) {
2330 trueDef
->isConstant() ? trueDef
->toConstant() : falseDef
->toConstant();
2331 MDefinition
* testArg
= (trueDef
== c
) ? falseDef
: trueDef
;
2332 if (testArg
!= test
->input()) {
2336 // This check should be a tautology, except that the constant might be the
2337 // result of the removal of a branch. In such case the domination scope of
2338 // the block which is holding the constant might be incomplete. This
2339 // condition is used to prevent doing this optimization based on incomplete
2342 // As GVN removed a branch, it will update the dominations rules before
2343 // trying to fold this MPhi again. Thus, this condition does not inhibit
2344 // this optimization.
2345 MBasicBlock
* truePred
= block()->getPredecessor(firstIsTrueBranch
? 0 : 1);
2346 MBasicBlock
* falsePred
= block()->getPredecessor(firstIsTrueBranch
? 1 : 0);
2347 if (!trueDef
->block()->dominates(truePred
) ||
2348 !falseDef
->block()->dominates(falsePred
)) {
2352 // If testArg is an int32 type we can:
2353 // - fold testArg ? testArg : 0 to testArg
2354 // - fold testArg ? 0 : testArg to 0
2355 if (testArg
->type() == MIRType::Int32
&& c
->numberToDouble() == 0) {
2356 testArg
->setGuardRangeBailoutsUnchecked();
2358 // When folding to the constant we need to hoist it.
2359 if (trueDef
== c
&& !c
->block()->dominates(block())) {
2360 c
->block()->moveBefore(pred
->lastIns(), c
);
2365 // If testArg is an double type we can:
2366 // - fold testArg ? testArg : 0.0 to MNaNToZero(testArg)
2367 if (testArg
->type() == MIRType::Double
&&
2368 mozilla::IsPositiveZero(c
->numberToDouble()) && c
!= trueDef
) {
2369 MNaNToZero
* replace
= MNaNToZero::New(alloc
, testArg
);
2370 test
->block()->insertBefore(test
, replace
);
2374 // If testArg is a string type we can:
2375 // - fold testArg ? testArg : "" to testArg
2376 // - fold testArg ? "" : testArg to ""
2377 if (testArg
->type() == MIRType::String
&&
2378 c
->toString() == GetJitContext()->runtime
->emptyString()) {
2379 // When folding to the constant we need to hoist it.
2380 if (trueDef
== c
&& !c
->block()->dominates(block())) {
2381 c
->block()->moveBefore(pred
->lastIns(), c
);
2389 MDefinition
* MPhi::operandIfRedundant() {
2390 if (inputs_
.length() == 0) {
2394 // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
2395 // returns the operand that it will always be equal to (a, in
2396 // those two cases).
2397 MDefinition
* first
= getOperand(0);
2398 for (size_t i
= 1, e
= numOperands(); i
< e
; i
++) {
2399 MDefinition
* op
= getOperand(i
);
2400 if (op
!= first
&& op
!= this) {
2407 MDefinition
* MPhi::foldsTo(TempAllocator
& alloc
) {
2408 if (MDefinition
* def
= operandIfRedundant()) {
2412 if (MDefinition
* def
= foldsTernary(alloc
)) {
2419 bool MPhi::congruentTo(const MDefinition
* ins
) const {
2420 if (!ins
->isPhi()) {
2424 // Phis in different blocks may have different control conditions.
2425 // For example, these phis:
2437 // have identical operands, but they are not equvalent because t is
2438 // effectively p?x:y and s is effectively q?x:y.
2440 // For now, consider phis in different blocks incongruent.
2441 if (ins
->block() != block()) {
2445 return congruentIfOperandsEqual(ins
);
2448 void MPhi::updateForReplacement(MPhi
* other
) {
2449 // This function is called to fix the current Phi flags using it as a
2450 // replacement of the other Phi instruction |other|.
2452 // When dealing with usage analysis, any Use will replace all other values,
2453 // such as Unused and Unknown. Unless both are Unused, the merge would be
2455 if (usageAnalysis_
== PhiUsage::Used
||
2456 other
->usageAnalysis_
== PhiUsage::Used
) {
2457 usageAnalysis_
= PhiUsage::Used
;
2458 } else if (usageAnalysis_
!= other
->usageAnalysis_
) {
2459 // this == unused && other == unknown
2460 // or this == unknown && other == unused
2461 usageAnalysis_
= PhiUsage::Unknown
;
2463 // this == unused && other == unused
2464 // or this == unknown && other = unknown
2465 MOZ_ASSERT(usageAnalysis_
== PhiUsage::Unused
||
2466 usageAnalysis_
== PhiUsage::Unknown
);
2467 MOZ_ASSERT(usageAnalysis_
== other
->usageAnalysis_
);
2472 bool MPhi::markIteratorPhis(const PhiVector
& iterators
) {
2473 // Find and mark phis that must transitively hold an iterator live.
2475 Vector
<MPhi
*, 8, SystemAllocPolicy
> worklist
;
2477 for (MPhi
* iter
: iterators
) {
2478 if (!iter
->isInWorklist()) {
2479 if (!worklist
.append(iter
)) {
2482 iter
->setInWorklist();
2486 while (!worklist
.empty()) {
2487 MPhi
* phi
= worklist
.popCopy();
2488 phi
->setNotInWorklist();
2491 phi
->setImplicitlyUsedUnchecked();
2493 for (MUseDefIterator
iter(phi
); iter
; iter
++) {
2494 MDefinition
* use
= iter
.def();
2495 if (!use
->isInWorklist() && use
->isPhi() && !use
->toPhi()->isIterator()) {
2496 if (!worklist
.append(use
->toPhi())) {
2499 use
->setInWorklist();
2507 bool MPhi::typeIncludes(MDefinition
* def
) {
2508 MOZ_ASSERT(!IsMagicType(def
->type()));
2510 if (def
->type() == MIRType::Int32
&& this->type() == MIRType::Double
) {
2514 if (def
->type() == MIRType::Value
) {
2515 // This phi must be able to be any value.
2516 return this->type() == MIRType::Value
;
2519 return this->mightBeType(def
->type());
2522 void MCallBase::addArg(size_t argnum
, MDefinition
* arg
) {
2523 // The operand vector is initialized in reverse order by WarpBuilder.
2524 // It cannot be checked for consistency until all arguments are added.
2525 // FixedList doesn't initialize its elements, so do an unchecked init.
2526 initOperand(argnum
+ NumNonArgumentOperands
, arg
);
2529 static inline bool IsConstant(MDefinition
* def
, double v
) {
2530 if (!def
->isConstant()) {
2534 return NumbersAreIdentical(def
->toConstant()->numberToDouble(), v
);
2537 MDefinition
* MBinaryBitwiseInstruction::foldsTo(TempAllocator
& alloc
) {
2538 // Identity operations are removed (for int32 only) in foldUnnecessaryBitop.
2540 if (type() == MIRType::Int32
) {
2541 if (MDefinition
* folded
= EvaluateConstantOperands(alloc
, this)) {
2544 } else if (type() == MIRType::Int64
) {
2545 if (MDefinition
* folded
= EvaluateInt64ConstantOperands(alloc
, this)) {
2553 MDefinition
* MBinaryBitwiseInstruction::foldUnnecessaryBitop() {
2554 // It's probably OK to perform this optimization only for int32, as it will
2555 // have the greatest effect for asm.js code that is compiled with the JS
2556 // pipeline, and that code will not see int64 values.
2558 if (type() != MIRType::Int32
) {
2562 // Fold unsigned shift right operator when the second operand is zero and
2563 // the only use is an unsigned modulo. Thus, the expression
2564 // |(x >>> 0) % y| becomes |x % y|.
2565 if (isUrsh() && IsUint32Type(this)) {
2566 MDefinition
* defUse
= maybeSingleDefUse();
2567 if (defUse
&& defUse
->isMod() && defUse
->toMod()->isUnsigned()) {
2568 return getOperand(0);
2572 // Eliminate bitwise operations that are no-ops when used on integer
2573 // inputs, such as (x | 0).
2575 MDefinition
* lhs
= getOperand(0);
2576 MDefinition
* rhs
= getOperand(1);
2578 if (IsConstant(lhs
, 0)) {
2579 return foldIfZero(0);
2582 if (IsConstant(rhs
, 0)) {
2583 return foldIfZero(1);
2586 if (IsConstant(lhs
, -1)) {
2587 return foldIfNegOne(0);
2590 if (IsConstant(rhs
, -1)) {
2591 return foldIfNegOne(1);
2595 return foldIfEqual();
2598 if (maskMatchesRightRange
) {
2599 MOZ_ASSERT(lhs
->isConstant());
2600 MOZ_ASSERT(lhs
->type() == MIRType::Int32
);
2601 return foldIfAllBitsSet(0);
2604 if (maskMatchesLeftRange
) {
2605 MOZ_ASSERT(rhs
->isConstant());
2606 MOZ_ASSERT(rhs
->type() == MIRType::Int32
);
2607 return foldIfAllBitsSet(1);
2613 static inline bool CanProduceNegativeZero(MDefinition
* def
) {
2614 // Test if this instruction can produce negative zero even when bailing out
2615 // and changing types.
2616 switch (def
->op()) {
2617 case MDefinition::Opcode::Constant
:
2618 if (def
->type() == MIRType::Double
&&
2619 def
->toConstant()->toDouble() == -0.0) {
2623 case MDefinition::Opcode::BitAnd
:
2624 case MDefinition::Opcode::BitOr
:
2625 case MDefinition::Opcode::BitXor
:
2626 case MDefinition::Opcode::BitNot
:
2627 case MDefinition::Opcode::Lsh
:
2628 case MDefinition::Opcode::Rsh
:
2635 static inline bool NeedNegativeZeroCheck(MDefinition
* def
) {
2636 if (def
->isGuard() || def
->isGuardRangeBailouts()) {
2640 // Test if all uses have the same semantics for -0 and 0
2641 for (MUseIterator use
= def
->usesBegin(); use
!= def
->usesEnd(); use
++) {
2642 if (use
->consumer()->isResumePoint()) {
2646 MDefinition
* use_def
= use
->consumer()->toDefinition();
2647 switch (use_def
->op()) {
2648 case MDefinition::Opcode::Add
: {
2649 // If add is truncating -0 and 0 are observed as the same.
2650 if (use_def
->toAdd()->isTruncated()) {
2654 // x + y gives -0, when both x and y are -0
2656 // Figure out the order in which the addition's operands will
2657 // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR
2658 // definitions for us so that this just requires comparing ids.
2659 MDefinition
* first
= use_def
->toAdd()->lhs();
2660 MDefinition
* second
= use_def
->toAdd()->rhs();
2661 if (first
->id() > second
->id()) {
2662 std::swap(first
, second
);
2664 // Negative zero checks can be removed on the first executed
2665 // operand only if it is guaranteed the second executed operand
2666 // will produce a value other than -0. While the second is
2667 // typed as an int32, a bailout taken between execution of the
2668 // operands may change that type and cause a -0 to flow to the
2671 // There is no way to test whether there are any bailouts
2672 // between execution of the operands, so remove negative
2673 // zero checks from the first only if the second's type is
2674 // independent from type changes that may occur after bailing.
2675 if (def
== first
&& CanProduceNegativeZero(second
)) {
2679 // The negative zero check can always be removed on the second
2680 // executed operand; by the time this executes the first will have
2681 // been evaluated as int32 and the addition's result cannot be -0.
2684 case MDefinition::Opcode::Sub
: {
2685 // If sub is truncating -0 and 0 are observed as the same
2686 if (use_def
->toSub()->isTruncated()) {
2690 // x + y gives -0, when x is -0 and y is 0
2692 // We can remove the negative zero check on the rhs, only if we
2693 // are sure the lhs isn't negative zero.
2695 // The lhs is typed as integer (i.e. not -0.0), but it can bailout
2696 // and change type. This should be fine if the lhs is executed
2697 // first. However if the rhs is executed first, the lhs can bail,
2698 // change type and become -0.0 while the rhs has already been
2699 // optimized to not make a difference between zero and negative zero.
2700 MDefinition
* lhs
= use_def
->toSub()->lhs();
2701 MDefinition
* rhs
= use_def
->toSub()->rhs();
2702 if (rhs
->id() < lhs
->id() && CanProduceNegativeZero(lhs
)) {
2708 case MDefinition::Opcode::StoreElement
:
2709 case MDefinition::Opcode::StoreHoleValueElement
:
2710 case MDefinition::Opcode::LoadElement
:
2711 case MDefinition::Opcode::LoadElementHole
:
2712 case MDefinition::Opcode::LoadUnboxedScalar
:
2713 case MDefinition::Opcode::LoadDataViewElement
:
2714 case MDefinition::Opcode::LoadTypedArrayElementHole
:
2715 case MDefinition::Opcode::CharCodeAt
:
2716 case MDefinition::Opcode::Mod
:
2717 case MDefinition::Opcode::InArray
:
2718 // Only allowed to remove check when definition is the second operand
2719 if (use_def
->getOperand(0) == def
) {
2722 for (size_t i
= 2, e
= use_def
->numOperands(); i
< e
; i
++) {
2723 if (use_def
->getOperand(i
) == def
) {
2728 case MDefinition::Opcode::BoundsCheck
:
2729 // Only allowed to remove check when definition is the first operand
2730 if (use_def
->toBoundsCheck()->getOperand(1) == def
) {
2734 case MDefinition::Opcode::ToString
:
2735 case MDefinition::Opcode::FromCharCode
:
2736 case MDefinition::Opcode::FromCodePoint
:
2737 case MDefinition::Opcode::TableSwitch
:
2738 case MDefinition::Opcode::Compare
:
2739 case MDefinition::Opcode::BitAnd
:
2740 case MDefinition::Opcode::BitOr
:
2741 case MDefinition::Opcode::BitXor
:
2742 case MDefinition::Opcode::Abs
:
2743 case MDefinition::Opcode::TruncateToInt32
:
2744 // Always allowed to remove check. No matter which operand.
2746 case MDefinition::Opcode::StoreElementHole
:
2747 case MDefinition::Opcode::StoreTypedArrayElementHole
:
2748 case MDefinition::Opcode::PostWriteElementBarrier
:
2749 // Only allowed to remove check when definition is the third operand.
2750 for (size_t i
= 0, e
= use_def
->numOperands(); i
< e
; i
++) {
2754 if (use_def
->getOperand(i
) == def
) {
2767 void MBinaryArithInstruction::printOpcode(GenericPrinter
& out
) const {
2768 MDefinition::printOpcode(out
);
2771 case MIRType::Int32
:
2773 out
.printf(" [%s]", toDiv()->isUnsigned() ? "uint32" : "int32");
2774 } else if (isMod()) {
2775 out
.printf(" [%s]", toMod()->isUnsigned() ? "uint32" : "int32");
2777 out
.printf(" [int32]");
2780 case MIRType::Int64
:
2782 out
.printf(" [%s]", toDiv()->isUnsigned() ? "uint64" : "int64");
2783 } else if (isMod()) {
2784 out
.printf(" [%s]", toMod()->isUnsigned() ? "uint64" : "int64");
2786 out
.printf(" [int64]");
2789 case MIRType::Float32
:
2790 out
.printf(" [float]");
2792 case MIRType::Double
:
2793 out
.printf(" [double]");
2801 MDefinition
* MRsh::foldsTo(TempAllocator
& alloc
) {
2802 MDefinition
* f
= MBinaryBitwiseInstruction::foldsTo(alloc
);
2808 MDefinition
* lhs
= getOperand(0);
2809 MDefinition
* rhs
= getOperand(1);
2811 // It's probably OK to perform this optimization only for int32, as it will
2812 // have the greatest effect for asm.js code that is compiled with the JS
2813 // pipeline, and that code will not see int64 values.
2815 if (!lhs
->isLsh() || !rhs
->isConstant() || rhs
->type() != MIRType::Int32
) {
2819 if (!lhs
->getOperand(1)->isConstant() ||
2820 lhs
->getOperand(1)->type() != MIRType::Int32
) {
2824 uint32_t shift
= rhs
->toConstant()->toInt32();
2825 uint32_t shift_lhs
= lhs
->getOperand(1)->toConstant()->toInt32();
2826 if (shift
!= shift_lhs
) {
2832 return MSignExtendInt32::New(alloc
, lhs
->getOperand(0),
2833 MSignExtendInt32::Half
);
2835 return MSignExtendInt32::New(alloc
, lhs
->getOperand(0),
2836 MSignExtendInt32::Byte
);
2842 MDefinition
* MBinaryArithInstruction::foldsTo(TempAllocator
& alloc
) {
2843 MOZ_ASSERT(IsNumberType(type()));
2845 MDefinition
* lhs
= getOperand(0);
2846 MDefinition
* rhs
= getOperand(1);
2848 if (type() == MIRType::Int64
) {
2849 MOZ_ASSERT(!isTruncated());
2851 if (MConstant
* folded
= EvaluateInt64ConstantOperands(alloc
, this)) {
2852 if (!folded
->block()) {
2853 block()->insertBefore(this, folded
);
2857 if (isSub() || isDiv() || isMod()) {
2860 if (rhs
->isConstant() &&
2861 rhs
->toConstant()->toInt64() == int64_t(getIdentity())) {
2864 if (lhs
->isConstant() &&
2865 lhs
->toConstant()->toInt64() == int64_t(getIdentity())) {
2871 if (MConstant
* folded
= EvaluateConstantOperands(alloc
, this)) {
2872 if (isTruncated()) {
2873 if (!folded
->block()) {
2874 block()->insertBefore(this, folded
);
2876 if (folded
->type() != MIRType::Int32
) {
2877 return MTruncateToInt32::New(alloc
, folded
);
2883 if (MConstant
* folded
= EvaluateConstantNaNOperand(this)) {
2884 MOZ_ASSERT(!isTruncated());
2888 if (mustPreserveNaN_
) {
2892 // 0 + -0 = 0. So we can't remove addition
2893 if (isAdd() && type() != MIRType::Int32
) {
2897 if (IsConstant(rhs
, getIdentity())) {
2898 if (isTruncated()) {
2899 return MTruncateToInt32::New(alloc
, lhs
);
2904 // subtraction isn't commutative. So we can't remove subtraction when lhs
2910 if (IsConstant(lhs
, getIdentity())) {
2911 if (isTruncated()) {
2912 return MTruncateToInt32::New(alloc
, rhs
);
2914 return rhs
; // id op x => x
2920 void MBinaryArithInstruction::trySpecializeFloat32(TempAllocator
& alloc
) {
2921 MOZ_ASSERT(IsNumberType(type()));
2923 // Do not use Float32 if we can use int32.
2924 if (type() == MIRType::Int32
) {
2928 if (EnsureFloatConsumersAndInputOrConvert(this, alloc
)) {
2929 setResultType(MIRType::Float32
);
2933 void MMinMax::trySpecializeFloat32(TempAllocator
& alloc
) {
2934 if (type() == MIRType::Int32
) {
2938 MDefinition
* left
= lhs();
2939 MDefinition
* right
= rhs();
2941 if ((left
->canProduceFloat32() ||
2942 (left
->isMinMax() && left
->type() == MIRType::Float32
)) &&
2943 (right
->canProduceFloat32() ||
2944 (right
->isMinMax() && right
->type() == MIRType::Float32
))) {
2945 setResultType(MIRType::Float32
);
2947 ConvertOperandsToDouble(this, alloc
);
2951 MDefinition
* MMinMax::foldsTo(TempAllocator
& alloc
) {
2952 MOZ_ASSERT(lhs()->type() == type());
2953 MOZ_ASSERT(rhs()->type() == type());
2955 if (lhs() == rhs()) {
2959 auto foldConstants
= [&alloc
](MDefinition
* lhs
, MDefinition
* rhs
,
2960 bool isMax
) -> MConstant
* {
2961 MOZ_ASSERT(lhs
->type() == rhs
->type());
2962 MOZ_ASSERT(lhs
->toConstant()->isTypeRepresentableAsDouble());
2963 MOZ_ASSERT(rhs
->toConstant()->isTypeRepresentableAsDouble());
2965 double lnum
= lhs
->toConstant()->numberToDouble();
2966 double rnum
= rhs
->toConstant()->numberToDouble();
2970 result
= js::math_max_impl(lnum
, rnum
);
2972 result
= js::math_min_impl(lnum
, rnum
);
2975 // The folded MConstant should maintain the same MIRType with the original
2977 if (lhs
->type() == MIRType::Int32
) {
2979 if (mozilla::NumberEqualsInt32(result
, &cast
)) {
2980 return MConstant::New(alloc
, Int32Value(cast
));
2984 if (lhs
->type() == MIRType::Float32
) {
2985 return MConstant::NewFloat32(alloc
, result
);
2987 MOZ_ASSERT(lhs
->type() == MIRType::Double
);
2988 return MConstant::New(alloc
, DoubleValue(result
));
2991 // Try to fold the following patterns when |x| and |y| are constants.
2993 // min(min(x, z), min(y, z)) = min(min(x, y), z)
2994 // max(max(x, z), max(y, z)) = max(max(x, y), z)
2995 // max(min(x, z), min(y, z)) = min(max(x, y), z)
2996 // min(max(x, z), max(y, z)) = max(min(x, y), z)
2997 if (lhs()->isMinMax() && rhs()->isMinMax()) {
2999 auto* left
= lhs()->toMinMax();
3000 auto* right
= rhs()->toMinMax();
3001 if (left
->isMax() != right
->isMax()) {
3008 if (left
->lhs() == right
->lhs()) {
3009 std::tie(x
, y
, z
) = std::tuple
{left
->rhs(), right
->rhs(), left
->lhs()};
3010 } else if (left
->lhs() == right
->rhs()) {
3011 std::tie(x
, y
, z
) = std::tuple
{left
->rhs(), right
->lhs(), left
->lhs()};
3012 } else if (left
->rhs() == right
->lhs()) {
3013 std::tie(x
, y
, z
) = std::tuple
{left
->lhs(), right
->rhs(), left
->rhs()};
3014 } else if (left
->rhs() == right
->rhs()) {
3015 std::tie(x
, y
, z
) = std::tuple
{left
->lhs(), right
->lhs(), left
->rhs()};
3020 if (!x
->isConstant() || !x
->toConstant()->isTypeRepresentableAsDouble() ||
3021 !y
->isConstant() || !y
->toConstant()->isTypeRepresentableAsDouble()) {
3025 if (auto* folded
= foldConstants(x
, y
, isMax())) {
3026 block()->insertBefore(this, folded
);
3027 return MMinMax::New(alloc
, folded
, z
, type(), left
->isMax());
3032 // Fold min/max operations with same inputs.
3033 if (lhs()->isMinMax() || rhs()->isMinMax()) {
3034 auto* other
= lhs()->isMinMax() ? lhs()->toMinMax() : rhs()->toMinMax();
3035 auto* operand
= lhs()->isMinMax() ? rhs() : lhs();
3037 if (operand
== other
->lhs() || operand
== other
->rhs()) {
3038 if (isMax() == other
->isMax()) {
3039 // min(x, min(x, y)) = min(x, y)
3040 // max(x, max(x, y)) = max(x, y)
3043 if (!IsFloatingPointType(type())) {
3044 // When neither value is NaN:
3045 // max(x, min(x, y)) = x
3046 // min(x, max(x, y)) = x
3048 // Ensure that any bailouts that we depend on to guarantee that |y| is
3049 // Int32 are not removed.
3050 auto* otherOp
= operand
== other
->lhs() ? other
->rhs() : other
->lhs();
3051 otherOp
->setGuardRangeBailoutsUnchecked();
3058 if (!lhs()->isConstant() && !rhs()->isConstant()) {
3062 // Directly apply math utility to compare the rhs() and lhs() when
3063 // they are both constants.
3064 if (lhs()->isConstant() && rhs()->isConstant()) {
3065 if (!lhs()->toConstant()->isTypeRepresentableAsDouble() ||
3066 !rhs()->toConstant()->isTypeRepresentableAsDouble()) {
3070 if (auto* folded
= foldConstants(lhs(), rhs(), isMax())) {
3075 MDefinition
* operand
= lhs()->isConstant() ? rhs() : lhs();
3076 MConstant
* constant
=
3077 lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant();
3079 if (operand
->isToDouble() &&
3080 operand
->getOperand(0)->type() == MIRType::Int32
) {
3081 // min(int32, cte >= INT32_MAX) = int32
3082 if (!isMax() && constant
->isTypeRepresentableAsDouble() &&
3083 constant
->numberToDouble() >= INT32_MAX
) {
3084 MLimitedTruncate
* limit
= MLimitedTruncate::New(
3085 alloc
, operand
->getOperand(0), TruncateKind::NoTruncate
);
3086 block()->insertBefore(this, limit
);
3087 MToDouble
* toDouble
= MToDouble::New(alloc
, limit
);
3091 // max(int32, cte <= INT32_MIN) = int32
3092 if (isMax() && constant
->isTypeRepresentableAsDouble() &&
3093 constant
->numberToDouble() <= INT32_MIN
) {
3094 MLimitedTruncate
* limit
= MLimitedTruncate::New(
3095 alloc
, operand
->getOperand(0), TruncateKind::NoTruncate
);
3096 block()->insertBefore(this, limit
);
3097 MToDouble
* toDouble
= MToDouble::New(alloc
, limit
);
3102 auto foldLength
= [](MDefinition
* operand
, MConstant
* constant
,
3103 bool isMax
) -> MDefinition
* {
3104 if ((operand
->isArrayLength() || operand
->isArrayBufferViewLength() ||
3105 operand
->isArgumentsLength() || operand
->isStringLength()) &&
3106 constant
->type() == MIRType::Int32
) {
3107 // (Array|ArrayBufferView|Arguments|String)Length is always >= 0.
3108 // max(array.length, cte <= 0) = array.length
3109 // min(array.length, cte <= 0) = cte
3110 if (constant
->toInt32() <= 0) {
3111 return isMax
? operand
: constant
;
3117 if (auto* folded
= foldLength(operand
, constant
, isMax())) {
3121 // Attempt to fold nested min/max operations which are produced by
3122 // self-hosted built-in functions.
3123 if (operand
->isMinMax()) {
3124 auto* other
= operand
->toMinMax();
3125 MOZ_ASSERT(other
->lhs()->type() == type());
3126 MOZ_ASSERT(other
->rhs()->type() == type());
3128 MConstant
* otherConstant
= nullptr;
3129 MDefinition
* otherOperand
= nullptr;
3130 if (other
->lhs()->isConstant()) {
3131 otherConstant
= other
->lhs()->toConstant();
3132 otherOperand
= other
->rhs();
3133 } else if (other
->rhs()->isConstant()) {
3134 otherConstant
= other
->rhs()->toConstant();
3135 otherOperand
= other
->lhs();
3138 if (otherConstant
&& constant
->isTypeRepresentableAsDouble() &&
3139 otherConstant
->isTypeRepresentableAsDouble()) {
3140 if (isMax() == other
->isMax()) {
3141 // Fold min(x, min(y, z)) to min(min(x, y), z) with constant min(x, y).
3142 // Fold max(x, max(y, z)) to max(max(x, y), z) with constant max(x, y).
3143 if (auto* left
= foldConstants(constant
, otherConstant
, isMax())) {
3144 block()->insertBefore(this, left
);
3145 return MMinMax::New(alloc
, left
, otherOperand
, type(), isMax());
3148 // Fold min(x, max(y, z)) to max(min(x, y), min(x, z)).
3149 // Fold max(x, min(y, z)) to min(max(x, y), max(x, z)).
3151 // But only do this when min(x, z) can also be simplified.
3152 if (auto* right
= foldLength(otherOperand
, constant
, isMax())) {
3153 if (auto* left
= foldConstants(constant
, otherConstant
, isMax())) {
3154 block()->insertBefore(this, left
);
3155 return MMinMax::New(alloc
, left
, right
, type(), !isMax());
3166 void MMinMax::printOpcode(GenericPrinter
& out
) const {
3167 MDefinition::printOpcode(out
);
3168 out
.printf(" (%s)", isMax() ? "max" : "min");
3171 void MMinMaxArray::printOpcode(GenericPrinter
& out
) const {
3172 MDefinition::printOpcode(out
);
3173 out
.printf(" (%s)", isMax() ? "max" : "min");
3177 MDefinition
* MPow::foldsConstant(TempAllocator
& alloc
) {
3178 // Both `x` and `p` in `x^p` must be constants in order to precompute.
3179 if (!input()->isConstant() || !power()->isConstant()) {
3182 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3185 if (!input()->toConstant()->isTypeRepresentableAsDouble()) {
3189 double x
= input()->toConstant()->numberToDouble();
3190 double p
= power()->toConstant()->numberToDouble();
3191 double result
= js::ecmaPow(x
, p
);
3192 if (type() == MIRType::Int32
) {
3194 if (!mozilla::NumberIsInt32(result
, &cast
)) {
3195 // Reject folding if the result isn't an int32, because we'll bail anyway.
3198 return MConstant::New(alloc
, Int32Value(cast
));
3200 return MConstant::New(alloc
, DoubleValue(result
));
3203 MDefinition
* MPow::foldsConstantPower(TempAllocator
& alloc
) {
3204 // If `p` in `x^p` isn't constant, we can't apply these folds.
3205 if (!power()->isConstant()) {
3208 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3212 MOZ_ASSERT(type() == MIRType::Double
|| type() == MIRType::Int32
);
3214 // NOTE: The optimizations must match the optimizations used in |js::ecmaPow|
3215 // resp. |js::powi| to avoid differential testing issues.
3217 double pow
= power()->toConstant()->numberToDouble();
3219 // Math.pow(x, 0.5) is a sqrt with edge-case detection.
3221 MOZ_ASSERT(type() == MIRType::Double
);
3222 return MPowHalf::New(alloc
, input());
3225 // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases.
3227 MOZ_ASSERT(type() == MIRType::Double
);
3228 MPowHalf
* half
= MPowHalf::New(alloc
, input());
3229 block()->insertBefore(this, half
);
3230 MConstant
* one
= MConstant::New(alloc
, DoubleValue(1.0));
3231 block()->insertBefore(this, one
);
3232 return MDiv::New(alloc
, one
, half
, MIRType::Double
);
3235 // Math.pow(x, 1) == x.
3240 auto multiply
= [this, &alloc
](MDefinition
* lhs
, MDefinition
* rhs
) {
3241 MMul
* mul
= MMul::New(alloc
, lhs
, rhs
, type());
3242 mul
->setBailoutKind(bailoutKind());
3244 // Multiplying the same number can't yield negative zero.
3245 mul
->setCanBeNegativeZero(lhs
!= rhs
&& canBeNegativeZero());
3249 // Math.pow(x, 2) == x*x.
3251 return multiply(input(), input());
3254 // Math.pow(x, 3) == x*x*x.
3256 MMul
* mul1
= multiply(input(), input());
3257 block()->insertBefore(this, mul1
);
3258 return multiply(input(), mul1
);
3261 // Math.pow(x, 4) == y*y, where y = x*x.
3263 MMul
* y
= multiply(input(), input());
3264 block()->insertBefore(this, y
);
3265 return multiply(y
, y
);
3268 // Math.pow(x, NaN) == NaN.
3269 if (std::isnan(pow
)) {
3277 MDefinition
* MPow::foldsTo(TempAllocator
& alloc
) {
3278 if (MDefinition
* def
= foldsConstant(alloc
)) {
3281 if (MDefinition
* def
= foldsConstantPower(alloc
)) {
3287 MDefinition
* MBigIntPow::foldsTo(TempAllocator
& alloc
) {
3289 MOZ_ASSERT(base
->type() == MIRType::BigInt
);
3291 auto* power
= rhs();
3292 MOZ_ASSERT(power
->type() == MIRType::BigInt
);
3294 // |power| must be a constant.
3295 if (!power
->isConstant()) {
3300 if (BigInt::isInt32(power
->toConstant()->toBigInt(), &pow
)) {
3308 auto* mul
= MBigIntMul::New(alloc
, base
, base
);
3309 mul
->setBailoutKind(bailoutKind());
3318 bool MBigIntPtrBinaryArithInstruction::isMaybeZero(MDefinition
* ins
) {
3319 MOZ_ASSERT(ins
->type() == MIRType::IntPtr
);
3320 if (ins
->isBigIntToIntPtr()) {
3321 ins
= ins
->toBigIntToIntPtr()->input();
3323 if (ins
->isConstant()) {
3324 if (ins
->type() == MIRType::IntPtr
) {
3325 return ins
->toConstant()->toIntPtr() == 0;
3327 MOZ_ASSERT(ins
->type() == MIRType::BigInt
);
3328 return ins
->toConstant()->toBigInt()->isZero();
3333 bool MBigIntPtrBinaryArithInstruction::isMaybeNegative(MDefinition
* ins
) {
3334 MOZ_ASSERT(ins
->type() == MIRType::IntPtr
);
3335 if (ins
->isBigIntToIntPtr()) {
3336 ins
= ins
->toBigIntToIntPtr()->input();
3338 if (ins
->isConstant()) {
3339 if (ins
->type() == MIRType::IntPtr
) {
3340 return ins
->toConstant()->toIntPtr() < 0;
3342 MOZ_ASSERT(ins
->type() == MIRType::BigInt
);
3343 return ins
->toConstant()->toBigInt()->isNegative();
3348 MDefinition
* MInt32ToIntPtr::foldsTo(TempAllocator
& alloc
) {
3349 MDefinition
* def
= input();
3350 if (def
->isConstant()) {
3351 int32_t i
= def
->toConstant()->toInt32();
3352 return MConstant::NewIntPtr(alloc
, intptr_t(i
));
3355 if (def
->isNonNegativeIntPtrToInt32()) {
3356 return def
->toNonNegativeIntPtrToInt32()->input();
3362 bool MAbs::fallible() const {
3363 return !implicitTruncate_
&& (!range() || !range()->hasInt32Bounds());
3366 void MAbs::trySpecializeFloat32(TempAllocator
& alloc
) {
3367 // Do not use Float32 if we can use int32.
3368 if (input()->type() == MIRType::Int32
) {
3372 if (EnsureFloatConsumersAndInputOrConvert(this, alloc
)) {
3373 setResultType(MIRType::Float32
);
3377 MDefinition
* MDiv::foldsTo(TempAllocator
& alloc
) {
3378 MOZ_ASSERT(IsNumberType(type()));
3380 if (type() == MIRType::Int64
) {
3381 if (MDefinition
* folded
= EvaluateInt64ConstantOperands(alloc
, this)) {
3387 if (MDefinition
* folded
= EvaluateConstantOperands(alloc
, this)) {
3391 if (MDefinition
* folded
= EvaluateExactReciprocal(alloc
, this)) {
3398 void MDiv::analyzeEdgeCasesForward() {
3399 // This is only meaningful when doing integer division.
3400 if (type() != MIRType::Int32
) {
3404 MOZ_ASSERT(lhs()->type() == MIRType::Int32
);
3405 MOZ_ASSERT(rhs()->type() == MIRType::Int32
);
3407 // Try removing divide by zero check
3408 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
3409 canBeDivideByZero_
= false;
3412 // If lhs is a constant int != INT32_MIN, then
3413 // negative overflow check can be skipped.
3414 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(INT32_MIN
)) {
3415 canBeNegativeOverflow_
= false;
3418 // If rhs is a constant int != -1, likewise.
3419 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(-1)) {
3420 canBeNegativeOverflow_
= false;
3423 // If lhs is != 0, then negative zero check can be skipped.
3424 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(0)) {
3425 setCanBeNegativeZero(false);
3428 // If rhs is >= 0, likewise.
3429 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32
) {
3430 if (rhs()->toConstant()->toInt32() >= 0) {
3431 setCanBeNegativeZero(false);
3436 void MDiv::analyzeEdgeCasesBackward() {
3437 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) {
3438 setCanBeNegativeZero(false);
3442 bool MDiv::fallible() const { return !isTruncated(); }
3444 MDefinition
* MMod::foldsTo(TempAllocator
& alloc
) {
3445 MOZ_ASSERT(IsNumberType(type()));
3447 if (type() == MIRType::Int64
) {
3448 if (MDefinition
* folded
= EvaluateInt64ConstantOperands(alloc
, this)) {
3452 if (MDefinition
* folded
= EvaluateConstantOperands(alloc
, this)) {
3459 void MMod::analyzeEdgeCasesForward() {
3460 // These optimizations make sense only for integer division
3461 if (type() != MIRType::Int32
) {
3465 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
3466 canBeDivideByZero_
= false;
3469 if (rhs()->isConstant()) {
3470 int32_t n
= rhs()->toConstant()->toInt32();
3471 if (n
> 0 && !IsPowerOfTwo(uint32_t(n
))) {
3472 canBePowerOfTwoDivisor_
= false;
3477 bool MMod::fallible() const {
3478 return !isTruncated() &&
3479 (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend());
3482 void MMathFunction::trySpecializeFloat32(TempAllocator
& alloc
) {
3483 if (EnsureFloatConsumersAndInputOrConvert(this, alloc
)) {
3484 setResultType(MIRType::Float32
);
3485 specialization_
= MIRType::Float32
;
3489 bool MMathFunction::isFloat32Commutative() const {
3490 switch (function_
) {
3491 case UnaryMathFunction::Floor
:
3492 case UnaryMathFunction::Ceil
:
3493 case UnaryMathFunction::Round
:
3494 case UnaryMathFunction::Trunc
:
3501 MHypot
* MHypot::New(TempAllocator
& alloc
, const MDefinitionVector
& vector
) {
3502 uint32_t length
= vector
.length();
3503 MHypot
* hypot
= new (alloc
) MHypot
;
3504 if (!hypot
->init(alloc
, length
)) {
3508 for (uint32_t i
= 0; i
< length
; ++i
) {
3509 hypot
->initOperand(i
, vector
[i
]);
3514 bool MAdd::fallible() const {
3515 // the add is fallible if range analysis does not say that it is finite, AND
3516 // either the truncation analysis shows that there are non-truncated uses.
3517 if (truncateKind() >= TruncateKind::IndirectTruncate
) {
3520 if (range() && range()->hasInt32Bounds()) {
3526 bool MSub::fallible() const {
3527 // see comment in MAdd::fallible()
3528 if (truncateKind() >= TruncateKind::IndirectTruncate
) {
3531 if (range() && range()->hasInt32Bounds()) {
3537 MDefinition
* MSub::foldsTo(TempAllocator
& alloc
) {
3538 MDefinition
* out
= MBinaryArithInstruction::foldsTo(alloc
);
3543 if (type() != MIRType::Int32
) {
3547 // Optimize X - X to 0. This optimization is only valid for Int32
3548 // values. Subtracting a floating point value from itself returns
3549 // NaN when the operand is either Infinity or NaN.
3550 if (lhs() == rhs()) {
3551 // Ensure that any bailouts that we depend on to guarantee that X
3552 // is Int32 are not removed.
3553 lhs()->setGuardRangeBailoutsUnchecked();
3554 return MConstant::New(alloc
, Int32Value(0));
3560 MDefinition
* MMul::foldsTo(TempAllocator
& alloc
) {
3561 MDefinition
* out
= MBinaryArithInstruction::foldsTo(alloc
);
3566 if (type() != MIRType::Int32
) {
3570 if (lhs() == rhs()) {
3571 setCanBeNegativeZero(false);
3577 void MMul::analyzeEdgeCasesForward() {
3578 // Try to remove the check for negative zero
3579 // This only makes sense when using the integer multiplication
3580 if (type() != MIRType::Int32
) {
3584 // If lhs is > 0, no need for negative zero check.
3585 if (lhs()->isConstant() && lhs()->type() == MIRType::Int32
) {
3586 if (lhs()->toConstant()->toInt32() > 0) {
3587 setCanBeNegativeZero(false);
3591 // If rhs is > 0, likewise.
3592 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32
) {
3593 if (rhs()->toConstant()->toInt32() > 0) {
3594 setCanBeNegativeZero(false);
3599 void MMul::analyzeEdgeCasesBackward() {
3600 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) {
3601 setCanBeNegativeZero(false);
3605 bool MMul::canOverflow() const {
3606 if (isTruncated()) {
3609 return !range() || !range()->hasInt32Bounds();
3612 bool MUrsh::fallible() const {
3613 if (bailoutsDisabled()) {
3616 return !range() || !range()->hasInt32Bounds();
3619 MIRType
MCompare::inputType() {
3620 switch (compareType_
) {
3621 case Compare_Undefined
:
3622 return MIRType::Undefined
;
3624 return MIRType::Null
;
3625 case Compare_UInt32
:
3627 return MIRType::Int32
;
3628 case Compare_IntPtr
:
3629 case Compare_UIntPtr
:
3630 return MIRType::IntPtr
;
3631 case Compare_Double
:
3632 return MIRType::Double
;
3633 case Compare_Float32
:
3634 return MIRType::Float32
;
3635 case Compare_String
:
3636 return MIRType::String
;
3637 case Compare_Symbol
:
3638 return MIRType::Symbol
;
3639 case Compare_Object
:
3640 return MIRType::Object
;
3641 case Compare_BigInt
:
3642 case Compare_BigInt_Int32
:
3643 case Compare_BigInt_Double
:
3644 case Compare_BigInt_String
:
3645 return MIRType::BigInt
;
3647 MOZ_CRASH("No known conversion");
3651 static inline bool MustBeUInt32(MDefinition
* def
, MDefinition
** pwrapped
) {
3652 if (def
->isUrsh()) {
3653 *pwrapped
= def
->toUrsh()->lhs();
3654 MDefinition
* rhs
= def
->toUrsh()->rhs();
3655 return def
->toUrsh()->bailoutsDisabled() && rhs
->maybeConstantValue() &&
3656 rhs
->maybeConstantValue()->isInt32(0);
3659 if (MConstant
* defConst
= def
->maybeConstantValue()) {
3660 *pwrapped
= defConst
;
3661 return defConst
->type() == MIRType::Int32
&& defConst
->toInt32() >= 0;
3664 *pwrapped
= nullptr; // silence GCC warning
3669 bool MBinaryInstruction::unsignedOperands(MDefinition
* left
,
3670 MDefinition
* right
) {
3671 MDefinition
* replace
;
3672 if (!MustBeUInt32(left
, &replace
)) {
3675 if (replace
->type() != MIRType::Int32
) {
3678 if (!MustBeUInt32(right
, &replace
)) {
3681 if (replace
->type() != MIRType::Int32
) {
3687 bool MBinaryInstruction::unsignedOperands() {
3688 return unsignedOperands(getOperand(0), getOperand(1));
3691 void MBinaryInstruction::replaceWithUnsignedOperands() {
3692 MOZ_ASSERT(unsignedOperands());
3694 for (size_t i
= 0; i
< numOperands(); i
++) {
3695 MDefinition
* replace
;
3696 MustBeUInt32(getOperand(i
), &replace
);
3697 if (replace
== getOperand(i
)) {
3701 getOperand(i
)->setImplicitlyUsedUnchecked();
3702 replaceOperand(i
, replace
);
3706 MDefinition
* MBitNot::foldsTo(TempAllocator
& alloc
) {
3707 if (type() == MIRType::Int64
) {
3710 MOZ_ASSERT(type() == MIRType::Int32
);
3712 MDefinition
* input
= getOperand(0);
3714 if (input
->isConstant()) {
3715 js::Value v
= Int32Value(~(input
->toConstant()->toInt32()));
3716 return MConstant::New(alloc
, v
);
3719 if (input
->isBitNot()) {
3720 MOZ_ASSERT(input
->toBitNot()->type() == MIRType::Int32
);
3721 MOZ_ASSERT(input
->toBitNot()->getOperand(0)->type() == MIRType::Int32
);
3722 return MTruncateToInt32::New(alloc
,
3723 input
->toBitNot()->input()); // ~~x => x | 0
3729 static void AssertKnownClass(TempAllocator
& alloc
, MInstruction
* ins
,
3732 const JSClass
* clasp
= GetObjectKnownJSClass(obj
);
3735 auto* assert = MAssertClass::New(alloc
, obj
, clasp
);
3736 ins
->block()->insertBefore(ins
, assert);
3740 MDefinition
* MBoxNonStrictThis::foldsTo(TempAllocator
& alloc
) {
3741 MDefinition
* in
= input();
3743 in
= in
->toBox()->input();
3746 if (in
->type() == MIRType::Object
) {
3753 AliasSet
MLoadArgumentsObjectArg::getAliasSet() const {
3754 return AliasSet::Load(AliasSet::Any
);
3757 AliasSet
MLoadArgumentsObjectArgHole::getAliasSet() const {
3758 return AliasSet::Load(AliasSet::Any
);
3761 AliasSet
MInArgumentsObjectArg::getAliasSet() const {
3762 // Loads |arguments.length|, but not the actual element, so we can use the
3763 // same alias-set as MArgumentsObjectLength.
3764 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
3765 AliasSet::DynamicSlot
);
3768 AliasSet
MArgumentsObjectLength::getAliasSet() const {
3769 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
3770 AliasSet::DynamicSlot
);
3773 bool MGuardArgumentsObjectFlags::congruentTo(const MDefinition
* ins
) const {
3774 if (!ins
->isGuardArgumentsObjectFlags() ||
3775 ins
->toGuardArgumentsObjectFlags()->flags() != flags()) {
3778 return congruentIfOperandsEqual(ins
);
3781 AliasSet
MGuardArgumentsObjectFlags::getAliasSet() const {
3782 // The flags are packed with the length in a fixed private slot.
3783 return AliasSet::Load(AliasSet::FixedSlot
);
3786 MDefinition
* MIdToStringOrSymbol::foldsTo(TempAllocator
& alloc
) {
3787 if (idVal()->isBox()) {
3788 auto* input
= idVal()->toBox()->input();
3789 MIRType idType
= input
->type();
3790 if (idType
== MIRType::String
|| idType
== MIRType::Symbol
) {
3793 if (idType
== MIRType::Int32
) {
3795 MToString::New(alloc
, input
, MToString::SideEffectHandling::Bailout
);
3796 block()->insertBefore(this, toString
);
3798 return MBox::New(alloc
, toString
);
3805 MDefinition
* MReturnFromCtor::foldsTo(TempAllocator
& alloc
) {
3806 MDefinition
* rval
= value();
3807 if (rval
->isBox()) {
3808 rval
= rval
->toBox()->input();
3811 if (rval
->type() == MIRType::Object
) {
3815 if (rval
->type() != MIRType::Value
) {
3822 MDefinition
* MTypeOf::foldsTo(TempAllocator
& alloc
) {
3823 MDefinition
* unboxed
= input();
3824 if (unboxed
->isBox()) {
3825 unboxed
= unboxed
->toBox()->input();
3829 switch (unboxed
->type()) {
3830 case MIRType::Double
:
3831 case MIRType::Float32
:
3832 case MIRType::Int32
:
3833 type
= JSTYPE_NUMBER
;
3835 case MIRType::String
:
3836 type
= JSTYPE_STRING
;
3838 case MIRType::Symbol
:
3839 type
= JSTYPE_SYMBOL
;
3841 case MIRType::BigInt
:
3842 type
= JSTYPE_BIGINT
;
3845 type
= JSTYPE_OBJECT
;
3847 case MIRType::Undefined
:
3848 type
= JSTYPE_UNDEFINED
;
3850 case MIRType::Boolean
:
3851 type
= JSTYPE_BOOLEAN
;
3853 case MIRType::Object
: {
3854 KnownClass known
= GetObjectKnownClass(unboxed
);
3855 if (known
!= KnownClass::None
) {
3856 if (known
== KnownClass::Function
) {
3857 type
= JSTYPE_FUNCTION
;
3859 type
= JSTYPE_OBJECT
;
3862 AssertKnownClass(alloc
, this, unboxed
);
3871 return MConstant::New(alloc
, Int32Value(static_cast<int32_t>(type
)));
3874 MDefinition
* MTypeOfName::foldsTo(TempAllocator
& alloc
) {
3875 MOZ_ASSERT(input()->type() == MIRType::Int32
);
3877 if (!input()->isConstant()) {
3881 static_assert(JSTYPE_UNDEFINED
== 0);
3883 int32_t type
= input()->toConstant()->toInt32();
3884 MOZ_ASSERT(JSTYPE_UNDEFINED
<= type
&& type
< JSTYPE_LIMIT
);
3887 TypeName(static_cast<JSType
>(type
), GetJitContext()->runtime
->names());
3888 return MConstant::New(alloc
, StringValue(name
));
3891 MUrsh
* MUrsh::NewWasm(TempAllocator
& alloc
, MDefinition
* left
,
3892 MDefinition
* right
, MIRType type
) {
3893 MUrsh
* ins
= new (alloc
) MUrsh(left
, right
, type
);
3895 // Since Ion has no UInt32 type, we use Int32 and we have a special
3896 // exception to the type rules: we can return values in
3897 // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
3898 // without bailing out. This is necessary because Ion has no UInt32
3899 // type and we can't have bailouts in wasm code.
3900 ins
->bailoutsDisabled_
= true;
3905 MResumePoint
* MResumePoint::New(TempAllocator
& alloc
, MBasicBlock
* block
,
3906 jsbytecode
* pc
, ResumeMode mode
) {
3907 MResumePoint
* resume
= new (alloc
) MResumePoint(block
, pc
, mode
);
3908 if (!resume
->init(alloc
)) {
3909 block
->discardPreAllocatedResumePoint(resume
);
3912 resume
->inherit(block
);
3916 MResumePoint::MResumePoint(MBasicBlock
* block
, jsbytecode
* pc
, ResumeMode mode
)
3917 : MNode(block
, Kind::ResumePoint
),
3919 instruction_(nullptr),
3921 block
->addResumePoint(this);
3924 bool MResumePoint::init(TempAllocator
& alloc
) {
3925 return operands_
.init(alloc
, block()->stackDepth());
3928 MResumePoint
* MResumePoint::caller() const {
3929 return block()->callerResumePoint();
3932 void MResumePoint::inherit(MBasicBlock
* block
) {
3933 // FixedList doesn't initialize its elements, so do unchecked inits.
3934 for (size_t i
= 0; i
< stackDepth(); i
++) {
3935 initOperand(i
, block
->getSlot(i
));
3939 void MResumePoint::addStore(TempAllocator
& alloc
, MDefinition
* store
,
3940 const MResumePoint
* cache
) {
3941 MOZ_ASSERT(block()->outerResumePoint() != this);
3942 MOZ_ASSERT_IF(cache
, !cache
->stores_
.empty());
3944 if (cache
&& cache
->stores_
.begin()->operand
== store
) {
3945 // If the last resume point had the same side-effect stack, then we can
3946 // reuse the current side effect without cloning it. This is a simple
3947 // way to share common context by making a spaghetti stack.
3948 if (++cache
->stores_
.begin() == stores_
.begin()) {
3949 stores_
.copy(cache
->stores_
);
3954 // Ensure that the store would not be deleted by DCE.
3955 MOZ_ASSERT(store
->isEffectful());
3957 MStoreToRecover
* top
= new (alloc
) MStoreToRecover(store
);
3962 void MResumePoint::dump(GenericPrinter
& out
) const {
3963 out
.printf("resumepoint mode=");
3966 case ResumeMode::ResumeAt
:
3968 out
.printf("ResumeAt(%u)", instruction_
->id());
3970 out
.printf("ResumeAt");
3974 out
.put(ResumeModeToString(mode()));
3978 if (MResumePoint
* c
= caller()) {
3979 out
.printf(" (caller in block%u)", c
->block()->id());
3982 for (size_t i
= 0; i
< numOperands(); i
++) {
3984 if (operands_
[i
].hasProducer()) {
3985 getOperand(i
)->printName(out
);
3987 out
.printf("(null)");
3993 void MResumePoint::dump() const {
3994 Fprinter
out(stderr
);
4000 bool MResumePoint::isObservableOperand(MUse
* u
) const {
4001 return isObservableOperand(indexOf(u
));
4004 bool MResumePoint::isObservableOperand(size_t index
) const {
4005 return block()->info().isObservableSlot(index
);
4008 bool MResumePoint::isRecoverableOperand(MUse
* u
) const {
4009 return block()->info().isRecoverableOperand(indexOf(u
));
4012 MDefinition
* MBigIntToIntPtr::foldsTo(TempAllocator
& alloc
) {
4013 MDefinition
* def
= input();
4015 // If the operand converts an IntPtr to BigInt, drop both conversions.
4016 if (def
->isIntPtrToBigInt()) {
4017 return def
->toIntPtrToBigInt()->input();
4020 // Fold this operation if the input operand is constant.
4021 if (def
->isConstant()) {
4022 BigInt
* bigInt
= def
->toConstant()->toBigInt();
4024 if (BigInt::isIntPtr(bigInt
, &i
)) {
4025 return MConstant::NewIntPtr(alloc
, i
);
4029 // Fold BigIntToIntPtr(Int64ToBigInt(int64)) to Int64ToIntPtr(int64)
4030 if (def
->isInt64ToBigInt()) {
4031 auto* toBigInt
= def
->toInt64ToBigInt();
4032 return MInt64ToIntPtr::New(alloc
, toBigInt
->input(),
4033 toBigInt
->elementType());
4039 MDefinition
* MIntPtrToBigInt::foldsTo(TempAllocator
& alloc
) {
4040 MDefinition
* def
= input();
4042 // If the operand converts a BigInt to IntPtr, drop both conversions.
4043 if (def
->isBigIntToIntPtr()) {
4044 return def
->toBigIntToIntPtr()->input();
4050 MDefinition
* MTruncateBigIntToInt64::foldsTo(TempAllocator
& alloc
) {
4051 MDefinition
* input
= getOperand(0);
4053 if (input
->isBox()) {
4054 input
= input
->getOperand(0);
4057 // If the operand converts an I64 to BigInt, drop both conversions.
4058 if (input
->isInt64ToBigInt()) {
4059 return input
->getOperand(0);
4062 // If the operand converts an I32 to BigInt, extend the I32 to I64.
4063 if (input
->isInt32ToBigInt()) {
4064 auto* int32
= input
->toInt32ToBigInt()->input();
4065 if (int32
->isConstant()) {
4066 int32_t c
= int32
->toConstant()->toInt32();
4067 return MConstant::NewInt64(alloc
, int64_t(c
));
4069 return MExtendInt32ToInt64::New(alloc
, int32
, /* isUnsigned = */ false);
4072 // If the operand is an IntPtr, extend the IntPtr to I64.
4073 if (input
->isIntPtrToBigInt()) {
4074 auto* intPtr
= input
->toIntPtrToBigInt()->input();
4075 return MIntPtrToInt64::New(alloc
, intPtr
);
4078 // Fold this operation if the input operand is constant.
4079 if (input
->isConstant()) {
4080 return MConstant::NewInt64(
4081 alloc
, BigInt::toInt64(input
->toConstant()->toBigInt()));
4087 MDefinition
* MToInt64::foldsTo(TempAllocator
& alloc
) {
4088 MDefinition
* input
= getOperand(0);
4090 if (input
->isBox()) {
4091 input
= input
->getOperand(0);
4094 // Unwrap MInt64ToBigInt: MToInt64(MInt64ToBigInt(int64)) = int64.
4095 if (input
->isInt64ToBigInt()) {
4096 return input
->getOperand(0);
4099 // Unwrap Int32ToBigInt:
4100 // MToInt64(MInt32ToBigInt(int32)) = MExtendInt32ToInt64(int32).
4101 if (input
->isInt32ToBigInt()) {
4102 auto* int32
= input
->toInt32ToBigInt()->input();
4103 if (int32
->isConstant()) {
4104 int32_t c
= int32
->toConstant()->toInt32();
4105 return MConstant::NewInt64(alloc
, int64_t(c
));
4107 return MExtendInt32ToInt64::New(alloc
, int32
, /* isUnsigned = */ false);
4110 // When the input is an Int64 already, just return it.
4111 if (input
->type() == MIRType::Int64
) {
4115 // Fold this operation if the input operand is constant.
4116 if (input
->isConstant()) {
4117 switch (input
->type()) {
4118 case MIRType::Boolean
:
4119 return MConstant::NewInt64(alloc
, input
->toConstant()->toBoolean());
4128 MDefinition
* MToNumberInt32::foldsTo(TempAllocator
& alloc
) {
4129 // Fold this operation if the input operand is constant.
4130 if (MConstant
* cst
= input()->maybeConstantValue()) {
4131 switch (cst
->type()) {
4133 if (conversion() == IntConversionInputKind::Any
) {
4134 return MConstant::New(alloc
, Int32Value(0));
4137 case MIRType::Boolean
:
4138 if (conversion() == IntConversionInputKind::Any
) {
4139 return MConstant::New(alloc
, Int32Value(cst
->toBoolean()));
4142 case MIRType::Int32
:
4143 return MConstant::New(alloc
, Int32Value(cst
->toInt32()));
4144 case MIRType::Float32
:
4145 case MIRType::Double
:
4147 // Only the value within the range of Int32 can be substituted as
4149 if (mozilla::NumberIsInt32(cst
->numberToDouble(), &ival
)) {
4150 return MConstant::New(alloc
, Int32Value(ival
));
4158 MDefinition
* input
= getOperand(0);
4159 if (input
->isBox()) {
4160 input
= input
->toBox()->input();
4163 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4164 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4165 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4166 // is folded to a MTruncateToInt32 node, which will result in this MIR:
4167 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4168 // the MUrsh node's type is int32 (since uint32 is not implemented), and
4169 // that would fold the MTruncateToInt32 node. This will make the modulo
4170 // unsigned, while is should have been signed.
4171 if (input
->type() == MIRType::Int32
&& !IsUint32Type(input
)) {
4178 MDefinition
* MBooleanToInt32::foldsTo(TempAllocator
& alloc
) {
4179 MDefinition
* input
= getOperand(0);
4180 MOZ_ASSERT(input
->type() == MIRType::Boolean
);
4182 if (input
->isConstant()) {
4183 return MConstant::New(alloc
, Int32Value(input
->toConstant()->toBoolean()));
4189 void MToNumberInt32::analyzeEdgeCasesBackward() {
4190 if (!NeedNegativeZeroCheck(this)) {
4191 setNeedsNegativeZeroCheck(false);
4195 MDefinition
* MTruncateToInt32::foldsTo(TempAllocator
& alloc
) {
4196 MDefinition
* input
= getOperand(0);
4197 if (input
->isBox()) {
4198 input
= input
->getOperand(0);
4201 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4202 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4203 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4204 // is folded to a MTruncateToInt32 node, which will result in this MIR:
4205 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4206 // the MUrsh node's type is int32 (since uint32 is not implemented), and
4207 // that would fold the MTruncateToInt32 node. This will make the modulo
4208 // unsigned, while is should have been signed.
4209 if (input
->type() == MIRType::Int32
&& !IsUint32Type(input
)) {
4213 if (input
->type() == MIRType::Double
&& input
->isConstant()) {
4214 int32_t ret
= ToInt32(input
->toConstant()->toDouble());
4215 return MConstant::New(alloc
, Int32Value(ret
));
4221 MDefinition
* MWrapInt64ToInt32::foldsTo(TempAllocator
& alloc
) {
4222 MDefinition
* input
= this->input();
4223 if (input
->isConstant()) {
4224 uint64_t c
= input
->toConstant()->toInt64();
4225 int32_t output
= bottomHalf() ? int32_t(c
) : int32_t(c
>> 32);
4226 return MConstant::New(alloc
, Int32Value(output
));
4232 MDefinition
* MExtendInt32ToInt64::foldsTo(TempAllocator
& alloc
) {
4233 MDefinition
* input
= this->input();
4234 if (input
->isConstant()) {
4235 int32_t c
= input
->toConstant()->toInt32();
4236 int64_t res
= isUnsigned() ? int64_t(uint32_t(c
)) : int64_t(c
);
4237 return MConstant::NewInt64(alloc
, res
);
4243 MDefinition
* MSignExtendInt32::foldsTo(TempAllocator
& alloc
) {
4244 MDefinition
* input
= this->input();
4245 if (input
->isConstant()) {
4246 int32_t c
= input
->toConstant()->toInt32();
4250 res
= int32_t(int8_t(c
& 0xFF));
4253 res
= int32_t(int16_t(c
& 0xFFFF));
4256 return MConstant::New(alloc
, Int32Value(res
));
4262 MDefinition
* MSignExtendInt64::foldsTo(TempAllocator
& alloc
) {
4263 MDefinition
* input
= this->input();
4264 if (input
->isConstant()) {
4265 int64_t c
= input
->toConstant()->toInt64();
4269 res
= int64_t(int8_t(c
& 0xFF));
4272 res
= int64_t(int16_t(c
& 0xFFFF));
4275 res
= int64_t(int32_t(c
& 0xFFFFFFFFU
));
4278 return MConstant::NewInt64(alloc
, res
);
4284 MDefinition
* MToDouble::foldsTo(TempAllocator
& alloc
) {
4285 MDefinition
* input
= getOperand(0);
4286 if (input
->isBox()) {
4287 input
= input
->getOperand(0);
4290 if (input
->type() == MIRType::Double
) {
4294 if (input
->isConstant() &&
4295 input
->toConstant()->isTypeRepresentableAsDouble()) {
4296 return MConstant::New(alloc
,
4297 DoubleValue(input
->toConstant()->numberToDouble()));
4303 MDefinition
* MToFloat32::foldsTo(TempAllocator
& alloc
) {
4304 MDefinition
* input
= getOperand(0);
4305 if (input
->isBox()) {
4306 input
= input
->getOperand(0);
4309 if (input
->type() == MIRType::Float32
) {
4313 // If x is a Float32, Float32(Double(x)) == x
4314 if (!mustPreserveNaN_
&& input
->isToDouble() &&
4315 input
->toToDouble()->input()->type() == MIRType::Float32
) {
4316 return input
->toToDouble()->input();
4319 if (input
->isConstant() &&
4320 input
->toConstant()->isTypeRepresentableAsDouble()) {
4321 return MConstant::NewFloat32(alloc
,
4322 float(input
->toConstant()->numberToDouble()));
4325 // Fold ToFloat32(ToDouble(int32)) to ToFloat32(int32).
4326 if (input
->isToDouble() &&
4327 input
->toToDouble()->input()->type() == MIRType::Int32
) {
4328 return MToFloat32::New(alloc
, input
->toToDouble()->input());
4334 MDefinition
* MToFloat16::foldsTo(TempAllocator
& alloc
) {
4335 MDefinition
* in
= input();
4337 in
= in
->toBox()->input();
4340 if (in
->isConstant()) {
4341 auto* cst
= in
->toConstant();
4342 if (cst
->isTypeRepresentableAsDouble()) {
4343 double num
= cst
->numberToDouble();
4344 return MConstant::NewFloat32(alloc
, static_cast<float>(js::float16
{num
}));
4348 auto isFloat16
= [](auto* def
) -> MDefinition
* {
4349 // ToFloat16(ToDouble(float16)) => float16
4350 // ToFloat16(ToFloat32(float16)) => float16
4351 if (def
->isToDouble()) {
4352 def
= def
->toToDouble()->input();
4353 } else if (def
->isToFloat32()) {
4354 def
= def
->toToFloat32()->input();
4357 // ToFloat16(ToFloat16(x)) => ToFloat16(x)
4358 if (def
->isToFloat16()) {
4362 // ToFloat16(LoadFloat16(x)) => LoadFloat16(x)
4363 if (def
->isLoadUnboxedScalar() &&
4364 def
->toLoadUnboxedScalar()->storageType() == Scalar::Float16
) {
4367 if (def
->isLoadDataViewElement() &&
4368 def
->toLoadDataViewElement()->storageType() == Scalar::Float16
) {
4374 // Fold loads which are guaranteed to return Float16.
4375 if (auto* f16
= isFloat16(in
)) {
4379 // Fold ToFloat16(ToDouble(float32)) to ToFloat16(float32).
4380 // Fold ToFloat16(ToDouble(int32)) to ToFloat16(int32).
4381 if (in
->isToDouble()) {
4382 auto* toDoubleInput
= in
->toToDouble()->input();
4383 if (toDoubleInput
->type() == MIRType::Float32
||
4384 toDoubleInput
->type() == MIRType::Int32
) {
4385 return MToFloat16::New(alloc
, toDoubleInput
);
4392 MDefinition
* MToString::foldsTo(TempAllocator
& alloc
) {
4393 MDefinition
* in
= input();
4395 in
= in
->getOperand(0);
4398 if (in
->type() == MIRType::String
) {
4404 MDefinition
* MClampToUint8::foldsTo(TempAllocator
& alloc
) {
4405 if (MConstant
* inputConst
= input()->maybeConstantValue()) {
4406 if (inputConst
->isTypeRepresentableAsDouble()) {
4407 int32_t clamped
= ClampDoubleToUint8(inputConst
->numberToDouble());
4408 return MConstant::New(alloc
, Int32Value(clamped
));
4414 bool MCompare::tryFoldEqualOperands(bool* result
) {
4415 if (lhs() != rhs()) {
4419 // Intuitively somebody would think that if lhs === rhs,
4420 // then we can just return true. (Or false for !==)
4421 // However NaN !== NaN is true! So we spend some time trying
4422 // to eliminate this case.
4424 if (!IsEqualityOp(jsop())) {
4428 switch (compareType_
) {
4430 case Compare_UInt32
:
4432 case Compare_UInt64
:
4433 case Compare_IntPtr
:
4434 case Compare_UIntPtr
:
4435 case Compare_Float32
:
4436 case Compare_Double
:
4437 case Compare_String
:
4438 case Compare_Object
:
4439 case Compare_Symbol
:
4440 case Compare_BigInt
:
4441 case Compare_WasmAnyRef
:
4443 case Compare_Undefined
:
4445 case Compare_BigInt_Int32
:
4446 case Compare_BigInt_String
:
4447 case Compare_BigInt_Double
:
4448 MOZ_CRASH("Expecting different operands for lhs and rhs");
4451 if (isDoubleComparison() || isFloat32Comparison()) {
4452 if (!operandsAreNeverNaN()) {
4456 MOZ_ASSERT(!IsFloatingPointType(lhs()->type()));
4459 lhs()->setGuardRangeBailoutsUnchecked();
4461 *result
= (jsop() == JSOp::StrictEq
|| jsop() == JSOp::Eq
);
4465 static JSType
TypeOfName(const JSLinearString
* str
) {
4466 static constexpr std::array types
= {
4467 JSTYPE_UNDEFINED
, JSTYPE_OBJECT
, JSTYPE_FUNCTION
, JSTYPE_STRING
,
4468 JSTYPE_NUMBER
, JSTYPE_BOOLEAN
, JSTYPE_SYMBOL
, JSTYPE_BIGINT
,
4469 #ifdef ENABLE_RECORD_TUPLE
4470 JSTYPE_RECORD
, JSTYPE_TUPLE
,
4473 static_assert(types
.size() == JSTYPE_LIMIT
);
4475 const JSAtomState
& names
= GetJitContext()->runtime
->names();
4476 for (auto type
: types
) {
4477 if (EqualStrings(str
, TypeName(type
, names
))) {
4481 return JSTYPE_LIMIT
;
4484 struct TypeOfCompareInput
{
4485 // The `typeof expr` side of the comparison.
4486 // MTypeOfName for JSOp::Typeof/JSOp::TypeofExpr, and
4487 // MTypeOf for JSOp::TypeofEq (same pointer as typeOf).
4488 MDefinition
* typeOfSide
;
4490 // The actual `typeof` operation.
4493 // The string side of the comparison.
4496 // True if the comparison uses raw JSType (Generated for JSOp::TypeofEq).
4497 bool isIntComparison
;
4499 TypeOfCompareInput(MDefinition
* typeOfSide
, MTypeOf
* typeOf
, JSType type
,
4500 bool isIntComparison
)
4501 : typeOfSide(typeOfSide
),
4504 isIntComparison(isIntComparison
) {}
4507 static mozilla::Maybe
<TypeOfCompareInput
> IsTypeOfCompare(MCompare
* ins
) {
4508 if (!IsEqualityOp(ins
->jsop())) {
4509 return mozilla::Nothing();
4512 if (ins
->compareType() == MCompare::Compare_Int32
) {
4513 auto* lhs
= ins
->lhs();
4514 auto* rhs
= ins
->rhs();
4516 if (ins
->type() != MIRType::Boolean
|| lhs
->type() != MIRType::Int32
||
4517 rhs
->type() != MIRType::Int32
) {
4518 return mozilla::Nothing();
4521 // NOTE: The comparison is generated inside JIT, and typeof should always
4523 if (!lhs
->isTypeOf() || !rhs
->isConstant()) {
4524 return mozilla::Nothing();
4527 auto* typeOf
= lhs
->toTypeOf();
4528 auto* constant
= rhs
->toConstant();
4530 JSType type
= JSType(constant
->toInt32());
4531 return mozilla::Some(TypeOfCompareInput(typeOf
, typeOf
, type
, true));
4534 if (ins
->compareType() != MCompare::Compare_String
) {
4535 return mozilla::Nothing();
4538 auto* lhs
= ins
->lhs();
4539 auto* rhs
= ins
->rhs();
4541 MOZ_ASSERT(ins
->type() == MIRType::Boolean
);
4542 MOZ_ASSERT(lhs
->type() == MIRType::String
);
4543 MOZ_ASSERT(rhs
->type() == MIRType::String
);
4545 if (!lhs
->isTypeOfName() && !rhs
->isTypeOfName()) {
4546 return mozilla::Nothing();
4548 if (!lhs
->isConstant() && !rhs
->isConstant()) {
4549 return mozilla::Nothing();
4553 lhs
->isTypeOfName() ? lhs
->toTypeOfName() : rhs
->toTypeOfName();
4554 auto* typeOf
= typeOfName
->input()->toTypeOf();
4556 auto* constant
= lhs
->isConstant() ? lhs
->toConstant() : rhs
->toConstant();
4558 JSType type
= TypeOfName(&constant
->toString()->asLinear());
4559 return mozilla::Some(TypeOfCompareInput(typeOfName
, typeOf
, type
, false));
4562 bool MCompare::tryFoldTypeOf(bool* result
) {
4563 auto typeOfCompare
= IsTypeOfCompare(this);
4564 if (!typeOfCompare
) {
4567 auto* typeOf
= typeOfCompare
->typeOf
;
4568 JSType type
= typeOfCompare
->type
;
4571 case JSTYPE_BOOLEAN
:
4572 if (!typeOf
->input()->mightBeType(MIRType::Boolean
)) {
4573 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4578 if (!typeOf
->input()->mightBeType(MIRType::Int32
) &&
4579 !typeOf
->input()->mightBeType(MIRType::Float32
) &&
4580 !typeOf
->input()->mightBeType(MIRType::Double
)) {
4581 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4586 if (!typeOf
->input()->mightBeType(MIRType::String
)) {
4587 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4592 if (!typeOf
->input()->mightBeType(MIRType::Symbol
)) {
4593 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4598 if (!typeOf
->input()->mightBeType(MIRType::BigInt
)) {
4599 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4604 if (!typeOf
->input()->mightBeType(MIRType::Object
) &&
4605 !typeOf
->input()->mightBeType(MIRType::Null
)) {
4606 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4610 case JSTYPE_UNDEFINED
:
4611 if (!typeOf
->input()->mightBeType(MIRType::Object
) &&
4612 !typeOf
->input()->mightBeType(MIRType::Undefined
)) {
4613 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4617 case JSTYPE_FUNCTION
:
4618 if (!typeOf
->input()->mightBeType(MIRType::Object
)) {
4619 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4624 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4626 #ifdef ENABLE_RECORD_TUPLE
4629 MOZ_CRASH("Records and Tuples are not supported yet.");
4636 bool MCompare::tryFold(bool* result
) {
4639 if (tryFoldEqualOperands(result
)) {
4643 if (tryFoldTypeOf(result
)) {
4647 if (compareType_
== Compare_Null
|| compareType_
== Compare_Undefined
) {
4648 // The LHS is the value we want to test against null or undefined.
4649 if (IsStrictEqualityOp(op
)) {
4650 if (lhs()->type() == inputType()) {
4651 *result
= (op
== JSOp::StrictEq
);
4654 if (!lhs()->mightBeType(inputType())) {
4655 *result
= (op
== JSOp::StrictNe
);
4659 MOZ_ASSERT(IsLooseEqualityOp(op
));
4660 if (IsNullOrUndefined(lhs()->type())) {
4661 *result
= (op
== JSOp::Eq
);
4664 if (!lhs()->mightBeType(MIRType::Null
) &&
4665 !lhs()->mightBeType(MIRType::Undefined
) &&
4666 !lhs()->mightBeType(MIRType::Object
)) {
4667 *result
= (op
== JSOp::Ne
);
4677 template <typename T
>
4678 static bool FoldComparison(JSOp op
, T left
, T right
) {
4681 return left
< right
;
4683 return left
<= right
;
4685 return left
> right
;
4687 return left
>= right
;
4688 case JSOp::StrictEq
:
4690 return left
== right
;
4691 case JSOp::StrictNe
:
4693 return left
!= right
;
4695 MOZ_CRASH("Unexpected op.");
4699 static bool FoldBigIntComparison(JSOp op
, const BigInt
* left
, double right
) {
4702 return BigInt::lessThan(left
, right
).valueOr(false);
4704 return !BigInt::lessThan(right
, left
).valueOr(true);
4706 return BigInt::lessThan(right
, left
).valueOr(false);
4708 return !BigInt::lessThan(left
, right
).valueOr(true);
4709 case JSOp::StrictEq
:
4711 return BigInt::equal(left
, right
);
4712 case JSOp::StrictNe
:
4714 return !BigInt::equal(left
, right
);
4716 MOZ_CRASH("Unexpected op.");
4720 bool MCompare::evaluateConstantOperands(TempAllocator
& alloc
, bool* result
) {
4721 if (type() != MIRType::Boolean
&& type() != MIRType::Int32
) {
4725 MDefinition
* left
= getOperand(0);
4726 MDefinition
* right
= getOperand(1);
4728 if (compareType() == Compare_Double
) {
4729 // Optimize "MCompare MConstant (MToDouble SomethingInInt32Range).
4730 // In most cases the MToDouble was added, because the constant is
4732 // e.g. v < 9007199254740991, where v is an int32 is always true.
4733 if (!lhs()->isConstant() && !rhs()->isConstant()) {
4737 MDefinition
* operand
= left
->isConstant() ? right
: left
;
4738 MConstant
* constant
=
4739 left
->isConstant() ? left
->toConstant() : right
->toConstant();
4740 MOZ_ASSERT(constant
->type() == MIRType::Double
);
4741 double cte
= constant
->toDouble();
4743 if (operand
->isToDouble() &&
4744 operand
->getOperand(0)->type() == MIRType::Int32
) {
4745 bool replaced
= false;
4748 if (cte
> INT32_MAX
|| cte
< INT32_MIN
) {
4749 *result
= !((constant
== lhs()) ^ (cte
< INT32_MIN
));
4754 if (constant
== lhs()) {
4755 if (cte
> INT32_MAX
|| cte
<= INT32_MIN
) {
4756 *result
= (cte
<= INT32_MIN
);
4760 if (cte
>= INT32_MAX
|| cte
< INT32_MIN
) {
4761 *result
= (cte
>= INT32_MIN
);
4767 if (cte
> INT32_MAX
|| cte
< INT32_MIN
) {
4768 *result
= !((constant
== rhs()) ^ (cte
< INT32_MIN
));
4773 if (constant
== lhs()) {
4774 if (cte
>= INT32_MAX
|| cte
< INT32_MIN
) {
4775 *result
= (cte
>= INT32_MAX
);
4779 if (cte
> INT32_MAX
|| cte
<= INT32_MIN
) {
4780 *result
= (cte
<= INT32_MIN
);
4785 case JSOp::StrictEq
: // Fall through.
4787 if (cte
> INT32_MAX
|| cte
< INT32_MIN
) {
4792 case JSOp::StrictNe
: // Fall through.
4794 if (cte
> INT32_MAX
|| cte
< INT32_MIN
) {
4800 MOZ_CRASH("Unexpected op.");
4803 MLimitedTruncate
* limit
= MLimitedTruncate::New(
4804 alloc
, operand
->getOperand(0), TruncateKind::NoTruncate
);
4805 limit
->setGuardUnchecked();
4806 block()->insertBefore(this, limit
);
4811 // Optimize comparison against NaN.
4812 if (std::isnan(cte
)) {
4819 case JSOp::StrictEq
:
4823 case JSOp::StrictNe
:
4827 MOZ_CRASH("Unexpected op.");
4833 if (!left
->isConstant() || !right
->isConstant()) {
4837 MConstant
* lhs
= left
->toConstant();
4838 MConstant
* rhs
= right
->toConstant();
4840 switch (compareType()) {
4842 case Compare_Double
:
4843 case Compare_Float32
: {
4845 FoldComparison(jsop_
, lhs
->numberToDouble(), rhs
->numberToDouble());
4848 case Compare_UInt32
: {
4849 *result
= FoldComparison(jsop_
, uint32_t(lhs
->toInt32()),
4850 uint32_t(rhs
->toInt32()));
4853 case Compare_Int64
: {
4854 *result
= FoldComparison(jsop_
, lhs
->toInt64(), rhs
->toInt64());
4857 case Compare_UInt64
: {
4858 *result
= FoldComparison(jsop_
, uint64_t(lhs
->toInt64()),
4859 uint64_t(rhs
->toInt64()));
4862 case Compare_IntPtr
: {
4863 *result
= FoldComparison(jsop_
, lhs
->toIntPtr(), rhs
->toIntPtr());
4866 case Compare_UIntPtr
: {
4867 *result
= FoldComparison(jsop_
, uintptr_t(lhs
->toIntPtr()),
4868 uintptr_t(rhs
->toIntPtr()));
4871 case Compare_String
: {
4872 int32_t comp
= CompareStrings(&lhs
->toString()->asLinear(),
4873 &rhs
->toString()->asLinear());
4874 *result
= FoldComparison(jsop_
, comp
, 0);
4877 case Compare_BigInt
: {
4878 int32_t comp
= BigInt::compare(lhs
->toBigInt(), rhs
->toBigInt());
4879 *result
= FoldComparison(jsop_
, comp
, 0);
4882 case Compare_BigInt_Int32
:
4883 case Compare_BigInt_Double
: {
4885 FoldBigIntComparison(jsop_
, lhs
->toBigInt(), rhs
->numberToDouble());
4888 case Compare_BigInt_String
: {
4889 JSLinearString
* linear
= &rhs
->toString()->asLinear();
4890 if (!linear
->hasIndexValue()) {
4894 FoldBigIntComparison(jsop_
, lhs
->toBigInt(), linear
->getIndexValue());
4898 case Compare_Undefined
:
4900 case Compare_Symbol
:
4901 case Compare_Object
:
4902 case Compare_WasmAnyRef
:
4906 MOZ_CRASH("unexpected compare type");
4909 MDefinition
* MCompare::tryFoldTypeOf(TempAllocator
& alloc
) {
4910 auto typeOfCompare
= IsTypeOfCompare(this);
4911 if (!typeOfCompare
) {
4914 auto* typeOf
= typeOfCompare
->typeOf
;
4915 JSType type
= typeOfCompare
->type
;
4917 auto* input
= typeOf
->input();
4918 MOZ_ASSERT(input
->type() == MIRType::Value
||
4919 input
->type() == MIRType::Object
);
4921 // Constant typeof folding handles the other cases.
4922 MOZ_ASSERT_IF(input
->type() == MIRType::Object
, type
== JSTYPE_UNDEFINED
||
4923 type
== JSTYPE_OBJECT
||
4924 type
== JSTYPE_FUNCTION
);
4926 MOZ_ASSERT(type
!= JSTYPE_LIMIT
, "unknown typeof strings folded earlier");
4928 // If there's only a single use, assume this |typeof| is used in a simple
4929 // comparison context.
4931 // if (typeof thing === "number") { ... }
4933 // It'll be compiled into something similar to:
4935 // if (IsNumber(thing)) { ... }
4937 // This heuristic can go wrong when repeated |typeof| are used in consecutive
4940 // if (typeof thing === "number") { ... }
4941 // else if (typeof thing === "string") { ... }
4942 // ... repeated for all possible types
4944 // In that case it'd more efficient to emit MTypeOf compared to MTypeOfIs. We
4945 // don't yet handle that case, because it'd require a separate optimization
4946 // pass to correctly detect it.
4947 if (typeOfCompare
->typeOfSide
->hasOneUse()) {
4948 return MTypeOfIs::New(alloc
, input
, jsop(), type
);
4951 if (typeOfCompare
->isIntComparison
) {
4952 // Already optimized.
4956 MConstant
* cst
= MConstant::New(alloc
, Int32Value(type
));
4957 block()->insertBefore(this, cst
);
4959 return MCompare::New(alloc
, typeOf
, cst
, jsop(), MCompare::Compare_Int32
);
4962 MDefinition
* MCompare::tryFoldCharCompare(TempAllocator
& alloc
) {
4963 if (compareType() != Compare_String
) {
4967 MDefinition
* left
= lhs();
4968 MOZ_ASSERT(left
->type() == MIRType::String
);
4970 MDefinition
* right
= rhs();
4971 MOZ_ASSERT(right
->type() == MIRType::String
);
4973 // |str[i]| is compiled as |MFromCharCode(MCharCodeAt(str, i))|.
4974 // Out-of-bounds access is compiled as
4975 // |FromCharCodeEmptyIfNegative(CharCodeAtOrNegative(str, i))|.
4976 auto isCharAccess
= [](MDefinition
* ins
) {
4977 if (ins
->isFromCharCode()) {
4978 return ins
->toFromCharCode()->code()->isCharCodeAt();
4980 if (ins
->isFromCharCodeEmptyIfNegative()) {
4981 auto* fromCharCode
= ins
->toFromCharCodeEmptyIfNegative();
4982 return fromCharCode
->code()->isCharCodeAtOrNegative();
4987 auto charAccessCode
= [](MDefinition
* ins
) {
4988 if (ins
->isFromCharCode()) {
4989 return ins
->toFromCharCode()->code();
4991 return ins
->toFromCharCodeEmptyIfNegative()->code();
4994 if (left
->isConstant() || right
->isConstant()) {
4995 // Try to optimize |MConstant(string) <compare> (MFromCharCode MCharCodeAt)|
4996 // as |MConstant(charcode) <compare> MCharCodeAt|.
4997 MConstant
* constant
;
4998 MDefinition
* operand
;
4999 if (left
->isConstant()) {
5000 constant
= left
->toConstant();
5003 constant
= right
->toConstant();
5007 if (constant
->toString()->length() != 1 || !isCharAccess(operand
)) {
5011 char16_t charCode
= constant
->toString()->asLinear().latin1OrTwoByteChar(0);
5012 MConstant
* charCodeConst
= MConstant::New(alloc
, Int32Value(charCode
));
5013 block()->insertBefore(this, charCodeConst
);
5015 MDefinition
* charCodeAt
= charAccessCode(operand
);
5017 if (left
->isConstant()) {
5018 left
= charCodeConst
;
5022 right
= charCodeConst
;
5024 } else if (isCharAccess(left
) && isCharAccess(right
)) {
5025 // Try to optimize |(MFromCharCode MCharCodeAt) <compare> (MFromCharCode
5026 // MCharCodeAt)| as |MCharCodeAt <compare> MCharCodeAt|.
5028 left
= charAccessCode(left
);
5029 right
= charAccessCode(right
);
5034 return MCompare::New(alloc
, left
, right
, jsop(), MCompare::Compare_Int32
);
5037 MDefinition
* MCompare::tryFoldStringCompare(TempAllocator
& alloc
) {
5038 if (compareType() != Compare_String
) {
5042 MDefinition
* left
= lhs();
5043 MOZ_ASSERT(left
->type() == MIRType::String
);
5045 MDefinition
* right
= rhs();
5046 MOZ_ASSERT(right
->type() == MIRType::String
);
5048 if (!left
->isConstant() && !right
->isConstant()) {
5052 // Try to optimize |string <compare> MConstant("")| as |MStringLength(string)
5053 // <compare> MConstant(0)|.
5055 MConstant
* constant
=
5056 left
->isConstant() ? left
->toConstant() : right
->toConstant();
5057 if (!constant
->toString()->empty()) {
5061 MDefinition
* operand
= left
->isConstant() ? right
: left
;
5063 auto* strLength
= MStringLength::New(alloc
, operand
);
5064 block()->insertBefore(this, strLength
);
5066 auto* zero
= MConstant::New(alloc
, Int32Value(0));
5067 block()->insertBefore(this, zero
);
5069 if (left
->isConstant()) {
5077 return MCompare::New(alloc
, left
, right
, jsop(), MCompare::Compare_Int32
);
5080 MDefinition
* MCompare::tryFoldStringSubstring(TempAllocator
& alloc
) {
5081 if (compareType() != Compare_String
) {
5084 if (!IsEqualityOp(jsop())) {
5089 MOZ_ASSERT(left
->type() == MIRType::String
);
5091 auto* right
= rhs();
5092 MOZ_ASSERT(right
->type() == MIRType::String
);
5094 // One operand must be a constant string.
5095 if (!left
->isConstant() && !right
->isConstant()) {
5099 // The constant string must be non-empty.
5101 left
->isConstant() ? left
->toConstant() : right
->toConstant();
5102 if (constant
->toString()->empty()) {
5106 // The other operand must be a substring operation.
5107 auto* operand
= left
->isConstant() ? right
: left
;
5108 if (!operand
->isSubstr()) {
5111 auto* substr
= operand
->toSubstr();
5113 static_assert(JSString::MAX_LENGTH
< INT32_MAX
,
5114 "string length can be casted to int32_t");
5116 if (!IsSubstrTo(substr
, int32_t(constant
->toString()->length()))) {
5120 // Now fold code like |str.substring(0, 2) == "aa"| to |str.startsWith("aa")|.
5122 auto* startsWith
= MStringStartsWith::New(alloc
, substr
->string(), constant
);
5123 if (jsop() == JSOp::Eq
|| jsop() == JSOp::StrictEq
) {
5127 // Invert for inequality.
5128 MOZ_ASSERT(jsop() == JSOp::Ne
|| jsop() == JSOp::StrictNe
);
5130 block()->insertBefore(this, startsWith
);
5131 return MNot::New(alloc
, startsWith
);
5134 MDefinition
* MCompare::tryFoldStringIndexOf(TempAllocator
& alloc
) {
5135 if (compareType() != Compare_Int32
) {
5138 if (!IsEqualityOp(jsop())) {
5143 MOZ_ASSERT(left
->type() == MIRType::Int32
);
5145 auto* right
= rhs();
5146 MOZ_ASSERT(right
->type() == MIRType::Int32
);
5148 // One operand must be a constant integer.
5149 if (!left
->isConstant() && !right
->isConstant()) {
5153 // The constant must be zero.
5155 left
->isConstant() ? left
->toConstant() : right
->toConstant();
5156 if (!constant
->isInt32(0)) {
5160 // The other operand must be an indexOf operation.
5161 auto* operand
= left
->isConstant() ? right
: left
;
5162 if (!operand
->isStringIndexOf()) {
5166 // Fold |str.indexOf(searchStr) == 0| to |str.startsWith(searchStr)|.
5168 auto* indexOf
= operand
->toStringIndexOf();
5170 MStringStartsWith::New(alloc
, indexOf
->string(), indexOf
->searchString());
5171 if (jsop() == JSOp::Eq
|| jsop() == JSOp::StrictEq
) {
5175 // Invert for inequality.
5176 MOZ_ASSERT(jsop() == JSOp::Ne
|| jsop() == JSOp::StrictNe
);
5178 block()->insertBefore(this, startsWith
);
5179 return MNot::New(alloc
, startsWith
);
5182 MDefinition
* MCompare::tryFoldBigInt64(TempAllocator
& alloc
) {
5183 if (compareType() == Compare_BigInt
) {
5185 MOZ_ASSERT(left
->type() == MIRType::BigInt
);
5187 auto* right
= rhs();
5188 MOZ_ASSERT(right
->type() == MIRType::BigInt
);
5190 // At least one operand must be MInt64ToBigInt.
5191 if (!left
->isInt64ToBigInt() && !right
->isInt64ToBigInt()) {
5195 // Unwrap MInt64ToBigInt on both sides and perform a Int64 comparison.
5196 if (left
->isInt64ToBigInt() && right
->isInt64ToBigInt()) {
5197 auto* lhsInt64
= left
->toInt64ToBigInt();
5198 auto* rhsInt64
= right
->toInt64ToBigInt();
5200 // Don't optimize if Int64 against Uint64 comparison.
5201 if (lhsInt64
->elementType() != rhsInt64
->elementType()) {
5205 bool isSigned
= lhsInt64
->elementType() == Scalar::BigInt64
;
5207 isSigned
? MCompare::Compare_Int64
: MCompare::Compare_UInt64
;
5208 return MCompare::New(alloc
, lhsInt64
->input(), rhsInt64
->input(), jsop_
,
5212 // Optimize IntPtr x Int64 comparison to Int64 x Int64 comparison.
5213 if (left
->isIntPtrToBigInt() || right
->isIntPtrToBigInt()) {
5214 auto* int64ToBigInt
= left
->isInt64ToBigInt() ? left
->toInt64ToBigInt()
5215 : right
->toInt64ToBigInt();
5217 // Can't optimize when comparing Uint64 against IntPtr.
5218 if (int64ToBigInt
->elementType() == Scalar::BigUint64
) {
5222 auto* intPtrToBigInt
= left
->isIntPtrToBigInt()
5223 ? left
->toIntPtrToBigInt()
5224 : right
->toIntPtrToBigInt();
5226 auto* intPtrToInt64
= MIntPtrToInt64::New(alloc
, intPtrToBigInt
->input());
5227 block()->insertBefore(this, intPtrToInt64
);
5229 if (left
== int64ToBigInt
) {
5230 left
= int64ToBigInt
->input();
5231 right
= intPtrToInt64
;
5233 left
= intPtrToInt64
;
5234 right
= int64ToBigInt
->input();
5236 return MCompare::New(alloc
, left
, right
, jsop_
, MCompare::Compare_Int64
);
5239 // The other operand must be a constant.
5240 if (!left
->isConstant() && !right
->isConstant()) {
5244 auto* int64ToBigInt
= left
->isInt64ToBigInt() ? left
->toInt64ToBigInt()
5245 : right
->toInt64ToBigInt();
5246 bool isSigned
= int64ToBigInt
->elementType() == Scalar::BigInt64
;
5249 left
->isConstant() ? left
->toConstant() : right
->toConstant();
5250 auto* bigInt
= constant
->toBigInt();
5252 // Extract the BigInt value if representable as Int64/Uint64.
5253 mozilla::Maybe
<int64_t> value
;
5256 if (BigInt::isInt64(bigInt
, &x
)) {
5257 value
= mozilla::Some(x
);
5261 if (BigInt::isUint64(bigInt
, &x
)) {
5262 value
= mozilla::Some(static_cast<int64_t>(x
));
5266 // The comparison is a constant if the BigInt has too many digits.
5268 int32_t repr
= bigInt
->isNegative() ? -1 : 1;
5271 if (left
== int64ToBigInt
) {
5272 result
= FoldComparison(jsop_
, 0, repr
);
5274 result
= FoldComparison(jsop_
, repr
, 0);
5276 return MConstant::New(alloc
, BooleanValue(result
));
5279 auto* cst
= MConstant::NewInt64(alloc
, *value
);
5280 block()->insertBefore(this, cst
);
5283 isSigned
? MCompare::Compare_Int64
: MCompare::Compare_UInt64
;
5284 if (left
== int64ToBigInt
) {
5285 return MCompare::New(alloc
, int64ToBigInt
->input(), cst
, jsop_
,
5288 return MCompare::New(alloc
, cst
, int64ToBigInt
->input(), jsop_
,
5292 if (compareType() == Compare_BigInt_Int32
) {
5294 MOZ_ASSERT(left
->type() == MIRType::BigInt
);
5296 auto* right
= rhs();
5297 MOZ_ASSERT(right
->type() == MIRType::Int32
);
5299 // Optimize MInt64ToBigInt against a constant int32.
5300 if (!left
->isInt64ToBigInt() || !right
->isConstant()) {
5304 auto* int64ToBigInt
= left
->toInt64ToBigInt();
5305 bool isSigned
= int64ToBigInt
->elementType() == Scalar::BigInt64
;
5307 int32_t constInt32
= right
->toConstant()->toInt32();
5309 // The unsigned comparison against a negative operand is a constant.
5310 if (!isSigned
&& constInt32
< 0) {
5311 bool result
= FoldComparison(jsop_
, 0, constInt32
);
5312 return MConstant::New(alloc
, BooleanValue(result
));
5315 auto* cst
= MConstant::NewInt64(alloc
, int64_t(constInt32
));
5316 block()->insertBefore(this, cst
);
5319 isSigned
? MCompare::Compare_Int64
: MCompare::Compare_UInt64
;
5320 return MCompare::New(alloc
, int64ToBigInt
->input(), cst
, jsop_
,
5327 MDefinition
* MCompare::tryFoldBigIntPtr(TempAllocator
& alloc
) {
5328 if (compareType() == Compare_BigInt
) {
5330 MOZ_ASSERT(left
->type() == MIRType::BigInt
);
5332 auto* right
= rhs();
5333 MOZ_ASSERT(right
->type() == MIRType::BigInt
);
5335 // At least one operand must be MIntPtrToBigInt.
5336 if (!left
->isIntPtrToBigInt() && !right
->isIntPtrToBigInt()) {
5340 // Unwrap MIntPtrToBigInt on both sides and perform an IntPtr comparison.
5341 if (left
->isIntPtrToBigInt() && right
->isIntPtrToBigInt()) {
5342 auto* lhsIntPtr
= left
->toIntPtrToBigInt();
5343 auto* rhsIntPtr
= right
->toIntPtrToBigInt();
5345 return MCompare::New(alloc
, lhsIntPtr
->input(), rhsIntPtr
->input(), jsop_
,
5346 MCompare::Compare_IntPtr
);
5349 // The other operand must be a constant.
5350 if (!left
->isConstant() && !right
->isConstant()) {
5354 auto* intPtrToBigInt
= left
->isIntPtrToBigInt() ? left
->toIntPtrToBigInt()
5355 : right
->toIntPtrToBigInt();
5358 left
->isConstant() ? left
->toConstant() : right
->toConstant();
5359 auto* bigInt
= constant
->toBigInt();
5361 // Extract the BigInt value if representable as intptr_t.
5363 if (!BigInt::isIntPtr(bigInt
, &value
)) {
5364 // The comparison is a constant if the BigInt has too many digits.
5365 int32_t repr
= bigInt
->isNegative() ? -1 : 1;
5368 if (left
== intPtrToBigInt
) {
5369 result
= FoldComparison(jsop_
, 0, repr
);
5371 result
= FoldComparison(jsop_
, repr
, 0);
5373 return MConstant::New(alloc
, BooleanValue(result
));
5376 auto* cst
= MConstant::NewIntPtr(alloc
, value
);
5377 block()->insertBefore(this, cst
);
5379 if (left
== intPtrToBigInt
) {
5380 left
= intPtrToBigInt
->input();
5384 right
= intPtrToBigInt
->input();
5386 return MCompare::New(alloc
, left
, right
, jsop_
, MCompare::Compare_IntPtr
);
5389 if (compareType() == Compare_BigInt_Int32
) {
5391 MOZ_ASSERT(left
->type() == MIRType::BigInt
);
5393 auto* right
= rhs();
5394 MOZ_ASSERT(right
->type() == MIRType::Int32
);
5396 // Optimize MIntPtrToBigInt against a constant int32.
5397 if (!left
->isIntPtrToBigInt() || !right
->isConstant()) {
5402 MConstant::NewIntPtr(alloc
, intptr_t(right
->toConstant()->toInt32()));
5403 block()->insertBefore(this, cst
);
5405 return MCompare::New(alloc
, left
->toIntPtrToBigInt()->input(), cst
, jsop_
,
5406 MCompare::Compare_IntPtr
);
5412 MDefinition
* MCompare::tryFoldBigInt(TempAllocator
& alloc
) {
5413 if (compareType() != Compare_BigInt
) {
5418 MOZ_ASSERT(left
->type() == MIRType::BigInt
);
5420 auto* right
= rhs();
5421 MOZ_ASSERT(right
->type() == MIRType::BigInt
);
5423 // One operand must be a constant.
5424 if (!left
->isConstant() && !right
->isConstant()) {
5429 left
->isConstant() ? left
->toConstant() : right
->toConstant();
5430 auto* operand
= left
->isConstant() ? right
: left
;
5432 // The constant must be representable as an Int32.
5434 if (!BigInt::isInt32(constant
->toBigInt(), &x
)) {
5438 MConstant
* int32Const
= MConstant::New(alloc
, Int32Value(x
));
5439 block()->insertBefore(this, int32Const
);
5442 if (IsStrictEqualityOp(op
)) {
5443 // Compare_BigInt_Int32 is only valid for loose comparison.
5444 op
= op
== JSOp::StrictEq
? JSOp::Eq
: JSOp::Ne
;
5445 } else if (operand
== right
) {
5446 // Reverse the comparison operator if the operands were reordered.
5447 op
= ReverseCompareOp(op
);
5450 return MCompare::New(alloc
, operand
, int32Const
, op
,
5451 MCompare::Compare_BigInt_Int32
);
5454 MDefinition
* MCompare::foldsTo(TempAllocator
& alloc
) {
5457 if (tryFold(&result
) || evaluateConstantOperands(alloc
, &result
)) {
5458 if (type() == MIRType::Int32
) {
5459 return MConstant::New(alloc
, Int32Value(result
));
5462 MOZ_ASSERT(type() == MIRType::Boolean
);
5463 return MConstant::New(alloc
, BooleanValue(result
));
5466 if (MDefinition
* folded
= tryFoldTypeOf(alloc
); folded
!= this) {
5470 if (MDefinition
* folded
= tryFoldCharCompare(alloc
); folded
!= this) {
5474 if (MDefinition
* folded
= tryFoldStringCompare(alloc
); folded
!= this) {
5478 if (MDefinition
* folded
= tryFoldStringSubstring(alloc
); folded
!= this) {
5482 if (MDefinition
* folded
= tryFoldStringIndexOf(alloc
); folded
!= this) {
5486 if (MDefinition
* folded
= tryFoldBigInt64(alloc
); folded
!= this) {
5490 if (MDefinition
* folded
= tryFoldBigIntPtr(alloc
); folded
!= this) {
5494 if (MDefinition
* folded
= tryFoldBigInt(alloc
); folded
!= this) {
5501 void MCompare::trySpecializeFloat32(TempAllocator
& alloc
) {
5502 if (AllOperandsCanProduceFloat32(this) && compareType_
== Compare_Double
) {
5503 compareType_
= Compare_Float32
;
5505 ConvertOperandsToDouble(this, alloc
);
5509 MDefinition
* MNot::foldsTo(TempAllocator
& alloc
) {
5510 // Fold if the input is constant
5511 if (MConstant
* inputConst
= input()->maybeConstantValue()) {
5513 if (inputConst
->valueToBoolean(&b
)) {
5514 if (type() == MIRType::Int32
|| type() == MIRType::Int64
) {
5515 return MConstant::New(alloc
, Int32Value(!b
));
5517 return MConstant::New(alloc
, BooleanValue(!b
));
5521 // If the operand of the Not is itself a Not, they cancel out. But we can't
5522 // always convert Not(Not(x)) to x because that may loose the conversion to
5523 // boolean. We can simplify Not(Not(Not(x))) to Not(x) though.
5524 MDefinition
* op
= getOperand(0);
5526 MDefinition
* opop
= op
->getOperand(0);
5527 if (opop
->isNot()) {
5532 // Not of an undefined or null value is always true
5533 if (input()->type() == MIRType::Undefined
||
5534 input()->type() == MIRType::Null
) {
5535 return MConstant::New(alloc
, BooleanValue(true));
5538 // Not of a symbol is always false.
5539 if (input()->type() == MIRType::Symbol
) {
5540 return MConstant::New(alloc
, BooleanValue(false));
5543 // Drop the conversion in `Not(Int64ToBigInt(int64))` to `Not(int64)`.
5544 if (input()->isInt64ToBigInt()) {
5545 return MNot::New(alloc
, input()->toInt64ToBigInt()->input());
5548 // Drop the conversion in `Not(IntPtrToBigInt(intptr))` to `Not(intptr)`.
5549 if (input()->isIntPtrToBigInt()) {
5550 return MNot::New(alloc
, input()->toIntPtrToBigInt()->input());
5556 void MNot::trySpecializeFloat32(TempAllocator
& alloc
) {
5557 (void)EnsureFloatInputOrConvert(this, alloc
);
5561 void MBeta::printOpcode(GenericPrinter
& out
) const {
5562 MDefinition::printOpcode(out
);
5565 comparison_
->dump(out
);
5569 AliasSet
MCreateThis::getAliasSet() const {
5570 return AliasSet::Load(AliasSet::Any
);
5573 bool MGetArgumentsObjectArg::congruentTo(const MDefinition
* ins
) const {
5574 if (!ins
->isGetArgumentsObjectArg()) {
5577 if (ins
->toGetArgumentsObjectArg()->argno() != argno()) {
5580 return congruentIfOperandsEqual(ins
);
5583 AliasSet
MGetArgumentsObjectArg::getAliasSet() const {
5584 return AliasSet::Load(AliasSet::Any
);
5587 AliasSet
MSetArgumentsObjectArg::getAliasSet() const {
5588 return AliasSet::Store(AliasSet::Any
);
5591 MObjectState::MObjectState(MObjectState
* state
)
5592 : MVariadicInstruction(classOpcode
),
5593 numSlots_(state
->numSlots_
),
5594 numFixedSlots_(state
->numFixedSlots_
) {
5595 // This instruction is only used as a summary for bailout paths.
5596 setResultType(MIRType::Object
);
5597 setRecoveredOnBailout();
5600 MObjectState::MObjectState(JSObject
* templateObject
)
5601 : MObjectState(templateObject
->as
<NativeObject
>().shape()) {}
5603 MObjectState::MObjectState(const Shape
* shape
)
5604 : MVariadicInstruction(classOpcode
) {
5605 // This instruction is only used as a summary for bailout paths.
5606 setResultType(MIRType::Object
);
5607 setRecoveredOnBailout();
5609 numSlots_
= shape
->asShared().slotSpan();
5610 numFixedSlots_
= shape
->asShared().numFixedSlots();
5614 JSObject
* MObjectState::templateObjectOf(MDefinition
* obj
) {
5615 // MNewPlainObject uses a shape constant, not an object.
5616 MOZ_ASSERT(!obj
->isNewPlainObject());
5618 if (obj
->isNewObject()) {
5619 return obj
->toNewObject()->templateObject();
5620 } else if (obj
->isNewCallObject()) {
5621 return obj
->toNewCallObject()->templateObject();
5622 } else if (obj
->isNewIterator()) {
5623 return obj
->toNewIterator()->templateObject();
5626 MOZ_CRASH("unreachable");
5629 bool MObjectState::init(TempAllocator
& alloc
, MDefinition
* obj
) {
5630 if (!MVariadicInstruction::init(alloc
, numSlots() + 1)) {
5633 // +1, for the Object.
5634 initOperand(0, obj
);
5638 void MObjectState::initFromTemplateObject(TempAllocator
& alloc
,
5639 MDefinition
* undefinedVal
) {
5640 if (object()->isNewPlainObject()) {
5641 MOZ_ASSERT(object()->toNewPlainObject()->shape()->asShared().slotSpan() ==
5643 for (size_t i
= 0; i
< numSlots(); i
++) {
5644 initSlot(i
, undefinedVal
);
5649 JSObject
* templateObject
= templateObjectOf(object());
5651 // Initialize all the slots of the object state with the value contained in
5652 // the template object. This is needed to account values which are baked in
5653 // the template objects and not visible in IonMonkey, such as the
5654 // uninitialized-lexical magic value of call objects.
5656 MOZ_ASSERT(templateObject
->is
<NativeObject
>());
5657 NativeObject
& nativeObject
= templateObject
->as
<NativeObject
>();
5658 MOZ_ASSERT(nativeObject
.slotSpan() == numSlots());
5660 for (size_t i
= 0; i
< numSlots(); i
++) {
5661 Value val
= nativeObject
.getSlot(i
);
5662 MDefinition
* def
= undefinedVal
;
5663 if (!val
.isUndefined()) {
5664 MConstant
* ins
= MConstant::New(alloc
, val
);
5665 block()->insertBefore(this, ins
);
5672 MObjectState
* MObjectState::New(TempAllocator
& alloc
, MDefinition
* obj
) {
5674 if (obj
->isNewPlainObject()) {
5675 const Shape
* shape
= obj
->toNewPlainObject()->shape();
5676 res
= new (alloc
) MObjectState(shape
);
5678 JSObject
* templateObject
= templateObjectOf(obj
);
5679 MOZ_ASSERT(templateObject
, "Unexpected object creation.");
5680 res
= new (alloc
) MObjectState(templateObject
);
5683 if (!res
|| !res
->init(alloc
, obj
)) {
5689 MObjectState
* MObjectState::Copy(TempAllocator
& alloc
, MObjectState
* state
) {
5690 MObjectState
* res
= new (alloc
) MObjectState(state
);
5691 if (!res
|| !res
->init(alloc
, state
->object())) {
5694 for (size_t i
= 0; i
< res
->numSlots(); i
++) {
5695 res
->initSlot(i
, state
->getSlot(i
));
5700 MArrayState::MArrayState(MDefinition
* arr
) : MVariadicInstruction(classOpcode
) {
5701 // This instruction is only used as a summary for bailout paths.
5702 setResultType(MIRType::Object
);
5703 setRecoveredOnBailout();
5704 if (arr
->isNewArrayObject()) {
5705 numElements_
= arr
->toNewArrayObject()->length();
5707 numElements_
= arr
->toNewArray()->length();
5711 bool MArrayState::init(TempAllocator
& alloc
, MDefinition
* obj
,
5713 if (!MVariadicInstruction::init(alloc
, numElements() + 2)) {
5716 // +1, for the Array object.
5717 initOperand(0, obj
);
5718 // +1, for the length value of the array.
5719 initOperand(1, len
);
5723 void MArrayState::initFromTemplateObject(TempAllocator
& alloc
,
5724 MDefinition
* undefinedVal
) {
5725 for (size_t i
= 0; i
< numElements(); i
++) {
5726 initElement(i
, undefinedVal
);
5730 MArrayState
* MArrayState::New(TempAllocator
& alloc
, MDefinition
* arr
,
5731 MDefinition
* initLength
) {
5732 MArrayState
* res
= new (alloc
) MArrayState(arr
);
5733 if (!res
|| !res
->init(alloc
, arr
, initLength
)) {
5739 MArrayState
* MArrayState::Copy(TempAllocator
& alloc
, MArrayState
* state
) {
5740 MDefinition
* arr
= state
->array();
5741 MDefinition
* len
= state
->initializedLength();
5742 MArrayState
* res
= new (alloc
) MArrayState(arr
);
5743 if (!res
|| !res
->init(alloc
, arr
, len
)) {
5746 for (size_t i
= 0; i
< res
->numElements(); i
++) {
5747 res
->initElement(i
, state
->getElement(i
));
5752 MNewArray::MNewArray(uint32_t length
, MConstant
* templateConst
,
5753 gc::Heap initialHeap
, bool vmCall
)
5754 : MUnaryInstruction(classOpcode
, templateConst
),
5756 initialHeap_(initialHeap
),
5758 setResultType(MIRType::Object
);
5761 MDefinition::AliasType
MLoadFixedSlot::mightAlias(
5762 const MDefinition
* def
) const {
5763 if (def
->isStoreFixedSlot()) {
5764 const MStoreFixedSlot
* store
= def
->toStoreFixedSlot();
5765 if (store
->slot() != slot()) {
5766 return AliasType::NoAlias
;
5768 if (store
->object() != object()) {
5769 return AliasType::MayAlias
;
5771 return AliasType::MustAlias
;
5773 return AliasType::MayAlias
;
5776 MDefinition
* MLoadFixedSlot::foldsTo(TempAllocator
& alloc
) {
5777 if (MDefinition
* def
= foldsToStore(alloc
)) {
5784 MDefinition::AliasType
MLoadFixedSlotAndUnbox::mightAlias(
5785 const MDefinition
* def
) const {
5786 if (def
->isStoreFixedSlot()) {
5787 const MStoreFixedSlot
* store
= def
->toStoreFixedSlot();
5788 if (store
->slot() != slot()) {
5789 return AliasType::NoAlias
;
5791 if (store
->object() != object()) {
5792 return AliasType::MayAlias
;
5794 return AliasType::MustAlias
;
5796 return AliasType::MayAlias
;
5799 MDefinition
* MLoadFixedSlotAndUnbox::foldsTo(TempAllocator
& alloc
) {
5800 if (MDefinition
* def
= foldsToStore(alloc
)) {
5807 MDefinition::AliasType
MLoadDynamicSlot::mightAlias(
5808 const MDefinition
* def
) const {
5809 if (def
->isStoreDynamicSlot()) {
5810 const MStoreDynamicSlot
* store
= def
->toStoreDynamicSlot();
5811 if (store
->slot() != slot()) {
5812 return AliasType::NoAlias
;
5815 if (store
->slots() != slots()) {
5816 return AliasType::MayAlias
;
5819 return AliasType::MustAlias
;
5821 return AliasType::MayAlias
;
5824 HashNumber
MLoadDynamicSlot::valueHash() const {
5825 HashNumber hash
= MDefinition::valueHash();
5826 hash
= addU32ToHash(hash
, slot_
);
5830 MDefinition
* MLoadDynamicSlot::foldsTo(TempAllocator
& alloc
) {
5831 if (MDefinition
* def
= foldsToStore(alloc
)) {
5839 void MLoadDynamicSlot::printOpcode(GenericPrinter
& out
) const {
5840 MDefinition::printOpcode(out
);
5841 out
.printf(" (slot %u)", slot());
5844 void MLoadDynamicSlotAndUnbox::printOpcode(GenericPrinter
& out
) const {
5845 MDefinition::printOpcode(out
);
5846 out
.printf(" (slot %zu)", slot());
5849 void MStoreDynamicSlot::printOpcode(GenericPrinter
& out
) const {
5850 MDefinition::printOpcode(out
);
5851 out
.printf(" (slot %u)", slot());
5854 void MLoadFixedSlot::printOpcode(GenericPrinter
& out
) const {
5855 MDefinition::printOpcode(out
);
5856 out
.printf(" (slot %zu)", slot());
5859 void MLoadFixedSlotAndUnbox::printOpcode(GenericPrinter
& out
) const {
5860 MDefinition::printOpcode(out
);
5861 out
.printf(" (slot %zu)", slot());
5864 void MStoreFixedSlot::printOpcode(GenericPrinter
& out
) const {
5865 MDefinition::printOpcode(out
);
5866 out
.printf(" (slot %zu)", slot());
5870 MDefinition
* MGuardFunctionScript::foldsTo(TempAllocator
& alloc
) {
5871 MDefinition
* in
= input();
5872 if (in
->isLambda() &&
5873 in
->toLambda()->templateFunction()->baseScript() == expected()) {
5879 MDefinition
* MFunctionEnvironment::foldsTo(TempAllocator
& alloc
) {
5880 if (input()->isLambda()) {
5881 return input()->toLambda()->environmentChain();
5883 if (input()->isFunctionWithProto()) {
5884 return input()->toFunctionWithProto()->environmentChain();
5889 static bool AddIsANonZeroAdditionOf(MAdd
* add
, MDefinition
* ins
) {
5890 if (add
->lhs() != ins
&& add
->rhs() != ins
) {
5893 MDefinition
* other
= (add
->lhs() == ins
) ? add
->rhs() : add
->lhs();
5894 if (!IsNumberType(other
->type())) {
5897 if (!other
->isConstant()) {
5900 if (other
->toConstant()->numberToDouble() == 0) {
5906 // Skip over instructions that usually appear between the actual index
5907 // value being used and the MLoadElement.
5908 // They don't modify the index value in a meaningful way.
5909 static MDefinition
* SkipUninterestingInstructions(MDefinition
* ins
) {
5910 // Drop the MToNumberInt32 added by the TypePolicy for double and float
5912 if (ins
->isToNumberInt32()) {
5913 return SkipUninterestingInstructions(ins
->toToNumberInt32()->input());
5916 // Ignore the bounds check, which don't modify the index.
5917 if (ins
->isBoundsCheck()) {
5918 return SkipUninterestingInstructions(ins
->toBoundsCheck()->index());
5921 // Masking the index for Spectre-mitigation is not observable.
5922 if (ins
->isSpectreMaskIndex()) {
5923 return SkipUninterestingInstructions(ins
->toSpectreMaskIndex()->index());
5929 static bool DefinitelyDifferentValue(MDefinition
* ins1
, MDefinition
* ins2
) {
5930 ins1
= SkipUninterestingInstructions(ins1
);
5931 ins2
= SkipUninterestingInstructions(ins2
);
5937 // For constants check they are not equal.
5938 if (ins1
->isConstant() && ins2
->isConstant()) {
5939 MConstant
* cst1
= ins1
->toConstant();
5940 MConstant
* cst2
= ins2
->toConstant();
5942 if (!cst1
->isTypeRepresentableAsDouble() ||
5943 !cst2
->isTypeRepresentableAsDouble()) {
5947 // Be conservative and only allow values that fit into int32.
5949 if (!mozilla::NumberIsInt32(cst1
->numberToDouble(), &n1
) ||
5950 !mozilla::NumberIsInt32(cst2
->numberToDouble(), &n2
)) {
5957 // Check if "ins1 = ins2 + cte", which would make both instructions
5958 // have different values.
5959 if (ins1
->isAdd()) {
5960 if (AddIsANonZeroAdditionOf(ins1
->toAdd(), ins2
)) {
5964 if (ins2
->isAdd()) {
5965 if (AddIsANonZeroAdditionOf(ins2
->toAdd(), ins1
)) {
5973 MDefinition::AliasType
MLoadElement::mightAlias(const MDefinition
* def
) const {
5974 if (def
->isStoreElement()) {
5975 const MStoreElement
* store
= def
->toStoreElement();
5976 if (store
->index() != index()) {
5977 if (DefinitelyDifferentValue(store
->index(), index())) {
5978 return AliasType::NoAlias
;
5980 return AliasType::MayAlias
;
5983 if (store
->elements() != elements()) {
5984 return AliasType::MayAlias
;
5987 return AliasType::MustAlias
;
5989 return AliasType::MayAlias
;
5992 MDefinition
* MLoadElement::foldsTo(TempAllocator
& alloc
) {
5993 if (MDefinition
* def
= foldsToStore(alloc
)) {
6000 void MSqrt::trySpecializeFloat32(TempAllocator
& alloc
) {
6001 if (EnsureFloatConsumersAndInputOrConvert(this, alloc
)) {
6002 setResultType(MIRType::Float32
);
6003 specialization_
= MIRType::Float32
;
6007 MDefinition
* MClz::foldsTo(TempAllocator
& alloc
) {
6008 if (num()->isConstant()) {
6009 MConstant
* c
= num()->toConstant();
6010 if (type() == MIRType::Int32
) {
6011 int32_t n
= c
->toInt32();
6013 return MConstant::New(alloc
, Int32Value(32));
6015 return MConstant::New(alloc
,
6016 Int32Value(mozilla::CountLeadingZeroes32(n
)));
6018 int64_t n
= c
->toInt64();
6020 return MConstant::NewInt64(alloc
, int64_t(64));
6022 return MConstant::NewInt64(alloc
,
6023 int64_t(mozilla::CountLeadingZeroes64(n
)));
6029 MDefinition
* MCtz::foldsTo(TempAllocator
& alloc
) {
6030 if (num()->isConstant()) {
6031 MConstant
* c
= num()->toConstant();
6032 if (type() == MIRType::Int32
) {
6033 int32_t n
= num()->toConstant()->toInt32();
6035 return MConstant::New(alloc
, Int32Value(32));
6037 return MConstant::New(alloc
,
6038 Int32Value(mozilla::CountTrailingZeroes32(n
)));
6040 int64_t n
= c
->toInt64();
6042 return MConstant::NewInt64(alloc
, int64_t(64));
6044 return MConstant::NewInt64(alloc
,
6045 int64_t(mozilla::CountTrailingZeroes64(n
)));
6051 MDefinition
* MPopcnt::foldsTo(TempAllocator
& alloc
) {
6052 if (num()->isConstant()) {
6053 MConstant
* c
= num()->toConstant();
6054 if (type() == MIRType::Int32
) {
6055 int32_t n
= num()->toConstant()->toInt32();
6056 return MConstant::New(alloc
, Int32Value(mozilla::CountPopulation32(n
)));
6058 int64_t n
= c
->toInt64();
6059 return MConstant::NewInt64(alloc
, int64_t(mozilla::CountPopulation64(n
)));
6065 MDefinition
* MBoundsCheck::foldsTo(TempAllocator
& alloc
) {
6066 if (type() == MIRType::Int32
&& index()->isConstant() &&
6067 length()->isConstant()) {
6068 uint32_t len
= length()->toConstant()->toInt32();
6069 uint32_t idx
= index()->toConstant()->toInt32();
6070 if (idx
+ uint32_t(minimum()) < len
&& idx
+ uint32_t(maximum()) < len
) {
6078 MDefinition
* MTableSwitch::foldsTo(TempAllocator
& alloc
) {
6079 MDefinition
* op
= getOperand(0);
6081 // If we only have one successor, convert to a plain goto to the only
6082 // successor. TableSwitch indices are numeric; other types will always go to
6083 // the only successor.
6084 if (numSuccessors() == 1 ||
6085 (op
->type() != MIRType::Value
&& !IsNumberType(op
->type()))) {
6086 return MGoto::New(alloc
, getDefault());
6089 if (MConstant
* opConst
= op
->maybeConstantValue()) {
6090 if (op
->type() == MIRType::Int32
) {
6091 int32_t i
= opConst
->toInt32() - low_
;
6092 MBasicBlock
* target
;
6093 if (size_t(i
) < numCases()) {
6094 target
= getCase(size_t(i
));
6096 target
= getDefault();
6099 return MGoto::New(alloc
, target
);
6106 MDefinition
* MArrayJoin::foldsTo(TempAllocator
& alloc
) {
6107 MDefinition
* arr
= array();
6109 if (!arr
->isStringSplit()) {
6113 setRecoveredOnBailout();
6114 if (arr
->hasLiveDefUses()) {
6115 setNotRecoveredOnBailout();
6119 // The MStringSplit won't generate any code.
6120 arr
->setRecoveredOnBailout();
6122 // We're replacing foo.split(bar).join(baz) by
6123 // foo.replace(bar, baz). MStringSplit could be recovered by
6124 // a bailout. As we are removing its last use, and its result
6125 // could be captured by a resume point, this MStringSplit will
6126 // be executed on the bailout path.
6127 MDefinition
* string
= arr
->toStringSplit()->string();
6128 MDefinition
* pattern
= arr
->toStringSplit()->separator();
6129 MDefinition
* replacement
= sep();
6131 MStringReplace
* substr
=
6132 MStringReplace::New(alloc
, string
, pattern
, replacement
);
6133 substr
->setFlatReplacement();
6137 MDefinition
* MGetFirstDollarIndex::foldsTo(TempAllocator
& alloc
) {
6138 MDefinition
* strArg
= str();
6139 if (!strArg
->isConstant()) {
6143 JSLinearString
* str
= &strArg
->toConstant()->toString()->asLinear();
6144 int32_t index
= GetFirstDollarIndexRawFlat(str
);
6145 return MConstant::New(alloc
, Int32Value(index
));
6148 AliasSet
MThrowRuntimeLexicalError::getAliasSet() const {
6149 return AliasSet::Store(AliasSet::ExceptionState
);
6152 AliasSet
MSlots::getAliasSet() const {
6153 return AliasSet::Load(AliasSet::ObjectFields
);
6156 MDefinition::AliasType
MSlots::mightAlias(const MDefinition
* store
) const {
6157 // ArrayPush only modifies object elements, but not object slots.
6158 if (store
->isArrayPush()) {
6159 return AliasType::NoAlias
;
6161 return MInstruction::mightAlias(store
);
6164 AliasSet
MElements::getAliasSet() const {
6165 return AliasSet::Load(AliasSet::ObjectFields
);
6168 AliasSet
MInitializedLength::getAliasSet() const {
6169 return AliasSet::Load(AliasSet::ObjectFields
);
6172 AliasSet
MSetInitializedLength::getAliasSet() const {
6173 return AliasSet::Store(AliasSet::ObjectFields
);
6176 AliasSet
MObjectKeysLength::getAliasSet() const {
6177 return AliasSet::Load(AliasSet::ObjectFields
);
6180 AliasSet
MArrayLength::getAliasSet() const {
6181 return AliasSet::Load(AliasSet::ObjectFields
);
6184 AliasSet
MSetArrayLength::getAliasSet() const {
6185 return AliasSet::Store(AliasSet::ObjectFields
);
6188 AliasSet
MFunctionLength::getAliasSet() const {
6189 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6190 AliasSet::DynamicSlot
);
6193 AliasSet
MFunctionName::getAliasSet() const {
6194 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6195 AliasSet::DynamicSlot
);
6198 AliasSet
MArrayBufferByteLength::getAliasSet() const {
6199 return AliasSet::Load(AliasSet::FixedSlot
);
6202 AliasSet
MArrayBufferViewLength::getAliasSet() const {
6203 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset
);
6206 AliasSet
MArrayBufferViewByteOffset::getAliasSet() const {
6207 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset
);
6210 AliasSet
MArrayBufferViewElements::getAliasSet() const {
6211 return AliasSet::Load(AliasSet::ObjectFields
);
6214 AliasSet
MGuardHasAttachedArrayBuffer::getAliasSet() const {
6215 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
);
6218 AliasSet
MResizableTypedArrayByteOffsetMaybeOutOfBounds::getAliasSet() const {
6219 // Loads the byteOffset and additionally checks for detached buffers, so the
6220 // alias set also has to include |ObjectFields| and |FixedSlot|.
6221 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset
|
6222 AliasSet::ObjectFields
| AliasSet::FixedSlot
);
6225 AliasSet
MResizableTypedArrayLength::getAliasSet() const {
6226 // Loads the length and byteOffset slots, the shared-elements flag, the
6227 // auto-length fixed slot, and the shared raw-buffer length.
6228 auto flags
= AliasSet::ArrayBufferViewLengthOrOffset
|
6229 AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6230 AliasSet::SharedArrayRawBufferLength
;
6232 // When a barrier is needed make the instruction effectful by giving it a
6233 // "store" effect. Also prevent reordering LoadUnboxedScalar before this
6234 // instruction by including |UnboxedElement| in the alias set.
6235 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required
) {
6236 return AliasSet::Store(flags
| AliasSet::UnboxedElement
);
6238 return AliasSet::Load(flags
);
6241 bool MResizableTypedArrayLength::congruentTo(const MDefinition
* ins
) const {
6242 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required
) {
6245 return congruentIfOperandsEqual(ins
);
6248 AliasSet
MResizableDataViewByteLength::getAliasSet() const {
6249 // Loads the length and byteOffset slots, the shared-elements flag, the
6250 // auto-length fixed slot, and the shared raw-buffer length.
6251 auto flags
= AliasSet::ArrayBufferViewLengthOrOffset
|
6252 AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6253 AliasSet::SharedArrayRawBufferLength
;
6255 // When a barrier is needed make the instruction effectful by giving it a
6256 // "store" effect. Also prevent reordering LoadUnboxedScalar before this
6257 // instruction by including |UnboxedElement| in the alias set.
6258 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required
) {
6259 return AliasSet::Store(flags
| AliasSet::UnboxedElement
);
6261 return AliasSet::Load(flags
);
6264 bool MResizableDataViewByteLength::congruentTo(const MDefinition
* ins
) const {
6265 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required
) {
6268 return congruentIfOperandsEqual(ins
);
6271 AliasSet
MGrowableSharedArrayBufferByteLength::getAliasSet() const {
6272 // Requires a barrier, so make the instruction effectful by giving it a
6273 // "store" effect. Also prevent reordering LoadUnboxedScalar before this
6274 // instruction by including |UnboxedElement| in the alias set.
6275 return AliasSet::Store(AliasSet::FixedSlot
|
6276 AliasSet::SharedArrayRawBufferLength
|
6277 AliasSet::UnboxedElement
);
6280 AliasSet
MGuardResizableArrayBufferViewInBounds::getAliasSet() const {
6281 // Additionally reads the |initialLength| and |initialByteOffset| slots, but
6282 // since these can't change after construction, we don't need to track them.
6283 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset
);
6286 AliasSet
MGuardResizableArrayBufferViewInBoundsOrDetached::getAliasSet() const {
6287 // Loads the byteOffset and additionally checks for detached buffers, so the
6288 // alias set also has to include |ObjectFields| and |FixedSlot|.
6289 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset
|
6290 AliasSet::ObjectFields
| AliasSet::FixedSlot
);
6293 AliasSet
MArrayPush::getAliasSet() const {
6294 return AliasSet::Store(AliasSet::ObjectFields
| AliasSet::Element
);
6297 MDefinition
* MGuardNumberToIntPtrIndex::foldsTo(TempAllocator
& alloc
) {
6298 MDefinition
* input
= this->input();
6300 if (input
->isToDouble() && input
->getOperand(0)->type() == MIRType::Int32
) {
6301 return MInt32ToIntPtr::New(alloc
, input
->getOperand(0));
6304 if (!input
->isConstant()) {
6308 // Fold constant double representable as intptr to intptr.
6310 if (!mozilla::NumberEqualsInt64(input
->toConstant()->toDouble(), &ival
)) {
6311 // If not representable as an int64, this access is equal to an OOB access.
6312 // So replace it with a known int64/intptr value which also produces an OOB
6313 // access. If we don't support OOB accesses we have to bail out.
6314 if (!supportOOB()) {
6320 if (ival
< INTPTR_MIN
|| ival
> INTPTR_MAX
) {
6324 return MConstant::NewIntPtr(alloc
, intptr_t(ival
));
6327 MDefinition
* MIsObject::foldsTo(TempAllocator
& alloc
) {
6328 if (!object()->isBox()) {
6332 MDefinition
* unboxed
= object()->getOperand(0);
6333 if (unboxed
->type() == MIRType::Object
) {
6334 return MConstant::New(alloc
, BooleanValue(true));
6340 MDefinition
* MIsNullOrUndefined::foldsTo(TempAllocator
& alloc
) {
6341 MDefinition
* input
= value();
6342 if (input
->isBox()) {
6343 input
= input
->toBox()->input();
6346 if (input
->definitelyType({MIRType::Null
, MIRType::Undefined
})) {
6347 return MConstant::New(alloc
, BooleanValue(true));
6350 if (!input
->mightBeType(MIRType::Null
) &&
6351 !input
->mightBeType(MIRType::Undefined
)) {
6352 return MConstant::New(alloc
, BooleanValue(false));
6358 AliasSet
MHomeObjectSuperBase::getAliasSet() const {
6359 return AliasSet::Load(AliasSet::ObjectFields
);
6362 MDefinition
* MGuardValue::foldsTo(TempAllocator
& alloc
) {
6363 if (MConstant
* cst
= value()->maybeConstantValue()) {
6364 if (cst
->toJSValue() == expected()) {
6372 MDefinition
* MGuardNullOrUndefined::foldsTo(TempAllocator
& alloc
) {
6373 MDefinition
* input
= value();
6374 if (input
->isBox()) {
6375 input
= input
->toBox()->input();
6378 if (input
->definitelyType({MIRType::Null
, MIRType::Undefined
})) {
6385 MDefinition
* MGuardIsNotObject::foldsTo(TempAllocator
& alloc
) {
6386 MDefinition
* input
= value();
6387 if (input
->isBox()) {
6388 input
= input
->toBox()->input();
6391 if (!input
->mightBeType(MIRType::Object
)) {
6398 MDefinition
* MGuardObjectIdentity::foldsTo(TempAllocator
& alloc
) {
6399 if (object()->isConstant() && expected()->isConstant()) {
6400 JSObject
* obj
= &object()->toConstant()->toObject();
6401 JSObject
* other
= &expected()->toConstant()->toObject();
6402 if (!bailOnEquality()) {
6413 if (!bailOnEquality() && object()->isNurseryObject() &&
6414 expected()->isNurseryObject()) {
6415 uint32_t objIndex
= object()->toNurseryObject()->nurseryIndex();
6416 uint32_t otherIndex
= expected()->toNurseryObject()->nurseryIndex();
6417 if (objIndex
== otherIndex
) {
6425 MDefinition
* MGuardSpecificFunction::foldsTo(TempAllocator
& alloc
) {
6426 if (function()->isConstant() && expected()->isConstant()) {
6427 JSObject
* fun
= &function()->toConstant()->toObject();
6428 JSObject
* other
= &expected()->toConstant()->toObject();
6434 if (function()->isNurseryObject() && expected()->isNurseryObject()) {
6435 uint32_t funIndex
= function()->toNurseryObject()->nurseryIndex();
6436 uint32_t otherIndex
= expected()->toNurseryObject()->nurseryIndex();
6437 if (funIndex
== otherIndex
) {
6445 MDefinition
* MGuardSpecificAtom::foldsTo(TempAllocator
& alloc
) {
6446 if (str()->isConstant()) {
6447 JSString
* s
= str()->toConstant()->toString();
6449 JSAtom
* cstAtom
= &s
->asAtom();
6450 if (cstAtom
== atom()) {
6459 MDefinition
* MGuardSpecificSymbol::foldsTo(TempAllocator
& alloc
) {
6460 if (symbol()->isConstant()) {
6461 if (symbol()->toConstant()->toSymbol() == expected()) {
6469 MDefinition
* MGuardSpecificInt32::foldsTo(TempAllocator
& alloc
) {
6470 if (num()->isConstant() && num()->toConstant()->isInt32(expected())) {
6476 bool MCallBindVar::congruentTo(const MDefinition
* ins
) const {
6477 if (!ins
->isCallBindVar()) {
6480 return congruentIfOperandsEqual(ins
);
6483 bool MGuardShape::congruentTo(const MDefinition
* ins
) const {
6484 if (!ins
->isGuardShape()) {
6487 if (shape() != ins
->toGuardShape()->shape()) {
6490 return congruentIfOperandsEqual(ins
);
6493 AliasSet
MGuardShape::getAliasSet() const {
6494 return AliasSet::Load(AliasSet::ObjectFields
);
6497 MDefinition::AliasType
MGuardShape::mightAlias(const MDefinition
* store
) const {
6498 // These instructions only modify object elements, but not the shape.
6499 if (store
->isStoreElementHole() || store
->isArrayPush()) {
6500 return AliasType::NoAlias
;
6502 if (object()->isConstantProto()) {
6503 const MDefinition
* receiverObject
=
6504 object()->toConstantProto()->getReceiverObject();
6505 switch (store
->op()) {
6506 case MDefinition::Opcode::StoreFixedSlot
:
6507 if (store
->toStoreFixedSlot()->object()->skipObjectGuards() ==
6509 return AliasType::NoAlias
;
6512 case MDefinition::Opcode::StoreDynamicSlot
:
6513 if (store
->toStoreDynamicSlot()
6517 ->skipObjectGuards() == receiverObject
) {
6518 return AliasType::NoAlias
;
6521 case MDefinition::Opcode::AddAndStoreSlot
:
6522 if (store
->toAddAndStoreSlot()->object()->skipObjectGuards() ==
6524 return AliasType::NoAlias
;
6527 case MDefinition::Opcode::AllocateAndStoreSlot
:
6528 if (store
->toAllocateAndStoreSlot()->object()->skipObjectGuards() ==
6530 return AliasType::NoAlias
;
6537 return MInstruction::mightAlias(store
);
6540 bool MGuardFuse::congruentTo(const MDefinition
* ins
) const {
6541 if (!ins
->isGuardFuse()) {
6544 if (fuseIndex() != ins
->toGuardFuse()->fuseIndex()) {
6547 return congruentIfOperandsEqual(ins
);
6550 AliasSet
MGuardFuse::getAliasSet() const {
6551 // The alias set below reflects the set of operations which could cause a fuse
6552 // to be popped, and therefore MGuardFuse aliases with.
6553 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::DynamicSlot
|
6554 AliasSet::FixedSlot
|
6555 AliasSet::GlobalGenerationCounter
);
6558 AliasSet
MGuardMultipleShapes::getAliasSet() const {
6559 // Note: This instruction loads the elements of the ListObject used to
6560 // store the list of shapes, but that object is internal and not exposed
6561 // to script, so it doesn't have to be in the alias set.
6562 return AliasSet::Load(AliasSet::ObjectFields
);
6565 AliasSet
MGuardGlobalGeneration::getAliasSet() const {
6566 return AliasSet::Load(AliasSet::GlobalGenerationCounter
);
6569 bool MGuardGlobalGeneration::congruentTo(const MDefinition
* ins
) const {
6570 return ins
->isGuardGlobalGeneration() &&
6571 ins
->toGuardGlobalGeneration()->expected() == expected() &&
6572 ins
->toGuardGlobalGeneration()->generationAddr() == generationAddr();
6575 MDefinition
* MGuardIsNotProxy::foldsTo(TempAllocator
& alloc
) {
6576 KnownClass known
= GetObjectKnownClass(object());
6577 if (known
== KnownClass::None
) {
6581 MOZ_ASSERT(!GetObjectKnownJSClass(object())->isProxyObject());
6582 AssertKnownClass(alloc
, this, object());
6586 AliasSet
MMegamorphicLoadSlotByValue::getAliasSet() const {
6587 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6588 AliasSet::DynamicSlot
);
6591 MDefinition
* MMegamorphicLoadSlotByValue::foldsTo(TempAllocator
& alloc
) {
6592 MDefinition
* input
= idVal();
6593 if (input
->isBox()) {
6594 input
= input
->toBox()->input();
6597 MDefinition
* result
= this;
6599 if (input
->isConstant()) {
6600 MConstant
* constant
= input
->toConstant();
6601 if (constant
->type() == MIRType::Symbol
) {
6602 PropertyKey id
= PropertyKey::Symbol(constant
->toSymbol());
6603 result
= MMegamorphicLoadSlot::New(alloc
, object(), id
);
6606 if (constant
->type() == MIRType::String
) {
6607 JSString
* str
= constant
->toString();
6608 if (str
->isAtom() && !str
->asAtom().isIndex()) {
6609 PropertyKey id
= PropertyKey::NonIntAtom(str
);
6610 result
= MMegamorphicLoadSlot::New(alloc
, object(), id
);
6615 if (result
!= this) {
6616 result
->setDependency(dependency());
6622 MDefinition
* MMegamorphicLoadSlotByValuePermissive::foldsTo(
6623 TempAllocator
& alloc
) {
6624 MDefinition
* input
= idVal();
6625 if (input
->isBox()) {
6626 input
= input
->toBox()->input();
6629 MDefinition
* result
= this;
6631 if (input
->isConstant()) {
6632 MConstant
* constant
= input
->toConstant();
6633 if (constant
->type() == MIRType::Symbol
) {
6634 PropertyKey id
= PropertyKey::Symbol(constant
->toSymbol());
6635 result
= MMegamorphicLoadSlotPermissive::New(alloc
, object(), id
);
6638 if (constant
->type() == MIRType::String
) {
6639 JSString
* str
= constant
->toString();
6640 if (str
->isAtom() && !str
->asAtom().isIndex()) {
6641 PropertyKey id
= PropertyKey::NonIntAtom(str
);
6642 result
= MMegamorphicLoadSlotPermissive::New(alloc
, object(), id
);
6647 if (result
!= this) {
6648 result
->toMegamorphicLoadSlotPermissive()->stealResumePoint(this);
6654 bool MMegamorphicLoadSlot::congruentTo(const MDefinition
* ins
) const {
6655 if (!ins
->isMegamorphicLoadSlot()) {
6658 if (ins
->toMegamorphicLoadSlot()->name() != name()) {
6661 return congruentIfOperandsEqual(ins
);
6664 AliasSet
MMegamorphicLoadSlot::getAliasSet() const {
6665 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6666 AliasSet::DynamicSlot
);
6669 bool MSmallObjectVariableKeyHasProp::congruentTo(const MDefinition
* ins
) const {
6670 if (!ins
->isSmallObjectVariableKeyHasProp()) {
6673 if (ins
->toSmallObjectVariableKeyHasProp()->shape() != shape()) {
6676 return congruentIfOperandsEqual(ins
);
6679 AliasSet
MSmallObjectVariableKeyHasProp::getAliasSet() const {
6680 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6681 AliasSet::DynamicSlot
);
6684 bool MMegamorphicHasProp::congruentTo(const MDefinition
* ins
) const {
6685 if (!ins
->isMegamorphicHasProp()) {
6688 if (ins
->toMegamorphicHasProp()->hasOwn() != hasOwn()) {
6691 return congruentIfOperandsEqual(ins
);
6694 AliasSet
MMegamorphicHasProp::getAliasSet() const {
6695 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6696 AliasSet::DynamicSlot
);
6699 bool MNurseryObject::congruentTo(const MDefinition
* ins
) const {
6700 if (!ins
->isNurseryObject()) {
6703 return nurseryIndex() == ins
->toNurseryObject()->nurseryIndex();
6706 AliasSet
MGuardFunctionIsNonBuiltinCtor::getAliasSet() const {
6707 return AliasSet::Load(AliasSet::ObjectFields
);
6710 bool MGuardFunctionKind::congruentTo(const MDefinition
* ins
) const {
6711 if (!ins
->isGuardFunctionKind()) {
6714 if (expected() != ins
->toGuardFunctionKind()->expected()) {
6717 if (bailOnEquality() != ins
->toGuardFunctionKind()->bailOnEquality()) {
6720 return congruentIfOperandsEqual(ins
);
6723 AliasSet
MGuardFunctionKind::getAliasSet() const {
6724 return AliasSet::Load(AliasSet::ObjectFields
);
6727 bool MGuardFunctionScript::congruentTo(const MDefinition
* ins
) const {
6728 if (!ins
->isGuardFunctionScript()) {
6731 if (expected() != ins
->toGuardFunctionScript()->expected()) {
6734 return congruentIfOperandsEqual(ins
);
6737 AliasSet
MGuardFunctionScript::getAliasSet() const {
6738 // A JSFunction's BaseScript pointer is immutable. Relazification of
6739 // top-level/named self-hosted functions is an exception to this, but we don't
6740 // use this guard for those self-hosted functions.
6741 // See IRGenerator::emitCalleeGuard.
6742 MOZ_ASSERT_IF(flags_
.isSelfHostedOrIntrinsic(), flags_
.isLambda());
6743 return AliasSet::None();
6746 bool MGuardSpecificAtom::congruentTo(const MDefinition
* ins
) const {
6747 if (!ins
->isGuardSpecificAtom()) {
6750 if (atom() != ins
->toGuardSpecificAtom()->atom()) {
6753 return congruentIfOperandsEqual(ins
);
6756 MDefinition
* MGuardStringToIndex::foldsTo(TempAllocator
& alloc
) {
6757 if (!string()->isConstant()) {
6761 JSString
* str
= string()->toConstant()->toString();
6763 int32_t index
= GetIndexFromString(str
);
6768 return MConstant::New(alloc
, Int32Value(index
));
6771 MDefinition
* MGuardStringToInt32::foldsTo(TempAllocator
& alloc
) {
6772 if (!string()->isConstant()) {
6776 JSLinearString
* str
= &string()->toConstant()->toString()->asLinear();
6777 double number
= LinearStringToNumber(str
);
6780 if (!mozilla::NumberIsInt32(number
, &n
)) {
6784 return MConstant::New(alloc
, Int32Value(n
));
6787 MDefinition
* MGuardStringToDouble::foldsTo(TempAllocator
& alloc
) {
6788 if (!string()->isConstant()) {
6792 JSLinearString
* str
= &string()->toConstant()->toString()->asLinear();
6793 double number
= LinearStringToNumber(str
);
6794 return MConstant::New(alloc
, DoubleValue(number
));
6797 AliasSet
MGuardNoDenseElements::getAliasSet() const {
6798 return AliasSet::Load(AliasSet::ObjectFields
);
6801 AliasSet
MIteratorHasIndices::getAliasSet() const {
6802 return AliasSet::Load(AliasSet::ObjectFields
);
6805 AliasSet
MAllocateAndStoreSlot::getAliasSet() const {
6806 return AliasSet::Store(AliasSet::ObjectFields
| AliasSet::DynamicSlot
);
6809 AliasSet
MLoadDOMExpandoValue::getAliasSet() const {
6810 return AliasSet::Load(AliasSet::DOMProxyExpando
);
6813 AliasSet
MLoadDOMExpandoValueIgnoreGeneration::getAliasSet() const {
6814 return AliasSet::Load(AliasSet::DOMProxyExpando
);
6817 bool MGuardDOMExpandoMissingOrGuardShape::congruentTo(
6818 const MDefinition
* ins
) const {
6819 if (!ins
->isGuardDOMExpandoMissingOrGuardShape()) {
6822 if (shape() != ins
->toGuardDOMExpandoMissingOrGuardShape()->shape()) {
6825 return congruentIfOperandsEqual(ins
);
6828 AliasSet
MGuardDOMExpandoMissingOrGuardShape::getAliasSet() const {
6829 return AliasSet::Load(AliasSet::ObjectFields
);
6832 MDefinition
* MGuardToClass::foldsTo(TempAllocator
& alloc
) {
6833 const JSClass
* clasp
= GetObjectKnownJSClass(object());
6834 if (!clasp
|| getClass() != clasp
) {
6838 AssertKnownClass(alloc
, this, object());
6842 MDefinition
* MGuardToEitherClass::foldsTo(TempAllocator
& alloc
) {
6843 const JSClass
* clasp
= GetObjectKnownJSClass(object());
6844 if (!clasp
|| (getClass1() != clasp
&& getClass2() != clasp
)) {
6848 AssertKnownClass(alloc
, this, object());
6852 MDefinition
* MGuardToFunction::foldsTo(TempAllocator
& alloc
) {
6853 if (GetObjectKnownClass(object()) != KnownClass::Function
) {
6857 AssertKnownClass(alloc
, this, object());
6861 MDefinition
* MHasClass::foldsTo(TempAllocator
& alloc
) {
6862 const JSClass
* clasp
= GetObjectKnownJSClass(object());
6867 AssertKnownClass(alloc
, this, object());
6868 return MConstant::New(alloc
, BooleanValue(getClass() == clasp
));
6871 MDefinition
* MIsCallable::foldsTo(TempAllocator
& alloc
) {
6872 if (input()->type() != MIRType::Object
) {
6876 KnownClass known
= GetObjectKnownClass(input());
6877 if (known
== KnownClass::None
) {
6881 AssertKnownClass(alloc
, this, input());
6882 return MConstant::New(alloc
, BooleanValue(known
== KnownClass::Function
));
6885 MDefinition
* MIsArray::foldsTo(TempAllocator
& alloc
) {
6886 if (input()->type() != MIRType::Object
) {
6890 KnownClass known
= GetObjectKnownClass(input());
6891 if (known
== KnownClass::None
) {
6895 AssertKnownClass(alloc
, this, input());
6896 return MConstant::New(alloc
, BooleanValue(known
== KnownClass::Array
));
6899 AliasSet
MObjectClassToString::getAliasSet() const {
6900 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6901 AliasSet::DynamicSlot
);
6904 MDefinition
* MGuardIsNotArrayBufferMaybeShared::foldsTo(TempAllocator
& alloc
) {
6905 switch (GetObjectKnownClass(object())) {
6906 case KnownClass::PlainObject
:
6907 case KnownClass::Array
:
6908 case KnownClass::Function
:
6909 case KnownClass::RegExp
:
6910 case KnownClass::ArrayIterator
:
6911 case KnownClass::StringIterator
:
6912 case KnownClass::RegExpStringIterator
: {
6913 AssertKnownClass(alloc
, this, object());
6916 case KnownClass::None
:
6923 MDefinition
* MCheckIsObj::foldsTo(TempAllocator
& alloc
) {
6924 if (!input()->isBox()) {
6928 MDefinition
* unboxed
= input()->getOperand(0);
6929 if (unboxed
->type() == MIRType::Object
) {
6936 AliasSet
MCheckIsObj::getAliasSet() const {
6937 return AliasSet::Store(AliasSet::ExceptionState
);
6941 AliasSet
MCheckScriptedProxyGetResult::getAliasSet() const {
6942 return AliasSet::Store(AliasSet::ExceptionState
);
6946 static bool IsBoxedObject(MDefinition
* def
) {
6947 MOZ_ASSERT(def
->type() == MIRType::Value
);
6950 return def
->toBox()->input()->type() == MIRType::Object
;
6953 // Construct calls are always returning a boxed object.
6955 // TODO: We should consider encoding this directly in the graph instead of
6956 // having to special case it here.
6957 if (def
->isCall()) {
6958 return def
->toCall()->isConstructing();
6960 if (def
->isConstructArray()) {
6963 if (def
->isConstructArgs()) {
6970 MDefinition
* MCheckReturn::foldsTo(TempAllocator
& alloc
) {
6971 auto* returnVal
= returnValue();
6972 if (!returnVal
->isBox()) {
6976 auto* unboxedReturnVal
= returnVal
->toBox()->input();
6977 if (unboxedReturnVal
->type() == MIRType::Object
) {
6981 if (unboxedReturnVal
->type() != MIRType::Undefined
) {
6985 auto* thisVal
= thisValue();
6986 if (IsBoxedObject(thisVal
)) {
6993 MDefinition
* MCheckThis::foldsTo(TempAllocator
& alloc
) {
6994 MDefinition
* input
= thisValue();
6995 if (!input
->isBox()) {
6999 MDefinition
* unboxed
= input
->getOperand(0);
7000 if (unboxed
->mightBeMagicType()) {
7007 MDefinition
* MCheckThisReinit::foldsTo(TempAllocator
& alloc
) {
7008 MDefinition
* input
= thisValue();
7009 if (!input
->isBox()) {
7013 MDefinition
* unboxed
= input
->getOperand(0);
7014 if (unboxed
->type() != MIRType::MagicUninitializedLexical
) {
7021 MDefinition
* MCheckObjCoercible::foldsTo(TempAllocator
& alloc
) {
7022 MDefinition
* input
= checkValue();
7023 if (!input
->isBox()) {
7027 MDefinition
* unboxed
= input
->getOperand(0);
7028 if (unboxed
->mightBeType(MIRType::Null
) ||
7029 unboxed
->mightBeType(MIRType::Undefined
)) {
7036 AliasSet
MCheckObjCoercible::getAliasSet() const {
7037 return AliasSet::Store(AliasSet::ExceptionState
);
7040 AliasSet
MCheckReturn::getAliasSet() const {
7041 return AliasSet::Store(AliasSet::ExceptionState
);
7044 AliasSet
MCheckThis::getAliasSet() const {
7045 return AliasSet::Store(AliasSet::ExceptionState
);
7048 AliasSet
MCheckThisReinit::getAliasSet() const {
7049 return AliasSet::Store(AliasSet::ExceptionState
);
7052 AliasSet
MIsPackedArray::getAliasSet() const {
7053 return AliasSet::Load(AliasSet::ObjectFields
);
7056 AliasSet
MGuardArrayIsPacked::getAliasSet() const {
7057 return AliasSet::Load(AliasSet::ObjectFields
);
7060 AliasSet
MSuperFunction::getAliasSet() const {
7061 return AliasSet::Load(AliasSet::ObjectFields
);
7064 AliasSet
MInitHomeObject::getAliasSet() const {
7065 return AliasSet::Store(AliasSet::ObjectFields
);
7068 AliasSet
MLoadWrapperTarget::getAliasSet() const {
7069 return AliasSet::Load(AliasSet::Any
);
7072 bool MLoadWrapperTarget::congruentTo(const MDefinition
* ins
) const {
7073 if (!ins
->isLoadWrapperTarget()) {
7076 if (ins
->toLoadWrapperTarget()->fallible() != fallible()) {
7079 return congruentIfOperandsEqual(ins
);
7082 AliasSet
MGuardHasGetterSetter::getAliasSet() const {
7083 return AliasSet::Load(AliasSet::ObjectFields
);
7086 bool MGuardHasGetterSetter::congruentTo(const MDefinition
* ins
) const {
7087 if (!ins
->isGuardHasGetterSetter()) {
7090 if (ins
->toGuardHasGetterSetter()->propId() != propId()) {
7093 if (ins
->toGuardHasGetterSetter()->getterSetter() != getterSetter()) {
7096 return congruentIfOperandsEqual(ins
);
7099 AliasSet
MGuardIsExtensible::getAliasSet() const {
7100 return AliasSet::Load(AliasSet::ObjectFields
);
7103 AliasSet
MGuardIndexIsNotDenseElement::getAliasSet() const {
7104 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::Element
);
7107 AliasSet
MGuardIndexIsValidUpdateOrAdd::getAliasSet() const {
7108 return AliasSet::Load(AliasSet::ObjectFields
);
7111 AliasSet
MCallObjectHasSparseElement::getAliasSet() const {
7112 return AliasSet::Load(AliasSet::Element
| AliasSet::ObjectFields
|
7113 AliasSet::FixedSlot
| AliasSet::DynamicSlot
);
7116 AliasSet
MLoadSlotByIteratorIndex::getAliasSet() const {
7117 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
7118 AliasSet::DynamicSlot
| AliasSet::Element
);
7121 AliasSet
MStoreSlotByIteratorIndex::getAliasSet() const {
7122 return AliasSet::Store(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
7123 AliasSet::DynamicSlot
| AliasSet::Element
);
7126 MDefinition
* MGuardInt32IsNonNegative::foldsTo(TempAllocator
& alloc
) {
7127 MOZ_ASSERT(index()->type() == MIRType::Int32
);
7129 MDefinition
* input
= index();
7130 if (!input
->isConstant() || input
->toConstant()->toInt32() < 0) {
7136 MDefinition
* MGuardInt32Range::foldsTo(TempAllocator
& alloc
) {
7137 MOZ_ASSERT(input()->type() == MIRType::Int32
);
7138 MOZ_ASSERT(minimum() <= maximum());
7140 MDefinition
* in
= input();
7141 if (!in
->isConstant()) {
7144 int32_t cst
= in
->toConstant()->toInt32();
7145 if (cst
< minimum() || cst
> maximum()) {
7151 MDefinition
* MGuardNonGCThing::foldsTo(TempAllocator
& alloc
) {
7152 if (!input()->isBox()) {
7156 MDefinition
* unboxed
= input()->getOperand(0);
7157 if (!IsNonGCThing(unboxed
->type())) {
7163 AliasSet
MSetObjectHasNonBigInt::getAliasSet() const {
7164 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7167 AliasSet
MSetObjectHasBigInt::getAliasSet() const {
7168 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7171 AliasSet
MSetObjectHasValue::getAliasSet() const {
7172 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7175 AliasSet
MSetObjectHasValueVMCall::getAliasSet() const {
7176 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7179 AliasSet
MSetObjectSize::getAliasSet() const {
7180 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7183 AliasSet
MMapObjectHasNonBigInt::getAliasSet() const {
7184 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7187 AliasSet
MMapObjectHasBigInt::getAliasSet() const {
7188 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7191 AliasSet
MMapObjectHasValue::getAliasSet() const {
7192 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7195 AliasSet
MMapObjectHasValueVMCall::getAliasSet() const {
7196 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7199 AliasSet
MMapObjectGetNonBigInt::getAliasSet() const {
7200 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7203 AliasSet
MMapObjectGetBigInt::getAliasSet() const {
7204 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7207 AliasSet
MMapObjectGetValue::getAliasSet() const {
7208 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7211 AliasSet
MMapObjectGetValueVMCall::getAliasSet() const {
7212 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7215 AliasSet
MMapObjectSize::getAliasSet() const {
7216 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7219 MBindFunction
* MBindFunction::New(TempAllocator
& alloc
, MDefinition
* target
,
7220 uint32_t argc
, JSObject
* templateObj
) {
7221 auto* ins
= new (alloc
) MBindFunction(templateObj
);
7222 if (!ins
->init(alloc
, NumNonArgumentOperands
+ argc
)) {
7225 ins
->initOperand(0, target
);
7229 MCreateInlinedArgumentsObject
* MCreateInlinedArgumentsObject::New(
7230 TempAllocator
& alloc
, MDefinition
* callObj
, MDefinition
* callee
,
7231 MDefinitionVector
& args
, ArgumentsObject
* templateObj
) {
7232 MCreateInlinedArgumentsObject
* ins
=
7233 new (alloc
) MCreateInlinedArgumentsObject(templateObj
);
7235 uint32_t argc
= args
.length();
7236 MOZ_ASSERT(argc
<= ArgumentsObject::MaxInlinedArgs
);
7238 if (!ins
->init(alloc
, argc
+ NumNonArgumentOperands
)) {
7242 ins
->initOperand(0, callObj
);
7243 ins
->initOperand(1, callee
);
7244 for (uint32_t i
= 0; i
< argc
; i
++) {
7245 ins
->initOperand(i
+ NumNonArgumentOperands
, args
[i
]);
7251 MGetInlinedArgument
* MGetInlinedArgument::New(
7252 TempAllocator
& alloc
, MDefinition
* index
,
7253 MCreateInlinedArgumentsObject
* args
) {
7254 MGetInlinedArgument
* ins
= new (alloc
) MGetInlinedArgument();
7256 uint32_t argc
= args
->numActuals();
7257 MOZ_ASSERT(argc
<= ArgumentsObject::MaxInlinedArgs
);
7259 if (!ins
->init(alloc
, argc
+ NumNonArgumentOperands
)) {
7263 ins
->initOperand(0, index
);
7264 for (uint32_t i
= 0; i
< argc
; i
++) {
7265 ins
->initOperand(i
+ NumNonArgumentOperands
, args
->getArg(i
));
7271 MGetInlinedArgument
* MGetInlinedArgument::New(TempAllocator
& alloc
,
7273 const CallInfo
& callInfo
) {
7274 MGetInlinedArgument
* ins
= new (alloc
) MGetInlinedArgument();
7276 uint32_t argc
= callInfo
.argc();
7277 MOZ_ASSERT(argc
<= ArgumentsObject::MaxInlinedArgs
);
7279 if (!ins
->init(alloc
, argc
+ NumNonArgumentOperands
)) {
7283 ins
->initOperand(0, index
);
7284 for (uint32_t i
= 0; i
< argc
; i
++) {
7285 ins
->initOperand(i
+ NumNonArgumentOperands
, callInfo
.getArg(i
));
7291 MDefinition
* MGetInlinedArgument::foldsTo(TempAllocator
& alloc
) {
7292 MDefinition
* indexDef
= SkipUninterestingInstructions(index());
7293 if (!indexDef
->isConstant() || indexDef
->type() != MIRType::Int32
) {
7297 int32_t indexConst
= indexDef
->toConstant()->toInt32();
7298 if (indexConst
< 0 || uint32_t(indexConst
) >= numActuals()) {
7302 MDefinition
* arg
= getArg(indexConst
);
7303 if (arg
->type() != MIRType::Value
) {
7304 arg
= MBox::New(alloc
, arg
);
7310 MGetInlinedArgumentHole
* MGetInlinedArgumentHole::New(
7311 TempAllocator
& alloc
, MDefinition
* index
,
7312 MCreateInlinedArgumentsObject
* args
) {
7313 auto* ins
= new (alloc
) MGetInlinedArgumentHole();
7315 uint32_t argc
= args
->numActuals();
7316 MOZ_ASSERT(argc
<= ArgumentsObject::MaxInlinedArgs
);
7318 if (!ins
->init(alloc
, argc
+ NumNonArgumentOperands
)) {
7322 ins
->initOperand(0, index
);
7323 for (uint32_t i
= 0; i
< argc
; i
++) {
7324 ins
->initOperand(i
+ NumNonArgumentOperands
, args
->getArg(i
));
7330 MDefinition
* MGetInlinedArgumentHole::foldsTo(TempAllocator
& alloc
) {
7331 MDefinition
* indexDef
= SkipUninterestingInstructions(index());
7332 if (!indexDef
->isConstant() || indexDef
->type() != MIRType::Int32
) {
7336 int32_t indexConst
= indexDef
->toConstant()->toInt32();
7337 if (indexConst
< 0) {
7342 if (uint32_t(indexConst
) < numActuals()) {
7343 arg
= getArg(indexConst
);
7345 if (arg
->type() != MIRType::Value
) {
7346 arg
= MBox::New(alloc
, arg
);
7349 auto* undefined
= MConstant::New(alloc
, UndefinedValue());
7350 block()->insertBefore(this, undefined
);
7352 arg
= MBox::New(alloc
, undefined
);
7358 MInlineArgumentsSlice
* MInlineArgumentsSlice::New(
7359 TempAllocator
& alloc
, MDefinition
* begin
, MDefinition
* count
,
7360 MCreateInlinedArgumentsObject
* args
, JSObject
* templateObj
,
7361 gc::Heap initialHeap
) {
7362 auto* ins
= new (alloc
) MInlineArgumentsSlice(templateObj
, initialHeap
);
7364 uint32_t argc
= args
->numActuals();
7365 MOZ_ASSERT(argc
<= ArgumentsObject::MaxInlinedArgs
);
7367 if (!ins
->init(alloc
, argc
+ NumNonArgumentOperands
)) {
7371 ins
->initOperand(0, begin
);
7372 ins
->initOperand(1, count
);
7373 for (uint32_t i
= 0; i
< argc
; i
++) {
7374 ins
->initOperand(i
+ NumNonArgumentOperands
, args
->getArg(i
));
7380 MDefinition
* MArrayLength::foldsTo(TempAllocator
& alloc
) {
7381 // Object.keys() is potentially effectful, in case of Proxies. Otherwise, when
7382 // it is only computed for its length property, there is no need to
7383 // materialize the Array which results from it and it can be marked as
7384 // recovered on bailout as long as no properties are added to / removed from
7386 MDefinition
* elems
= elements();
7387 if (!elems
->isElements()) {
7391 MDefinition
* guardshape
= elems
->toElements()->object();
7392 if (!guardshape
->isGuardShape()) {
7396 // The Guard shape is guarding the shape of the object returned by
7397 // Object.keys, this guard can be removed as knowing the function is good
7398 // enough to infer that we are returning an array.
7399 MDefinition
* keys
= guardshape
->toGuardShape()->object();
7400 if (!keys
->isObjectKeys()) {
7404 // Object.keys() inline cache guards against proxies when creating the IC. We
7405 // rely on this here as we are looking to elide `Object.keys(...)` call, which
7406 // is only possible if we know for sure that no side-effect might have
7408 MDefinition
* noproxy
= keys
->toObjectKeys()->object();
7409 if (!noproxy
->isGuardIsNotProxy()) {
7410 // The guard might have been replaced by an assertion, in case the class is
7411 // known at compile time. IF the guard has been removed check whether check
7412 // has been removed.
7413 MOZ_RELEASE_ASSERT(GetObjectKnownClass(noproxy
) != KnownClass::None
);
7414 MOZ_RELEASE_ASSERT(!GetObjectKnownJSClass(noproxy
)->isProxyObject());
7417 // Check if both the elements and the Object.keys() have a single use. We only
7418 // check for live uses, and are ok if a branch which was previously using the
7419 // keys array has been removed since.
7420 if (!elems
->hasOneLiveDefUse() || !guardshape
->hasOneLiveDefUse() ||
7421 !keys
->hasOneLiveDefUse()) {
7425 // Check that the latest active resume point is the one from Object.keys(), in
7426 // order to steal it. If this is not the latest active resume point then some
7427 // side-effect might happen which updates the content of the object, making
7428 // any recovery of the keys exhibit a different behavior than expected.
7429 if (keys
->toObjectKeys()->resumePoint() != block()->activeResumePoint(this)) {
7433 // Verify whether any resume point captures the keys array after any aliasing
7434 // mutations. If this were to be the case the recovery of ObjectKeys on
7435 // bailout might compute a version which might not match with the elided
7438 // Iterate over the resume point uses of ObjectKeys, and check whether the
7439 // instructions they are attached to are aliasing Object fields. If so, skip
7440 // this optimization.
7441 AliasSet enumKeysAliasSet
= AliasSet::Load(AliasSet::Flag::ObjectFields
);
7442 for (auto* use
: UsesIterator(keys
)) {
7443 if (!use
->consumer()->isResumePoint()) {
7444 // There is only a single use, and this is the length computation as
7445 // asserted with `hasOneLiveDefUse`.
7449 MResumePoint
* rp
= use
->consumer()->toResumePoint();
7450 if (!rp
->instruction()) {
7451 // If there is no instruction, this is a resume point which is attached to
7452 // the entry of a block. Thus no risk of mutating the object on which the
7453 // keys are queried.
7457 MInstruction
* ins
= rp
->instruction();
7462 // Check whether the instruction can potentially alias the object fields of
7463 // the object from which we are querying the keys.
7464 AliasSet mightAlias
= ins
->getAliasSet() & enumKeysAliasSet
;
7465 if (!mightAlias
.isNone()) {
7470 // Flag every instructions since Object.keys(..) as recovered on bailout, and
7471 // make Object.keys(..) be the recovered value in-place of the shape guard.
7472 setRecoveredOnBailout();
7473 elems
->setRecoveredOnBailout();
7474 guardshape
->replaceAllUsesWith(keys
);
7475 guardshape
->block()->discard(guardshape
->toGuardShape());
7476 keys
->setRecoveredOnBailout();
7478 // Steal the resume point from Object.keys, which is ok as we confirmed that
7479 // there is no other resume point in-between.
7480 MObjectKeysLength
* keysLength
= MObjectKeysLength::New(alloc
, noproxy
);
7481 keysLength
->stealResumePoint(keys
->toObjectKeys());
7486 MDefinition
* MNormalizeSliceTerm::foldsTo(TempAllocator
& alloc
) {
7487 auto* length
= this->length();
7488 if (!length
->isConstant() && !length
->isArgumentsLength()) {
7492 if (length
->isConstant()) {
7493 int32_t lengthConst
= length
->toConstant()->toInt32();
7494 MOZ_ASSERT(lengthConst
>= 0);
7496 // Result is always zero when |length| is zero.
7497 if (lengthConst
== 0) {
7501 auto* value
= this->value();
7502 if (value
->isConstant()) {
7503 int32_t valueConst
= value
->toConstant()->toInt32();
7506 if (valueConst
< 0) {
7507 normalized
= std::max(valueConst
+ lengthConst
, 0);
7509 normalized
= std::min(valueConst
, lengthConst
);
7512 if (normalized
== valueConst
) {
7515 if (normalized
== lengthConst
) {
7518 return MConstant::New(alloc
, Int32Value(normalized
));
7524 auto* value
= this->value();
7525 if (value
->isConstant()) {
7526 int32_t valueConst
= value
->toConstant()->toInt32();
7528 // Minimum of |value| and |length|.
7529 if (valueConst
> 0) {
7531 return MMinMax::New(alloc
, value
, length
, MIRType::Int32
, isMax
);
7534 // Maximum of |value + length| and zero.
7535 if (valueConst
< 0) {
7536 // Safe to truncate because |length| is never negative.
7537 auto* add
= MAdd::New(alloc
, value
, length
, TruncateKind::Truncate
);
7538 block()->insertBefore(this, add
);
7540 auto* zero
= MConstant::New(alloc
, Int32Value(0));
7541 block()->insertBefore(this, zero
);
7544 return MMinMax::New(alloc
, add
, zero
, MIRType::Int32
, isMax
);
7547 // Directly return the value when it's zero.
7551 // Normalizing MArgumentsLength is a no-op.
7552 if (value
->isArgumentsLength()) {
7559 bool MInt32ToStringWithBase::congruentTo(const MDefinition
* ins
) const {
7560 if (!ins
->isInt32ToStringWithBase()) {
7563 if (ins
->toInt32ToStringWithBase()->lowerCase() != lowerCase()) {
7566 return congruentIfOperandsEqual(ins
);