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/CheckedInt.h"
10 #include "mozilla/EndianUtils.h"
11 #include "mozilla/FloatingPoint.h"
12 #include "mozilla/MathAlgorithms.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/ScopeExit.h"
19 #include "jslibmath.h"
23 #include "builtin/RegExp.h"
24 #include "jit/AtomicOperations.h"
25 #include "jit/CompileInfo.h"
26 #include "jit/KnownClass.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/Iteration.h" // js::NativeIterator
37 #include "vm/PlainObject.h" // js::PlainObject
38 #include "vm/Uint8Clamped.h"
39 #include "wasm/WasmCode.h"
40 #include "wasm/WasmFeatures.h" // for wasm::ReportSimdAnalysis
42 #include "vm/JSAtomUtils-inl.h" // TypeName
43 #include "wasm/WasmInstance-inl.h"
46 using namespace js::jit
;
50 using mozilla::CheckedInt
;
51 using mozilla::DebugOnly
;
52 using mozilla::IsFloat32Representable
;
53 using mozilla::IsPowerOfTwo
;
55 using mozilla::NumbersAreIdentical
;
57 NON_GC_POINTER_TYPE_ASSERTIONS_GENERATED
60 size_t MUse::index() const { return consumer()->indexOf(this); }
64 static void ConvertDefinitionToDouble(TempAllocator
& alloc
, MDefinition
* def
,
65 MInstruction
* consumer
) {
66 MInstruction
* replace
= MToDouble::New(alloc
, def
);
67 consumer
->replaceOperand(Op
, replace
);
68 consumer
->block()->insertBefore(consumer
, replace
);
71 template <size_t Arity
, size_t Index
>
72 static void ConvertOperandToDouble(MAryInstruction
<Arity
>* def
,
73 TempAllocator
& alloc
) {
74 static_assert(Index
< Arity
);
75 auto* operand
= def
->getOperand(Index
);
76 if (operand
->type() == MIRType::Float32
) {
77 ConvertDefinitionToDouble
<Index
>(alloc
, operand
, def
);
81 template <size_t Arity
, size_t... ISeq
>
82 static void ConvertOperandsToDouble(MAryInstruction
<Arity
>* def
,
84 std::index_sequence
<ISeq
...>) {
85 (ConvertOperandToDouble
<Arity
, ISeq
>(def
, alloc
), ...);
88 template <size_t Arity
>
89 static void ConvertOperandsToDouble(MAryInstruction
<Arity
>* def
,
90 TempAllocator
& alloc
) {
91 ConvertOperandsToDouble
<Arity
>(def
, alloc
, std::make_index_sequence
<Arity
>{});
94 template <size_t Arity
, size_t... ISeq
>
95 static bool AllOperandsCanProduceFloat32(MAryInstruction
<Arity
>* def
,
96 std::index_sequence
<ISeq
...>) {
97 return (def
->getOperand(ISeq
)->canProduceFloat32() && ...);
100 template <size_t Arity
>
101 static bool AllOperandsCanProduceFloat32(MAryInstruction
<Arity
>* def
) {
102 return AllOperandsCanProduceFloat32
<Arity
>(def
,
103 std::make_index_sequence
<Arity
>{});
106 static bool CheckUsesAreFloat32Consumers(const MInstruction
* ins
) {
107 if (ins
->isImplicitlyUsed()) {
110 bool allConsumerUses
= true;
111 for (MUseDefIterator
use(ins
); allConsumerUses
&& use
; use
++) {
112 allConsumerUses
&= use
.def()->canConsumeFloat32(use
.use());
114 return allConsumerUses
;
118 static const char* OpcodeName(MDefinition::Opcode op
) {
119 static const char* const names
[] = {
121 MIR_OPCODE_LIST(NAME
)
124 return names
[unsigned(op
)];
127 void MDefinition::PrintOpcodeName(GenericPrinter
& out
, Opcode op
) {
128 const char* name
= OpcodeName(op
);
129 size_t len
= strlen(name
);
130 for (size_t i
= 0; i
< len
; i
++) {
131 out
.printf("%c", unicode::ToLowerCase(name
[i
]));
135 uint32_t js::jit::GetMBasicBlockId(const MBasicBlock
* block
) {
140 static MConstant
* EvaluateInt64ConstantOperands(TempAllocator
& alloc
,
141 MBinaryInstruction
* ins
) {
142 MDefinition
* left
= ins
->getOperand(0);
143 MDefinition
* right
= ins
->getOperand(1);
145 if (!left
->isConstant() || !right
->isConstant()) {
149 MOZ_ASSERT(left
->type() == MIRType::Int64
);
150 MOZ_ASSERT(right
->type() == MIRType::Int64
);
152 int64_t lhs
= left
->toConstant()->toInt64();
153 int64_t rhs
= right
->toConstant()->toInt64();
157 case MDefinition::Opcode::BitAnd
:
160 case MDefinition::Opcode::BitOr
:
163 case MDefinition::Opcode::BitXor
:
166 case MDefinition::Opcode::Lsh
:
167 ret
= lhs
<< (rhs
& 0x3F);
169 case MDefinition::Opcode::Rsh
:
170 ret
= lhs
>> (rhs
& 0x3F);
172 case MDefinition::Opcode::Ursh
:
173 ret
= uint64_t(lhs
) >> (uint64_t(rhs
) & 0x3F);
175 case MDefinition::Opcode::Add
:
178 case MDefinition::Opcode::Sub
:
181 case MDefinition::Opcode::Mul
:
184 case MDefinition::Opcode::Div
:
186 // Division by zero will trap at runtime.
189 if (ins
->toDiv()->isUnsigned()) {
190 ret
= int64_t(uint64_t(lhs
) / uint64_t(rhs
));
191 } else if (lhs
== INT64_MIN
|| rhs
== -1) {
192 // Overflow will trap at runtime.
198 case MDefinition::Opcode::Mod
:
200 // Division by zero will trap at runtime.
203 if (!ins
->toMod()->isUnsigned() && (lhs
< 0 || rhs
< 0)) {
204 // Handle all negative values at runtime, for simplicity.
207 ret
= int64_t(uint64_t(lhs
) % uint64_t(rhs
));
213 return MConstant::NewInt64(alloc
, ret
);
216 static MConstant
* EvaluateConstantOperands(TempAllocator
& alloc
,
217 MBinaryInstruction
* ins
,
218 bool* ptypeChange
= nullptr) {
219 MDefinition
* left
= ins
->getOperand(0);
220 MDefinition
* right
= ins
->getOperand(1);
222 MOZ_ASSERT(IsTypeRepresentableAsDouble(left
->type()));
223 MOZ_ASSERT(IsTypeRepresentableAsDouble(right
->type()));
225 if (!left
->isConstant() || !right
->isConstant()) {
229 MConstant
* lhs
= left
->toConstant();
230 MConstant
* rhs
= right
->toConstant();
231 double ret
= JS::GenericNaN();
234 case MDefinition::Opcode::BitAnd
:
235 ret
= double(lhs
->toInt32() & rhs
->toInt32());
237 case MDefinition::Opcode::BitOr
:
238 ret
= double(lhs
->toInt32() | rhs
->toInt32());
240 case MDefinition::Opcode::BitXor
:
241 ret
= double(lhs
->toInt32() ^ rhs
->toInt32());
243 case MDefinition::Opcode::Lsh
:
244 ret
= double(uint32_t(lhs
->toInt32()) << (rhs
->toInt32() & 0x1F));
246 case MDefinition::Opcode::Rsh
:
247 ret
= double(lhs
->toInt32() >> (rhs
->toInt32() & 0x1F));
249 case MDefinition::Opcode::Ursh
:
250 ret
= double(uint32_t(lhs
->toInt32()) >> (rhs
->toInt32() & 0x1F));
252 case MDefinition::Opcode::Add
:
253 ret
= lhs
->numberToDouble() + rhs
->numberToDouble();
255 case MDefinition::Opcode::Sub
:
256 ret
= lhs
->numberToDouble() - rhs
->numberToDouble();
258 case MDefinition::Opcode::Mul
:
259 ret
= lhs
->numberToDouble() * rhs
->numberToDouble();
261 case MDefinition::Opcode::Div
:
262 if (ins
->toDiv()->isUnsigned()) {
263 if (rhs
->isInt32(0)) {
264 if (ins
->toDiv()->trapOnError()) {
269 ret
= double(uint32_t(lhs
->toInt32()) / uint32_t(rhs
->toInt32()));
272 ret
= NumberDiv(lhs
->numberToDouble(), rhs
->numberToDouble());
275 case MDefinition::Opcode::Mod
:
276 if (ins
->toMod()->isUnsigned()) {
277 if (rhs
->isInt32(0)) {
278 if (ins
->toMod()->trapOnError()) {
283 ret
= double(uint32_t(lhs
->toInt32()) % uint32_t(rhs
->toInt32()));
286 ret
= NumberMod(lhs
->numberToDouble(), rhs
->numberToDouble());
293 if (ins
->type() == MIRType::Float32
) {
294 return MConstant::NewFloat32(alloc
, float(ret
));
296 if (ins
->type() == MIRType::Double
) {
297 return MConstant::New(alloc
, DoubleValue(ret
));
301 retVal
.setNumber(JS::CanonicalizeNaN(ret
));
303 // If this was an int32 operation but the result isn't an int32 (for
304 // example, a division where the numerator isn't evenly divisible by the
305 // denominator), decline folding.
306 MOZ_ASSERT(ins
->type() == MIRType::Int32
);
307 if (!retVal
.isInt32()) {
314 return MConstant::New(alloc
, retVal
);
317 static MMul
* EvaluateExactReciprocal(TempAllocator
& alloc
, MDiv
* ins
) {
318 // we should fold only when it is a floating point operation
319 if (!IsFloatingPointType(ins
->type())) {
323 MDefinition
* left
= ins
->getOperand(0);
324 MDefinition
* right
= ins
->getOperand(1);
326 if (!right
->isConstant()) {
331 if (!mozilla::NumberIsInt32(right
->toConstant()->numberToDouble(), &num
)) {
335 // check if rhs is a power of two
336 if (mozilla::Abs(num
) & (mozilla::Abs(num
) - 1)) {
341 ret
.setDouble(1.0 / double(num
));
343 MConstant
* foldedRhs
;
344 if (ins
->type() == MIRType::Float32
) {
345 foldedRhs
= MConstant::NewFloat32(alloc
, ret
.toDouble());
347 foldedRhs
= MConstant::New(alloc
, ret
);
350 MOZ_ASSERT(foldedRhs
->type() == ins
->type());
351 ins
->block()->insertBefore(ins
, foldedRhs
);
353 MMul
* mul
= MMul::New(alloc
, left
, foldedRhs
, ins
->type());
354 mul
->setMustPreserveNaN(ins
->mustPreserveNaN());
359 const char* MDefinition::opName() const { return OpcodeName(op()); }
361 void MDefinition::printName(GenericPrinter
& out
) const {
362 PrintOpcodeName(out
, op());
363 out
.printf("%u", id());
367 HashNumber
MDefinition::valueHash() const {
368 HashNumber out
= HashNumber(op());
369 for (size_t i
= 0, e
= numOperands(); i
< e
; i
++) {
370 out
= addU32ToHash(out
, getOperand(i
)->id());
372 if (MDefinition
* dep
= dependency()) {
373 out
= addU32ToHash(out
, dep
->id());
378 HashNumber
MNullaryInstruction::valueHash() const {
379 HashNumber hash
= HashNumber(op());
380 if (MDefinition
* dep
= dependency()) {
381 hash
= addU32ToHash(hash
, dep
->id());
383 MOZ_ASSERT(hash
== MDefinition::valueHash());
387 HashNumber
MUnaryInstruction::valueHash() const {
388 HashNumber hash
= HashNumber(op());
389 hash
= addU32ToHash(hash
, getOperand(0)->id());
390 if (MDefinition
* dep
= dependency()) {
391 hash
= addU32ToHash(hash
, dep
->id());
393 MOZ_ASSERT(hash
== MDefinition::valueHash());
397 HashNumber
MBinaryInstruction::valueHash() const {
398 HashNumber hash
= HashNumber(op());
399 hash
= addU32ToHash(hash
, getOperand(0)->id());
400 hash
= addU32ToHash(hash
, getOperand(1)->id());
401 if (MDefinition
* dep
= dependency()) {
402 hash
= addU32ToHash(hash
, dep
->id());
404 MOZ_ASSERT(hash
== MDefinition::valueHash());
408 HashNumber
MTernaryInstruction::valueHash() const {
409 HashNumber hash
= HashNumber(op());
410 hash
= addU32ToHash(hash
, getOperand(0)->id());
411 hash
= addU32ToHash(hash
, getOperand(1)->id());
412 hash
= addU32ToHash(hash
, getOperand(2)->id());
413 if (MDefinition
* dep
= dependency()) {
414 hash
= addU32ToHash(hash
, dep
->id());
416 MOZ_ASSERT(hash
== MDefinition::valueHash());
420 HashNumber
MQuaternaryInstruction::valueHash() const {
421 HashNumber hash
= HashNumber(op());
422 hash
= addU32ToHash(hash
, getOperand(0)->id());
423 hash
= addU32ToHash(hash
, getOperand(1)->id());
424 hash
= addU32ToHash(hash
, getOperand(2)->id());
425 hash
= addU32ToHash(hash
, getOperand(3)->id());
426 if (MDefinition
* dep
= dependency()) {
427 hash
= addU32ToHash(hash
, dep
->id());
429 MOZ_ASSERT(hash
== MDefinition::valueHash());
433 const MDefinition
* MDefinition::skipObjectGuards() const {
434 const MDefinition
* result
= this;
435 // These instructions don't modify the object and just guard specific
438 if (result
->isGuardShape()) {
439 result
= result
->toGuardShape()->object();
442 if (result
->isGuardNullProto()) {
443 result
= result
->toGuardNullProto()->object();
446 if (result
->isGuardProto()) {
447 result
= result
->toGuardProto()->object();
457 bool MDefinition::congruentIfOperandsEqual(const MDefinition
* ins
) const {
458 if (op() != ins
->op()) {
462 if (type() != ins
->type()) {
466 if (isEffectful() || ins
->isEffectful()) {
470 if (numOperands() != ins
->numOperands()) {
474 for (size_t i
= 0, e
= numOperands(); i
< e
; i
++) {
475 if (getOperand(i
) != ins
->getOperand(i
)) {
483 MDefinition
* MDefinition::foldsTo(TempAllocator
& alloc
) {
484 // In the default case, there are no constants to fold.
488 bool MDefinition::mightBeMagicType() const {
489 if (IsMagicType(type())) {
493 if (MIRType::Value
!= type()) {
500 bool MDefinition::definitelyType(std::initializer_list
<MIRType
> types
) const {
502 // Only support specialized, non-magic types.
503 auto isSpecializedNonMagic
= [](MIRType type
) {
504 return type
<= MIRType::Object
;
508 MOZ_ASSERT(types
.size() > 0);
509 MOZ_ASSERT(std::all_of(types
.begin(), types
.end(), isSpecializedNonMagic
));
511 if (type() == MIRType::Value
) {
515 return std::find(types
.begin(), types
.end(), type()) != types
.end();
518 MDefinition
* MInstruction::foldsToStore(TempAllocator
& alloc
) {
523 MDefinition
* store
= dependency();
524 if (mightAlias(store
) != AliasType::MustAlias
) {
528 if (!store
->block()->dominates(block())) {
533 switch (store
->op()) {
534 case Opcode::StoreFixedSlot
:
535 value
= store
->toStoreFixedSlot()->value();
537 case Opcode::StoreDynamicSlot
:
538 value
= store
->toStoreDynamicSlot()->value();
540 case Opcode::StoreElement
:
541 value
= store
->toStoreElement()->value();
544 MOZ_CRASH("unknown store");
547 // If the type are matching then we return the value which is used as
548 // argument of the store.
549 if (value
->type() != type()) {
550 // If we expect to read a type which is more generic than the type seen
551 // by the store, then we box the value used by the store.
552 if (type() != MIRType::Value
) {
556 MOZ_ASSERT(value
->type() < MIRType::Value
);
557 MBox
* box
= MBox::New(alloc
, value
);
564 void MDefinition::analyzeEdgeCasesForward() {}
566 void MDefinition::analyzeEdgeCasesBackward() {}
568 void MInstruction::setResumePoint(MResumePoint
* resumePoint
) {
569 MOZ_ASSERT(!resumePoint_
);
570 resumePoint_
= resumePoint
;
571 resumePoint_
->setInstruction(this);
574 void MInstruction::stealResumePoint(MInstruction
* other
) {
575 MResumePoint
* resumePoint
= other
->resumePoint_
;
576 other
->resumePoint_
= nullptr;
578 resumePoint
->resetInstruction();
579 setResumePoint(resumePoint
);
582 void MInstruction::moveResumePointAsEntry() {
584 block()->clearEntryResumePoint();
585 block()->setEntryResumePoint(resumePoint_
);
586 resumePoint_
->resetInstruction();
587 resumePoint_
= nullptr;
590 void MInstruction::clearResumePoint() {
591 resumePoint_
->resetInstruction();
592 block()->discardPreAllocatedResumePoint(resumePoint_
);
593 resumePoint_
= nullptr;
596 MDefinition
* MTest::foldsDoubleNegation(TempAllocator
& alloc
) {
597 MDefinition
* op
= getOperand(0);
600 // If the operand of the Not is itself a Not, they cancel out.
601 MDefinition
* opop
= op
->getOperand(0);
603 return MTest::New(alloc
, opop
->toNot()->input(), ifTrue(), ifFalse());
605 return MTest::New(alloc
, op
->toNot()->input(), ifFalse(), ifTrue());
610 MDefinition
* MTest::foldsConstant(TempAllocator
& alloc
) {
611 MDefinition
* op
= getOperand(0);
612 if (MConstant
* opConst
= op
->maybeConstantValue()) {
614 if (opConst
->valueToBoolean(&b
)) {
615 return MGoto::New(alloc
, b
? ifTrue() : ifFalse());
621 MDefinition
* MTest::foldsTypes(TempAllocator
& alloc
) {
622 MDefinition
* op
= getOperand(0);
624 switch (op
->type()) {
625 case MIRType::Undefined
:
627 return MGoto::New(alloc
, ifFalse());
628 case MIRType::Symbol
:
629 return MGoto::New(alloc
, ifTrue());
640 explicit UsesIterator(MDefinition
* def
) : def_(def
) {}
641 auto begin() const { return def_
->usesBegin(); }
642 auto end() const { return def_
->usesEnd(); }
645 static bool AllInstructionsDeadIfUnused(MBasicBlock
* block
) {
646 for (auto* ins
: *block
) {
647 // Skip trivial instructions.
648 if (ins
->isNop() || ins
->isGoto()) {
652 // All uses must be within the current block.
653 for (auto* use
: UsesIterator(ins
)) {
654 if (use
->consumer()->block() != block
) {
659 // All instructions within this block must be dead if unused.
660 if (!DeadIfUnused(ins
)) {
667 MDefinition
* MTest::foldsNeedlessControlFlow(TempAllocator
& alloc
) {
668 // All instructions within both successors need be dead if unused.
669 if (!AllInstructionsDeadIfUnused(ifTrue()) ||
670 !AllInstructionsDeadIfUnused(ifFalse())) {
674 // Both successors must have the same target successor.
675 if (ifTrue()->numSuccessors() != 1 || ifFalse()->numSuccessors() != 1) {
678 if (ifTrue()->getSuccessor(0) != ifFalse()->getSuccessor(0)) {
682 // The target successor's phis must be redundant. Redundant phis should have
683 // been removed in an earlier pass, so only check if any phis are present,
684 // which is a stronger condition.
685 if (ifTrue()->successorWithPhis()) {
689 return MGoto::New(alloc
, ifTrue());
692 // If a test is dominated by either the true or false path of a previous test of
693 // the same condition, then the test is redundant and can be converted into a
694 // goto true or goto false, respectively.
695 MDefinition
* MTest::foldsRedundantTest(TempAllocator
& alloc
) {
696 MBasicBlock
* myBlock
= this->block();
697 MDefinition
* originalInput
= getOperand(0);
699 // Handle single and double negatives. This ensures that we do not miss a
700 // folding opportunity due to a condition being inverted.
701 MDefinition
* newInput
= input();
702 bool inverted
= false;
703 if (originalInput
->isNot()) {
704 newInput
= originalInput
->toNot()->input();
706 if (originalInput
->toNot()->input()->isNot()) {
707 newInput
= originalInput
->toNot()->input()->toNot()->input();
712 // The specific order of traversal does not matter. If there are multiple
713 // dominating redundant tests, they will either agree on direction (in which
714 // case we will prune the same way regardless of order), or they will
715 // disagree, in which case we will eventually be marked entirely dead by the
716 // folding of the redundant parent.
717 for (MUseIterator
i(newInput
->usesBegin()), e(newInput
->usesEnd()); i
!= e
;
719 if (!i
->consumer()->isDefinition()) {
722 if (!i
->consumer()->toDefinition()->isTest()) {
725 MTest
* otherTest
= i
->consumer()->toDefinition()->toTest();
726 if (otherTest
== this) {
730 if (otherTest
->ifFalse()->dominates(myBlock
)) {
731 // This test cannot be true, so fold to a goto false.
732 return MGoto::New(alloc
, inverted
? ifTrue() : ifFalse());
734 if (otherTest
->ifTrue()->dominates(myBlock
)) {
735 // This test cannot be false, so fold to a goto true.
736 return MGoto::New(alloc
, inverted
? ifFalse() : ifTrue());
743 MDefinition
* MTest::foldsTo(TempAllocator
& alloc
) {
744 if (MDefinition
* def
= foldsRedundantTest(alloc
)) {
748 if (MDefinition
* def
= foldsDoubleNegation(alloc
)) {
752 if (MDefinition
* def
= foldsConstant(alloc
)) {
756 if (MDefinition
* def
= foldsTypes(alloc
)) {
760 if (MDefinition
* def
= foldsNeedlessControlFlow(alloc
)) {
767 AliasSet
MThrow::getAliasSet() const {
768 return AliasSet::Store(AliasSet::ExceptionState
);
771 AliasSet
MThrowWithStack::getAliasSet() const {
772 return AliasSet::Store(AliasSet::ExceptionState
);
775 AliasSet
MNewArrayDynamicLength::getAliasSet() const {
776 return AliasSet::Store(AliasSet::ExceptionState
);
779 AliasSet
MNewTypedArrayDynamicLength::getAliasSet() const {
780 return AliasSet::Store(AliasSet::ExceptionState
);
784 void MDefinition::printOpcode(GenericPrinter
& out
) const {
785 PrintOpcodeName(out
, op());
786 for (size_t j
= 0, e
= numOperands(); j
< e
; j
++) {
788 if (getUseFor(j
)->hasProducer()) {
789 getOperand(j
)->printName(out
);
790 out
.printf(":%s", StringFromMIRType(getOperand(j
)->type()));
792 out
.printf("(null)");
797 void MDefinition::dump(GenericPrinter
& out
) const {
799 out
.printf(":%s", StringFromMIRType(type()));
804 if (isInstruction()) {
805 if (MResumePoint
* resume
= toInstruction()->resumePoint()) {
811 void MDefinition::dump() const {
812 Fprinter
out(stderr
);
817 void MDefinition::dumpLocation(GenericPrinter
& out
) const {
818 MResumePoint
* rp
= nullptr;
819 const char* linkWord
= nullptr;
820 if (isInstruction() && toInstruction()->resumePoint()) {
821 rp
= toInstruction()->resumePoint();
824 rp
= block()->entryResumePoint();
829 JSScript
* script
= rp
->block()->info().script();
830 uint32_t lineno
= PCToLineNumber(rp
->block()->info().script(), rp
->pc());
831 out
.printf(" %s %s:%u\n", linkWord
, script
->filename(), lineno
);
837 void MDefinition::dumpLocation() const {
838 Fprinter
out(stderr
);
844 #if defined(DEBUG) || defined(JS_JITSPEW)
845 size_t MDefinition::useCount() const {
847 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
853 size_t MDefinition::defUseCount() const {
855 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
856 if ((*i
)->consumer()->isDefinition()) {
864 bool MDefinition::hasOneUse() const {
865 MUseIterator
i(uses_
.begin());
866 if (i
== uses_
.end()) {
870 return i
== uses_
.end();
873 bool MDefinition::hasOneDefUse() const {
874 bool hasOneDefUse
= false;
875 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
876 if (!(*i
)->consumer()->isDefinition()) {
880 // We already have a definition use. So 1+
885 // We saw one definition. Loop to test if there is another.
892 bool MDefinition::hasOneLiveDefUse() const {
893 bool hasOneDefUse
= false;
894 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
895 if (!(*i
)->consumer()->isDefinition()) {
899 MDefinition
* def
= (*i
)->consumer()->toDefinition();
900 if (def
->isRecoveredOnBailout()) {
904 // We already have a definition use. So 1+
909 // We saw one definition. Loop to test if there is another.
916 bool MDefinition::hasDefUses() const {
917 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
918 if ((*i
)->consumer()->isDefinition()) {
926 bool MDefinition::hasLiveDefUses() const {
927 for (MUseIterator
i(uses_
.begin()); i
!= uses_
.end(); i
++) {
928 MNode
* ins
= (*i
)->consumer();
929 if (ins
->isDefinition()) {
930 if (!ins
->toDefinition()->isRecoveredOnBailout()) {
934 MOZ_ASSERT(ins
->isResumePoint());
935 if (!ins
->toResumePoint()->isRecoverableOperand(*i
)) {
944 MDefinition
* MDefinition::maybeSingleDefUse() const {
945 MUseDefIterator
use(this);
951 MDefinition
* useDef
= use
.def();
955 // More than one def-use.
962 MDefinition
* MDefinition::maybeMostRecentlyAddedDefUse() const {
963 MUseDefIterator
use(this);
969 MDefinition
* mostRecentUse
= use
.def();
972 // This function relies on addUse adding new uses to the front of the list.
973 // Check this invariant by asserting the next few uses are 'older'. Skip this
974 // for phis because setBackedge can add a new use for a loop phi even if the
975 // loop body has a use with an id greater than the loop phi's id.
976 if (!mostRecentUse
->isPhi()) {
977 static constexpr size_t NumUsesToCheck
= 3;
979 for (size_t i
= 0; use
&& i
< NumUsesToCheck
; i
++, use
++) {
980 MOZ_ASSERT(use
.def()->id() <= mostRecentUse
->id());
985 return mostRecentUse
;
988 void MDefinition::replaceAllUsesWith(MDefinition
* dom
) {
989 for (size_t i
= 0, e
= numOperands(); i
< e
; ++i
) {
990 getOperand(i
)->setImplicitlyUsedUnchecked();
993 justReplaceAllUsesWith(dom
);
996 void MDefinition::justReplaceAllUsesWith(MDefinition
* dom
) {
997 MOZ_ASSERT(dom
!= nullptr);
998 MOZ_ASSERT(dom
!= this);
1000 // Carry over the fact the value has uses which are no longer inspectable
1002 if (isImplicitlyUsed()) {
1003 dom
->setImplicitlyUsedUnchecked();
1006 for (MUseIterator
i(usesBegin()), e(usesEnd()); i
!= e
; ++i
) {
1007 i
->setProducerUnchecked(dom
);
1009 dom
->uses_
.takeElements(uses_
);
1012 bool MDefinition::optimizeOutAllUses(TempAllocator
& alloc
) {
1013 for (MUseIterator
i(usesBegin()), e(usesEnd()); i
!= e
;) {
1015 MConstant
* constant
= use
->consumer()->block()->optimizedOutConstant(alloc
);
1016 if (!alloc
.ensureBallast()) {
1020 // Update the resume point operand to use the optimized-out constant.
1021 use
->setProducerUnchecked(constant
);
1022 constant
->addUseUnchecked(use
);
1025 // Remove dangling pointers.
1026 this->uses_
.clear();
1030 void MDefinition::replaceAllLiveUsesWith(MDefinition
* dom
) {
1031 for (MUseIterator
i(usesBegin()), e(usesEnd()); i
!= e
;) {
1033 MNode
* consumer
= use
->consumer();
1034 if (consumer
->isResumePoint()) {
1037 if (consumer
->isDefinition() &&
1038 consumer
->toDefinition()->isRecoveredOnBailout()) {
1042 // Update the operand to use the dominating definition.
1043 use
->replaceProducer(dom
);
1047 MConstant
* MConstant::New(TempAllocator
& alloc
, const Value
& v
) {
1048 return new (alloc
) MConstant(alloc
, v
);
1051 MConstant
* MConstant::New(TempAllocator::Fallible alloc
, const Value
& v
) {
1052 return new (alloc
) MConstant(alloc
.alloc
, v
);
1055 MConstant
* MConstant::NewFloat32(TempAllocator
& alloc
, double d
) {
1056 MOZ_ASSERT(std::isnan(d
) || d
== double(float(d
)));
1057 return new (alloc
) MConstant(float(d
));
1060 MConstant
* MConstant::NewInt64(TempAllocator
& alloc
, int64_t i
) {
1061 return new (alloc
) MConstant(MIRType::Int64
, i
);
1064 MConstant
* MConstant::NewIntPtr(TempAllocator
& alloc
, intptr_t i
) {
1065 return new (alloc
) MConstant(MIRType::IntPtr
, i
);
1068 MConstant
* MConstant::New(TempAllocator
& alloc
, const Value
& v
, MIRType type
) {
1069 if (type
== MIRType::Float32
) {
1070 return NewFloat32(alloc
, v
.toNumber());
1072 MConstant
* res
= New(alloc
, v
);
1073 MOZ_ASSERT(res
->type() == type
);
1077 MConstant
* MConstant::NewObject(TempAllocator
& alloc
, JSObject
* v
) {
1078 return new (alloc
) MConstant(v
);
1081 MConstant
* MConstant::NewShape(TempAllocator
& alloc
, Shape
* s
) {
1082 return new (alloc
) MConstant(s
);
1085 static MIRType
MIRTypeFromValue(const js::Value
& vp
) {
1086 if (vp
.isDouble()) {
1087 return MIRType::Double
;
1090 switch (vp
.whyMagic()) {
1091 case JS_OPTIMIZED_OUT
:
1092 return MIRType::MagicOptimizedOut
;
1093 case JS_ELEMENTS_HOLE
:
1094 return MIRType::MagicHole
;
1095 case JS_IS_CONSTRUCTING
:
1096 return MIRType::MagicIsConstructing
;
1097 case JS_UNINITIALIZED_LEXICAL
:
1098 return MIRType::MagicUninitializedLexical
;
1100 MOZ_ASSERT_UNREACHABLE("Unexpected magic constant");
1103 return MIRTypeFromValueType(vp
.extractNonDoubleType());
1106 MConstant::MConstant(TempAllocator
& alloc
, const js::Value
& vp
)
1107 : MNullaryInstruction(classOpcode
) {
1108 setResultType(MIRTypeFromValue(vp
));
1110 MOZ_ASSERT(payload_
.asBits
== 0);
1113 case MIRType::Undefined
:
1116 case MIRType::Boolean
:
1117 payload_
.b
= vp
.toBoolean();
1119 case MIRType::Int32
:
1120 payload_
.i32
= vp
.toInt32();
1122 case MIRType::Double
:
1123 payload_
.d
= vp
.toDouble();
1125 case MIRType::String
:
1126 MOZ_ASSERT(!IsInsideNursery(vp
.toString()));
1127 MOZ_ASSERT(vp
.toString()->isLinear());
1128 payload_
.str
= vp
.toString();
1130 case MIRType::Symbol
:
1131 payload_
.sym
= vp
.toSymbol();
1133 case MIRType::BigInt
:
1134 MOZ_ASSERT(!IsInsideNursery(vp
.toBigInt()));
1135 payload_
.bi
= vp
.toBigInt();
1137 case MIRType::Object
:
1138 MOZ_ASSERT(!IsInsideNursery(&vp
.toObject()));
1139 payload_
.obj
= &vp
.toObject();
1141 case MIRType::MagicOptimizedOut
:
1142 case MIRType::MagicHole
:
1143 case MIRType::MagicIsConstructing
:
1144 case MIRType::MagicUninitializedLexical
:
1147 MOZ_CRASH("Unexpected type");
1153 MConstant::MConstant(JSObject
* obj
) : MNullaryInstruction(classOpcode
) {
1154 MOZ_ASSERT(!IsInsideNursery(obj
));
1155 setResultType(MIRType::Object
);
1160 MConstant::MConstant(Shape
* shape
) : MNullaryInstruction(classOpcode
) {
1161 setResultType(MIRType::Shape
);
1162 payload_
.shape
= shape
;
1166 MConstant::MConstant(float f
) : MNullaryInstruction(classOpcode
) {
1167 setResultType(MIRType::Float32
);
1172 MConstant::MConstant(MIRType type
, int64_t i
)
1173 : MNullaryInstruction(classOpcode
) {
1174 MOZ_ASSERT(type
== MIRType::Int64
|| type
== MIRType::IntPtr
);
1175 setResultType(type
);
1176 if (type
== MIRType::Int64
) {
1185 void MConstant::assertInitializedPayload() const {
1186 // valueHash() and equals() expect the unused payload bits to be
1187 // initialized to zero. Assert this in debug builds.
1190 case MIRType::Int32
:
1191 case MIRType::Float32
:
1192 # if MOZ_LITTLE_ENDIAN()
1193 MOZ_ASSERT((payload_
.asBits
>> 32) == 0);
1195 MOZ_ASSERT((payload_
.asBits
<< 32) == 0);
1198 case MIRType::Boolean
:
1199 # if MOZ_LITTLE_ENDIAN()
1200 MOZ_ASSERT((payload_
.asBits
>> 1) == 0);
1202 MOZ_ASSERT((payload_
.asBits
& ~(1ULL << 56)) == 0);
1205 case MIRType::Double
:
1206 case MIRType::Int64
:
1208 case MIRType::String
:
1209 case MIRType::Object
:
1210 case MIRType::Symbol
:
1211 case MIRType::BigInt
:
1212 case MIRType::IntPtr
:
1213 case MIRType::Shape
:
1214 # if MOZ_LITTLE_ENDIAN()
1215 MOZ_ASSERT_IF(JS_BITS_PER_WORD
== 32, (payload_
.asBits
>> 32) == 0);
1217 MOZ_ASSERT_IF(JS_BITS_PER_WORD
== 32, (payload_
.asBits
<< 32) == 0);
1221 MOZ_ASSERT(IsNullOrUndefined(type()) || IsMagicType(type()));
1222 MOZ_ASSERT(payload_
.asBits
== 0);
1228 static HashNumber
ConstantValueHash(MIRType type
, uint64_t payload
) {
1229 // Build a 64-bit value holding both the payload and the type.
1230 static const size_t TypeBits
= 8;
1231 static const size_t TypeShift
= 64 - TypeBits
;
1232 MOZ_ASSERT(uintptr_t(type
) <= (1 << TypeBits
) - 1);
1233 uint64_t bits
= (uint64_t(type
) << TypeShift
) ^ payload
;
1235 // Fold all 64 bits into the 32-bit result. It's tempting to just discard
1236 // half of the bits, as this is just a hash, however there are many common
1237 // patterns of values where only the low or the high bits vary, so
1238 // discarding either side would lead to excessive hash collisions.
1239 return (HashNumber
)bits
^ (HashNumber
)(bits
>> 32);
1242 HashNumber
MConstant::valueHash() const {
1243 static_assert(sizeof(Payload
) == sizeof(uint64_t),
1244 "Code below assumes payload fits in 64 bits");
1246 assertInitializedPayload();
1247 return ConstantValueHash(type(), payload_
.asBits
);
1250 HashNumber
MConstantProto::valueHash() const {
1251 HashNumber hash
= protoObject()->valueHash();
1252 const MDefinition
* receiverObject
= getReceiverObject();
1253 if (receiverObject
) {
1254 hash
= addU32ToHash(hash
, receiverObject
->id());
1259 bool MConstant::congruentTo(const MDefinition
* ins
) const {
1260 return ins
->isConstant() && equals(ins
->toConstant());
1264 void MConstant::printOpcode(GenericPrinter
& out
) const {
1265 PrintOpcodeName(out
, op());
1268 case MIRType::Undefined
:
1269 out
.printf("undefined");
1274 case MIRType::Boolean
:
1275 out
.printf(toBoolean() ? "true" : "false");
1277 case MIRType::Int32
:
1278 out
.printf("0x%x", uint32_t(toInt32()));
1280 case MIRType::Int64
:
1281 out
.printf("0x%" PRIx64
, uint64_t(toInt64()));
1283 case MIRType::IntPtr
:
1284 out
.printf("0x%" PRIxPTR
, uintptr_t(toIntPtr()));
1286 case MIRType::Double
:
1287 out
.printf("%.16g", toDouble());
1289 case MIRType::Float32
: {
1290 float val
= toFloat32();
1291 out
.printf("%.16g", val
);
1294 case MIRType::Object
:
1295 if (toObject().is
<JSFunction
>()) {
1296 JSFunction
* fun
= &toObject().as
<JSFunction
>();
1297 if (fun
->maybePartialDisplayAtom()) {
1298 out
.put("function ");
1299 EscapedStringPrinter(out
, fun
->maybePartialDisplayAtom(), 0);
1301 out
.put("unnamed function");
1303 if (fun
->hasBaseScript()) {
1304 BaseScript
* script
= fun
->baseScript();
1305 out
.printf(" (%s:%u)", script
->filename() ? script
->filename() : "",
1308 out
.printf(" at %p", (void*)fun
);
1311 out
.printf("object %p (%s)", (void*)&toObject(),
1312 toObject().getClass()->name
);
1314 case MIRType::Symbol
:
1315 out
.printf("symbol at %p", (void*)toSymbol());
1317 case MIRType::BigInt
:
1318 out
.printf("BigInt at %p", (void*)toBigInt());
1320 case MIRType::String
:
1321 out
.printf("string %p", (void*)toString());
1323 case MIRType::Shape
:
1324 out
.printf("shape at %p", (void*)toShape());
1326 case MIRType::MagicHole
:
1327 out
.printf("magic hole");
1329 case MIRType::MagicIsConstructing
:
1330 out
.printf("magic is-constructing");
1332 case MIRType::MagicOptimizedOut
:
1333 out
.printf("magic optimized-out");
1335 case MIRType::MagicUninitializedLexical
:
1336 out
.printf("magic uninitialized-lexical");
1339 MOZ_CRASH("unexpected type");
1344 bool MConstant::canProduceFloat32() const {
1345 if (!isTypeRepresentableAsDouble()) {
1349 if (type() == MIRType::Int32
) {
1350 return IsFloat32Representable(static_cast<double>(toInt32()));
1352 if (type() == MIRType::Double
) {
1353 return IsFloat32Representable(toDouble());
1355 MOZ_ASSERT(type() == MIRType::Float32
);
1359 Value
MConstant::toJSValue() const {
1360 // Wasm has types like int64 that cannot be stored as js::Value. It also
1361 // doesn't want the NaN canonicalization enforced by js::Value.
1362 MOZ_ASSERT(!IsCompilingWasm());
1365 case MIRType::Undefined
:
1366 return UndefinedValue();
1369 case MIRType::Boolean
:
1370 return BooleanValue(toBoolean());
1371 case MIRType::Int32
:
1372 return Int32Value(toInt32());
1373 case MIRType::Double
:
1374 return DoubleValue(toDouble());
1375 case MIRType::Float32
:
1376 return Float32Value(toFloat32());
1377 case MIRType::String
:
1378 return StringValue(toString());
1379 case MIRType::Symbol
:
1380 return SymbolValue(toSymbol());
1381 case MIRType::BigInt
:
1382 return BigIntValue(toBigInt());
1383 case MIRType::Object
:
1384 return ObjectValue(toObject());
1385 case MIRType::Shape
:
1386 return PrivateGCThingValue(toShape());
1387 case MIRType::MagicOptimizedOut
:
1388 return MagicValue(JS_OPTIMIZED_OUT
);
1389 case MIRType::MagicHole
:
1390 return MagicValue(JS_ELEMENTS_HOLE
);
1391 case MIRType::MagicIsConstructing
:
1392 return MagicValue(JS_IS_CONSTRUCTING
);
1393 case MIRType::MagicUninitializedLexical
:
1394 return MagicValue(JS_UNINITIALIZED_LEXICAL
);
1396 MOZ_CRASH("Unexpected type");
1400 bool MConstant::valueToBoolean(bool* res
) const {
1402 case MIRType::Boolean
:
1405 case MIRType::Int32
:
1406 *res
= toInt32() != 0;
1408 case MIRType::Int64
:
1409 *res
= toInt64() != 0;
1411 case MIRType::Double
:
1412 *res
= !std::isnan(toDouble()) && toDouble() != 0.0;
1414 case MIRType::Float32
:
1415 *res
= !std::isnan(toFloat32()) && toFloat32() != 0.0f
;
1418 case MIRType::Undefined
:
1421 case MIRType::Symbol
:
1424 case MIRType::BigInt
:
1425 *res
= !toBigInt()->isZero();
1427 case MIRType::String
:
1428 *res
= toString()->length() != 0;
1430 case MIRType::Object
:
1431 // TODO(Warp): Lazy groups have been removed.
1432 // We have to call EmulatesUndefined but that reads obj->group->clasp
1433 // and so it's racy when the object has a lazy group. The main callers
1434 // of this (MTest, MNot) already know how to fold the object case, so
1438 MOZ_ASSERT(IsMagicType(type()));
1443 HashNumber
MWasmFloatConstant::valueHash() const {
1444 #ifdef ENABLE_WASM_SIMD
1445 return ConstantValueHash(type(), u
.bits_
[0] ^ u
.bits_
[1]);
1447 return ConstantValueHash(type(), u
.bits_
[0]);
1451 bool MWasmFloatConstant::congruentTo(const MDefinition
* ins
) const {
1452 return ins
->isWasmFloatConstant() && type() == ins
->type() &&
1453 #ifdef ENABLE_WASM_SIMD
1454 u
.bits_
[1] == ins
->toWasmFloatConstant()->u
.bits_
[1] &&
1456 u
.bits_
[0] == ins
->toWasmFloatConstant()->u
.bits_
[0];
1459 HashNumber
MWasmNullConstant::valueHash() const {
1460 return ConstantValueHash(MIRType::WasmAnyRef
, 0);
1464 void MControlInstruction::printOpcode(GenericPrinter
& out
) const {
1465 MDefinition::printOpcode(out
);
1466 for (size_t j
= 0; j
< numSuccessors(); j
++) {
1467 if (getSuccessor(j
)) {
1468 out
.printf(" block%u", getSuccessor(j
)->id());
1470 out
.printf(" (null-to-be-patched)");
1475 void MCompare::printOpcode(GenericPrinter
& out
) const {
1476 MDefinition::printOpcode(out
);
1477 out
.printf(" %s", CodeName(jsop()));
1480 void MTypeOfIs::printOpcode(GenericPrinter
& out
) const {
1481 MDefinition::printOpcode(out
);
1482 out
.printf(" %s", CodeName(jsop()));
1484 const char* name
= "";
1486 case JSTYPE_UNDEFINED
:
1492 case JSTYPE_FUNCTION
:
1501 case JSTYPE_BOOLEAN
:
1510 # ifdef ENABLE_RECORD_TUPLE
1515 MOZ_CRASH("Unexpected type");
1517 out
.printf(" '%s'", name
);
1520 void MLoadUnboxedScalar::printOpcode(GenericPrinter
& out
) const {
1521 MDefinition::printOpcode(out
);
1522 out
.printf(" %s", Scalar::name(storageType()));
1525 void MLoadDataViewElement::printOpcode(GenericPrinter
& out
) const {
1526 MDefinition::printOpcode(out
);
1527 out
.printf(" %s", Scalar::name(storageType()));
1530 void MAssertRange::printOpcode(GenericPrinter
& out
) const {
1531 MDefinition::printOpcode(out
);
1533 assertedRange()->dump(out
);
1536 void MNearbyInt::printOpcode(GenericPrinter
& out
) const {
1537 MDefinition::printOpcode(out
);
1538 const char* roundingModeStr
= nullptr;
1539 switch (roundingMode_
) {
1540 case RoundingMode::Up
:
1541 roundingModeStr
= "(up)";
1543 case RoundingMode::Down
:
1544 roundingModeStr
= "(down)";
1546 case RoundingMode::NearestTiesToEven
:
1547 roundingModeStr
= "(nearest ties even)";
1549 case RoundingMode::TowardsZero
:
1550 roundingModeStr
= "(towards zero)";
1553 out
.printf(" %s", roundingModeStr
);
1557 AliasSet
MRandom::getAliasSet() const { return AliasSet::Store(AliasSet::RNG
); }
1559 MDefinition
* MSign::foldsTo(TempAllocator
& alloc
) {
1560 MDefinition
* input
= getOperand(0);
1561 if (!input
->isConstant() ||
1562 !input
->toConstant()->isTypeRepresentableAsDouble()) {
1566 double in
= input
->toConstant()->numberToDouble();
1567 double out
= js::math_sign_impl(in
);
1569 if (type() == MIRType::Int32
) {
1570 // Decline folding if this is an int32 operation, but the result type
1572 Value outValue
= NumberValue(out
);
1573 if (!outValue
.isInt32()) {
1577 return MConstant::New(alloc
, outValue
);
1580 return MConstant::New(alloc
, DoubleValue(out
));
1583 const char* MMathFunction::FunctionName(UnaryMathFunction function
) {
1584 return GetUnaryMathFunctionName(function
);
1588 void MMathFunction::printOpcode(GenericPrinter
& out
) const {
1589 MDefinition::printOpcode(out
);
1590 out
.printf(" %s", FunctionName(function()));
1594 MDefinition
* MMathFunction::foldsTo(TempAllocator
& alloc
) {
1595 MDefinition
* input
= getOperand(0);
1596 if (!input
->isConstant() ||
1597 !input
->toConstant()->isTypeRepresentableAsDouble()) {
1601 UnaryMathFunctionType funPtr
= GetUnaryMathFunctionPtr(function());
1603 double in
= input
->toConstant()->numberToDouble();
1605 // The function pointer call can't GC.
1606 JS::AutoSuppressGCAnalysis nogc
;
1607 double out
= funPtr(in
);
1609 if (input
->type() == MIRType::Float32
) {
1610 return MConstant::NewFloat32(alloc
, out
);
1612 return MConstant::New(alloc
, DoubleValue(out
));
1615 MDefinition
* MAtomicIsLockFree::foldsTo(TempAllocator
& alloc
) {
1616 MDefinition
* input
= getOperand(0);
1617 if (!input
->isConstant() || input
->type() != MIRType::Int32
) {
1621 int32_t i
= input
->toConstant()->toInt32();
1622 return MConstant::New(alloc
, BooleanValue(AtomicOperations::isLockfreeJS(i
)));
1625 // Define |THIS_SLOT| as part of this translation unit, as it is used to
1626 // specialized the parameterized |New| function calls introduced by
1627 // TRIVIAL_NEW_WRAPPERS.
1628 const int32_t MParameter::THIS_SLOT
;
1631 void MParameter::printOpcode(GenericPrinter
& out
) const {
1632 PrintOpcodeName(out
, op());
1633 if (index() == THIS_SLOT
) {
1634 out
.printf(" THIS_SLOT");
1636 out
.printf(" %d", index());
1641 HashNumber
MParameter::valueHash() const {
1642 HashNumber hash
= MDefinition::valueHash();
1643 hash
= addU32ToHash(hash
, index_
);
1647 bool MParameter::congruentTo(const MDefinition
* ins
) const {
1648 if (!ins
->isParameter()) {
1652 return ins
->toParameter()->index() == index_
;
1655 WrappedFunction::WrappedFunction(JSFunction
* nativeFun
, uint16_t nargs
,
1656 FunctionFlags flags
)
1657 : nativeFun_(nativeFun
), nargs_(nargs
), flags_(flags
) {
1658 MOZ_ASSERT_IF(nativeFun
, isNativeWithoutJitEntry());
1661 // If we are not running off-main thread we can assert that the
1662 // metadata is consistent.
1663 if (!CanUseExtraThreads() && nativeFun
) {
1664 MOZ_ASSERT(nativeFun
->nargs() == nargs
);
1666 MOZ_ASSERT(nativeFun
->isNativeWithoutJitEntry() ==
1667 isNativeWithoutJitEntry());
1668 MOZ_ASSERT(nativeFun
->hasJitEntry() == hasJitEntry());
1669 MOZ_ASSERT(nativeFun
->isConstructor() == isConstructor());
1670 MOZ_ASSERT(nativeFun
->isClassConstructor() == isClassConstructor());
1675 MCall
* MCall::New(TempAllocator
& alloc
, WrappedFunction
* target
, size_t maxArgc
,
1676 size_t numActualArgs
, bool construct
, bool ignoresReturnValue
,
1677 bool isDOMCall
, mozilla::Maybe
<DOMObjectKind
> objectKind
) {
1678 MOZ_ASSERT(isDOMCall
== objectKind
.isSome());
1679 MOZ_ASSERT(maxArgc
>= numActualArgs
);
1682 MOZ_ASSERT(!construct
);
1683 ins
= new (alloc
) MCallDOMNative(target
, numActualArgs
, *objectKind
);
1686 new (alloc
) MCall(target
, numActualArgs
, construct
, ignoresReturnValue
);
1688 if (!ins
->init(alloc
, maxArgc
+ NumNonArgumentOperands
)) {
1694 AliasSet
MCallDOMNative::getAliasSet() const {
1695 const JSJitInfo
* jitInfo
= getJitInfo();
1697 // If we don't know anything about the types of our arguments, we have to
1698 // assume that type-coercions can have side-effects, so we need to alias
1700 if (jitInfo
->aliasSet() == JSJitInfo::AliasEverything
||
1701 !jitInfo
->isTypedMethodJitInfo()) {
1702 return AliasSet::Store(AliasSet::Any
);
1705 uint32_t argIndex
= 0;
1706 const JSTypedMethodJitInfo
* methodInfo
=
1707 reinterpret_cast<const JSTypedMethodJitInfo
*>(jitInfo
);
1708 for (const JSJitInfo::ArgType
* argType
= methodInfo
->argTypes
;
1709 *argType
!= JSJitInfo::ArgTypeListEnd
; ++argType
, ++argIndex
) {
1710 if (argIndex
>= numActualArgs()) {
1711 // Passing through undefined can't have side-effects
1714 // getArg(0) is "this", so skip it
1715 MDefinition
* arg
= getArg(argIndex
+ 1);
1716 MIRType actualType
= arg
->type();
1717 // The only way to reliably avoid side-effects given the information we
1718 // have here is if we're passing in a known primitive value to an
1719 // argument that expects a primitive value.
1721 // XXXbz maybe we need to communicate better information. For example,
1722 // a sequence argument will sort of unavoidably have side effects, while
1723 // a typed array argument won't have any, but both are claimed to be
1724 // JSJitInfo::Object. But if we do that, we need to watch out for our
1725 // movability/DCE-ability bits: if we have an arg type that can reliably
1726 // throw an exception on conversion, that might not affect our alias set
1727 // per se, but it should prevent us being moved or DCE-ed, unless we
1728 // know the incoming things match that arg type and won't throw.
1730 if ((actualType
== MIRType::Value
|| actualType
== MIRType::Object
) ||
1731 (*argType
& JSJitInfo::Object
)) {
1732 return AliasSet::Store(AliasSet::Any
);
1736 // We checked all the args, and they check out. So we only alias DOM
1737 // mutations or alias nothing, depending on the alias set in the jitinfo.
1738 if (jitInfo
->aliasSet() == JSJitInfo::AliasNone
) {
1739 return AliasSet::None();
1742 MOZ_ASSERT(jitInfo
->aliasSet() == JSJitInfo::AliasDOMSets
);
1743 return AliasSet::Load(AliasSet::DOMProperty
);
1746 void MCallDOMNative::computeMovable() {
1747 // We are movable if the jitinfo says we can be and if we're also not
1748 // effectful. The jitinfo can't check for the latter, since it depends on
1749 // the types of our arguments.
1750 const JSJitInfo
* jitInfo
= getJitInfo();
1752 MOZ_ASSERT_IF(jitInfo
->isMovable
,
1753 jitInfo
->aliasSet() != JSJitInfo::AliasEverything
);
1755 if (jitInfo
->isMovable
&& !isEffectful()) {
1760 bool MCallDOMNative::congruentTo(const MDefinition
* ins
) const {
1765 if (!ins
->isCall()) {
1769 const MCall
* call
= ins
->toCall();
1771 if (!call
->isCallDOMNative()) {
1775 if (getSingleTarget() != call
->getSingleTarget()) {
1779 if (isConstructing() != call
->isConstructing()) {
1783 if (numActualArgs() != call
->numActualArgs()) {
1787 if (!congruentIfOperandsEqual(call
)) {
1791 // The other call had better be movable at this point!
1792 MOZ_ASSERT(call
->isMovable());
1797 const JSJitInfo
* MCallDOMNative::getJitInfo() const {
1798 MOZ_ASSERT(getSingleTarget()->hasJitInfo());
1799 return getSingleTarget()->jitInfo();
1802 MCallClassHook
* MCallClassHook::New(TempAllocator
& alloc
, JSNative target
,
1803 uint32_t argc
, bool constructing
) {
1804 auto* ins
= new (alloc
) MCallClassHook(target
, constructing
);
1806 // Add callee + |this| + (if constructing) newTarget.
1807 uint32_t numOperands
= 2 + argc
+ constructing
;
1809 if (!ins
->init(alloc
, numOperands
)) {
1816 MDefinition
* MStringLength::foldsTo(TempAllocator
& alloc
) {
1817 if (string()->isConstant()) {
1818 JSString
* str
= string()->toConstant()->toString();
1819 return MConstant::New(alloc
, Int32Value(str
->length()));
1822 // MFromCharCode returns a one-element string.
1823 if (string()->isFromCharCode()) {
1824 return MConstant::New(alloc
, Int32Value(1));
1830 MDefinition
* MConcat::foldsTo(TempAllocator
& alloc
) {
1831 if (lhs()->isConstant() && lhs()->toConstant()->toString()->empty()) {
1835 if (rhs()->isConstant() && rhs()->toConstant()->toString()->empty()) {
1842 MDefinition
* MStringConvertCase::foldsTo(TempAllocator
& alloc
) {
1843 MDefinition
* string
= this->string();
1845 // Handle the pattern |str[idx].toUpperCase()| and simplify it from
1846 // |StringConvertCase(FromCharCode(CharCodeAt(str, idx)))| to just
1847 // |CharCodeConvertCase(CharCodeAt(str, idx))|.
1848 if (string
->isFromCharCode()) {
1849 auto* charCode
= string
->toFromCharCode()->code();
1850 auto mode
= mode_
== Mode::LowerCase
? MCharCodeConvertCase::LowerCase
1851 : MCharCodeConvertCase::UpperCase
;
1852 return MCharCodeConvertCase::New(alloc
, charCode
, mode
);
1855 // Handle the pattern |num.toString(base).toUpperCase()| and simplify it to
1856 // directly return the string representation in the correct case.
1857 if (string
->isInt32ToStringWithBase()) {
1858 auto* toString
= string
->toInt32ToStringWithBase();
1860 bool lowerCase
= mode_
== Mode::LowerCase
;
1861 if (toString
->lowerCase() == lowerCase
) {
1864 return MInt32ToStringWithBase::New(alloc
, toString
->input(),
1865 toString
->base(), lowerCase
);
1871 static bool IsSubstrTo(MSubstr
* substr
, int32_t len
) {
1872 // We want to match this pattern:
1874 // Substr(string, Constant(0), Min(Constant(length), StringLength(string)))
1876 // which is generated for the self-hosted `String.p.{substring,slice,substr}`
1877 // functions when called with constants `start` and `end` parameters.
1879 auto isConstantZero
= [](auto* def
) {
1880 return def
->isConstant() && def
->toConstant()->isInt32(0);
1883 if (!isConstantZero(substr
->begin())) {
1887 auto* length
= substr
->length();
1888 if (length
->isBitOr()) {
1889 // Unnecessary bit-ops haven't yet been removed.
1890 auto* bitOr
= length
->toBitOr();
1891 if (isConstantZero(bitOr
->lhs())) {
1892 length
= bitOr
->rhs();
1893 } else if (isConstantZero(bitOr
->rhs())) {
1894 length
= bitOr
->lhs();
1897 if (!length
->isMinMax() || length
->toMinMax()->isMax()) {
1901 auto* min
= length
->toMinMax();
1902 if (!min
->lhs()->isConstant() && !min
->rhs()->isConstant()) {
1906 auto* minConstant
= min
->lhs()->isConstant() ? min
->lhs()->toConstant()
1907 : min
->rhs()->toConstant();
1909 auto* minOperand
= min
->lhs()->isConstant() ? min
->rhs() : min
->lhs();
1910 if (!minOperand
->isStringLength() ||
1911 minOperand
->toStringLength()->string() != substr
->string()) {
1915 // Ensure |len| matches the substring's length.
1916 return minConstant
->isInt32(len
);
1919 MDefinition
* MSubstr::foldsTo(TempAllocator
& alloc
) {
1920 // Fold |str.substring(0, 1)| to |str.charAt(0)|.
1921 if (!IsSubstrTo(this, 1)) {
1925 auto* charCode
= MCharCodeAtOrNegative::New(alloc
, string(), begin());
1926 block()->insertBefore(this, charCode
);
1928 return MFromCharCodeEmptyIfNegative::New(alloc
, charCode
);
1931 MDefinition
* MCharCodeAt::foldsTo(TempAllocator
& alloc
) {
1932 MDefinition
* string
= this->string();
1933 if (!string
->isConstant() && !string
->isFromCharCode()) {
1937 MDefinition
* index
= this->index();
1938 if (index
->isSpectreMaskIndex()) {
1939 index
= index
->toSpectreMaskIndex()->index();
1941 if (!index
->isConstant()) {
1944 int32_t idx
= index
->toConstant()->toInt32();
1946 // Handle the pattern |s[idx].charCodeAt(0)|.
1947 if (string
->isFromCharCode()) {
1952 // Simplify |CharCodeAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
1953 // |CharCodeAt(s, idx)|.
1954 auto* charCode
= string
->toFromCharCode()->code();
1955 if (!charCode
->isCharCodeAt()) {
1962 JSLinearString
* str
= &string
->toConstant()->toString()->asLinear();
1963 if (idx
< 0 || uint32_t(idx
) >= str
->length()) {
1967 char16_t ch
= str
->latin1OrTwoByteChar(idx
);
1968 return MConstant::New(alloc
, Int32Value(ch
));
1971 MDefinition
* MCodePointAt::foldsTo(TempAllocator
& alloc
) {
1972 MDefinition
* string
= this->string();
1973 if (!string
->isConstant() && !string
->isFromCharCode()) {
1977 MDefinition
* index
= this->index();
1978 if (index
->isSpectreMaskIndex()) {
1979 index
= index
->toSpectreMaskIndex()->index();
1981 if (!index
->isConstant()) {
1984 int32_t idx
= index
->toConstant()->toInt32();
1986 // Handle the pattern |s[idx].codePointAt(0)|.
1987 if (string
->isFromCharCode()) {
1992 // Simplify |CodePointAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
1993 // |CharCodeAt(s, idx)|.
1994 auto* charCode
= string
->toFromCharCode()->code();
1995 if (!charCode
->isCharCodeAt()) {
2002 JSLinearString
* str
= &string
->toConstant()->toString()->asLinear();
2003 if (idx
< 0 || uint32_t(idx
) >= str
->length()) {
2007 char32_t first
= str
->latin1OrTwoByteChar(idx
);
2008 if (unicode::IsLeadSurrogate(first
) && uint32_t(idx
) + 1 < str
->length()) {
2009 char32_t second
= str
->latin1OrTwoByteChar(idx
+ 1);
2010 if (unicode::IsTrailSurrogate(second
)) {
2011 first
= unicode::UTF16Decode(first
, second
);
2014 return MConstant::New(alloc
, Int32Value(first
));
2017 MDefinition
* MToRelativeStringIndex::foldsTo(TempAllocator
& alloc
) {
2018 MDefinition
* index
= this->index();
2019 MDefinition
* length
= this->length();
2021 if (!index
->isConstant()) {
2024 if (!length
->isStringLength() && !length
->isConstant()) {
2027 MOZ_ASSERT_IF(length
->isConstant(), length
->toConstant()->toInt32() >= 0);
2029 int32_t relativeIndex
= index
->toConstant()->toInt32();
2030 if (relativeIndex
>= 0) {
2034 // Safe to truncate because |length| is never negative.
2035 return MAdd::New(alloc
, index
, length
, TruncateKind::Truncate
);
2038 template <size_t Arity
>
2039 [[nodiscard
]] static bool EnsureFloatInputOrConvert(
2040 MAryInstruction
<Arity
>* owner
, TempAllocator
& alloc
) {
2041 MOZ_ASSERT(!IsFloatingPointType(owner
->type()),
2042 "Floating point types must check consumers");
2044 if (AllOperandsCanProduceFloat32(owner
)) {
2047 ConvertOperandsToDouble(owner
, alloc
);
2051 template <size_t Arity
>
2052 [[nodiscard
]] static bool EnsureFloatConsumersAndInputOrConvert(
2053 MAryInstruction
<Arity
>* owner
, TempAllocator
& alloc
) {
2054 MOZ_ASSERT(IsFloatingPointType(owner
->type()),
2055 "Integer types don't need to check consumers");
2057 if (AllOperandsCanProduceFloat32(owner
) &&
2058 CheckUsesAreFloat32Consumers(owner
)) {
2061 ConvertOperandsToDouble(owner
, alloc
);
2065 void MFloor::trySpecializeFloat32(TempAllocator
& alloc
) {
2066 MOZ_ASSERT(type() == MIRType::Int32
);
2067 if (EnsureFloatInputOrConvert(this, alloc
)) {
2068 specialization_
= MIRType::Float32
;
2072 void MCeil::trySpecializeFloat32(TempAllocator
& alloc
) {
2073 MOZ_ASSERT(type() == MIRType::Int32
);
2074 if (EnsureFloatInputOrConvert(this, alloc
)) {
2075 specialization_
= MIRType::Float32
;
2079 void MRound::trySpecializeFloat32(TempAllocator
& alloc
) {
2080 MOZ_ASSERT(type() == MIRType::Int32
);
2081 if (EnsureFloatInputOrConvert(this, alloc
)) {
2082 specialization_
= MIRType::Float32
;
2086 void MTrunc::trySpecializeFloat32(TempAllocator
& alloc
) {
2087 MOZ_ASSERT(type() == MIRType::Int32
);
2088 if (EnsureFloatInputOrConvert(this, alloc
)) {
2089 specialization_
= MIRType::Float32
;
2093 void MNearbyInt::trySpecializeFloat32(TempAllocator
& alloc
) {
2094 if (EnsureFloatConsumersAndInputOrConvert(this, alloc
)) {
2095 specialization_
= MIRType::Float32
;
2096 setResultType(MIRType::Float32
);
2100 MGoto
* MGoto::New(TempAllocator
& alloc
, MBasicBlock
* target
) {
2101 return new (alloc
) MGoto(target
);
2104 MGoto
* MGoto::New(TempAllocator::Fallible alloc
, MBasicBlock
* target
) {
2106 return new (alloc
) MGoto(target
);
2109 MGoto
* MGoto::New(TempAllocator
& alloc
) { return new (alloc
) MGoto(nullptr); }
2111 MDefinition
* MBox::foldsTo(TempAllocator
& alloc
) {
2112 if (input()->isUnbox()) {
2113 return input()->toUnbox()->input();
2119 void MUnbox::printOpcode(GenericPrinter
& out
) const {
2120 PrintOpcodeName(out
, op());
2122 getOperand(0)->printName(out
);
2126 case MIRType::Int32
:
2127 out
.printf("to Int32");
2129 case MIRType::Double
:
2130 out
.printf("to Double");
2132 case MIRType::Boolean
:
2133 out
.printf("to Boolean");
2135 case MIRType::String
:
2136 out
.printf("to String");
2138 case MIRType::Symbol
:
2139 out
.printf("to Symbol");
2141 case MIRType::BigInt
:
2142 out
.printf("to BigInt");
2144 case MIRType::Object
:
2145 out
.printf("to Object");
2153 out
.printf(" (fallible)");
2156 out
.printf(" (infallible)");
2164 MDefinition
* MUnbox::foldsTo(TempAllocator
& alloc
) {
2165 if (input()->isBox()) {
2166 MDefinition
* unboxed
= input()->toBox()->input();
2168 // Fold MUnbox(MBox(x)) => x if types match.
2169 if (unboxed
->type() == type()) {
2171 unboxed
->setImplicitlyUsedUnchecked();
2176 // Fold MUnbox(MBox(x)) => MToDouble(x) if possible.
2177 if (type() == MIRType::Double
&&
2178 IsTypeRepresentableAsDouble(unboxed
->type())) {
2179 if (unboxed
->isConstant()) {
2180 return MConstant::New(
2181 alloc
, DoubleValue(unboxed
->toConstant()->numberToDouble()));
2184 return MToDouble::New(alloc
, unboxed
);
2187 // MUnbox<Int32>(MBox<Double>(x)) will always fail, even if x can be
2188 // represented as an Int32. Fold to avoid unnecessary bailouts.
2189 if (type() == MIRType::Int32
&& unboxed
->type() == MIRType::Double
) {
2190 auto* folded
= MToNumberInt32::New(alloc
, unboxed
,
2191 IntConversionInputKind::NumbersOnly
);
2201 void MPhi::assertLoopPhi() const {
2202 // getLoopPredecessorOperand and getLoopBackedgeOperand rely on these
2203 // predecessors being at known indices.
2204 if (block()->numPredecessors() == 2) {
2205 MBasicBlock
* pred
= block()->getPredecessor(0);
2206 MBasicBlock
* back
= block()->getPredecessor(1);
2207 MOZ_ASSERT(pred
== block()->loopPredecessor());
2208 MOZ_ASSERT(pred
->successorWithPhis() == block());
2209 MOZ_ASSERT(pred
->positionInPhiSuccessor() == 0);
2210 MOZ_ASSERT(back
== block()->backedge());
2211 MOZ_ASSERT(back
->successorWithPhis() == block());
2212 MOZ_ASSERT(back
->positionInPhiSuccessor() == 1);
2214 // After we remove fake loop predecessors for loop headers that
2215 // are only reachable via OSR, the only predecessor is the
2217 MOZ_ASSERT(block()->numPredecessors() == 1);
2218 MOZ_ASSERT(block()->graph().osrBlock());
2219 MOZ_ASSERT(!block()->graph().canBuildDominators());
2220 MBasicBlock
* back
= block()->getPredecessor(0);
2221 MOZ_ASSERT(back
== block()->backedge());
2222 MOZ_ASSERT(back
->successorWithPhis() == block());
2223 MOZ_ASSERT(back
->positionInPhiSuccessor() == 0);
2228 MDefinition
* MPhi::getLoopPredecessorOperand() const {
2229 // This should not be called after removing fake loop predecessors.
2230 MOZ_ASSERT(block()->numPredecessors() == 2);
2232 return getOperand(0);
2235 MDefinition
* MPhi::getLoopBackedgeOperand() const {
2237 uint32_t idx
= block()->numPredecessors() == 2 ? 1 : 0;
2238 return getOperand(idx
);
2241 void MPhi::removeOperand(size_t index
) {
2242 MOZ_ASSERT(index
< numOperands());
2243 MOZ_ASSERT(getUseFor(index
)->index() == index
);
2244 MOZ_ASSERT(getUseFor(index
)->consumer() == this);
2246 // If we have phi(..., a, b, c, d, ..., z) and we plan
2247 // on removing a, then first shift downward so that we have
2248 // phi(..., b, c, d, ..., z, z):
2249 MUse
* p
= inputs_
.begin() + index
;
2250 MUse
* e
= inputs_
.end();
2251 p
->producer()->removeUse(p
);
2252 for (; p
< e
- 1; ++p
) {
2253 MDefinition
* producer
= (p
+ 1)->producer();
2254 p
->setProducerUnchecked(producer
);
2255 producer
->replaceUse(p
+ 1, p
);
2258 // truncate the inputs_ list:
2262 void MPhi::removeAllOperands() {
2263 for (MUse
& p
: inputs_
) {
2264 p
.producer()->removeUse(&p
);
2269 MDefinition
* MPhi::foldsTernary(TempAllocator
& alloc
) {
2270 /* Look if this MPhi is a ternary construct.
2271 * This is a very loose term as it actually only checks for
2279 * Which we will simply call:
2280 * x ? x : y or x ? y : x
2283 if (numOperands() != 2) {
2287 MOZ_ASSERT(block()->numPredecessors() == 2);
2289 MBasicBlock
* pred
= block()->immediateDominator();
2290 if (!pred
|| !pred
->lastIns()->isTest()) {
2294 MTest
* test
= pred
->lastIns()->toTest();
2296 // True branch may only dominate one edge of MPhi.
2297 if (test
->ifTrue()->dominates(block()->getPredecessor(0)) ==
2298 test
->ifTrue()->dominates(block()->getPredecessor(1))) {
2302 // False branch may only dominate one edge of MPhi.
2303 if (test
->ifFalse()->dominates(block()->getPredecessor(0)) ==
2304 test
->ifFalse()->dominates(block()->getPredecessor(1))) {
2308 // True and false branch must dominate different edges of MPhi.
2309 if (test
->ifTrue()->dominates(block()->getPredecessor(0)) ==
2310 test
->ifFalse()->dominates(block()->getPredecessor(0))) {
2314 // We found a ternary construct.
2315 bool firstIsTrueBranch
=
2316 test
->ifTrue()->dominates(block()->getPredecessor(0));
2317 MDefinition
* trueDef
= firstIsTrueBranch
? getOperand(0) : getOperand(1);
2318 MDefinition
* falseDef
= firstIsTrueBranch
? getOperand(1) : getOperand(0);
2321 // testArg ? testArg : constant or
2322 // testArg ? constant : testArg
2323 if (!trueDef
->isConstant() && !falseDef
->isConstant()) {
2328 trueDef
->isConstant() ? trueDef
->toConstant() : falseDef
->toConstant();
2329 MDefinition
* testArg
= (trueDef
== c
) ? falseDef
: trueDef
;
2330 if (testArg
!= test
->input()) {
2334 // This check should be a tautology, except that the constant might be the
2335 // result of the removal of a branch. In such case the domination scope of
2336 // the block which is holding the constant might be incomplete. This
2337 // condition is used to prevent doing this optimization based on incomplete
2340 // As GVN removed a branch, it will update the dominations rules before
2341 // trying to fold this MPhi again. Thus, this condition does not inhibit
2342 // this optimization.
2343 MBasicBlock
* truePred
= block()->getPredecessor(firstIsTrueBranch
? 0 : 1);
2344 MBasicBlock
* falsePred
= block()->getPredecessor(firstIsTrueBranch
? 1 : 0);
2345 if (!trueDef
->block()->dominates(truePred
) ||
2346 !falseDef
->block()->dominates(falsePred
)) {
2350 // If testArg is an int32 type we can:
2351 // - fold testArg ? testArg : 0 to testArg
2352 // - fold testArg ? 0 : testArg to 0
2353 if (testArg
->type() == MIRType::Int32
&& c
->numberToDouble() == 0) {
2354 testArg
->setGuardRangeBailoutsUnchecked();
2356 // When folding to the constant we need to hoist it.
2357 if (trueDef
== c
&& !c
->block()->dominates(block())) {
2358 c
->block()->moveBefore(pred
->lastIns(), c
);
2363 // If testArg is an double type we can:
2364 // - fold testArg ? testArg : 0.0 to MNaNToZero(testArg)
2365 if (testArg
->type() == MIRType::Double
&&
2366 mozilla::IsPositiveZero(c
->numberToDouble()) && c
!= trueDef
) {
2367 MNaNToZero
* replace
= MNaNToZero::New(alloc
, testArg
);
2368 test
->block()->insertBefore(test
, replace
);
2372 // If testArg is a string type we can:
2373 // - fold testArg ? testArg : "" to testArg
2374 // - fold testArg ? "" : testArg to ""
2375 if (testArg
->type() == MIRType::String
&&
2376 c
->toString() == GetJitContext()->runtime
->emptyString()) {
2377 // When folding to the constant we need to hoist it.
2378 if (trueDef
== c
&& !c
->block()->dominates(block())) {
2379 c
->block()->moveBefore(pred
->lastIns(), c
);
2387 MDefinition
* MPhi::operandIfRedundant() {
2388 if (inputs_
.length() == 0) {
2392 // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
2393 // returns the operand that it will always be equal to (a, in
2394 // those two cases).
2395 MDefinition
* first
= getOperand(0);
2396 for (size_t i
= 1, e
= numOperands(); i
< e
; i
++) {
2397 MDefinition
* op
= getOperand(i
);
2398 if (op
!= first
&& op
!= this) {
2405 MDefinition
* MPhi::foldsTo(TempAllocator
& alloc
) {
2406 if (MDefinition
* def
= operandIfRedundant()) {
2410 if (MDefinition
* def
= foldsTernary(alloc
)) {
2417 bool MPhi::congruentTo(const MDefinition
* ins
) const {
2418 if (!ins
->isPhi()) {
2422 // Phis in different blocks may have different control conditions.
2423 // For example, these phis:
2435 // have identical operands, but they are not equvalent because t is
2436 // effectively p?x:y and s is effectively q?x:y.
2438 // For now, consider phis in different blocks incongruent.
2439 if (ins
->block() != block()) {
2443 return congruentIfOperandsEqual(ins
);
2446 void MPhi::updateForReplacement(MPhi
* other
) {
2447 // This function is called to fix the current Phi flags using it as a
2448 // replacement of the other Phi instruction |other|.
2450 // When dealing with usage analysis, any Use will replace all other values,
2451 // such as Unused and Unknown. Unless both are Unused, the merge would be
2453 if (usageAnalysis_
== PhiUsage::Used
||
2454 other
->usageAnalysis_
== PhiUsage::Used
) {
2455 usageAnalysis_
= PhiUsage::Used
;
2456 } else if (usageAnalysis_
!= other
->usageAnalysis_
) {
2457 // this == unused && other == unknown
2458 // or this == unknown && other == unused
2459 usageAnalysis_
= PhiUsage::Unknown
;
2461 // this == unused && other == unused
2462 // or this == unknown && other = unknown
2463 MOZ_ASSERT(usageAnalysis_
== PhiUsage::Unused
||
2464 usageAnalysis_
== PhiUsage::Unknown
);
2465 MOZ_ASSERT(usageAnalysis_
== other
->usageAnalysis_
);
2470 bool MPhi::markIteratorPhis(const PhiVector
& iterators
) {
2471 // Find and mark phis that must transitively hold an iterator live.
2473 Vector
<MPhi
*, 8, SystemAllocPolicy
> worklist
;
2475 for (MPhi
* iter
: iterators
) {
2476 if (!iter
->isInWorklist()) {
2477 if (!worklist
.append(iter
)) {
2480 iter
->setInWorklist();
2484 while (!worklist
.empty()) {
2485 MPhi
* phi
= worklist
.popCopy();
2486 phi
->setNotInWorklist();
2489 phi
->setImplicitlyUsedUnchecked();
2491 for (MUseDefIterator
iter(phi
); iter
; iter
++) {
2492 MDefinition
* use
= iter
.def();
2493 if (!use
->isInWorklist() && use
->isPhi() && !use
->toPhi()->isIterator()) {
2494 if (!worklist
.append(use
->toPhi())) {
2497 use
->setInWorklist();
2505 bool MPhi::typeIncludes(MDefinition
* def
) {
2506 MOZ_ASSERT(!IsMagicType(def
->type()));
2508 if (def
->type() == MIRType::Int32
&& this->type() == MIRType::Double
) {
2512 if (def
->type() == MIRType::Value
) {
2513 // This phi must be able to be any value.
2514 return this->type() == MIRType::Value
;
2517 return this->mightBeType(def
->type());
2520 void MCallBase::addArg(size_t argnum
, MDefinition
* arg
) {
2521 // The operand vector is initialized in reverse order by WarpBuilder.
2522 // It cannot be checked for consistency until all arguments are added.
2523 // FixedList doesn't initialize its elements, so do an unchecked init.
2524 initOperand(argnum
+ NumNonArgumentOperands
, arg
);
2527 static inline bool IsConstant(MDefinition
* def
, double v
) {
2528 if (!def
->isConstant()) {
2532 return NumbersAreIdentical(def
->toConstant()->numberToDouble(), v
);
2535 MDefinition
* MBinaryBitwiseInstruction::foldsTo(TempAllocator
& alloc
) {
2536 // Identity operations are removed (for int32 only) in foldUnnecessaryBitop.
2538 if (type() == MIRType::Int32
) {
2539 if (MDefinition
* folded
= EvaluateConstantOperands(alloc
, this)) {
2542 } else if (type() == MIRType::Int64
) {
2543 if (MDefinition
* folded
= EvaluateInt64ConstantOperands(alloc
, this)) {
2551 MDefinition
* MBinaryBitwiseInstruction::foldUnnecessaryBitop() {
2552 // It's probably OK to perform this optimization only for int32, as it will
2553 // have the greatest effect for asm.js code that is compiled with the JS
2554 // pipeline, and that code will not see int64 values.
2556 if (type() != MIRType::Int32
) {
2560 // Fold unsigned shift right operator when the second operand is zero and
2561 // the only use is an unsigned modulo. Thus, the expression
2562 // |(x >>> 0) % y| becomes |x % y|.
2563 if (isUrsh() && IsUint32Type(this)) {
2564 MDefinition
* defUse
= maybeSingleDefUse();
2565 if (defUse
&& defUse
->isMod() && defUse
->toMod()->isUnsigned()) {
2566 return getOperand(0);
2570 // Eliminate bitwise operations that are no-ops when used on integer
2571 // inputs, such as (x | 0).
2573 MDefinition
* lhs
= getOperand(0);
2574 MDefinition
* rhs
= getOperand(1);
2576 if (IsConstant(lhs
, 0)) {
2577 return foldIfZero(0);
2580 if (IsConstant(rhs
, 0)) {
2581 return foldIfZero(1);
2584 if (IsConstant(lhs
, -1)) {
2585 return foldIfNegOne(0);
2588 if (IsConstant(rhs
, -1)) {
2589 return foldIfNegOne(1);
2593 return foldIfEqual();
2596 if (maskMatchesRightRange
) {
2597 MOZ_ASSERT(lhs
->isConstant());
2598 MOZ_ASSERT(lhs
->type() == MIRType::Int32
);
2599 return foldIfAllBitsSet(0);
2602 if (maskMatchesLeftRange
) {
2603 MOZ_ASSERT(rhs
->isConstant());
2604 MOZ_ASSERT(rhs
->type() == MIRType::Int32
);
2605 return foldIfAllBitsSet(1);
2611 static inline bool CanProduceNegativeZero(MDefinition
* def
) {
2612 // Test if this instruction can produce negative zero even when bailing out
2613 // and changing types.
2614 switch (def
->op()) {
2615 case MDefinition::Opcode::Constant
:
2616 if (def
->type() == MIRType::Double
&&
2617 def
->toConstant()->toDouble() == -0.0) {
2621 case MDefinition::Opcode::BitAnd
:
2622 case MDefinition::Opcode::BitOr
:
2623 case MDefinition::Opcode::BitXor
:
2624 case MDefinition::Opcode::BitNot
:
2625 case MDefinition::Opcode::Lsh
:
2626 case MDefinition::Opcode::Rsh
:
2633 static inline bool NeedNegativeZeroCheck(MDefinition
* def
) {
2634 if (def
->isGuard() || def
->isGuardRangeBailouts()) {
2638 // Test if all uses have the same semantics for -0 and 0
2639 for (MUseIterator use
= def
->usesBegin(); use
!= def
->usesEnd(); use
++) {
2640 if (use
->consumer()->isResumePoint()) {
2644 MDefinition
* use_def
= use
->consumer()->toDefinition();
2645 switch (use_def
->op()) {
2646 case MDefinition::Opcode::Add
: {
2647 // If add is truncating -0 and 0 are observed as the same.
2648 if (use_def
->toAdd()->isTruncated()) {
2652 // x + y gives -0, when both x and y are -0
2654 // Figure out the order in which the addition's operands will
2655 // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR
2656 // definitions for us so that this just requires comparing ids.
2657 MDefinition
* first
= use_def
->toAdd()->lhs();
2658 MDefinition
* second
= use_def
->toAdd()->rhs();
2659 if (first
->id() > second
->id()) {
2660 std::swap(first
, second
);
2662 // Negative zero checks can be removed on the first executed
2663 // operand only if it is guaranteed the second executed operand
2664 // will produce a value other than -0. While the second is
2665 // typed as an int32, a bailout taken between execution of the
2666 // operands may change that type and cause a -0 to flow to the
2669 // There is no way to test whether there are any bailouts
2670 // between execution of the operands, so remove negative
2671 // zero checks from the first only if the second's type is
2672 // independent from type changes that may occur after bailing.
2673 if (def
== first
&& CanProduceNegativeZero(second
)) {
2677 // The negative zero check can always be removed on the second
2678 // executed operand; by the time this executes the first will have
2679 // been evaluated as int32 and the addition's result cannot be -0.
2682 case MDefinition::Opcode::Sub
: {
2683 // If sub is truncating -0 and 0 are observed as the same
2684 if (use_def
->toSub()->isTruncated()) {
2688 // x + y gives -0, when x is -0 and y is 0
2690 // We can remove the negative zero check on the rhs, only if we
2691 // are sure the lhs isn't negative zero.
2693 // The lhs is typed as integer (i.e. not -0.0), but it can bailout
2694 // and change type. This should be fine if the lhs is executed
2695 // first. However if the rhs is executed first, the lhs can bail,
2696 // change type and become -0.0 while the rhs has already been
2697 // optimized to not make a difference between zero and negative zero.
2698 MDefinition
* lhs
= use_def
->toSub()->lhs();
2699 MDefinition
* rhs
= use_def
->toSub()->rhs();
2700 if (rhs
->id() < lhs
->id() && CanProduceNegativeZero(lhs
)) {
2706 case MDefinition::Opcode::StoreElement
:
2707 case MDefinition::Opcode::StoreHoleValueElement
:
2708 case MDefinition::Opcode::LoadElement
:
2709 case MDefinition::Opcode::LoadElementHole
:
2710 case MDefinition::Opcode::LoadUnboxedScalar
:
2711 case MDefinition::Opcode::LoadDataViewElement
:
2712 case MDefinition::Opcode::LoadTypedArrayElementHole
:
2713 case MDefinition::Opcode::CharCodeAt
:
2714 case MDefinition::Opcode::Mod
:
2715 case MDefinition::Opcode::InArray
:
2716 // Only allowed to remove check when definition is the second operand
2717 if (use_def
->getOperand(0) == def
) {
2720 for (size_t i
= 2, e
= use_def
->numOperands(); i
< e
; i
++) {
2721 if (use_def
->getOperand(i
) == def
) {
2726 case MDefinition::Opcode::BoundsCheck
:
2727 // Only allowed to remove check when definition is the first operand
2728 if (use_def
->toBoundsCheck()->getOperand(1) == def
) {
2732 case MDefinition::Opcode::ToString
:
2733 case MDefinition::Opcode::FromCharCode
:
2734 case MDefinition::Opcode::FromCodePoint
:
2735 case MDefinition::Opcode::TableSwitch
:
2736 case MDefinition::Opcode::Compare
:
2737 case MDefinition::Opcode::BitAnd
:
2738 case MDefinition::Opcode::BitOr
:
2739 case MDefinition::Opcode::BitXor
:
2740 case MDefinition::Opcode::Abs
:
2741 case MDefinition::Opcode::TruncateToInt32
:
2742 // Always allowed to remove check. No matter which operand.
2744 case MDefinition::Opcode::StoreElementHole
:
2745 case MDefinition::Opcode::StoreTypedArrayElementHole
:
2746 case MDefinition::Opcode::PostWriteElementBarrier
:
2747 // Only allowed to remove check when definition is the third operand.
2748 for (size_t i
= 0, e
= use_def
->numOperands(); i
< e
; i
++) {
2752 if (use_def
->getOperand(i
) == def
) {
2765 void MBinaryArithInstruction::printOpcode(GenericPrinter
& out
) const {
2766 MDefinition::printOpcode(out
);
2769 case MIRType::Int32
:
2771 out
.printf(" [%s]", toDiv()->isUnsigned() ? "uint32" : "int32");
2772 } else if (isMod()) {
2773 out
.printf(" [%s]", toMod()->isUnsigned() ? "uint32" : "int32");
2775 out
.printf(" [int32]");
2778 case MIRType::Int64
:
2780 out
.printf(" [%s]", toDiv()->isUnsigned() ? "uint64" : "int64");
2781 } else if (isMod()) {
2782 out
.printf(" [%s]", toMod()->isUnsigned() ? "uint64" : "int64");
2784 out
.printf(" [int64]");
2787 case MIRType::Float32
:
2788 out
.printf(" [float]");
2790 case MIRType::Double
:
2791 out
.printf(" [double]");
2799 MDefinition
* MRsh::foldsTo(TempAllocator
& alloc
) {
2800 MDefinition
* f
= MBinaryBitwiseInstruction::foldsTo(alloc
);
2806 MDefinition
* lhs
= getOperand(0);
2807 MDefinition
* rhs
= getOperand(1);
2809 // It's probably OK to perform this optimization only for int32, as it will
2810 // have the greatest effect for asm.js code that is compiled with the JS
2811 // pipeline, and that code will not see int64 values.
2813 if (!lhs
->isLsh() || !rhs
->isConstant() || rhs
->type() != MIRType::Int32
) {
2817 if (!lhs
->getOperand(1)->isConstant() ||
2818 lhs
->getOperand(1)->type() != MIRType::Int32
) {
2822 uint32_t shift
= rhs
->toConstant()->toInt32();
2823 uint32_t shift_lhs
= lhs
->getOperand(1)->toConstant()->toInt32();
2824 if (shift
!= shift_lhs
) {
2830 return MSignExtendInt32::New(alloc
, lhs
->getOperand(0),
2831 MSignExtendInt32::Half
);
2833 return MSignExtendInt32::New(alloc
, lhs
->getOperand(0),
2834 MSignExtendInt32::Byte
);
2840 MDefinition
* MBinaryArithInstruction::foldsTo(TempAllocator
& alloc
) {
2841 MOZ_ASSERT(IsNumberType(type()));
2843 MDefinition
* lhs
= getOperand(0);
2844 MDefinition
* rhs
= getOperand(1);
2846 if (type() == MIRType::Int64
) {
2847 MOZ_ASSERT(!isTruncated());
2849 if (MConstant
* folded
= EvaluateInt64ConstantOperands(alloc
, this)) {
2850 if (!folded
->block()) {
2851 block()->insertBefore(this, folded
);
2855 if (isSub() || isDiv() || isMod()) {
2858 if (rhs
->isConstant() &&
2859 rhs
->toConstant()->toInt64() == int64_t(getIdentity())) {
2862 if (lhs
->isConstant() &&
2863 lhs
->toConstant()->toInt64() == int64_t(getIdentity())) {
2869 if (MConstant
* folded
= EvaluateConstantOperands(alloc
, this)) {
2870 if (isTruncated()) {
2871 if (!folded
->block()) {
2872 block()->insertBefore(this, folded
);
2874 if (folded
->type() != MIRType::Int32
) {
2875 return MTruncateToInt32::New(alloc
, folded
);
2881 if (mustPreserveNaN_
) {
2885 // 0 + -0 = 0. So we can't remove addition
2886 if (isAdd() && type() != MIRType::Int32
) {
2890 if (IsConstant(rhs
, getIdentity())) {
2891 if (isTruncated()) {
2892 return MTruncateToInt32::New(alloc
, lhs
);
2897 // subtraction isn't commutative. So we can't remove subtraction when lhs
2903 if (IsConstant(lhs
, getIdentity())) {
2904 if (isTruncated()) {
2905 return MTruncateToInt32::New(alloc
, rhs
);
2907 return rhs
; // id op x => x
2913 void MBinaryArithInstruction::trySpecializeFloat32(TempAllocator
& alloc
) {
2914 MOZ_ASSERT(IsNumberType(type()));
2916 // Do not use Float32 if we can use int32.
2917 if (type() == MIRType::Int32
) {
2921 if (EnsureFloatConsumersAndInputOrConvert(this, alloc
)) {
2922 setResultType(MIRType::Float32
);
2926 void MMinMax::trySpecializeFloat32(TempAllocator
& alloc
) {
2927 if (type() == MIRType::Int32
) {
2931 MDefinition
* left
= lhs();
2932 MDefinition
* right
= rhs();
2934 if ((left
->canProduceFloat32() ||
2935 (left
->isMinMax() && left
->type() == MIRType::Float32
)) &&
2936 (right
->canProduceFloat32() ||
2937 (right
->isMinMax() && right
->type() == MIRType::Float32
))) {
2938 setResultType(MIRType::Float32
);
2940 ConvertOperandsToDouble(this, alloc
);
2944 MDefinition
* MMinMax::foldsTo(TempAllocator
& alloc
) {
2945 MOZ_ASSERT(lhs()->type() == type());
2946 MOZ_ASSERT(rhs()->type() == type());
2948 if (lhs() == rhs()) {
2952 auto foldConstants
= [&alloc
](MDefinition
* lhs
, MDefinition
* rhs
,
2953 bool isMax
) -> MConstant
* {
2954 MOZ_ASSERT(lhs
->type() == rhs
->type());
2955 MOZ_ASSERT(lhs
->toConstant()->isTypeRepresentableAsDouble());
2956 MOZ_ASSERT(rhs
->toConstant()->isTypeRepresentableAsDouble());
2958 double lnum
= lhs
->toConstant()->numberToDouble();
2959 double rnum
= rhs
->toConstant()->numberToDouble();
2963 result
= js::math_max_impl(lnum
, rnum
);
2965 result
= js::math_min_impl(lnum
, rnum
);
2968 // The folded MConstant should maintain the same MIRType with the original
2970 if (lhs
->type() == MIRType::Int32
) {
2972 if (mozilla::NumberEqualsInt32(result
, &cast
)) {
2973 return MConstant::New(alloc
, Int32Value(cast
));
2977 if (lhs
->type() == MIRType::Float32
) {
2978 return MConstant::NewFloat32(alloc
, result
);
2980 MOZ_ASSERT(lhs
->type() == MIRType::Double
);
2981 return MConstant::New(alloc
, DoubleValue(result
));
2984 // Try to fold the following patterns when |x| and |y| are constants.
2986 // min(min(x, z), min(y, z)) = min(min(x, y), z)
2987 // max(max(x, z), max(y, z)) = max(max(x, y), z)
2988 // max(min(x, z), min(y, z)) = min(max(x, y), z)
2989 // min(max(x, z), max(y, z)) = max(min(x, y), z)
2990 if (lhs()->isMinMax() && rhs()->isMinMax()) {
2992 auto* left
= lhs()->toMinMax();
2993 auto* right
= rhs()->toMinMax();
2994 if (left
->isMax() != right
->isMax()) {
3001 if (left
->lhs() == right
->lhs()) {
3002 std::tie(x
, y
, z
) = std::tuple
{left
->rhs(), right
->rhs(), left
->lhs()};
3003 } else if (left
->lhs() == right
->rhs()) {
3004 std::tie(x
, y
, z
) = std::tuple
{left
->rhs(), right
->lhs(), left
->lhs()};
3005 } else if (left
->rhs() == right
->lhs()) {
3006 std::tie(x
, y
, z
) = std::tuple
{left
->lhs(), right
->rhs(), left
->rhs()};
3007 } else if (left
->rhs() == right
->rhs()) {
3008 std::tie(x
, y
, z
) = std::tuple
{left
->lhs(), right
->lhs(), left
->rhs()};
3013 if (!x
->isConstant() || !x
->toConstant()->isTypeRepresentableAsDouble() ||
3014 !y
->isConstant() || !y
->toConstant()->isTypeRepresentableAsDouble()) {
3018 if (auto* folded
= foldConstants(x
, y
, isMax())) {
3019 block()->insertBefore(this, folded
);
3020 return MMinMax::New(alloc
, folded
, z
, type(), left
->isMax());
3025 // Fold min/max operations with same inputs.
3026 if (lhs()->isMinMax() || rhs()->isMinMax()) {
3027 auto* other
= lhs()->isMinMax() ? lhs()->toMinMax() : rhs()->toMinMax();
3028 auto* operand
= lhs()->isMinMax() ? rhs() : lhs();
3030 if (operand
== other
->lhs() || operand
== other
->rhs()) {
3031 if (isMax() == other
->isMax()) {
3032 // min(x, min(x, y)) = min(x, y)
3033 // max(x, max(x, y)) = max(x, y)
3036 if (!IsFloatingPointType(type())) {
3037 // When neither value is NaN:
3038 // max(x, min(x, y)) = x
3039 // min(x, max(x, y)) = x
3041 // Ensure that any bailouts that we depend on to guarantee that |y| is
3042 // Int32 are not removed.
3043 auto* otherOp
= operand
== other
->lhs() ? other
->rhs() : other
->lhs();
3044 otherOp
->setGuardRangeBailoutsUnchecked();
3051 if (!lhs()->isConstant() && !rhs()->isConstant()) {
3055 // Directly apply math utility to compare the rhs() and lhs() when
3056 // they are both constants.
3057 if (lhs()->isConstant() && rhs()->isConstant()) {
3058 if (!lhs()->toConstant()->isTypeRepresentableAsDouble() ||
3059 !rhs()->toConstant()->isTypeRepresentableAsDouble()) {
3063 if (auto* folded
= foldConstants(lhs(), rhs(), isMax())) {
3068 MDefinition
* operand
= lhs()->isConstant() ? rhs() : lhs();
3069 MConstant
* constant
=
3070 lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant();
3072 if (operand
->isToDouble() &&
3073 operand
->getOperand(0)->type() == MIRType::Int32
) {
3074 // min(int32, cte >= INT32_MAX) = int32
3075 if (!isMax() && constant
->isTypeRepresentableAsDouble() &&
3076 constant
->numberToDouble() >= INT32_MAX
) {
3077 MLimitedTruncate
* limit
= MLimitedTruncate::New(
3078 alloc
, operand
->getOperand(0), TruncateKind::NoTruncate
);
3079 block()->insertBefore(this, limit
);
3080 MToDouble
* toDouble
= MToDouble::New(alloc
, limit
);
3084 // max(int32, cte <= INT32_MIN) = int32
3085 if (isMax() && constant
->isTypeRepresentableAsDouble() &&
3086 constant
->numberToDouble() <= INT32_MIN
) {
3087 MLimitedTruncate
* limit
= MLimitedTruncate::New(
3088 alloc
, operand
->getOperand(0), TruncateKind::NoTruncate
);
3089 block()->insertBefore(this, limit
);
3090 MToDouble
* toDouble
= MToDouble::New(alloc
, limit
);
3095 auto foldLength
= [](MDefinition
* operand
, MConstant
* constant
,
3096 bool isMax
) -> MDefinition
* {
3097 if ((operand
->isArrayLength() || operand
->isArrayBufferViewLength() ||
3098 operand
->isArgumentsLength() || operand
->isStringLength()) &&
3099 constant
->type() == MIRType::Int32
) {
3100 // (Array|ArrayBufferView|Arguments|String)Length is always >= 0.
3101 // max(array.length, cte <= 0) = array.length
3102 // min(array.length, cte <= 0) = cte
3103 if (constant
->toInt32() <= 0) {
3104 return isMax
? operand
: constant
;
3110 if (auto* folded
= foldLength(operand
, constant
, isMax())) {
3114 // Attempt to fold nested min/max operations which are produced by
3115 // self-hosted built-in functions.
3116 if (operand
->isMinMax()) {
3117 auto* other
= operand
->toMinMax();
3118 MOZ_ASSERT(other
->lhs()->type() == type());
3119 MOZ_ASSERT(other
->rhs()->type() == type());
3121 MConstant
* otherConstant
= nullptr;
3122 MDefinition
* otherOperand
= nullptr;
3123 if (other
->lhs()->isConstant()) {
3124 otherConstant
= other
->lhs()->toConstant();
3125 otherOperand
= other
->rhs();
3126 } else if (other
->rhs()->isConstant()) {
3127 otherConstant
= other
->rhs()->toConstant();
3128 otherOperand
= other
->lhs();
3131 if (otherConstant
&& constant
->isTypeRepresentableAsDouble() &&
3132 otherConstant
->isTypeRepresentableAsDouble()) {
3133 if (isMax() == other
->isMax()) {
3134 // Fold min(x, min(y, z)) to min(min(x, y), z) with constant min(x, y).
3135 // Fold max(x, max(y, z)) to max(max(x, y), z) with constant max(x, y).
3136 if (auto* left
= foldConstants(constant
, otherConstant
, isMax())) {
3137 block()->insertBefore(this, left
);
3138 return MMinMax::New(alloc
, left
, otherOperand
, type(), isMax());
3141 // Fold min(x, max(y, z)) to max(min(x, y), min(x, z)).
3142 // Fold max(x, min(y, z)) to min(max(x, y), max(x, z)).
3144 // But only do this when min(x, z) can also be simplified.
3145 if (auto* right
= foldLength(otherOperand
, constant
, isMax())) {
3146 if (auto* left
= foldConstants(constant
, otherConstant
, isMax())) {
3147 block()->insertBefore(this, left
);
3148 return MMinMax::New(alloc
, left
, right
, type(), !isMax());
3159 void MMinMax::printOpcode(GenericPrinter
& out
) const {
3160 MDefinition::printOpcode(out
);
3161 out
.printf(" (%s)", isMax() ? "max" : "min");
3164 void MMinMaxArray::printOpcode(GenericPrinter
& out
) const {
3165 MDefinition::printOpcode(out
);
3166 out
.printf(" (%s)", isMax() ? "max" : "min");
3170 MDefinition
* MPow::foldsConstant(TempAllocator
& alloc
) {
3171 // Both `x` and `p` in `x^p` must be constants in order to precompute.
3172 if (!input()->isConstant() || !power()->isConstant()) {
3175 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3178 if (!input()->toConstant()->isTypeRepresentableAsDouble()) {
3182 double x
= input()->toConstant()->numberToDouble();
3183 double p
= power()->toConstant()->numberToDouble();
3184 double result
= js::ecmaPow(x
, p
);
3185 if (type() == MIRType::Int32
) {
3187 if (!mozilla::NumberIsInt32(result
, &cast
)) {
3188 // Reject folding if the result isn't an int32, because we'll bail anyway.
3191 return MConstant::New(alloc
, Int32Value(cast
));
3193 return MConstant::New(alloc
, DoubleValue(result
));
3196 MDefinition
* MPow::foldsConstantPower(TempAllocator
& alloc
) {
3197 // If `p` in `x^p` isn't constant, we can't apply these folds.
3198 if (!power()->isConstant()) {
3201 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3205 MOZ_ASSERT(type() == MIRType::Double
|| type() == MIRType::Int32
);
3207 // NOTE: The optimizations must match the optimizations used in |js::ecmaPow|
3208 // resp. |js::powi| to avoid differential testing issues.
3210 double pow
= power()->toConstant()->numberToDouble();
3212 // Math.pow(x, 0.5) is a sqrt with edge-case detection.
3214 MOZ_ASSERT(type() == MIRType::Double
);
3215 return MPowHalf::New(alloc
, input());
3218 // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases.
3220 MOZ_ASSERT(type() == MIRType::Double
);
3221 MPowHalf
* half
= MPowHalf::New(alloc
, input());
3222 block()->insertBefore(this, half
);
3223 MConstant
* one
= MConstant::New(alloc
, DoubleValue(1.0));
3224 block()->insertBefore(this, one
);
3225 return MDiv::New(alloc
, one
, half
, MIRType::Double
);
3228 // Math.pow(x, 1) == x.
3233 auto multiply
= [this, &alloc
](MDefinition
* lhs
, MDefinition
* rhs
) {
3234 MMul
* mul
= MMul::New(alloc
, lhs
, rhs
, type());
3235 mul
->setBailoutKind(bailoutKind());
3237 // Multiplying the same number can't yield negative zero.
3238 mul
->setCanBeNegativeZero(lhs
!= rhs
&& canBeNegativeZero());
3242 // Math.pow(x, 2) == x*x.
3244 return multiply(input(), input());
3247 // Math.pow(x, 3) == x*x*x.
3249 MMul
* mul1
= multiply(input(), input());
3250 block()->insertBefore(this, mul1
);
3251 return multiply(input(), mul1
);
3254 // Math.pow(x, 4) == y*y, where y = x*x.
3256 MMul
* y
= multiply(input(), input());
3257 block()->insertBefore(this, y
);
3258 return multiply(y
, y
);
3265 MDefinition
* MPow::foldsTo(TempAllocator
& alloc
) {
3266 if (MDefinition
* def
= foldsConstant(alloc
)) {
3269 if (MDefinition
* def
= foldsConstantPower(alloc
)) {
3275 MDefinition
* MInt32ToIntPtr::foldsTo(TempAllocator
& alloc
) {
3276 MDefinition
* def
= input();
3277 if (def
->isConstant()) {
3278 int32_t i
= def
->toConstant()->toInt32();
3279 return MConstant::NewIntPtr(alloc
, intptr_t(i
));
3282 if (def
->isNonNegativeIntPtrToInt32()) {
3283 return def
->toNonNegativeIntPtrToInt32()->input();
3289 bool MAbs::fallible() const {
3290 return !implicitTruncate_
&& (!range() || !range()->hasInt32Bounds());
3293 void MAbs::trySpecializeFloat32(TempAllocator
& alloc
) {
3294 // Do not use Float32 if we can use int32.
3295 if (input()->type() == MIRType::Int32
) {
3299 if (EnsureFloatConsumersAndInputOrConvert(this, alloc
)) {
3300 setResultType(MIRType::Float32
);
3304 MDefinition
* MDiv::foldsTo(TempAllocator
& alloc
) {
3305 MOZ_ASSERT(IsNumberType(type()));
3307 if (type() == MIRType::Int64
) {
3308 if (MDefinition
* folded
= EvaluateInt64ConstantOperands(alloc
, this)) {
3314 if (MDefinition
* folded
= EvaluateConstantOperands(alloc
, this)) {
3318 if (MDefinition
* folded
= EvaluateExactReciprocal(alloc
, this)) {
3325 void MDiv::analyzeEdgeCasesForward() {
3326 // This is only meaningful when doing integer division.
3327 if (type() != MIRType::Int32
) {
3331 MOZ_ASSERT(lhs()->type() == MIRType::Int32
);
3332 MOZ_ASSERT(rhs()->type() == MIRType::Int32
);
3334 // Try removing divide by zero check
3335 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
3336 canBeDivideByZero_
= false;
3339 // If lhs is a constant int != INT32_MIN, then
3340 // negative overflow check can be skipped.
3341 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(INT32_MIN
)) {
3342 canBeNegativeOverflow_
= false;
3345 // If rhs is a constant int != -1, likewise.
3346 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(-1)) {
3347 canBeNegativeOverflow_
= false;
3350 // If lhs is != 0, then negative zero check can be skipped.
3351 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(0)) {
3352 setCanBeNegativeZero(false);
3355 // If rhs is >= 0, likewise.
3356 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32
) {
3357 if (rhs()->toConstant()->toInt32() >= 0) {
3358 setCanBeNegativeZero(false);
3363 void MDiv::analyzeEdgeCasesBackward() {
3364 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) {
3365 setCanBeNegativeZero(false);
3369 bool MDiv::fallible() const { return !isTruncated(); }
3371 MDefinition
* MMod::foldsTo(TempAllocator
& alloc
) {
3372 MOZ_ASSERT(IsNumberType(type()));
3374 if (type() == MIRType::Int64
) {
3375 if (MDefinition
* folded
= EvaluateInt64ConstantOperands(alloc
, this)) {
3379 if (MDefinition
* folded
= EvaluateConstantOperands(alloc
, this)) {
3386 void MMod::analyzeEdgeCasesForward() {
3387 // These optimizations make sense only for integer division
3388 if (type() != MIRType::Int32
) {
3392 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
3393 canBeDivideByZero_
= false;
3396 if (rhs()->isConstant()) {
3397 int32_t n
= rhs()->toConstant()->toInt32();
3398 if (n
> 0 && !IsPowerOfTwo(uint32_t(n
))) {
3399 canBePowerOfTwoDivisor_
= false;
3404 bool MMod::fallible() const {
3405 return !isTruncated() &&
3406 (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend());
3409 void MMathFunction::trySpecializeFloat32(TempAllocator
& alloc
) {
3410 if (EnsureFloatConsumersAndInputOrConvert(this, alloc
)) {
3411 setResultType(MIRType::Float32
);
3412 specialization_
= MIRType::Float32
;
3416 bool MMathFunction::isFloat32Commutative() const {
3417 switch (function_
) {
3418 case UnaryMathFunction::Floor
:
3419 case UnaryMathFunction::Ceil
:
3420 case UnaryMathFunction::Round
:
3421 case UnaryMathFunction::Trunc
:
3428 MHypot
* MHypot::New(TempAllocator
& alloc
, const MDefinitionVector
& vector
) {
3429 uint32_t length
= vector
.length();
3430 MHypot
* hypot
= new (alloc
) MHypot
;
3431 if (!hypot
->init(alloc
, length
)) {
3435 for (uint32_t i
= 0; i
< length
; ++i
) {
3436 hypot
->initOperand(i
, vector
[i
]);
3441 bool MAdd::fallible() const {
3442 // the add is fallible if range analysis does not say that it is finite, AND
3443 // either the truncation analysis shows that there are non-truncated uses.
3444 if (truncateKind() >= TruncateKind::IndirectTruncate
) {
3447 if (range() && range()->hasInt32Bounds()) {
3453 bool MSub::fallible() const {
3454 // see comment in MAdd::fallible()
3455 if (truncateKind() >= TruncateKind::IndirectTruncate
) {
3458 if (range() && range()->hasInt32Bounds()) {
3464 MDefinition
* MSub::foldsTo(TempAllocator
& alloc
) {
3465 MDefinition
* out
= MBinaryArithInstruction::foldsTo(alloc
);
3470 if (type() != MIRType::Int32
) {
3474 // Optimize X - X to 0. This optimization is only valid for Int32
3475 // values. Subtracting a floating point value from itself returns
3476 // NaN when the operand is either Infinity or NaN.
3477 if (lhs() == rhs()) {
3478 // Ensure that any bailouts that we depend on to guarantee that X
3479 // is Int32 are not removed.
3480 lhs()->setGuardRangeBailoutsUnchecked();
3481 return MConstant::New(alloc
, Int32Value(0));
3487 MDefinition
* MMul::foldsTo(TempAllocator
& alloc
) {
3488 MDefinition
* out
= MBinaryArithInstruction::foldsTo(alloc
);
3493 if (type() != MIRType::Int32
) {
3497 if (lhs() == rhs()) {
3498 setCanBeNegativeZero(false);
3504 void MMul::analyzeEdgeCasesForward() {
3505 // Try to remove the check for negative zero
3506 // This only makes sense when using the integer multiplication
3507 if (type() != MIRType::Int32
) {
3511 // If lhs is > 0, no need for negative zero check.
3512 if (lhs()->isConstant() && lhs()->type() == MIRType::Int32
) {
3513 if (lhs()->toConstant()->toInt32() > 0) {
3514 setCanBeNegativeZero(false);
3518 // If rhs is > 0, likewise.
3519 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32
) {
3520 if (rhs()->toConstant()->toInt32() > 0) {
3521 setCanBeNegativeZero(false);
3526 void MMul::analyzeEdgeCasesBackward() {
3527 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) {
3528 setCanBeNegativeZero(false);
3532 bool MMul::canOverflow() const {
3533 if (isTruncated()) {
3536 return !range() || !range()->hasInt32Bounds();
3539 bool MUrsh::fallible() const {
3540 if (bailoutsDisabled()) {
3543 return !range() || !range()->hasInt32Bounds();
3546 MIRType
MCompare::inputType() {
3547 switch (compareType_
) {
3548 case Compare_Undefined
:
3549 return MIRType::Undefined
;
3551 return MIRType::Null
;
3552 case Compare_UInt32
:
3554 return MIRType::Int32
;
3555 case Compare_UIntPtr
:
3556 return MIRType::IntPtr
;
3557 case Compare_Double
:
3558 return MIRType::Double
;
3559 case Compare_Float32
:
3560 return MIRType::Float32
;
3561 case Compare_String
:
3562 return MIRType::String
;
3563 case Compare_Symbol
:
3564 return MIRType::Symbol
;
3565 case Compare_Object
:
3566 return MIRType::Object
;
3567 case Compare_BigInt
:
3568 case Compare_BigInt_Int32
:
3569 case Compare_BigInt_Double
:
3570 case Compare_BigInt_String
:
3571 return MIRType::BigInt
;
3573 MOZ_CRASH("No known conversion");
3577 static inline bool MustBeUInt32(MDefinition
* def
, MDefinition
** pwrapped
) {
3578 if (def
->isUrsh()) {
3579 *pwrapped
= def
->toUrsh()->lhs();
3580 MDefinition
* rhs
= def
->toUrsh()->rhs();
3581 return def
->toUrsh()->bailoutsDisabled() && rhs
->maybeConstantValue() &&
3582 rhs
->maybeConstantValue()->isInt32(0);
3585 if (MConstant
* defConst
= def
->maybeConstantValue()) {
3586 *pwrapped
= defConst
;
3587 return defConst
->type() == MIRType::Int32
&& defConst
->toInt32() >= 0;
3590 *pwrapped
= nullptr; // silence GCC warning
3595 bool MBinaryInstruction::unsignedOperands(MDefinition
* left
,
3596 MDefinition
* right
) {
3597 MDefinition
* replace
;
3598 if (!MustBeUInt32(left
, &replace
)) {
3601 if (replace
->type() != MIRType::Int32
) {
3604 if (!MustBeUInt32(right
, &replace
)) {
3607 if (replace
->type() != MIRType::Int32
) {
3613 bool MBinaryInstruction::unsignedOperands() {
3614 return unsignedOperands(getOperand(0), getOperand(1));
3617 void MBinaryInstruction::replaceWithUnsignedOperands() {
3618 MOZ_ASSERT(unsignedOperands());
3620 for (size_t i
= 0; i
< numOperands(); i
++) {
3621 MDefinition
* replace
;
3622 MustBeUInt32(getOperand(i
), &replace
);
3623 if (replace
== getOperand(i
)) {
3627 getOperand(i
)->setImplicitlyUsedUnchecked();
3628 replaceOperand(i
, replace
);
3632 MDefinition
* MBitNot::foldsTo(TempAllocator
& alloc
) {
3633 if (type() == MIRType::Int64
) {
3636 MOZ_ASSERT(type() == MIRType::Int32
);
3638 MDefinition
* input
= getOperand(0);
3640 if (input
->isConstant()) {
3641 js::Value v
= Int32Value(~(input
->toConstant()->toInt32()));
3642 return MConstant::New(alloc
, v
);
3645 if (input
->isBitNot()) {
3646 MOZ_ASSERT(input
->toBitNot()->type() == MIRType::Int32
);
3647 MOZ_ASSERT(input
->toBitNot()->getOperand(0)->type() == MIRType::Int32
);
3648 return MTruncateToInt32::New(alloc
,
3649 input
->toBitNot()->input()); // ~~x => x | 0
3655 static void AssertKnownClass(TempAllocator
& alloc
, MInstruction
* ins
,
3658 const JSClass
* clasp
= GetObjectKnownJSClass(obj
);
3661 auto* assert = MAssertClass::New(alloc
, obj
, clasp
);
3662 ins
->block()->insertBefore(ins
, assert);
3666 MDefinition
* MBoxNonStrictThis::foldsTo(TempAllocator
& alloc
) {
3667 MDefinition
* in
= input();
3669 in
= in
->toBox()->input();
3672 if (in
->type() == MIRType::Object
) {
3679 AliasSet
MLoadArgumentsObjectArg::getAliasSet() const {
3680 return AliasSet::Load(AliasSet::Any
);
3683 AliasSet
MLoadArgumentsObjectArgHole::getAliasSet() const {
3684 return AliasSet::Load(AliasSet::Any
);
3687 AliasSet
MInArgumentsObjectArg::getAliasSet() const {
3688 // Loads |arguments.length|, but not the actual element, so we can use the
3689 // same alias-set as MArgumentsObjectLength.
3690 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
3691 AliasSet::DynamicSlot
);
3694 AliasSet
MArgumentsObjectLength::getAliasSet() const {
3695 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
3696 AliasSet::DynamicSlot
);
3699 bool MGuardArgumentsObjectFlags::congruentTo(const MDefinition
* ins
) const {
3700 if (!ins
->isGuardArgumentsObjectFlags() ||
3701 ins
->toGuardArgumentsObjectFlags()->flags() != flags()) {
3704 return congruentIfOperandsEqual(ins
);
3707 AliasSet
MGuardArgumentsObjectFlags::getAliasSet() const {
3708 // The flags are packed with the length in a fixed private slot.
3709 return AliasSet::Load(AliasSet::FixedSlot
);
3712 MDefinition
* MIdToStringOrSymbol::foldsTo(TempAllocator
& alloc
) {
3713 if (idVal()->isBox()) {
3714 auto* input
= idVal()->toBox()->input();
3715 MIRType idType
= input
->type();
3716 if (idType
== MIRType::String
|| idType
== MIRType::Symbol
) {
3719 if (idType
== MIRType::Int32
) {
3721 MToString::New(alloc
, input
, MToString::SideEffectHandling::Bailout
);
3722 block()->insertBefore(this, toString
);
3724 return MBox::New(alloc
, toString
);
3731 MDefinition
* MReturnFromCtor::foldsTo(TempAllocator
& alloc
) {
3732 MDefinition
* rval
= value();
3733 if (rval
->isBox()) {
3734 rval
= rval
->toBox()->input();
3737 if (rval
->type() == MIRType::Object
) {
3741 if (rval
->type() != MIRType::Value
) {
3748 MDefinition
* MTypeOf::foldsTo(TempAllocator
& alloc
) {
3749 MDefinition
* unboxed
= input();
3750 if (unboxed
->isBox()) {
3751 unboxed
= unboxed
->toBox()->input();
3755 switch (unboxed
->type()) {
3756 case MIRType::Double
:
3757 case MIRType::Float32
:
3758 case MIRType::Int32
:
3759 type
= JSTYPE_NUMBER
;
3761 case MIRType::String
:
3762 type
= JSTYPE_STRING
;
3764 case MIRType::Symbol
:
3765 type
= JSTYPE_SYMBOL
;
3767 case MIRType::BigInt
:
3768 type
= JSTYPE_BIGINT
;
3771 type
= JSTYPE_OBJECT
;
3773 case MIRType::Undefined
:
3774 type
= JSTYPE_UNDEFINED
;
3776 case MIRType::Boolean
:
3777 type
= JSTYPE_BOOLEAN
;
3779 case MIRType::Object
: {
3780 KnownClass known
= GetObjectKnownClass(unboxed
);
3781 if (known
!= KnownClass::None
) {
3782 if (known
== KnownClass::Function
) {
3783 type
= JSTYPE_FUNCTION
;
3785 type
= JSTYPE_OBJECT
;
3788 AssertKnownClass(alloc
, this, unboxed
);
3797 return MConstant::New(alloc
, Int32Value(static_cast<int32_t>(type
)));
3800 MDefinition
* MTypeOfName::foldsTo(TempAllocator
& alloc
) {
3801 MOZ_ASSERT(input()->type() == MIRType::Int32
);
3803 if (!input()->isConstant()) {
3807 static_assert(JSTYPE_UNDEFINED
== 0);
3809 int32_t type
= input()->toConstant()->toInt32();
3810 MOZ_ASSERT(JSTYPE_UNDEFINED
<= type
&& type
< JSTYPE_LIMIT
);
3813 TypeName(static_cast<JSType
>(type
), GetJitContext()->runtime
->names());
3814 return MConstant::New(alloc
, StringValue(name
));
3817 MUrsh
* MUrsh::NewWasm(TempAllocator
& alloc
, MDefinition
* left
,
3818 MDefinition
* right
, MIRType type
) {
3819 MUrsh
* ins
= new (alloc
) MUrsh(left
, right
, type
);
3821 // Since Ion has no UInt32 type, we use Int32 and we have a special
3822 // exception to the type rules: we can return values in
3823 // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
3824 // without bailing out. This is necessary because Ion has no UInt32
3825 // type and we can't have bailouts in wasm code.
3826 ins
->bailoutsDisabled_
= true;
3831 MResumePoint
* MResumePoint::New(TempAllocator
& alloc
, MBasicBlock
* block
,
3832 jsbytecode
* pc
, ResumeMode mode
) {
3833 MResumePoint
* resume
= new (alloc
) MResumePoint(block
, pc
, mode
);
3834 if (!resume
->init(alloc
)) {
3835 block
->discardPreAllocatedResumePoint(resume
);
3838 resume
->inherit(block
);
3842 MResumePoint::MResumePoint(MBasicBlock
* block
, jsbytecode
* pc
, ResumeMode mode
)
3843 : MNode(block
, Kind::ResumePoint
),
3845 instruction_(nullptr),
3847 block
->addResumePoint(this);
3850 bool MResumePoint::init(TempAllocator
& alloc
) {
3851 return operands_
.init(alloc
, block()->stackDepth());
3854 MResumePoint
* MResumePoint::caller() const {
3855 return block()->callerResumePoint();
3858 void MResumePoint::inherit(MBasicBlock
* block
) {
3859 // FixedList doesn't initialize its elements, so do unchecked inits.
3860 for (size_t i
= 0; i
< stackDepth(); i
++) {
3861 initOperand(i
, block
->getSlot(i
));
3865 void MResumePoint::addStore(TempAllocator
& alloc
, MDefinition
* store
,
3866 const MResumePoint
* cache
) {
3867 MOZ_ASSERT(block()->outerResumePoint() != this);
3868 MOZ_ASSERT_IF(cache
, !cache
->stores_
.empty());
3870 if (cache
&& cache
->stores_
.begin()->operand
== store
) {
3871 // If the last resume point had the same side-effect stack, then we can
3872 // reuse the current side effect without cloning it. This is a simple
3873 // way to share common context by making a spaghetti stack.
3874 if (++cache
->stores_
.begin() == stores_
.begin()) {
3875 stores_
.copy(cache
->stores_
);
3880 // Ensure that the store would not be deleted by DCE.
3881 MOZ_ASSERT(store
->isEffectful());
3883 MStoreToRecover
* top
= new (alloc
) MStoreToRecover(store
);
3888 void MResumePoint::dump(GenericPrinter
& out
) const {
3889 out
.printf("resumepoint mode=");
3892 case ResumeMode::ResumeAt
:
3894 out
.printf("ResumeAt(%u)", instruction_
->id());
3896 out
.printf("ResumeAt");
3900 out
.put(ResumeModeToString(mode()));
3904 if (MResumePoint
* c
= caller()) {
3905 out
.printf(" (caller in block%u)", c
->block()->id());
3908 for (size_t i
= 0; i
< numOperands(); i
++) {
3910 if (operands_
[i
].hasProducer()) {
3911 getOperand(i
)->printName(out
);
3913 out
.printf("(null)");
3919 void MResumePoint::dump() const {
3920 Fprinter
out(stderr
);
3926 bool MResumePoint::isObservableOperand(MUse
* u
) const {
3927 return isObservableOperand(indexOf(u
));
3930 bool MResumePoint::isObservableOperand(size_t index
) const {
3931 return block()->info().isObservableSlot(index
);
3934 bool MResumePoint::isRecoverableOperand(MUse
* u
) const {
3935 return block()->info().isRecoverableOperand(indexOf(u
));
3938 MDefinition
* MTruncateBigIntToInt64::foldsTo(TempAllocator
& alloc
) {
3939 MDefinition
* input
= getOperand(0);
3941 if (input
->isBox()) {
3942 input
= input
->getOperand(0);
3945 // If the operand converts an I64 to BigInt, drop both conversions.
3946 if (input
->isInt64ToBigInt()) {
3947 return input
->getOperand(0);
3950 // Fold this operation if the input operand is constant.
3951 if (input
->isConstant()) {
3952 return MConstant::NewInt64(
3953 alloc
, BigInt::toInt64(input
->toConstant()->toBigInt()));
3959 MDefinition
* MToInt64::foldsTo(TempAllocator
& alloc
) {
3960 MDefinition
* input
= getOperand(0);
3962 if (input
->isBox()) {
3963 input
= input
->getOperand(0);
3966 // Unwrap MInt64ToBigInt: MToInt64(MInt64ToBigInt(int64)) = int64.
3967 if (input
->isInt64ToBigInt()) {
3968 return input
->getOperand(0);
3971 // When the input is an Int64 already, just return it.
3972 if (input
->type() == MIRType::Int64
) {
3976 // Fold this operation if the input operand is constant.
3977 if (input
->isConstant()) {
3978 switch (input
->type()) {
3979 case MIRType::Boolean
:
3980 return MConstant::NewInt64(alloc
, input
->toConstant()->toBoolean());
3989 MDefinition
* MToNumberInt32::foldsTo(TempAllocator
& alloc
) {
3990 // Fold this operation if the input operand is constant.
3991 if (MConstant
* cst
= input()->maybeConstantValue()) {
3992 switch (cst
->type()) {
3994 if (conversion() == IntConversionInputKind::Any
) {
3995 return MConstant::New(alloc
, Int32Value(0));
3998 case MIRType::Boolean
:
3999 if (conversion() == IntConversionInputKind::Any
||
4000 conversion() == IntConversionInputKind::NumbersOrBoolsOnly
) {
4001 return MConstant::New(alloc
, Int32Value(cst
->toBoolean()));
4004 case MIRType::Int32
:
4005 return MConstant::New(alloc
, Int32Value(cst
->toInt32()));
4006 case MIRType::Float32
:
4007 case MIRType::Double
:
4009 // Only the value within the range of Int32 can be substituted as
4011 if (mozilla::NumberIsInt32(cst
->numberToDouble(), &ival
)) {
4012 return MConstant::New(alloc
, Int32Value(ival
));
4020 MDefinition
* input
= getOperand(0);
4021 if (input
->isBox()) {
4022 input
= input
->toBox()->input();
4025 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4026 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4027 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4028 // is folded to a MTruncateToInt32 node, which will result in this MIR:
4029 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4030 // the MUrsh node's type is int32 (since uint32 is not implemented), and
4031 // that would fold the MTruncateToInt32 node. This will make the modulo
4032 // unsigned, while is should have been signed.
4033 if (input
->type() == MIRType::Int32
&& !IsUint32Type(input
)) {
4040 MDefinition
* MBooleanToInt32::foldsTo(TempAllocator
& alloc
) {
4041 MDefinition
* input
= getOperand(0);
4042 MOZ_ASSERT(input
->type() == MIRType::Boolean
);
4044 if (input
->isConstant()) {
4045 return MConstant::New(alloc
, Int32Value(input
->toConstant()->toBoolean()));
4051 void MToNumberInt32::analyzeEdgeCasesBackward() {
4052 if (!NeedNegativeZeroCheck(this)) {
4053 setNeedsNegativeZeroCheck(false);
4057 MDefinition
* MTruncateToInt32::foldsTo(TempAllocator
& alloc
) {
4058 MDefinition
* input
= getOperand(0);
4059 if (input
->isBox()) {
4060 input
= input
->getOperand(0);
4063 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4064 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4065 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4066 // is folded to a MTruncateToInt32 node, which will result in this MIR:
4067 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4068 // the MUrsh node's type is int32 (since uint32 is not implemented), and
4069 // that would fold the MTruncateToInt32 node. This will make the modulo
4070 // unsigned, while is should have been signed.
4071 if (input
->type() == MIRType::Int32
&& !IsUint32Type(input
)) {
4075 if (input
->type() == MIRType::Double
&& input
->isConstant()) {
4076 int32_t ret
= ToInt32(input
->toConstant()->toDouble());
4077 return MConstant::New(alloc
, Int32Value(ret
));
4083 MDefinition
* MWasmTruncateToInt32::foldsTo(TempAllocator
& alloc
) {
4084 MDefinition
* input
= getOperand(0);
4085 if (input
->type() == MIRType::Int32
) {
4089 if (input
->type() == MIRType::Double
&& input
->isConstant()) {
4090 double d
= input
->toConstant()->toDouble();
4091 if (std::isnan(d
)) {
4095 if (!isUnsigned() && d
<= double(INT32_MAX
) && d
>= double(INT32_MIN
)) {
4096 return MConstant::New(alloc
, Int32Value(ToInt32(d
)));
4099 if (isUnsigned() && d
<= double(UINT32_MAX
) && d
>= 0) {
4100 return MConstant::New(alloc
, Int32Value(ToInt32(d
)));
4104 if (input
->type() == MIRType::Float32
&& input
->isConstant()) {
4105 double f
= double(input
->toConstant()->toFloat32());
4106 if (std::isnan(f
)) {
4110 if (!isUnsigned() && f
<= double(INT32_MAX
) && f
>= double(INT32_MIN
)) {
4111 return MConstant::New(alloc
, Int32Value(ToInt32(f
)));
4114 if (isUnsigned() && f
<= double(UINT32_MAX
) && f
>= 0) {
4115 return MConstant::New(alloc
, Int32Value(ToInt32(f
)));
4122 MDefinition
* MWrapInt64ToInt32::foldsTo(TempAllocator
& alloc
) {
4123 MDefinition
* input
= this->input();
4124 if (input
->isConstant()) {
4125 uint64_t c
= input
->toConstant()->toInt64();
4126 int32_t output
= bottomHalf() ? int32_t(c
) : int32_t(c
>> 32);
4127 return MConstant::New(alloc
, Int32Value(output
));
4133 MDefinition
* MExtendInt32ToInt64::foldsTo(TempAllocator
& alloc
) {
4134 MDefinition
* input
= this->input();
4135 if (input
->isConstant()) {
4136 int32_t c
= input
->toConstant()->toInt32();
4137 int64_t res
= isUnsigned() ? int64_t(uint32_t(c
)) : int64_t(c
);
4138 return MConstant::NewInt64(alloc
, res
);
4144 MDefinition
* MSignExtendInt32::foldsTo(TempAllocator
& alloc
) {
4145 MDefinition
* input
= this->input();
4146 if (input
->isConstant()) {
4147 int32_t c
= input
->toConstant()->toInt32();
4151 res
= int32_t(int8_t(c
& 0xFF));
4154 res
= int32_t(int16_t(c
& 0xFFFF));
4157 return MConstant::New(alloc
, Int32Value(res
));
4163 MDefinition
* MSignExtendInt64::foldsTo(TempAllocator
& alloc
) {
4164 MDefinition
* input
= this->input();
4165 if (input
->isConstant()) {
4166 int64_t c
= input
->toConstant()->toInt64();
4170 res
= int64_t(int8_t(c
& 0xFF));
4173 res
= int64_t(int16_t(c
& 0xFFFF));
4176 res
= int64_t(int32_t(c
& 0xFFFFFFFFU
));
4179 return MConstant::NewInt64(alloc
, res
);
4185 MDefinition
* MToDouble::foldsTo(TempAllocator
& alloc
) {
4186 MDefinition
* input
= getOperand(0);
4187 if (input
->isBox()) {
4188 input
= input
->getOperand(0);
4191 if (input
->type() == MIRType::Double
) {
4195 if (input
->isConstant() &&
4196 input
->toConstant()->isTypeRepresentableAsDouble()) {
4197 return MConstant::New(alloc
,
4198 DoubleValue(input
->toConstant()->numberToDouble()));
4204 MDefinition
* MToFloat32::foldsTo(TempAllocator
& alloc
) {
4205 MDefinition
* input
= getOperand(0);
4206 if (input
->isBox()) {
4207 input
= input
->getOperand(0);
4210 if (input
->type() == MIRType::Float32
) {
4214 // If x is a Float32, Float32(Double(x)) == x
4215 if (!mustPreserveNaN_
&& input
->isToDouble() &&
4216 input
->toToDouble()->input()->type() == MIRType::Float32
) {
4217 return input
->toToDouble()->input();
4220 if (input
->isConstant() &&
4221 input
->toConstant()->isTypeRepresentableAsDouble()) {
4222 return MConstant::NewFloat32(alloc
,
4223 float(input
->toConstant()->numberToDouble()));
4226 // Fold ToFloat32(ToDouble(int32)) to ToFloat32(int32).
4227 if (input
->isToDouble() &&
4228 input
->toToDouble()->input()->type() == MIRType::Int32
) {
4229 return MToFloat32::New(alloc
, input
->toToDouble()->input());
4235 MDefinition
* MToString::foldsTo(TempAllocator
& alloc
) {
4236 MDefinition
* in
= input();
4238 in
= in
->getOperand(0);
4241 if (in
->type() == MIRType::String
) {
4247 MDefinition
* MClampToUint8::foldsTo(TempAllocator
& alloc
) {
4248 if (MConstant
* inputConst
= input()->maybeConstantValue()) {
4249 if (inputConst
->isTypeRepresentableAsDouble()) {
4250 int32_t clamped
= ClampDoubleToUint8(inputConst
->numberToDouble());
4251 return MConstant::New(alloc
, Int32Value(clamped
));
4257 bool MCompare::tryFoldEqualOperands(bool* result
) {
4258 if (lhs() != rhs()) {
4262 // Intuitively somebody would think that if lhs === rhs,
4263 // then we can just return true. (Or false for !==)
4264 // However NaN !== NaN is true! So we spend some time trying
4265 // to eliminate this case.
4267 if (!IsStrictEqualityOp(jsop())) {
4272 compareType_
== Compare_Undefined
|| compareType_
== Compare_Null
||
4273 compareType_
== Compare_Int32
|| compareType_
== Compare_UInt32
||
4274 compareType_
== Compare_UInt64
|| compareType_
== Compare_Double
||
4275 compareType_
== Compare_Float32
|| compareType_
== Compare_UIntPtr
||
4276 compareType_
== Compare_String
|| compareType_
== Compare_Object
||
4277 compareType_
== Compare_Symbol
|| compareType_
== Compare_BigInt
||
4278 compareType_
== Compare_BigInt_Int32
||
4279 compareType_
== Compare_BigInt_Double
||
4280 compareType_
== Compare_BigInt_String
);
4282 if (isDoubleComparison() || isFloat32Comparison()) {
4283 if (!operandsAreNeverNaN()) {
4288 lhs()->setGuardRangeBailoutsUnchecked();
4290 *result
= (jsop() == JSOp::StrictEq
);
4294 static JSType
TypeOfName(JSLinearString
* str
) {
4295 static constexpr std::array types
= {
4296 JSTYPE_UNDEFINED
, JSTYPE_OBJECT
, JSTYPE_FUNCTION
, JSTYPE_STRING
,
4297 JSTYPE_NUMBER
, JSTYPE_BOOLEAN
, JSTYPE_SYMBOL
, JSTYPE_BIGINT
,
4298 #ifdef ENABLE_RECORD_TUPLE
4299 JSTYPE_RECORD
, JSTYPE_TUPLE
,
4302 static_assert(types
.size() == JSTYPE_LIMIT
);
4304 const JSAtomState
& names
= GetJitContext()->runtime
->names();
4305 for (auto type
: types
) {
4306 if (EqualStrings(str
, TypeName(type
, names
))) {
4310 return JSTYPE_LIMIT
;
4313 static mozilla::Maybe
<std::pair
<MTypeOfName
*, JSType
>> IsTypeOfCompare(
4315 if (!IsEqualityOp(ins
->jsop())) {
4316 return mozilla::Nothing();
4318 if (ins
->compareType() != MCompare::Compare_String
) {
4319 return mozilla::Nothing();
4322 auto* lhs
= ins
->lhs();
4323 auto* rhs
= ins
->rhs();
4325 MOZ_ASSERT(ins
->type() == MIRType::Boolean
);
4326 MOZ_ASSERT(lhs
->type() == MIRType::String
);
4327 MOZ_ASSERT(rhs
->type() == MIRType::String
);
4329 if (!lhs
->isTypeOfName() && !rhs
->isTypeOfName()) {
4330 return mozilla::Nothing();
4332 if (!lhs
->isConstant() && !rhs
->isConstant()) {
4333 return mozilla::Nothing();
4337 lhs
->isTypeOfName() ? lhs
->toTypeOfName() : rhs
->toTypeOfName();
4338 MOZ_ASSERT(typeOfName
->input()->isTypeOf());
4340 auto* constant
= lhs
->isConstant() ? lhs
->toConstant() : rhs
->toConstant();
4342 JSType type
= TypeOfName(&constant
->toString()->asLinear());
4343 return mozilla::Some(std::pair(typeOfName
, type
));
4346 bool MCompare::tryFoldTypeOf(bool* result
) {
4347 auto typeOfPair
= IsTypeOfCompare(this);
4351 auto [typeOfName
, type
] = *typeOfPair
;
4352 auto* typeOf
= typeOfName
->input()->toTypeOf();
4355 case JSTYPE_BOOLEAN
:
4356 if (!typeOf
->input()->mightBeType(MIRType::Boolean
)) {
4357 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4362 if (!typeOf
->input()->mightBeType(MIRType::Int32
) &&
4363 !typeOf
->input()->mightBeType(MIRType::Float32
) &&
4364 !typeOf
->input()->mightBeType(MIRType::Double
)) {
4365 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4370 if (!typeOf
->input()->mightBeType(MIRType::String
)) {
4371 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4376 if (!typeOf
->input()->mightBeType(MIRType::Symbol
)) {
4377 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4382 if (!typeOf
->input()->mightBeType(MIRType::BigInt
)) {
4383 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4388 if (!typeOf
->input()->mightBeType(MIRType::Object
) &&
4389 !typeOf
->input()->mightBeType(MIRType::Null
)) {
4390 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4394 case JSTYPE_UNDEFINED
:
4395 if (!typeOf
->input()->mightBeType(MIRType::Object
) &&
4396 !typeOf
->input()->mightBeType(MIRType::Undefined
)) {
4397 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4401 case JSTYPE_FUNCTION
:
4402 if (!typeOf
->input()->mightBeType(MIRType::Object
)) {
4403 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4408 *result
= (jsop() == JSOp::StrictNe
|| jsop() == JSOp::Ne
);
4410 #ifdef ENABLE_RECORD_TUPLE
4413 MOZ_CRASH("Records and Tuples are not supported yet.");
4420 bool MCompare::tryFold(bool* result
) {
4423 if (tryFoldEqualOperands(result
)) {
4427 if (tryFoldTypeOf(result
)) {
4431 if (compareType_
== Compare_Null
|| compareType_
== Compare_Undefined
) {
4432 // The LHS is the value we want to test against null or undefined.
4433 if (IsStrictEqualityOp(op
)) {
4434 if (lhs()->type() == inputType()) {
4435 *result
= (op
== JSOp::StrictEq
);
4438 if (!lhs()->mightBeType(inputType())) {
4439 *result
= (op
== JSOp::StrictNe
);
4443 MOZ_ASSERT(IsLooseEqualityOp(op
));
4444 if (IsNullOrUndefined(lhs()->type())) {
4445 *result
= (op
== JSOp::Eq
);
4448 if (!lhs()->mightBeType(MIRType::Null
) &&
4449 !lhs()->mightBeType(MIRType::Undefined
) &&
4450 !lhs()->mightBeType(MIRType::Object
)) {
4451 *result
= (op
== JSOp::Ne
);
4461 template <typename T
>
4462 static bool FoldComparison(JSOp op
, T left
, T right
) {
4465 return left
< right
;
4467 return left
<= right
;
4469 return left
> right
;
4471 return left
>= right
;
4472 case JSOp::StrictEq
:
4474 return left
== right
;
4475 case JSOp::StrictNe
:
4477 return left
!= right
;
4479 MOZ_CRASH("Unexpected op.");
4483 bool MCompare::evaluateConstantOperands(TempAllocator
& alloc
, bool* result
) {
4484 if (type() != MIRType::Boolean
&& type() != MIRType::Int32
) {
4488 MDefinition
* left
= getOperand(0);
4489 MDefinition
* right
= getOperand(1);
4491 if (compareType() == Compare_Double
) {
4492 // Optimize "MCompare MConstant (MToDouble SomethingInInt32Range).
4493 // In most cases the MToDouble was added, because the constant is
4495 // e.g. v < 9007199254740991, where v is an int32 is always true.
4496 if (!lhs()->isConstant() && !rhs()->isConstant()) {
4500 MDefinition
* operand
= left
->isConstant() ? right
: left
;
4501 MConstant
* constant
=
4502 left
->isConstant() ? left
->toConstant() : right
->toConstant();
4503 MOZ_ASSERT(constant
->type() == MIRType::Double
);
4504 double cte
= constant
->toDouble();
4506 if (operand
->isToDouble() &&
4507 operand
->getOperand(0)->type() == MIRType::Int32
) {
4508 bool replaced
= false;
4511 if (cte
> INT32_MAX
|| cte
< INT32_MIN
) {
4512 *result
= !((constant
== lhs()) ^ (cte
< INT32_MIN
));
4517 if (constant
== lhs()) {
4518 if (cte
> INT32_MAX
|| cte
<= INT32_MIN
) {
4519 *result
= (cte
<= INT32_MIN
);
4523 if (cte
>= INT32_MAX
|| cte
< INT32_MIN
) {
4524 *result
= (cte
>= INT32_MIN
);
4530 if (cte
> INT32_MAX
|| cte
< INT32_MIN
) {
4531 *result
= !((constant
== rhs()) ^ (cte
< INT32_MIN
));
4536 if (constant
== lhs()) {
4537 if (cte
>= INT32_MAX
|| cte
< INT32_MIN
) {
4538 *result
= (cte
>= INT32_MAX
);
4542 if (cte
> INT32_MAX
|| cte
<= INT32_MIN
) {
4543 *result
= (cte
<= INT32_MIN
);
4548 case JSOp::StrictEq
: // Fall through.
4550 if (cte
> INT32_MAX
|| cte
< INT32_MIN
) {
4555 case JSOp::StrictNe
: // Fall through.
4557 if (cte
> INT32_MAX
|| cte
< INT32_MIN
) {
4563 MOZ_CRASH("Unexpected op.");
4566 MLimitedTruncate
* limit
= MLimitedTruncate::New(
4567 alloc
, operand
->getOperand(0), TruncateKind::NoTruncate
);
4568 limit
->setGuardUnchecked();
4569 block()->insertBefore(this, limit
);
4574 // Optimize comparison against NaN.
4575 if (std::isnan(cte
)) {
4582 case JSOp::StrictEq
:
4586 case JSOp::StrictNe
:
4590 MOZ_CRASH("Unexpected op.");
4596 if (!left
->isConstant() || !right
->isConstant()) {
4600 MConstant
* lhs
= left
->toConstant();
4601 MConstant
* rhs
= right
->toConstant();
4603 // Fold away some String equality comparisons.
4604 if (lhs
->type() == MIRType::String
&& rhs
->type() == MIRType::String
) {
4605 int32_t comp
= 0; // Default to equal.
4606 if (left
!= right
) {
4607 comp
= CompareStrings(&lhs
->toString()->asLinear(),
4608 &rhs
->toString()->asLinear());
4610 *result
= FoldComparison(jsop_
, comp
, 0);
4614 if (compareType_
== Compare_UInt32
) {
4615 *result
= FoldComparison(jsop_
, uint32_t(lhs
->toInt32()),
4616 uint32_t(rhs
->toInt32()));
4620 if (compareType_
== Compare_Int64
) {
4621 *result
= FoldComparison(jsop_
, lhs
->toInt64(), rhs
->toInt64());
4625 if (compareType_
== Compare_UInt64
) {
4626 *result
= FoldComparison(jsop_
, uint64_t(lhs
->toInt64()),
4627 uint64_t(rhs
->toInt64()));
4631 if (lhs
->isTypeRepresentableAsDouble() &&
4632 rhs
->isTypeRepresentableAsDouble()) {
4634 FoldComparison(jsop_
, lhs
->numberToDouble(), rhs
->numberToDouble());
4641 MDefinition
* MCompare::tryFoldTypeOf(TempAllocator
& alloc
) {
4642 auto typeOfPair
= IsTypeOfCompare(this);
4646 auto [typeOfName
, type
] = *typeOfPair
;
4647 auto* typeOf
= typeOfName
->input()->toTypeOf();
4649 auto* input
= typeOf
->input();
4650 MOZ_ASSERT(input
->type() == MIRType::Value
||
4651 input
->type() == MIRType::Object
);
4653 // Constant typeof folding handles the other cases.
4654 MOZ_ASSERT_IF(input
->type() == MIRType::Object
, type
== JSTYPE_UNDEFINED
||
4655 type
== JSTYPE_OBJECT
||
4656 type
== JSTYPE_FUNCTION
);
4658 MOZ_ASSERT(type
!= JSTYPE_LIMIT
, "unknown typeof strings folded earlier");
4660 // If there's only a single use, assume this |typeof| is used in a simple
4661 // comparison context.
4663 // if (typeof thing === "number") { ... }
4665 // It'll be compiled into something similar to:
4667 // if (IsNumber(thing)) { ... }
4669 // This heuristic can go wrong when repeated |typeof| are used in consecutive
4672 // if (typeof thing === "number") { ... }
4673 // else if (typeof thing === "string") { ... }
4674 // ... repeated for all possible types
4676 // In that case it'd more efficient to emit MTypeOf compared to MTypeOfIs. We
4677 // don't yet handle that case, because it'd require a separate optimization
4678 // pass to correctly detect it.
4679 if (typeOfName
->hasOneUse()) {
4680 return MTypeOfIs::New(alloc
, input
, jsop(), type
);
4683 MConstant
* cst
= MConstant::New(alloc
, Int32Value(type
));
4684 block()->insertBefore(this, cst
);
4686 return MCompare::New(alloc
, typeOf
, cst
, jsop(), MCompare::Compare_Int32
);
4689 MDefinition
* MCompare::tryFoldCharCompare(TempAllocator
& alloc
) {
4690 if (compareType() != Compare_String
) {
4694 MDefinition
* left
= lhs();
4695 MOZ_ASSERT(left
->type() == MIRType::String
);
4697 MDefinition
* right
= rhs();
4698 MOZ_ASSERT(right
->type() == MIRType::String
);
4700 // |str[i]| is compiled as |MFromCharCode(MCharCodeAt(str, i))|.
4701 // Out-of-bounds access is compiled as
4702 // |FromCharCodeEmptyIfNegative(CharCodeAtOrNegative(str, i))|.
4703 auto isCharAccess
= [](MDefinition
* ins
) {
4704 if (ins
->isFromCharCode()) {
4705 return ins
->toFromCharCode()->code()->isCharCodeAt();
4707 if (ins
->isFromCharCodeEmptyIfNegative()) {
4708 auto* fromCharCode
= ins
->toFromCharCodeEmptyIfNegative();
4709 return fromCharCode
->code()->isCharCodeAtOrNegative();
4714 auto charAccessCode
= [](MDefinition
* ins
) {
4715 if (ins
->isFromCharCode()) {
4716 return ins
->toFromCharCode()->code();
4718 return ins
->toFromCharCodeEmptyIfNegative()->code();
4721 if (left
->isConstant() || right
->isConstant()) {
4722 // Try to optimize |MConstant(string) <compare> (MFromCharCode MCharCodeAt)|
4723 // as |MConstant(charcode) <compare> MCharCodeAt|.
4724 MConstant
* constant
;
4725 MDefinition
* operand
;
4726 if (left
->isConstant()) {
4727 constant
= left
->toConstant();
4730 constant
= right
->toConstant();
4734 if (constant
->toString()->length() != 1 || !isCharAccess(operand
)) {
4738 char16_t charCode
= constant
->toString()->asLinear().latin1OrTwoByteChar(0);
4739 MConstant
* charCodeConst
= MConstant::New(alloc
, Int32Value(charCode
));
4740 block()->insertBefore(this, charCodeConst
);
4742 MDefinition
* charCodeAt
= charAccessCode(operand
);
4744 if (left
->isConstant()) {
4745 left
= charCodeConst
;
4749 right
= charCodeConst
;
4751 } else if (isCharAccess(left
) && isCharAccess(right
)) {
4752 // Try to optimize |(MFromCharCode MCharCodeAt) <compare> (MFromCharCode
4753 // MCharCodeAt)| as |MCharCodeAt <compare> MCharCodeAt|.
4755 left
= charAccessCode(left
);
4756 right
= charAccessCode(right
);
4761 return MCompare::New(alloc
, left
, right
, jsop(), MCompare::Compare_Int32
);
4764 MDefinition
* MCompare::tryFoldStringCompare(TempAllocator
& alloc
) {
4765 if (compareType() != Compare_String
) {
4769 MDefinition
* left
= lhs();
4770 MOZ_ASSERT(left
->type() == MIRType::String
);
4772 MDefinition
* right
= rhs();
4773 MOZ_ASSERT(right
->type() == MIRType::String
);
4775 if (!left
->isConstant() && !right
->isConstant()) {
4779 // Try to optimize |string <compare> MConstant("")| as |MStringLength(string)
4780 // <compare> MConstant(0)|.
4782 MConstant
* constant
=
4783 left
->isConstant() ? left
->toConstant() : right
->toConstant();
4784 if (!constant
->toString()->empty()) {
4788 MDefinition
* operand
= left
->isConstant() ? right
: left
;
4790 auto* strLength
= MStringLength::New(alloc
, operand
);
4791 block()->insertBefore(this, strLength
);
4793 auto* zero
= MConstant::New(alloc
, Int32Value(0));
4794 block()->insertBefore(this, zero
);
4796 if (left
->isConstant()) {
4804 return MCompare::New(alloc
, left
, right
, jsop(), MCompare::Compare_Int32
);
4807 MDefinition
* MCompare::tryFoldStringSubstring(TempAllocator
& alloc
) {
4808 if (compareType() != Compare_String
) {
4811 if (!IsEqualityOp(jsop())) {
4816 MOZ_ASSERT(left
->type() == MIRType::String
);
4818 auto* right
= rhs();
4819 MOZ_ASSERT(right
->type() == MIRType::String
);
4821 // One operand must be a constant string.
4822 if (!left
->isConstant() && !right
->isConstant()) {
4826 // The constant string must be non-empty.
4828 left
->isConstant() ? left
->toConstant() : right
->toConstant();
4829 if (constant
->toString()->empty()) {
4833 // The other operand must be a substring operation.
4834 auto* operand
= left
->isConstant() ? right
: left
;
4835 if (!operand
->isSubstr()) {
4838 auto* substr
= operand
->toSubstr();
4840 static_assert(JSString::MAX_LENGTH
< INT32_MAX
,
4841 "string length can be casted to int32_t");
4843 if (!IsSubstrTo(substr
, int32_t(constant
->toString()->length()))) {
4847 // Now fold code like |str.substring(0, 2) == "aa"| to |str.startsWith("aa")|.
4849 auto* startsWith
= MStringStartsWith::New(alloc
, substr
->string(), constant
);
4850 if (jsop() == JSOp::Eq
|| jsop() == JSOp::StrictEq
) {
4854 // Invert for inequality.
4855 MOZ_ASSERT(jsop() == JSOp::Ne
|| jsop() == JSOp::StrictNe
);
4857 block()->insertBefore(this, startsWith
);
4858 return MNot::New(alloc
, startsWith
);
4861 MDefinition
* MCompare::tryFoldStringIndexOf(TempAllocator
& alloc
) {
4862 if (compareType() != Compare_Int32
) {
4865 if (!IsEqualityOp(jsop())) {
4870 MOZ_ASSERT(left
->type() == MIRType::Int32
);
4872 auto* right
= rhs();
4873 MOZ_ASSERT(right
->type() == MIRType::Int32
);
4875 // One operand must be a constant integer.
4876 if (!left
->isConstant() && !right
->isConstant()) {
4880 // The constant must be zero.
4882 left
->isConstant() ? left
->toConstant() : right
->toConstant();
4883 if (!constant
->isInt32(0)) {
4887 // The other operand must be an indexOf operation.
4888 auto* operand
= left
->isConstant() ? right
: left
;
4889 if (!operand
->isStringIndexOf()) {
4893 // Fold |str.indexOf(searchStr) == 0| to |str.startsWith(searchStr)|.
4895 auto* indexOf
= operand
->toStringIndexOf();
4897 MStringStartsWith::New(alloc
, indexOf
->string(), indexOf
->searchString());
4898 if (jsop() == JSOp::Eq
|| jsop() == JSOp::StrictEq
) {
4902 // Invert for inequality.
4903 MOZ_ASSERT(jsop() == JSOp::Ne
|| jsop() == JSOp::StrictNe
);
4905 block()->insertBefore(this, startsWith
);
4906 return MNot::New(alloc
, startsWith
);
4909 MDefinition
* MCompare::foldsTo(TempAllocator
& alloc
) {
4912 if (tryFold(&result
) || evaluateConstantOperands(alloc
, &result
)) {
4913 if (type() == MIRType::Int32
) {
4914 return MConstant::New(alloc
, Int32Value(result
));
4917 MOZ_ASSERT(type() == MIRType::Boolean
);
4918 return MConstant::New(alloc
, BooleanValue(result
));
4921 if (MDefinition
* folded
= tryFoldTypeOf(alloc
); folded
!= this) {
4925 if (MDefinition
* folded
= tryFoldCharCompare(alloc
); folded
!= this) {
4929 if (MDefinition
* folded
= tryFoldStringCompare(alloc
); folded
!= this) {
4933 if (MDefinition
* folded
= tryFoldStringSubstring(alloc
); folded
!= this) {
4937 if (MDefinition
* folded
= tryFoldStringIndexOf(alloc
); folded
!= this) {
4944 void MCompare::trySpecializeFloat32(TempAllocator
& alloc
) {
4945 if (AllOperandsCanProduceFloat32(this) && compareType_
== Compare_Double
) {
4946 compareType_
= Compare_Float32
;
4948 ConvertOperandsToDouble(this, alloc
);
4952 MDefinition
* MNot::foldsTo(TempAllocator
& alloc
) {
4953 // Fold if the input is constant
4954 if (MConstant
* inputConst
= input()->maybeConstantValue()) {
4956 if (inputConst
->valueToBoolean(&b
)) {
4957 if (type() == MIRType::Int32
|| type() == MIRType::Int64
) {
4958 return MConstant::New(alloc
, Int32Value(!b
));
4960 return MConstant::New(alloc
, BooleanValue(!b
));
4964 // If the operand of the Not is itself a Not, they cancel out. But we can't
4965 // always convert Not(Not(x)) to x because that may loose the conversion to
4966 // boolean. We can simplify Not(Not(Not(x))) to Not(x) though.
4967 MDefinition
* op
= getOperand(0);
4969 MDefinition
* opop
= op
->getOperand(0);
4970 if (opop
->isNot()) {
4975 // Not of an undefined or null value is always true
4976 if (input()->type() == MIRType::Undefined
||
4977 input()->type() == MIRType::Null
) {
4978 return MConstant::New(alloc
, BooleanValue(true));
4981 // Not of a symbol is always false.
4982 if (input()->type() == MIRType::Symbol
) {
4983 return MConstant::New(alloc
, BooleanValue(false));
4989 void MNot::trySpecializeFloat32(TempAllocator
& alloc
) {
4990 (void)EnsureFloatInputOrConvert(this, alloc
);
4994 void MBeta::printOpcode(GenericPrinter
& out
) const {
4995 MDefinition::printOpcode(out
);
4998 comparison_
->dump(out
);
5002 AliasSet
MCreateThis::getAliasSet() const {
5003 return AliasSet::Load(AliasSet::Any
);
5006 bool MGetArgumentsObjectArg::congruentTo(const MDefinition
* ins
) const {
5007 if (!ins
->isGetArgumentsObjectArg()) {
5010 if (ins
->toGetArgumentsObjectArg()->argno() != argno()) {
5013 return congruentIfOperandsEqual(ins
);
5016 AliasSet
MGetArgumentsObjectArg::getAliasSet() const {
5017 return AliasSet::Load(AliasSet::Any
);
5020 AliasSet
MSetArgumentsObjectArg::getAliasSet() const {
5021 return AliasSet::Store(AliasSet::Any
);
5024 MObjectState::MObjectState(MObjectState
* state
)
5025 : MVariadicInstruction(classOpcode
),
5026 numSlots_(state
->numSlots_
),
5027 numFixedSlots_(state
->numFixedSlots_
) {
5028 // This instruction is only used as a summary for bailout paths.
5029 setResultType(MIRType::Object
);
5030 setRecoveredOnBailout();
5033 MObjectState::MObjectState(JSObject
* templateObject
)
5034 : MObjectState(templateObject
->as
<NativeObject
>().shape()) {}
5036 MObjectState::MObjectState(const Shape
* shape
)
5037 : MVariadicInstruction(classOpcode
) {
5038 // This instruction is only used as a summary for bailout paths.
5039 setResultType(MIRType::Object
);
5040 setRecoveredOnBailout();
5042 numSlots_
= shape
->asShared().slotSpan();
5043 numFixedSlots_
= shape
->asShared().numFixedSlots();
5047 JSObject
* MObjectState::templateObjectOf(MDefinition
* obj
) {
5048 // MNewPlainObject uses a shape constant, not an object.
5049 MOZ_ASSERT(!obj
->isNewPlainObject());
5051 if (obj
->isNewObject()) {
5052 return obj
->toNewObject()->templateObject();
5053 } else if (obj
->isNewCallObject()) {
5054 return obj
->toNewCallObject()->templateObject();
5055 } else if (obj
->isNewIterator()) {
5056 return obj
->toNewIterator()->templateObject();
5059 MOZ_CRASH("unreachable");
5062 bool MObjectState::init(TempAllocator
& alloc
, MDefinition
* obj
) {
5063 if (!MVariadicInstruction::init(alloc
, numSlots() + 1)) {
5066 // +1, for the Object.
5067 initOperand(0, obj
);
5071 void MObjectState::initFromTemplateObject(TempAllocator
& alloc
,
5072 MDefinition
* undefinedVal
) {
5073 if (object()->isNewPlainObject()) {
5074 MOZ_ASSERT(object()->toNewPlainObject()->shape()->asShared().slotSpan() ==
5076 for (size_t i
= 0; i
< numSlots(); i
++) {
5077 initSlot(i
, undefinedVal
);
5082 JSObject
* templateObject
= templateObjectOf(object());
5084 // Initialize all the slots of the object state with the value contained in
5085 // the template object. This is needed to account values which are baked in
5086 // the template objects and not visible in IonMonkey, such as the
5087 // uninitialized-lexical magic value of call objects.
5089 MOZ_ASSERT(templateObject
->is
<NativeObject
>());
5090 NativeObject
& nativeObject
= templateObject
->as
<NativeObject
>();
5091 MOZ_ASSERT(nativeObject
.slotSpan() == numSlots());
5093 for (size_t i
= 0; i
< numSlots(); i
++) {
5094 Value val
= nativeObject
.getSlot(i
);
5095 MDefinition
* def
= undefinedVal
;
5096 if (!val
.isUndefined()) {
5097 MConstant
* ins
= MConstant::New(alloc
, val
);
5098 block()->insertBefore(this, ins
);
5105 MObjectState
* MObjectState::New(TempAllocator
& alloc
, MDefinition
* obj
) {
5107 if (obj
->isNewPlainObject()) {
5108 const Shape
* shape
= obj
->toNewPlainObject()->shape();
5109 res
= new (alloc
) MObjectState(shape
);
5111 JSObject
* templateObject
= templateObjectOf(obj
);
5112 MOZ_ASSERT(templateObject
, "Unexpected object creation.");
5113 res
= new (alloc
) MObjectState(templateObject
);
5116 if (!res
|| !res
->init(alloc
, obj
)) {
5122 MObjectState
* MObjectState::Copy(TempAllocator
& alloc
, MObjectState
* state
) {
5123 MObjectState
* res
= new (alloc
) MObjectState(state
);
5124 if (!res
|| !res
->init(alloc
, state
->object())) {
5127 for (size_t i
= 0; i
< res
->numSlots(); i
++) {
5128 res
->initSlot(i
, state
->getSlot(i
));
5133 MArrayState::MArrayState(MDefinition
* arr
) : MVariadicInstruction(classOpcode
) {
5134 // This instruction is only used as a summary for bailout paths.
5135 setResultType(MIRType::Object
);
5136 setRecoveredOnBailout();
5137 if (arr
->isNewArrayObject()) {
5138 numElements_
= arr
->toNewArrayObject()->length();
5140 numElements_
= arr
->toNewArray()->length();
5144 bool MArrayState::init(TempAllocator
& alloc
, MDefinition
* obj
,
5146 if (!MVariadicInstruction::init(alloc
, numElements() + 2)) {
5149 // +1, for the Array object.
5150 initOperand(0, obj
);
5151 // +1, for the length value of the array.
5152 initOperand(1, len
);
5156 void MArrayState::initFromTemplateObject(TempAllocator
& alloc
,
5157 MDefinition
* undefinedVal
) {
5158 for (size_t i
= 0; i
< numElements(); i
++) {
5159 initElement(i
, undefinedVal
);
5163 MArrayState
* MArrayState::New(TempAllocator
& alloc
, MDefinition
* arr
,
5164 MDefinition
* initLength
) {
5165 MArrayState
* res
= new (alloc
) MArrayState(arr
);
5166 if (!res
|| !res
->init(alloc
, arr
, initLength
)) {
5172 MArrayState
* MArrayState::Copy(TempAllocator
& alloc
, MArrayState
* state
) {
5173 MDefinition
* arr
= state
->array();
5174 MDefinition
* len
= state
->initializedLength();
5175 MArrayState
* res
= new (alloc
) MArrayState(arr
);
5176 if (!res
|| !res
->init(alloc
, arr
, len
)) {
5179 for (size_t i
= 0; i
< res
->numElements(); i
++) {
5180 res
->initElement(i
, state
->getElement(i
));
5185 MNewArray::MNewArray(uint32_t length
, MConstant
* templateConst
,
5186 gc::Heap initialHeap
, bool vmCall
)
5187 : MUnaryInstruction(classOpcode
, templateConst
),
5189 initialHeap_(initialHeap
),
5191 setResultType(MIRType::Object
);
5194 MDefinition::AliasType
MLoadFixedSlot::mightAlias(
5195 const MDefinition
* def
) const {
5196 if (def
->isStoreFixedSlot()) {
5197 const MStoreFixedSlot
* store
= def
->toStoreFixedSlot();
5198 if (store
->slot() != slot()) {
5199 return AliasType::NoAlias
;
5201 if (store
->object() != object()) {
5202 return AliasType::MayAlias
;
5204 return AliasType::MustAlias
;
5206 return AliasType::MayAlias
;
5209 MDefinition
* MLoadFixedSlot::foldsTo(TempAllocator
& alloc
) {
5210 if (MDefinition
* def
= foldsToStore(alloc
)) {
5217 MDefinition::AliasType
MLoadFixedSlotAndUnbox::mightAlias(
5218 const MDefinition
* def
) const {
5219 if (def
->isStoreFixedSlot()) {
5220 const MStoreFixedSlot
* store
= def
->toStoreFixedSlot();
5221 if (store
->slot() != slot()) {
5222 return AliasType::NoAlias
;
5224 if (store
->object() != object()) {
5225 return AliasType::MayAlias
;
5227 return AliasType::MustAlias
;
5229 return AliasType::MayAlias
;
5232 MDefinition
* MLoadFixedSlotAndUnbox::foldsTo(TempAllocator
& alloc
) {
5233 if (MDefinition
* def
= foldsToStore(alloc
)) {
5240 MDefinition
* MWasmExtendU32Index::foldsTo(TempAllocator
& alloc
) {
5241 MDefinition
* input
= this->input();
5242 if (input
->isConstant()) {
5243 return MConstant::NewInt64(
5244 alloc
, int64_t(uint32_t(input
->toConstant()->toInt32())));
5250 MDefinition
* MWasmWrapU32Index::foldsTo(TempAllocator
& alloc
) {
5251 MDefinition
* input
= this->input();
5252 if (input
->isConstant()) {
5253 return MConstant::New(
5254 alloc
, Int32Value(int32_t(uint32_t(input
->toConstant()->toInt64()))));
5260 // Some helpers for folding wasm and/or/xor on int32/64 values. Rather than
5261 // duplicating these for 32 and 64-bit values, all folding is done on 64-bit
5262 // values and masked for the 32-bit case.
5264 const uint64_t Low32Mask
= uint64_t(0xFFFFFFFFULL
);
5266 // Routines to check and disassemble values.
5268 static bool IsIntegralConstant(const MDefinition
* def
) {
5269 return def
->isConstant() &&
5270 (def
->type() == MIRType::Int32
|| def
->type() == MIRType::Int64
);
5273 static uint64_t GetIntegralConstant(const MDefinition
* def
) {
5274 if (def
->type() == MIRType::Int32
) {
5275 return uint64_t(def
->toConstant()->toInt32()) & Low32Mask
;
5277 return uint64_t(def
->toConstant()->toInt64());
5280 static bool IsIntegralConstantZero(const MDefinition
* def
) {
5281 return IsIntegralConstant(def
) && GetIntegralConstant(def
) == 0;
5284 static bool IsIntegralConstantOnes(const MDefinition
* def
) {
5285 uint64_t ones
= def
->type() == MIRType::Int32
? Low32Mask
: ~uint64_t(0);
5286 return IsIntegralConstant(def
) && GetIntegralConstant(def
) == ones
;
5289 // Routines to create values.
5290 static MDefinition
* ToIntegralConstant(TempAllocator
& alloc
, MIRType ty
,
5293 case MIRType::Int32
:
5294 return MConstant::New(alloc
,
5295 Int32Value(int32_t(uint32_t(val
& Low32Mask
))));
5296 case MIRType::Int64
:
5297 return MConstant::NewInt64(alloc
, int64_t(val
));
5303 static MDefinition
* ZeroOfType(TempAllocator
& alloc
, MIRType ty
) {
5304 return ToIntegralConstant(alloc
, ty
, 0);
5307 static MDefinition
* OnesOfType(TempAllocator
& alloc
, MIRType ty
) {
5308 return ToIntegralConstant(alloc
, ty
, ~uint64_t(0));
5311 MDefinition
* MWasmBinaryBitwise::foldsTo(TempAllocator
& alloc
) {
5312 MOZ_ASSERT(op() == Opcode::WasmBinaryBitwise
);
5313 MOZ_ASSERT(type() == MIRType::Int32
|| type() == MIRType::Int64
);
5315 MDefinition
* argL
= getOperand(0);
5316 MDefinition
* argR
= getOperand(1);
5317 MOZ_ASSERT(argL
->type() == type() && argR
->type() == type());
5319 // The args are the same (SSA name)
5321 switch (subOpcode()) {
5322 case SubOpcode::And
:
5325 case SubOpcode::Xor
:
5326 return ZeroOfType(alloc
, type());
5332 // Both args constant
5333 if (IsIntegralConstant(argL
) && IsIntegralConstant(argR
)) {
5334 uint64_t valL
= GetIntegralConstant(argL
);
5335 uint64_t valR
= GetIntegralConstant(argR
);
5336 uint64_t val
= valL
;
5337 switch (subOpcode()) {
5338 case SubOpcode::And
:
5344 case SubOpcode::Xor
:
5350 return ToIntegralConstant(alloc
, type(), val
);
5354 if (IsIntegralConstantZero(argL
)) {
5355 switch (subOpcode()) {
5356 case SubOpcode::And
:
5357 return ZeroOfType(alloc
, type());
5359 case SubOpcode::Xor
:
5366 // Right arg is zero
5367 if (IsIntegralConstantZero(argR
)) {
5368 switch (subOpcode()) {
5369 case SubOpcode::And
:
5370 return ZeroOfType(alloc
, type());
5372 case SubOpcode::Xor
:
5380 if (IsIntegralConstantOnes(argL
)) {
5381 switch (subOpcode()) {
5382 case SubOpcode::And
:
5385 return OnesOfType(alloc
, type());
5386 case SubOpcode::Xor
:
5387 return MBitNot::New(alloc
, argR
);
5393 // Right arg is ones
5394 if (IsIntegralConstantOnes(argR
)) {
5395 switch (subOpcode()) {
5396 case SubOpcode::And
:
5399 return OnesOfType(alloc
, type());
5400 case SubOpcode::Xor
:
5401 return MBitNot::New(alloc
, argL
);
5410 MDefinition
* MWasmAddOffset::foldsTo(TempAllocator
& alloc
) {
5411 MDefinition
* baseArg
= base();
5412 if (!baseArg
->isConstant()) {
5416 if (baseArg
->type() == MIRType::Int32
) {
5417 CheckedInt
<uint32_t> ptr
= baseArg
->toConstant()->toInt32();
5419 if (!ptr
.isValid()) {
5422 return MConstant::New(alloc
, Int32Value(ptr
.value()));
5425 MOZ_ASSERT(baseArg
->type() == MIRType::Int64
);
5426 CheckedInt
<uint64_t> ptr
= baseArg
->toConstant()->toInt64();
5428 if (!ptr
.isValid()) {
5431 return MConstant::NewInt64(alloc
, ptr
.value());
5434 bool MWasmAlignmentCheck::congruentTo(const MDefinition
* ins
) const {
5435 if (!ins
->isWasmAlignmentCheck()) {
5438 const MWasmAlignmentCheck
* check
= ins
->toWasmAlignmentCheck();
5439 return byteSize_
== check
->byteSize() && congruentIfOperandsEqual(check
);
5442 MDefinition::AliasType
MAsmJSLoadHeap::mightAlias(
5443 const MDefinition
* def
) const {
5444 if (def
->isAsmJSStoreHeap()) {
5445 const MAsmJSStoreHeap
* store
= def
->toAsmJSStoreHeap();
5446 if (store
->accessType() != accessType()) {
5447 return AliasType::MayAlias
;
5449 if (!base()->isConstant() || !store
->base()->isConstant()) {
5450 return AliasType::MayAlias
;
5452 const MConstant
* otherBase
= store
->base()->toConstant();
5453 if (base()->toConstant()->equals(otherBase
)) {
5454 return AliasType::MayAlias
;
5456 return AliasType::NoAlias
;
5458 return AliasType::MayAlias
;
5461 bool MAsmJSLoadHeap::congruentTo(const MDefinition
* ins
) const {
5462 if (!ins
->isAsmJSLoadHeap()) {
5465 const MAsmJSLoadHeap
* load
= ins
->toAsmJSLoadHeap();
5466 return load
->accessType() == accessType() && congruentIfOperandsEqual(load
);
5469 MDefinition::AliasType
MWasmLoadInstanceDataField::mightAlias(
5470 const MDefinition
* def
) const {
5471 if (def
->isWasmStoreInstanceDataField()) {
5472 const MWasmStoreInstanceDataField
* store
=
5473 def
->toWasmStoreInstanceDataField();
5474 return store
->instanceDataOffset() == instanceDataOffset_
5475 ? AliasType::MayAlias
5476 : AliasType::NoAlias
;
5479 return AliasType::MayAlias
;
5482 MDefinition::AliasType
MWasmLoadGlobalCell::mightAlias(
5483 const MDefinition
* def
) const {
5484 if (def
->isWasmStoreGlobalCell()) {
5485 // No globals of different type can alias. See bug 1467415 comment 3.
5486 if (type() != def
->toWasmStoreGlobalCell()->value()->type()) {
5487 return AliasType::NoAlias
;
5490 // We could do better here. We're dealing with two indirect globals.
5491 // If at at least one of them is created in this module, then they
5492 // can't alias -- in other words they can only alias if they are both
5493 // imported. That would require having a flag on globals to indicate
5494 // which are imported. See bug 1467415 comment 3, 4th rule.
5497 return AliasType::MayAlias
;
5500 HashNumber
MWasmLoadInstanceDataField::valueHash() const {
5501 // Same comment as in MWasmLoadInstanceDataField::congruentTo() applies here.
5502 HashNumber hash
= MDefinition::valueHash();
5503 hash
= addU32ToHash(hash
, instanceDataOffset_
);
5507 bool MWasmLoadInstanceDataField::congruentTo(const MDefinition
* ins
) const {
5508 if (!ins
->isWasmLoadInstanceDataField()) {
5512 const MWasmLoadInstanceDataField
* other
= ins
->toWasmLoadInstanceDataField();
5514 // We don't need to consider the isConstant_ markings here, because
5515 // equivalence of offsets implies equivalence of constness.
5516 bool sameOffsets
= instanceDataOffset_
== other
->instanceDataOffset_
;
5517 MOZ_ASSERT_IF(sameOffsets
, isConstant_
== other
->isConstant_
);
5519 // We omit checking congruence of the operands. There is only one
5520 // operand, the instance pointer, and it only ever has one value within the
5521 // domain of optimization. If that should ever change then operand
5522 // congruence checking should be reinstated.
5523 return sameOffsets
/* && congruentIfOperandsEqual(other) */;
5526 MDefinition
* MWasmLoadInstanceDataField::foldsTo(TempAllocator
& alloc
) {
5527 if (!dependency() || !dependency()->isWasmStoreInstanceDataField()) {
5531 MWasmStoreInstanceDataField
* store
=
5532 dependency()->toWasmStoreInstanceDataField();
5533 if (!store
->block()->dominates(block())) {
5537 if (store
->instanceDataOffset() != instanceDataOffset()) {
5541 if (store
->value()->type() != type()) {
5545 return store
->value();
5548 bool MWasmLoadGlobalCell::congruentTo(const MDefinition
* ins
) const {
5549 if (!ins
->isWasmLoadGlobalCell()) {
5552 const MWasmLoadGlobalCell
* other
= ins
->toWasmLoadGlobalCell();
5553 return congruentIfOperandsEqual(other
);
5556 #ifdef ENABLE_WASM_SIMD
5557 MDefinition
* MWasmTernarySimd128::foldsTo(TempAllocator
& alloc
) {
5558 if (simdOp() == wasm::SimdOp::V128Bitselect
) {
5559 if (v2()->op() == MDefinition::Opcode::WasmFloatConstant
) {
5561 if (specializeBitselectConstantMaskAsShuffle(shuffle
)) {
5562 return BuildWasmShuffleSimd128(alloc
, shuffle
, v0(), v1());
5564 } else if (canRelaxBitselect()) {
5565 return MWasmTernarySimd128::New(alloc
, v0(), v1(), v2(),
5566 wasm::SimdOp::I8x16RelaxedLaneSelect
);
5572 inline static bool MatchSpecificShift(MDefinition
* instr
,
5573 wasm::SimdOp simdShiftOp
,
5575 return instr
->isWasmShiftSimd128() &&
5576 instr
->toWasmShiftSimd128()->simdOp() == simdShiftOp
&&
5577 instr
->toWasmShiftSimd128()->rhs()->isConstant() &&
5578 instr
->toWasmShiftSimd128()->rhs()->toConstant()->toInt32() ==
5582 // Matches MIR subtree that represents PMADDUBSW instruction generated by
5583 // emscripten. The a and b parameters return subtrees that correspond
5584 // operands of the instruction, if match is found.
5585 static bool MatchPmaddubswSequence(MWasmBinarySimd128
* lhs
,
5586 MWasmBinarySimd128
* rhs
, MDefinition
** a
,
5588 MOZ_ASSERT(lhs
->simdOp() == wasm::SimdOp::I16x8Mul
&&
5589 rhs
->simdOp() == wasm::SimdOp::I16x8Mul
);
5590 // The emscripten/LLVM produced the following sequence for _mm_maddubs_epi16:
5592 // return _mm_adds_epi16(
5594 // _mm_and_si128(__a, _mm_set1_epi16(0x00FF)),
5595 // _mm_srai_epi16(_mm_slli_epi16(__b, 8), 8)),
5596 // _mm_mullo_epi16(_mm_srli_epi16(__a, 8), _mm_srai_epi16(__b, 8)));
5598 // This will roughly correspond the following MIR:
5599 // MWasmBinarySimd128[I16x8AddSatS]
5600 // |-- lhs: MWasmBinarySimd128[I16x8Mul] (lhs)
5601 // | |-- lhs: MWasmBinarySimd128WithConstant[V128And] (op0)
5603 // | | -- rhs: SimdConstant::SplatX8(0x00FF)
5604 // | -- rhs: MWasmShiftSimd128[I16x8ShrS] (op1)
5605 // | |-- lhs: MWasmShiftSimd128[I16x8Shl]
5607 // | | -- rhs: MConstant[8]
5608 // | -- rhs: MConstant[8]
5609 // -- rhs: MWasmBinarySimd128[I16x8Mul] (rhs)
5610 // |-- lhs: MWasmShiftSimd128[I16x8ShrU] (op2)
5612 // | |-- rhs: MConstant[8]
5613 // -- rhs: MWasmShiftSimd128[I16x8ShrS] (op3)
5615 // -- rhs: MConstant[8]
5617 // The I16x8AddSatS and I16x8Mul are commutative, so their operands
5618 // may be swapped. Rearrange op0, op1, op2, op3 to be in the order
5620 MDefinition
*op0
= lhs
->lhs(), *op1
= lhs
->rhs(), *op2
= rhs
->lhs(),
5622 if (op1
->isWasmBinarySimd128WithConstant()) {
5623 // Move MWasmBinarySimd128WithConstant[V128And] as first operand in lhs.
5624 std::swap(op0
, op1
);
5625 } else if (op3
->isWasmBinarySimd128WithConstant()) {
5626 // Move MWasmBinarySimd128WithConstant[V128And] as first operand in rhs.
5627 std::swap(op2
, op3
);
5629 if (op2
->isWasmBinarySimd128WithConstant()) {
5630 // The lhs and rhs are swapped.
5631 // Make MWasmBinarySimd128WithConstant[V128And] to be op0.
5632 std::swap(op0
, op2
);
5633 std::swap(op1
, op3
);
5635 if (op2
->isWasmShiftSimd128() &&
5636 op2
->toWasmShiftSimd128()->simdOp() == wasm::SimdOp::I16x8ShrS
) {
5637 // The op2 and op3 appears to be in wrong order, swap.
5638 std::swap(op2
, op3
);
5641 // Check all instructions SIMD code and constant values for assigned
5642 // names op0, op1, op2, op3 (see diagram above).
5643 const uint16_t const00FF
[8] = {255, 255, 255, 255, 255, 255, 255, 255};
5644 if (!op0
->isWasmBinarySimd128WithConstant() ||
5645 op0
->toWasmBinarySimd128WithConstant()->simdOp() !=
5646 wasm::SimdOp::V128And
||
5647 memcmp(op0
->toWasmBinarySimd128WithConstant()->rhs().bytes(), const00FF
,
5649 !MatchSpecificShift(op1
, wasm::SimdOp::I16x8ShrS
, 8) ||
5650 !MatchSpecificShift(op2
, wasm::SimdOp::I16x8ShrU
, 8) ||
5651 !MatchSpecificShift(op3
, wasm::SimdOp::I16x8ShrS
, 8) ||
5652 !MatchSpecificShift(op1
->toWasmShiftSimd128()->lhs(),
5653 wasm::SimdOp::I16x8Shl
, 8)) {
5657 // Check if the instructions arguments that are subtrees match the
5658 // a and b assignments. May depend on GVN behavior.
5659 MDefinition
* maybeA
= op0
->toWasmBinarySimd128WithConstant()->lhs();
5660 MDefinition
* maybeB
= op3
->toWasmShiftSimd128()->lhs();
5661 if (maybeA
!= op2
->toWasmShiftSimd128()->lhs() ||
5662 maybeB
!= op1
->toWasmShiftSimd128()->lhs()->toWasmShiftSimd128()->lhs()) {
5671 MDefinition
* MWasmBinarySimd128::foldsTo(TempAllocator
& alloc
) {
5672 if (simdOp() == wasm::SimdOp::I8x16Swizzle
&& rhs()->isWasmFloatConstant()) {
5673 // Specialize swizzle(v, constant) as shuffle(mask, v, zero) to trigger all
5674 // our shuffle optimizations. We don't report this rewriting as the report
5675 // will be overwritten by the subsequent shuffle analysis.
5676 int8_t shuffleMask
[16];
5677 memcpy(shuffleMask
, rhs()->toWasmFloatConstant()->toSimd128().bytes(), 16);
5678 for (int i
= 0; i
< 16; i
++) {
5679 // Out-of-bounds lanes reference the zero vector; in many cases, the zero
5680 // vector is removed by subsequent optimizations.
5681 if (shuffleMask
[i
] < 0 || shuffleMask
[i
] > 15) {
5682 shuffleMask
[i
] = 16;
5685 MWasmFloatConstant
* zero
=
5686 MWasmFloatConstant::NewSimd128(alloc
, SimdConstant::SplatX4(0));
5690 block()->insertBefore(this, zero
);
5691 return BuildWasmShuffleSimd128(alloc
, shuffleMask
, lhs(), zero
);
5694 // Specialize var OP const / const OP var when possible.
5696 // As the LIR layer can't directly handle v128 constants as part of its normal
5697 // machinery we specialize some nodes here if they have single-use v128
5698 // constant arguments. The purpose is to generate code that inlines the
5699 // constant in the instruction stream, using either a rip-relative load+op or
5700 // quickly-synthesized constant in a scratch on x64. There is a general
5701 // assumption here that that is better than generating the constant into an
5702 // allocatable register, since that register value could not be reused. (This
5703 // ignores the possibility that the constant load could be hoisted).
5705 if (lhs()->isWasmFloatConstant() != rhs()->isWasmFloatConstant() &&
5706 specializeForConstantRhs()) {
5707 if (isCommutative() && lhs()->isWasmFloatConstant() && lhs()->hasOneUse()) {
5708 return MWasmBinarySimd128WithConstant::New(
5709 alloc
, rhs(), lhs()->toWasmFloatConstant()->toSimd128(), simdOp());
5712 if (rhs()->isWasmFloatConstant() && rhs()->hasOneUse()) {
5713 return MWasmBinarySimd128WithConstant::New(
5714 alloc
, lhs(), rhs()->toWasmFloatConstant()->toSimd128(), simdOp());
5718 // Check special encoding for PMADDUBSW.
5719 if (canPmaddubsw() && simdOp() == wasm::SimdOp::I16x8AddSatS
&&
5720 lhs()->isWasmBinarySimd128() && rhs()->isWasmBinarySimd128() &&
5721 lhs()->toWasmBinarySimd128()->simdOp() == wasm::SimdOp::I16x8Mul
&&
5722 rhs()->toWasmBinarySimd128()->simdOp() == wasm::SimdOp::I16x8Mul
) {
5724 if (MatchPmaddubswSequence(lhs()->toWasmBinarySimd128(),
5725 rhs()->toWasmBinarySimd128(), &a
, &b
)) {
5726 return MWasmBinarySimd128::New(alloc
, a
, b
, /* commutative = */ false,
5727 wasm::SimdOp::MozPMADDUBSW
);
5734 MDefinition
* MWasmScalarToSimd128::foldsTo(TempAllocator
& alloc
) {
5736 auto logging
= mozilla::MakeScopeExit([&] {
5737 js::wasm::ReportSimdAnalysis("scalar-to-simd128 -> constant folded");
5740 if (input()->isConstant()) {
5741 MConstant
* c
= input()->toConstant();
5743 case wasm::SimdOp::I8x16Splat
:
5744 return MWasmFloatConstant::NewSimd128(
5745 alloc
, SimdConstant::SplatX16(c
->toInt32()));
5746 case wasm::SimdOp::I16x8Splat
:
5747 return MWasmFloatConstant::NewSimd128(
5748 alloc
, SimdConstant::SplatX8(c
->toInt32()));
5749 case wasm::SimdOp::I32x4Splat
:
5750 return MWasmFloatConstant::NewSimd128(
5751 alloc
, SimdConstant::SplatX4(c
->toInt32()));
5752 case wasm::SimdOp::I64x2Splat
:
5753 return MWasmFloatConstant::NewSimd128(
5754 alloc
, SimdConstant::SplatX2(c
->toInt64()));
5762 if (input()->isWasmFloatConstant()) {
5763 MWasmFloatConstant
* c
= input()->toWasmFloatConstant();
5765 case wasm::SimdOp::F32x4Splat
:
5766 return MWasmFloatConstant::NewSimd128(
5767 alloc
, SimdConstant::SplatX4(c
->toFloat32()));
5768 case wasm::SimdOp::F64x2Splat
:
5769 return MWasmFloatConstant::NewSimd128(
5770 alloc
, SimdConstant::SplatX2(c
->toDouble()));
5784 template <typename T
>
5785 static bool AllTrue(const T
& v
) {
5786 constexpr size_t count
= sizeof(T
) / sizeof(*v
);
5787 static_assert(count
== 16 || count
== 8 || count
== 4 || count
== 2);
5789 for (unsigned i
= 0; i
< count
; i
++) {
5790 result
= result
&& v
[i
] != 0;
5795 template <typename T
>
5796 static int32_t Bitmask(const T
& v
) {
5797 constexpr size_t count
= sizeof(T
) / sizeof(*v
);
5798 constexpr size_t shift
= 8 * sizeof(*v
) - 1;
5799 static_assert(shift
== 7 || shift
== 15 || shift
== 31 || shift
== 63);
5801 for (unsigned i
= 0; i
< count
; i
++) {
5802 result
= result
| int32_t(((v
[i
] >> shift
) & 1) << i
);
5807 MDefinition
* MWasmReduceSimd128::foldsTo(TempAllocator
& alloc
) {
5809 auto logging
= mozilla::MakeScopeExit([&] {
5810 js::wasm::ReportSimdAnalysis("simd128-to-scalar -> constant folded");
5813 if (input()->isWasmFloatConstant()) {
5814 SimdConstant c
= input()->toWasmFloatConstant()->toSimd128();
5815 int32_t i32Result
= 0;
5817 case wasm::SimdOp::V128AnyTrue
:
5818 i32Result
= !c
.isZeroBits();
5820 case wasm::SimdOp::I8x16AllTrue
:
5821 i32Result
= AllTrue(
5822 SimdConstant::CreateSimd128((int8_t*)c
.bytes()).asInt8x16());
5824 case wasm::SimdOp::I8x16Bitmask
:
5825 i32Result
= Bitmask(
5826 SimdConstant::CreateSimd128((int8_t*)c
.bytes()).asInt8x16());
5828 case wasm::SimdOp::I16x8AllTrue
:
5829 i32Result
= AllTrue(
5830 SimdConstant::CreateSimd128((int16_t*)c
.bytes()).asInt16x8());
5832 case wasm::SimdOp::I16x8Bitmask
:
5833 i32Result
= Bitmask(
5834 SimdConstant::CreateSimd128((int16_t*)c
.bytes()).asInt16x8());
5836 case wasm::SimdOp::I32x4AllTrue
:
5837 i32Result
= AllTrue(
5838 SimdConstant::CreateSimd128((int32_t*)c
.bytes()).asInt32x4());
5840 case wasm::SimdOp::I32x4Bitmask
:
5841 i32Result
= Bitmask(
5842 SimdConstant::CreateSimd128((int32_t*)c
.bytes()).asInt32x4());
5844 case wasm::SimdOp::I64x2AllTrue
:
5845 i32Result
= AllTrue(
5846 SimdConstant::CreateSimd128((int64_t*)c
.bytes()).asInt64x2());
5848 case wasm::SimdOp::I64x2Bitmask
:
5849 i32Result
= Bitmask(
5850 SimdConstant::CreateSimd128((int64_t*)c
.bytes()).asInt64x2());
5852 case wasm::SimdOp::I8x16ExtractLaneS
:
5854 SimdConstant::CreateSimd128((int8_t*)c
.bytes()).asInt8x16()[imm()];
5856 case wasm::SimdOp::I8x16ExtractLaneU
:
5857 i32Result
= int32_t(SimdConstant::CreateSimd128((int8_t*)c
.bytes())
5858 .asInt8x16()[imm()]) &
5861 case wasm::SimdOp::I16x8ExtractLaneS
:
5863 SimdConstant::CreateSimd128((int16_t*)c
.bytes()).asInt16x8()[imm()];
5865 case wasm::SimdOp::I16x8ExtractLaneU
:
5866 i32Result
= int32_t(SimdConstant::CreateSimd128((int16_t*)c
.bytes())
5867 .asInt16x8()[imm()]) &
5870 case wasm::SimdOp::I32x4ExtractLane
:
5872 SimdConstant::CreateSimd128((int32_t*)c
.bytes()).asInt32x4()[imm()];
5874 case wasm::SimdOp::I64x2ExtractLane
:
5875 return MConstant::NewInt64(
5876 alloc
, SimdConstant::CreateSimd128((int64_t*)c
.bytes())
5877 .asInt64x2()[imm()]);
5878 case wasm::SimdOp::F32x4ExtractLane
:
5879 return MWasmFloatConstant::NewFloat32(
5880 alloc
, SimdConstant::CreateSimd128((float*)c
.bytes())
5881 .asFloat32x4()[imm()]);
5882 case wasm::SimdOp::F64x2ExtractLane
:
5883 return MWasmFloatConstant::NewDouble(
5884 alloc
, SimdConstant::CreateSimd128((double*)c
.bytes())
5885 .asFloat64x2()[imm()]);
5892 return MConstant::New(alloc
, Int32Value(i32Result
), MIRType::Int32
);
5899 #endif // ENABLE_WASM_SIMD
5901 MDefinition::AliasType
MLoadDynamicSlot::mightAlias(
5902 const MDefinition
* def
) const {
5903 if (def
->isStoreDynamicSlot()) {
5904 const MStoreDynamicSlot
* store
= def
->toStoreDynamicSlot();
5905 if (store
->slot() != slot()) {
5906 return AliasType::NoAlias
;
5909 if (store
->slots() != slots()) {
5910 return AliasType::MayAlias
;
5913 return AliasType::MustAlias
;
5915 return AliasType::MayAlias
;
5918 HashNumber
MLoadDynamicSlot::valueHash() const {
5919 HashNumber hash
= MDefinition::valueHash();
5920 hash
= addU32ToHash(hash
, slot_
);
5924 MDefinition
* MLoadDynamicSlot::foldsTo(TempAllocator
& alloc
) {
5925 if (MDefinition
* def
= foldsToStore(alloc
)) {
5933 void MLoadDynamicSlot::printOpcode(GenericPrinter
& out
) const {
5934 MDefinition::printOpcode(out
);
5935 out
.printf(" (slot %u)", slot());
5938 void MLoadDynamicSlotAndUnbox::printOpcode(GenericPrinter
& out
) const {
5939 MDefinition::printOpcode(out
);
5940 out
.printf(" (slot %zu)", slot());
5943 void MStoreDynamicSlot::printOpcode(GenericPrinter
& out
) const {
5944 MDefinition::printOpcode(out
);
5945 out
.printf(" (slot %u)", slot());
5948 void MLoadFixedSlot::printOpcode(GenericPrinter
& out
) const {
5949 MDefinition::printOpcode(out
);
5950 out
.printf(" (slot %zu)", slot());
5953 void MLoadFixedSlotAndUnbox::printOpcode(GenericPrinter
& out
) const {
5954 MDefinition::printOpcode(out
);
5955 out
.printf(" (slot %zu)", slot());
5958 void MStoreFixedSlot::printOpcode(GenericPrinter
& out
) const {
5959 MDefinition::printOpcode(out
);
5960 out
.printf(" (slot %zu)", slot());
5964 MDefinition
* MGuardFunctionScript::foldsTo(TempAllocator
& alloc
) {
5965 MDefinition
* in
= input();
5966 if (in
->isLambda() &&
5967 in
->toLambda()->templateFunction()->baseScript() == expected()) {
5973 MDefinition
* MFunctionEnvironment::foldsTo(TempAllocator
& alloc
) {
5974 if (input()->isLambda()) {
5975 return input()->toLambda()->environmentChain();
5977 if (input()->isFunctionWithProto()) {
5978 return input()->toFunctionWithProto()->environmentChain();
5983 static bool AddIsANonZeroAdditionOf(MAdd
* add
, MDefinition
* ins
) {
5984 if (add
->lhs() != ins
&& add
->rhs() != ins
) {
5987 MDefinition
* other
= (add
->lhs() == ins
) ? add
->rhs() : add
->lhs();
5988 if (!IsNumberType(other
->type())) {
5991 if (!other
->isConstant()) {
5994 if (other
->toConstant()->numberToDouble() == 0) {
6000 // Skip over instructions that usually appear between the actual index
6001 // value being used and the MLoadElement.
6002 // They don't modify the index value in a meaningful way.
6003 static MDefinition
* SkipUninterestingInstructions(MDefinition
* ins
) {
6004 // Drop the MToNumberInt32 added by the TypePolicy for double and float
6006 if (ins
->isToNumberInt32()) {
6007 return SkipUninterestingInstructions(ins
->toToNumberInt32()->input());
6010 // Ignore the bounds check, which don't modify the index.
6011 if (ins
->isBoundsCheck()) {
6012 return SkipUninterestingInstructions(ins
->toBoundsCheck()->index());
6015 // Masking the index for Spectre-mitigation is not observable.
6016 if (ins
->isSpectreMaskIndex()) {
6017 return SkipUninterestingInstructions(ins
->toSpectreMaskIndex()->index());
6023 static bool DefinitelyDifferentValue(MDefinition
* ins1
, MDefinition
* ins2
) {
6024 ins1
= SkipUninterestingInstructions(ins1
);
6025 ins2
= SkipUninterestingInstructions(ins2
);
6031 // For constants check they are not equal.
6032 if (ins1
->isConstant() && ins2
->isConstant()) {
6033 MConstant
* cst1
= ins1
->toConstant();
6034 MConstant
* cst2
= ins2
->toConstant();
6036 if (!cst1
->isTypeRepresentableAsDouble() ||
6037 !cst2
->isTypeRepresentableAsDouble()) {
6041 // Be conservative and only allow values that fit into int32.
6043 if (!mozilla::NumberIsInt32(cst1
->numberToDouble(), &n1
) ||
6044 !mozilla::NumberIsInt32(cst2
->numberToDouble(), &n2
)) {
6051 // Check if "ins1 = ins2 + cte", which would make both instructions
6052 // have different values.
6053 if (ins1
->isAdd()) {
6054 if (AddIsANonZeroAdditionOf(ins1
->toAdd(), ins2
)) {
6058 if (ins2
->isAdd()) {
6059 if (AddIsANonZeroAdditionOf(ins2
->toAdd(), ins1
)) {
6067 MDefinition::AliasType
MLoadElement::mightAlias(const MDefinition
* def
) const {
6068 if (def
->isStoreElement()) {
6069 const MStoreElement
* store
= def
->toStoreElement();
6070 if (store
->index() != index()) {
6071 if (DefinitelyDifferentValue(store
->index(), index())) {
6072 return AliasType::NoAlias
;
6074 return AliasType::MayAlias
;
6077 if (store
->elements() != elements()) {
6078 return AliasType::MayAlias
;
6081 return AliasType::MustAlias
;
6083 return AliasType::MayAlias
;
6086 MDefinition
* MLoadElement::foldsTo(TempAllocator
& alloc
) {
6087 if (MDefinition
* def
= foldsToStore(alloc
)) {
6094 MDefinition
* MWasmUnsignedToDouble::foldsTo(TempAllocator
& alloc
) {
6095 if (input()->isConstant()) {
6096 return MConstant::New(
6097 alloc
, DoubleValue(uint32_t(input()->toConstant()->toInt32())));
6103 MDefinition
* MWasmUnsignedToFloat32::foldsTo(TempAllocator
& alloc
) {
6104 if (input()->isConstant()) {
6105 double dval
= double(uint32_t(input()->toConstant()->toInt32()));
6106 if (IsFloat32Representable(dval
)) {
6107 return MConstant::NewFloat32(alloc
, float(dval
));
6114 MWasmCallCatchable
* MWasmCallCatchable::New(TempAllocator
& alloc
,
6115 const wasm::CallSiteDesc
& desc
,
6116 const wasm::CalleeDesc
& callee
,
6118 uint32_t stackArgAreaSizeUnaligned
,
6119 const MWasmCallTryDesc
& tryDesc
,
6120 MDefinition
* tableIndexOrRef
) {
6121 MOZ_ASSERT(tryDesc
.inTry
);
6123 MWasmCallCatchable
* call
= new (alloc
) MWasmCallCatchable(
6124 desc
, callee
, stackArgAreaSizeUnaligned
, tryDesc
.tryNoteIndex
);
6126 call
->setSuccessor(FallthroughBranchIndex
, tryDesc
.fallthroughBlock
);
6127 call
->setSuccessor(PrePadBranchIndex
, tryDesc
.prePadBlock
);
6129 MOZ_ASSERT_IF(callee
.isTable() || callee
.isFuncRef(), tableIndexOrRef
);
6130 if (!call
->initWithArgs(alloc
, call
, args
, tableIndexOrRef
)) {
6137 MWasmCallCatchable
* MWasmCallCatchable::NewBuiltinInstanceMethodCall(
6138 TempAllocator
& alloc
, const wasm::CallSiteDesc
& desc
,
6139 const wasm::SymbolicAddress builtin
, wasm::FailureMode failureMode
,
6140 const ABIArg
& instanceArg
, const Args
& args
,
6141 uint32_t stackArgAreaSizeUnaligned
, const MWasmCallTryDesc
& tryDesc
) {
6142 auto callee
= wasm::CalleeDesc::builtinInstanceMethod(builtin
);
6143 MWasmCallCatchable
* call
= MWasmCallCatchable::New(
6144 alloc
, desc
, callee
, args
, stackArgAreaSizeUnaligned
, tryDesc
, nullptr);
6149 MOZ_ASSERT(instanceArg
!= ABIArg());
6150 call
->instanceArg_
= instanceArg
;
6151 call
->builtinMethodFailureMode_
= failureMode
;
6155 MWasmCallUncatchable
* MWasmCallUncatchable::New(
6156 TempAllocator
& alloc
, const wasm::CallSiteDesc
& desc
,
6157 const wasm::CalleeDesc
& callee
, const Args
& args
,
6158 uint32_t stackArgAreaSizeUnaligned
, MDefinition
* tableIndexOrRef
) {
6159 MWasmCallUncatchable
* call
=
6160 new (alloc
) MWasmCallUncatchable(desc
, callee
, stackArgAreaSizeUnaligned
);
6162 MOZ_ASSERT_IF(callee
.isTable() || callee
.isFuncRef(), tableIndexOrRef
);
6163 if (!call
->initWithArgs(alloc
, call
, args
, tableIndexOrRef
)) {
6170 MWasmCallUncatchable
* MWasmCallUncatchable::NewBuiltinInstanceMethodCall(
6171 TempAllocator
& alloc
, const wasm::CallSiteDesc
& desc
,
6172 const wasm::SymbolicAddress builtin
, wasm::FailureMode failureMode
,
6173 const ABIArg
& instanceArg
, const Args
& args
,
6174 uint32_t stackArgAreaSizeUnaligned
) {
6175 auto callee
= wasm::CalleeDesc::builtinInstanceMethod(builtin
);
6176 MWasmCallUncatchable
* call
= MWasmCallUncatchable::New(
6177 alloc
, desc
, callee
, args
, stackArgAreaSizeUnaligned
, nullptr);
6182 MOZ_ASSERT(instanceArg
!= ABIArg());
6183 call
->instanceArg_
= instanceArg
;
6184 call
->builtinMethodFailureMode_
= failureMode
;
6188 MWasmReturnCall
* MWasmReturnCall::New(TempAllocator
& alloc
,
6189 const wasm::CallSiteDesc
& desc
,
6190 const wasm::CalleeDesc
& callee
,
6192 uint32_t stackArgAreaSizeUnaligned
,
6193 MDefinition
* tableIndexOrRef
) {
6194 MWasmReturnCall
* call
=
6195 new (alloc
) MWasmReturnCall(desc
, callee
, stackArgAreaSizeUnaligned
);
6197 MOZ_ASSERT_IF(callee
.isTable() || callee
.isFuncRef(), tableIndexOrRef
);
6198 if (!call
->initWithArgs(alloc
, call
, args
, tableIndexOrRef
)) {
6205 void MSqrt::trySpecializeFloat32(TempAllocator
& alloc
) {
6206 if (EnsureFloatConsumersAndInputOrConvert(this, alloc
)) {
6207 setResultType(MIRType::Float32
);
6208 specialization_
= MIRType::Float32
;
6212 MDefinition
* MClz::foldsTo(TempAllocator
& alloc
) {
6213 if (num()->isConstant()) {
6214 MConstant
* c
= num()->toConstant();
6215 if (type() == MIRType::Int32
) {
6216 int32_t n
= c
->toInt32();
6218 return MConstant::New(alloc
, Int32Value(32));
6220 return MConstant::New(alloc
,
6221 Int32Value(mozilla::CountLeadingZeroes32(n
)));
6223 int64_t n
= c
->toInt64();
6225 return MConstant::NewInt64(alloc
, int64_t(64));
6227 return MConstant::NewInt64(alloc
,
6228 int64_t(mozilla::CountLeadingZeroes64(n
)));
6234 MDefinition
* MCtz::foldsTo(TempAllocator
& alloc
) {
6235 if (num()->isConstant()) {
6236 MConstant
* c
= num()->toConstant();
6237 if (type() == MIRType::Int32
) {
6238 int32_t n
= num()->toConstant()->toInt32();
6240 return MConstant::New(alloc
, Int32Value(32));
6242 return MConstant::New(alloc
,
6243 Int32Value(mozilla::CountTrailingZeroes32(n
)));
6245 int64_t n
= c
->toInt64();
6247 return MConstant::NewInt64(alloc
, int64_t(64));
6249 return MConstant::NewInt64(alloc
,
6250 int64_t(mozilla::CountTrailingZeroes64(n
)));
6256 MDefinition
* MPopcnt::foldsTo(TempAllocator
& alloc
) {
6257 if (num()->isConstant()) {
6258 MConstant
* c
= num()->toConstant();
6259 if (type() == MIRType::Int32
) {
6260 int32_t n
= num()->toConstant()->toInt32();
6261 return MConstant::New(alloc
, Int32Value(mozilla::CountPopulation32(n
)));
6263 int64_t n
= c
->toInt64();
6264 return MConstant::NewInt64(alloc
, int64_t(mozilla::CountPopulation64(n
)));
6270 MDefinition
* MBoundsCheck::foldsTo(TempAllocator
& alloc
) {
6271 if (type() == MIRType::Int32
&& index()->isConstant() &&
6272 length()->isConstant()) {
6273 uint32_t len
= length()->toConstant()->toInt32();
6274 uint32_t idx
= index()->toConstant()->toInt32();
6275 if (idx
+ uint32_t(minimum()) < len
&& idx
+ uint32_t(maximum()) < len
) {
6283 MDefinition
* MTableSwitch::foldsTo(TempAllocator
& alloc
) {
6284 MDefinition
* op
= getOperand(0);
6286 // If we only have one successor, convert to a plain goto to the only
6287 // successor. TableSwitch indices are numeric; other types will always go to
6288 // the only successor.
6289 if (numSuccessors() == 1 ||
6290 (op
->type() != MIRType::Value
&& !IsNumberType(op
->type()))) {
6291 return MGoto::New(alloc
, getDefault());
6294 if (MConstant
* opConst
= op
->maybeConstantValue()) {
6295 if (op
->type() == MIRType::Int32
) {
6296 int32_t i
= opConst
->toInt32() - low_
;
6297 MBasicBlock
* target
;
6298 if (size_t(i
) < numCases()) {
6299 target
= getCase(size_t(i
));
6301 target
= getDefault();
6304 return MGoto::New(alloc
, target
);
6311 MDefinition
* MArrayJoin::foldsTo(TempAllocator
& alloc
) {
6312 MDefinition
* arr
= array();
6314 if (!arr
->isStringSplit()) {
6318 setRecoveredOnBailout();
6319 if (arr
->hasLiveDefUses()) {
6320 setNotRecoveredOnBailout();
6324 // The MStringSplit won't generate any code.
6325 arr
->setRecoveredOnBailout();
6327 // We're replacing foo.split(bar).join(baz) by
6328 // foo.replace(bar, baz). MStringSplit could be recovered by
6329 // a bailout. As we are removing its last use, and its result
6330 // could be captured by a resume point, this MStringSplit will
6331 // be executed on the bailout path.
6332 MDefinition
* string
= arr
->toStringSplit()->string();
6333 MDefinition
* pattern
= arr
->toStringSplit()->separator();
6334 MDefinition
* replacement
= sep();
6336 MStringReplace
* substr
=
6337 MStringReplace::New(alloc
, string
, pattern
, replacement
);
6338 substr
->setFlatReplacement();
6342 MDefinition
* MGetFirstDollarIndex::foldsTo(TempAllocator
& alloc
) {
6343 MDefinition
* strArg
= str();
6344 if (!strArg
->isConstant()) {
6348 JSLinearString
* str
= &strArg
->toConstant()->toString()->asLinear();
6349 int32_t index
= GetFirstDollarIndexRawFlat(str
);
6350 return MConstant::New(alloc
, Int32Value(index
));
6353 AliasSet
MThrowRuntimeLexicalError::getAliasSet() const {
6354 return AliasSet::Store(AliasSet::ExceptionState
);
6357 AliasSet
MSlots::getAliasSet() const {
6358 return AliasSet::Load(AliasSet::ObjectFields
);
6361 MDefinition::AliasType
MSlots::mightAlias(const MDefinition
* store
) const {
6362 // ArrayPush only modifies object elements, but not object slots.
6363 if (store
->isArrayPush()) {
6364 return AliasType::NoAlias
;
6366 return MInstruction::mightAlias(store
);
6369 AliasSet
MElements::getAliasSet() const {
6370 return AliasSet::Load(AliasSet::ObjectFields
);
6373 AliasSet
MInitializedLength::getAliasSet() const {
6374 return AliasSet::Load(AliasSet::ObjectFields
);
6377 AliasSet
MSetInitializedLength::getAliasSet() const {
6378 return AliasSet::Store(AliasSet::ObjectFields
);
6381 AliasSet
MObjectKeysLength::getAliasSet() const {
6382 return AliasSet::Load(AliasSet::ObjectFields
);
6385 AliasSet
MArrayLength::getAliasSet() const {
6386 return AliasSet::Load(AliasSet::ObjectFields
);
6389 AliasSet
MSetArrayLength::getAliasSet() const {
6390 return AliasSet::Store(AliasSet::ObjectFields
);
6393 AliasSet
MFunctionLength::getAliasSet() const {
6394 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6395 AliasSet::DynamicSlot
);
6398 AliasSet
MFunctionName::getAliasSet() const {
6399 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6400 AliasSet::DynamicSlot
);
6403 AliasSet
MArrayBufferByteLength::getAliasSet() const {
6404 return AliasSet::Load(AliasSet::FixedSlot
);
6407 AliasSet
MArrayBufferViewLength::getAliasSet() const {
6408 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset
);
6411 AliasSet
MArrayBufferViewByteOffset::getAliasSet() const {
6412 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset
);
6415 AliasSet
MArrayBufferViewElements::getAliasSet() const {
6416 return AliasSet::Load(AliasSet::ObjectFields
);
6419 AliasSet
MGuardHasAttachedArrayBuffer::getAliasSet() const {
6420 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
);
6423 AliasSet
MResizableTypedArrayByteOffsetMaybeOutOfBounds::getAliasSet() const {
6424 // Loads the byteOffset and additionally checks for detached buffers, so the
6425 // alias set also has to include |ObjectFields| and |FixedSlot|.
6426 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset
|
6427 AliasSet::ObjectFields
| AliasSet::FixedSlot
);
6430 AliasSet
MResizableTypedArrayLength::getAliasSet() const {
6431 // Loads the length and byteOffset slots, the shared-elements flag, the
6432 // auto-length fixed slot, and the shared raw-buffer length.
6433 auto flags
= AliasSet::ArrayBufferViewLengthOrOffset
|
6434 AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6435 AliasSet::SharedArrayRawBufferLength
;
6437 // When a barrier is needed make the instruction effectful by giving it a
6438 // "store" effect. Also prevent reordering LoadUnboxedScalar before this
6439 // instruction by including |UnboxedElement| in the alias set.
6440 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required
) {
6441 return AliasSet::Store(flags
| AliasSet::UnboxedElement
);
6443 return AliasSet::Load(flags
);
6446 bool MResizableTypedArrayLength::congruentTo(const MDefinition
* ins
) const {
6447 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required
) {
6450 return congruentIfOperandsEqual(ins
);
6453 AliasSet
MResizableDataViewByteLength::getAliasSet() const {
6454 // Loads the length and byteOffset slots, the shared-elements flag, the
6455 // auto-length fixed slot, and the shared raw-buffer length.
6456 auto flags
= AliasSet::ArrayBufferViewLengthOrOffset
|
6457 AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6458 AliasSet::SharedArrayRawBufferLength
;
6460 // When a barrier is needed make the instruction effectful by giving it a
6461 // "store" effect. Also prevent reordering LoadUnboxedScalar before this
6462 // instruction by including |UnboxedElement| in the alias set.
6463 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required
) {
6464 return AliasSet::Store(flags
| AliasSet::UnboxedElement
);
6466 return AliasSet::Load(flags
);
6469 bool MResizableDataViewByteLength::congruentTo(const MDefinition
* ins
) const {
6470 if (requiresMemoryBarrier() == MemoryBarrierRequirement::Required
) {
6473 return congruentIfOperandsEqual(ins
);
6476 AliasSet
MGrowableSharedArrayBufferByteLength::getAliasSet() const {
6477 // Requires a barrier, so make the instruction effectful by giving it a
6478 // "store" effect. Also prevent reordering LoadUnboxedScalar before this
6479 // instruction by including |UnboxedElement| in the alias set.
6480 return AliasSet::Store(AliasSet::FixedSlot
|
6481 AliasSet::SharedArrayRawBufferLength
|
6482 AliasSet::UnboxedElement
);
6485 AliasSet
MGuardResizableArrayBufferViewInBounds::getAliasSet() const {
6486 // Additionally reads the |initialLength| and |initialByteOffset| slots, but
6487 // since these can't change after construction, we don't need to track them.
6488 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset
);
6491 AliasSet
MGuardResizableArrayBufferViewInBoundsOrDetached::getAliasSet() const {
6492 // Loads the byteOffset and additionally checks for detached buffers, so the
6493 // alias set also has to include |ObjectFields| and |FixedSlot|.
6494 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset
|
6495 AliasSet::ObjectFields
| AliasSet::FixedSlot
);
6498 AliasSet
MArrayPush::getAliasSet() const {
6499 return AliasSet::Store(AliasSet::ObjectFields
| AliasSet::Element
);
6502 MDefinition
* MGuardNumberToIntPtrIndex::foldsTo(TempAllocator
& alloc
) {
6503 MDefinition
* input
= this->input();
6505 if (input
->isToDouble() && input
->getOperand(0)->type() == MIRType::Int32
) {
6506 return MInt32ToIntPtr::New(alloc
, input
->getOperand(0));
6509 if (!input
->isConstant()) {
6513 // Fold constant double representable as intptr to intptr.
6515 if (!mozilla::NumberEqualsInt64(input
->toConstant()->toDouble(), &ival
)) {
6516 // If not representable as an int64, this access is equal to an OOB access.
6517 // So replace it with a known int64/intptr value which also produces an OOB
6518 // access. If we don't support OOB accesses we have to bail out.
6519 if (!supportOOB()) {
6525 if (ival
< INTPTR_MIN
|| ival
> INTPTR_MAX
) {
6529 return MConstant::NewIntPtr(alloc
, intptr_t(ival
));
6532 MDefinition
* MIsObject::foldsTo(TempAllocator
& alloc
) {
6533 if (!object()->isBox()) {
6537 MDefinition
* unboxed
= object()->getOperand(0);
6538 if (unboxed
->type() == MIRType::Object
) {
6539 return MConstant::New(alloc
, BooleanValue(true));
6545 MDefinition
* MIsNullOrUndefined::foldsTo(TempAllocator
& alloc
) {
6546 MDefinition
* input
= value();
6547 if (input
->isBox()) {
6548 input
= input
->toBox()->input();
6551 if (input
->definitelyType({MIRType::Null
, MIRType::Undefined
})) {
6552 return MConstant::New(alloc
, BooleanValue(true));
6555 if (!input
->mightBeType(MIRType::Null
) &&
6556 !input
->mightBeType(MIRType::Undefined
)) {
6557 return MConstant::New(alloc
, BooleanValue(false));
6563 AliasSet
MHomeObjectSuperBase::getAliasSet() const {
6564 return AliasSet::Load(AliasSet::ObjectFields
);
6567 MDefinition
* MGuardValue::foldsTo(TempAllocator
& alloc
) {
6568 if (MConstant
* cst
= value()->maybeConstantValue()) {
6569 if (cst
->toJSValue() == expected()) {
6577 MDefinition
* MGuardNullOrUndefined::foldsTo(TempAllocator
& alloc
) {
6578 MDefinition
* input
= value();
6579 if (input
->isBox()) {
6580 input
= input
->toBox()->input();
6583 if (input
->definitelyType({MIRType::Null
, MIRType::Undefined
})) {
6590 MDefinition
* MGuardIsNotObject::foldsTo(TempAllocator
& alloc
) {
6591 MDefinition
* input
= value();
6592 if (input
->isBox()) {
6593 input
= input
->toBox()->input();
6596 if (!input
->mightBeType(MIRType::Object
)) {
6603 MDefinition
* MGuardObjectIdentity::foldsTo(TempAllocator
& alloc
) {
6604 if (object()->isConstant() && expected()->isConstant()) {
6605 JSObject
* obj
= &object()->toConstant()->toObject();
6606 JSObject
* other
= &expected()->toConstant()->toObject();
6607 if (!bailOnEquality()) {
6618 if (!bailOnEquality() && object()->isNurseryObject() &&
6619 expected()->isNurseryObject()) {
6620 uint32_t objIndex
= object()->toNurseryObject()->nurseryIndex();
6621 uint32_t otherIndex
= expected()->toNurseryObject()->nurseryIndex();
6622 if (objIndex
== otherIndex
) {
6630 MDefinition
* MGuardSpecificFunction::foldsTo(TempAllocator
& alloc
) {
6631 if (function()->isConstant() && expected()->isConstant()) {
6632 JSObject
* fun
= &function()->toConstant()->toObject();
6633 JSObject
* other
= &expected()->toConstant()->toObject();
6639 if (function()->isNurseryObject() && expected()->isNurseryObject()) {
6640 uint32_t funIndex
= function()->toNurseryObject()->nurseryIndex();
6641 uint32_t otherIndex
= expected()->toNurseryObject()->nurseryIndex();
6642 if (funIndex
== otherIndex
) {
6650 MDefinition
* MGuardSpecificAtom::foldsTo(TempAllocator
& alloc
) {
6651 if (str()->isConstant()) {
6652 JSString
* s
= str()->toConstant()->toString();
6654 JSAtom
* cstAtom
= &s
->asAtom();
6655 if (cstAtom
== atom()) {
6664 MDefinition
* MGuardSpecificSymbol::foldsTo(TempAllocator
& alloc
) {
6665 if (symbol()->isConstant()) {
6666 if (symbol()->toConstant()->toSymbol() == expected()) {
6674 MDefinition
* MGuardSpecificInt32::foldsTo(TempAllocator
& alloc
) {
6675 if (num()->isConstant() && num()->toConstant()->isInt32(expected())) {
6681 bool MCallBindVar::congruentTo(const MDefinition
* ins
) const {
6682 if (!ins
->isCallBindVar()) {
6685 return congruentIfOperandsEqual(ins
);
6688 bool MGuardShape::congruentTo(const MDefinition
* ins
) const {
6689 if (!ins
->isGuardShape()) {
6692 if (shape() != ins
->toGuardShape()->shape()) {
6695 return congruentIfOperandsEqual(ins
);
6698 AliasSet
MGuardShape::getAliasSet() const {
6699 return AliasSet::Load(AliasSet::ObjectFields
);
6702 MDefinition::AliasType
MGuardShape::mightAlias(const MDefinition
* store
) const {
6703 // These instructions only modify object elements, but not the shape.
6704 if (store
->isStoreElementHole() || store
->isArrayPush()) {
6705 return AliasType::NoAlias
;
6707 if (object()->isConstantProto()) {
6708 const MDefinition
* receiverObject
=
6709 object()->toConstantProto()->getReceiverObject();
6710 switch (store
->op()) {
6711 case MDefinition::Opcode::StoreFixedSlot
:
6712 if (store
->toStoreFixedSlot()->object()->skipObjectGuards() ==
6714 return AliasType::NoAlias
;
6717 case MDefinition::Opcode::StoreDynamicSlot
:
6718 if (store
->toStoreDynamicSlot()
6722 ->skipObjectGuards() == receiverObject
) {
6723 return AliasType::NoAlias
;
6726 case MDefinition::Opcode::AddAndStoreSlot
:
6727 if (store
->toAddAndStoreSlot()->object()->skipObjectGuards() ==
6729 return AliasType::NoAlias
;
6732 case MDefinition::Opcode::AllocateAndStoreSlot
:
6733 if (store
->toAllocateAndStoreSlot()->object()->skipObjectGuards() ==
6735 return AliasType::NoAlias
;
6742 return MInstruction::mightAlias(store
);
6745 bool MGuardFuse::congruentTo(const MDefinition
* ins
) const {
6746 if (!ins
->isGuardFuse()) {
6749 if (fuseIndex() != ins
->toGuardFuse()->fuseIndex()) {
6752 return congruentIfOperandsEqual(ins
);
6755 AliasSet
MGuardFuse::getAliasSet() const {
6756 // The alias set below reflects the set of operations which could cause a fuse
6757 // to be popped, and therefore MGuardFuse aliases with.
6758 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::DynamicSlot
|
6759 AliasSet::FixedSlot
|
6760 AliasSet::GlobalGenerationCounter
);
6763 AliasSet
MGuardMultipleShapes::getAliasSet() const {
6764 // Note: This instruction loads the elements of the ListObject used to
6765 // store the list of shapes, but that object is internal and not exposed
6766 // to script, so it doesn't have to be in the alias set.
6767 return AliasSet::Load(AliasSet::ObjectFields
);
6770 AliasSet
MGuardGlobalGeneration::getAliasSet() const {
6771 return AliasSet::Load(AliasSet::GlobalGenerationCounter
);
6774 bool MGuardGlobalGeneration::congruentTo(const MDefinition
* ins
) const {
6775 return ins
->isGuardGlobalGeneration() &&
6776 ins
->toGuardGlobalGeneration()->expected() == expected() &&
6777 ins
->toGuardGlobalGeneration()->generationAddr() == generationAddr();
6780 MDefinition
* MGuardIsNotProxy::foldsTo(TempAllocator
& alloc
) {
6781 KnownClass known
= GetObjectKnownClass(object());
6782 if (known
== KnownClass::None
) {
6786 MOZ_ASSERT(!GetObjectKnownJSClass(object())->isProxyObject());
6787 AssertKnownClass(alloc
, this, object());
6791 AliasSet
MMegamorphicLoadSlotByValue::getAliasSet() const {
6792 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6793 AliasSet::DynamicSlot
);
6796 MDefinition
* MMegamorphicLoadSlotByValue::foldsTo(TempAllocator
& alloc
) {
6797 MDefinition
* input
= idVal();
6798 if (input
->isBox()) {
6799 input
= input
->toBox()->input();
6802 MDefinition
* result
= this;
6804 if (input
->isConstant()) {
6805 MConstant
* constant
= input
->toConstant();
6806 if (constant
->type() == MIRType::Symbol
) {
6807 PropertyKey id
= PropertyKey::Symbol(constant
->toSymbol());
6808 result
= MMegamorphicLoadSlot::New(alloc
, object(), id
);
6811 if (constant
->type() == MIRType::String
) {
6812 JSString
* str
= constant
->toString();
6813 if (str
->isAtom() && !str
->asAtom().isIndex()) {
6814 PropertyKey id
= PropertyKey::NonIntAtom(str
);
6815 result
= MMegamorphicLoadSlot::New(alloc
, object(), id
);
6820 if (result
!= this) {
6821 result
->setDependency(dependency());
6827 bool MMegamorphicLoadSlot::congruentTo(const MDefinition
* ins
) const {
6828 if (!ins
->isMegamorphicLoadSlot()) {
6831 if (ins
->toMegamorphicLoadSlot()->name() != name()) {
6834 return congruentIfOperandsEqual(ins
);
6837 AliasSet
MMegamorphicLoadSlot::getAliasSet() const {
6838 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6839 AliasSet::DynamicSlot
);
6842 bool MSmallObjectVariableKeyHasProp::congruentTo(const MDefinition
* ins
) const {
6843 if (!ins
->isSmallObjectVariableKeyHasProp()) {
6846 if (ins
->toSmallObjectVariableKeyHasProp()->shape() != shape()) {
6849 return congruentIfOperandsEqual(ins
);
6852 AliasSet
MSmallObjectVariableKeyHasProp::getAliasSet() const {
6853 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6854 AliasSet::DynamicSlot
);
6857 bool MMegamorphicHasProp::congruentTo(const MDefinition
* ins
) const {
6858 if (!ins
->isMegamorphicHasProp()) {
6861 if (ins
->toMegamorphicHasProp()->hasOwn() != hasOwn()) {
6864 return congruentIfOperandsEqual(ins
);
6867 AliasSet
MMegamorphicHasProp::getAliasSet() const {
6868 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
6869 AliasSet::DynamicSlot
);
6872 bool MNurseryObject::congruentTo(const MDefinition
* ins
) const {
6873 if (!ins
->isNurseryObject()) {
6876 return nurseryIndex() == ins
->toNurseryObject()->nurseryIndex();
6879 AliasSet
MGuardFunctionIsNonBuiltinCtor::getAliasSet() const {
6880 return AliasSet::Load(AliasSet::ObjectFields
);
6883 bool MGuardFunctionKind::congruentTo(const MDefinition
* ins
) const {
6884 if (!ins
->isGuardFunctionKind()) {
6887 if (expected() != ins
->toGuardFunctionKind()->expected()) {
6890 if (bailOnEquality() != ins
->toGuardFunctionKind()->bailOnEquality()) {
6893 return congruentIfOperandsEqual(ins
);
6896 AliasSet
MGuardFunctionKind::getAliasSet() const {
6897 return AliasSet::Load(AliasSet::ObjectFields
);
6900 bool MGuardFunctionScript::congruentTo(const MDefinition
* ins
) const {
6901 if (!ins
->isGuardFunctionScript()) {
6904 if (expected() != ins
->toGuardFunctionScript()->expected()) {
6907 return congruentIfOperandsEqual(ins
);
6910 AliasSet
MGuardFunctionScript::getAliasSet() const {
6911 // A JSFunction's BaseScript pointer is immutable. Relazification of
6912 // top-level/named self-hosted functions is an exception to this, but we don't
6913 // use this guard for those self-hosted functions.
6914 // See IRGenerator::emitCalleeGuard.
6915 MOZ_ASSERT_IF(flags_
.isSelfHostedOrIntrinsic(), flags_
.isLambda());
6916 return AliasSet::None();
6919 bool MGuardSpecificAtom::congruentTo(const MDefinition
* ins
) const {
6920 if (!ins
->isGuardSpecificAtom()) {
6923 if (atom() != ins
->toGuardSpecificAtom()->atom()) {
6926 return congruentIfOperandsEqual(ins
);
6929 MDefinition
* MGuardStringToIndex::foldsTo(TempAllocator
& alloc
) {
6930 if (!string()->isConstant()) {
6934 JSString
* str
= string()->toConstant()->toString();
6936 int32_t index
= GetIndexFromString(str
);
6941 return MConstant::New(alloc
, Int32Value(index
));
6944 MDefinition
* MGuardStringToInt32::foldsTo(TempAllocator
& alloc
) {
6945 if (!string()->isConstant()) {
6949 JSLinearString
* str
= &string()->toConstant()->toString()->asLinear();
6950 double number
= LinearStringToNumber(str
);
6953 if (!mozilla::NumberIsInt32(number
, &n
)) {
6957 return MConstant::New(alloc
, Int32Value(n
));
6960 MDefinition
* MGuardStringToDouble::foldsTo(TempAllocator
& alloc
) {
6961 if (!string()->isConstant()) {
6965 JSLinearString
* str
= &string()->toConstant()->toString()->asLinear();
6966 double number
= LinearStringToNumber(str
);
6967 return MConstant::New(alloc
, DoubleValue(number
));
6970 AliasSet
MGuardNoDenseElements::getAliasSet() const {
6971 return AliasSet::Load(AliasSet::ObjectFields
);
6974 AliasSet
MIteratorHasIndices::getAliasSet() const {
6975 return AliasSet::Load(AliasSet::ObjectFields
);
6978 AliasSet
MAllocateAndStoreSlot::getAliasSet() const {
6979 return AliasSet::Store(AliasSet::ObjectFields
| AliasSet::DynamicSlot
);
6982 AliasSet
MLoadDOMExpandoValue::getAliasSet() const {
6983 return AliasSet::Load(AliasSet::DOMProxyExpando
);
6986 AliasSet
MLoadDOMExpandoValueIgnoreGeneration::getAliasSet() const {
6987 return AliasSet::Load(AliasSet::DOMProxyExpando
);
6990 bool MGuardDOMExpandoMissingOrGuardShape::congruentTo(
6991 const MDefinition
* ins
) const {
6992 if (!ins
->isGuardDOMExpandoMissingOrGuardShape()) {
6995 if (shape() != ins
->toGuardDOMExpandoMissingOrGuardShape()->shape()) {
6998 return congruentIfOperandsEqual(ins
);
7001 AliasSet
MGuardDOMExpandoMissingOrGuardShape::getAliasSet() const {
7002 return AliasSet::Load(AliasSet::ObjectFields
);
7005 MDefinition
* MGuardToClass::foldsTo(TempAllocator
& alloc
) {
7006 const JSClass
* clasp
= GetObjectKnownJSClass(object());
7007 if (!clasp
|| getClass() != clasp
) {
7011 AssertKnownClass(alloc
, this, object());
7015 MDefinition
* MGuardToEitherClass::foldsTo(TempAllocator
& alloc
) {
7016 const JSClass
* clasp
= GetObjectKnownJSClass(object());
7017 if (!clasp
|| (getClass1() != clasp
&& getClass2() != clasp
)) {
7021 AssertKnownClass(alloc
, this, object());
7025 MDefinition
* MGuardToFunction::foldsTo(TempAllocator
& alloc
) {
7026 if (GetObjectKnownClass(object()) != KnownClass::Function
) {
7030 AssertKnownClass(alloc
, this, object());
7034 MDefinition
* MHasClass::foldsTo(TempAllocator
& alloc
) {
7035 const JSClass
* clasp
= GetObjectKnownJSClass(object());
7040 AssertKnownClass(alloc
, this, object());
7041 return MConstant::New(alloc
, BooleanValue(getClass() == clasp
));
7044 MDefinition
* MIsCallable::foldsTo(TempAllocator
& alloc
) {
7045 if (input()->type() != MIRType::Object
) {
7049 KnownClass known
= GetObjectKnownClass(input());
7050 if (known
== KnownClass::None
) {
7054 AssertKnownClass(alloc
, this, input());
7055 return MConstant::New(alloc
, BooleanValue(known
== KnownClass::Function
));
7058 MDefinition
* MIsArray::foldsTo(TempAllocator
& alloc
) {
7059 if (input()->type() != MIRType::Object
) {
7063 KnownClass known
= GetObjectKnownClass(input());
7064 if (known
== KnownClass::None
) {
7068 AssertKnownClass(alloc
, this, input());
7069 return MConstant::New(alloc
, BooleanValue(known
== KnownClass::Array
));
7072 AliasSet
MObjectClassToString::getAliasSet() const {
7073 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
7074 AliasSet::DynamicSlot
);
7077 MDefinition
* MGuardIsNotArrayBufferMaybeShared::foldsTo(TempAllocator
& alloc
) {
7078 switch (GetObjectKnownClass(object())) {
7079 case KnownClass::PlainObject
:
7080 case KnownClass::Array
:
7081 case KnownClass::Function
:
7082 case KnownClass::RegExp
:
7083 case KnownClass::ArrayIterator
:
7084 case KnownClass::StringIterator
:
7085 case KnownClass::RegExpStringIterator
: {
7086 AssertKnownClass(alloc
, this, object());
7089 case KnownClass::None
:
7096 MDefinition
* MCheckIsObj::foldsTo(TempAllocator
& alloc
) {
7097 if (!input()->isBox()) {
7101 MDefinition
* unboxed
= input()->getOperand(0);
7102 if (unboxed
->type() == MIRType::Object
) {
7109 AliasSet
MCheckIsObj::getAliasSet() const {
7110 return AliasSet::Store(AliasSet::ExceptionState
);
7114 AliasSet
MCheckScriptedProxyGetResult::getAliasSet() const {
7115 return AliasSet::Store(AliasSet::ExceptionState
);
7119 static bool IsBoxedObject(MDefinition
* def
) {
7120 MOZ_ASSERT(def
->type() == MIRType::Value
);
7123 return def
->toBox()->input()->type() == MIRType::Object
;
7126 // Construct calls are always returning a boxed object.
7128 // TODO: We should consider encoding this directly in the graph instead of
7129 // having to special case it here.
7130 if (def
->isCall()) {
7131 return def
->toCall()->isConstructing();
7133 if (def
->isConstructArray()) {
7136 if (def
->isConstructArgs()) {
7143 MDefinition
* MCheckReturn::foldsTo(TempAllocator
& alloc
) {
7144 auto* returnVal
= returnValue();
7145 if (!returnVal
->isBox()) {
7149 auto* unboxedReturnVal
= returnVal
->toBox()->input();
7150 if (unboxedReturnVal
->type() == MIRType::Object
) {
7154 if (unboxedReturnVal
->type() != MIRType::Undefined
) {
7158 auto* thisVal
= thisValue();
7159 if (IsBoxedObject(thisVal
)) {
7166 MDefinition
* MCheckThis::foldsTo(TempAllocator
& alloc
) {
7167 MDefinition
* input
= thisValue();
7168 if (!input
->isBox()) {
7172 MDefinition
* unboxed
= input
->getOperand(0);
7173 if (unboxed
->mightBeMagicType()) {
7180 MDefinition
* MCheckThisReinit::foldsTo(TempAllocator
& alloc
) {
7181 MDefinition
* input
= thisValue();
7182 if (!input
->isBox()) {
7186 MDefinition
* unboxed
= input
->getOperand(0);
7187 if (unboxed
->type() != MIRType::MagicUninitializedLexical
) {
7194 MDefinition
* MCheckObjCoercible::foldsTo(TempAllocator
& alloc
) {
7195 MDefinition
* input
= checkValue();
7196 if (!input
->isBox()) {
7200 MDefinition
* unboxed
= input
->getOperand(0);
7201 if (unboxed
->mightBeType(MIRType::Null
) ||
7202 unboxed
->mightBeType(MIRType::Undefined
)) {
7209 AliasSet
MCheckObjCoercible::getAliasSet() const {
7210 return AliasSet::Store(AliasSet::ExceptionState
);
7213 AliasSet
MCheckReturn::getAliasSet() const {
7214 return AliasSet::Store(AliasSet::ExceptionState
);
7217 AliasSet
MCheckThis::getAliasSet() const {
7218 return AliasSet::Store(AliasSet::ExceptionState
);
7221 AliasSet
MCheckThisReinit::getAliasSet() const {
7222 return AliasSet::Store(AliasSet::ExceptionState
);
7225 AliasSet
MIsPackedArray::getAliasSet() const {
7226 return AliasSet::Load(AliasSet::ObjectFields
);
7229 AliasSet
MGuardArrayIsPacked::getAliasSet() const {
7230 return AliasSet::Load(AliasSet::ObjectFields
);
7233 AliasSet
MSuperFunction::getAliasSet() const {
7234 return AliasSet::Load(AliasSet::ObjectFields
);
7237 AliasSet
MInitHomeObject::getAliasSet() const {
7238 return AliasSet::Store(AliasSet::ObjectFields
);
7241 AliasSet
MLoadWrapperTarget::getAliasSet() const {
7242 return AliasSet::Load(AliasSet::Any
);
7245 bool MLoadWrapperTarget::congruentTo(const MDefinition
* ins
) const {
7246 if (!ins
->isLoadWrapperTarget()) {
7249 if (ins
->toLoadWrapperTarget()->fallible() != fallible()) {
7252 return congruentIfOperandsEqual(ins
);
7255 AliasSet
MGuardHasGetterSetter::getAliasSet() const {
7256 return AliasSet::Load(AliasSet::ObjectFields
);
7259 bool MGuardHasGetterSetter::congruentTo(const MDefinition
* ins
) const {
7260 if (!ins
->isGuardHasGetterSetter()) {
7263 if (ins
->toGuardHasGetterSetter()->propId() != propId()) {
7266 if (ins
->toGuardHasGetterSetter()->getterSetter() != getterSetter()) {
7269 return congruentIfOperandsEqual(ins
);
7272 AliasSet
MGuardIsExtensible::getAliasSet() const {
7273 return AliasSet::Load(AliasSet::ObjectFields
);
7276 AliasSet
MGuardIndexIsNotDenseElement::getAliasSet() const {
7277 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::Element
);
7280 AliasSet
MGuardIndexIsValidUpdateOrAdd::getAliasSet() const {
7281 return AliasSet::Load(AliasSet::ObjectFields
);
7284 AliasSet
MCallObjectHasSparseElement::getAliasSet() const {
7285 return AliasSet::Load(AliasSet::Element
| AliasSet::ObjectFields
|
7286 AliasSet::FixedSlot
| AliasSet::DynamicSlot
);
7289 AliasSet
MLoadSlotByIteratorIndex::getAliasSet() const {
7290 return AliasSet::Load(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
7291 AliasSet::DynamicSlot
| AliasSet::Element
);
7294 AliasSet
MStoreSlotByIteratorIndex::getAliasSet() const {
7295 return AliasSet::Store(AliasSet::ObjectFields
| AliasSet::FixedSlot
|
7296 AliasSet::DynamicSlot
| AliasSet::Element
);
7299 MDefinition
* MGuardInt32IsNonNegative::foldsTo(TempAllocator
& alloc
) {
7300 MOZ_ASSERT(index()->type() == MIRType::Int32
);
7302 MDefinition
* input
= index();
7303 if (!input
->isConstant() || input
->toConstant()->toInt32() < 0) {
7309 MDefinition
* MGuardInt32Range::foldsTo(TempAllocator
& alloc
) {
7310 MOZ_ASSERT(input()->type() == MIRType::Int32
);
7311 MOZ_ASSERT(minimum() <= maximum());
7313 MDefinition
* in
= input();
7314 if (!in
->isConstant()) {
7317 int32_t cst
= in
->toConstant()->toInt32();
7318 if (cst
< minimum() || cst
> maximum()) {
7324 MDefinition
* MGuardNonGCThing::foldsTo(TempAllocator
& alloc
) {
7325 if (!input()->isBox()) {
7329 MDefinition
* unboxed
= input()->getOperand(0);
7330 if (!IsNonGCThing(unboxed
->type())) {
7336 AliasSet
MSetObjectHasNonBigInt::getAliasSet() const {
7337 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7340 AliasSet
MSetObjectHasBigInt::getAliasSet() const {
7341 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7344 AliasSet
MSetObjectHasValue::getAliasSet() const {
7345 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7348 AliasSet
MSetObjectHasValueVMCall::getAliasSet() const {
7349 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7352 AliasSet
MSetObjectSize::getAliasSet() const {
7353 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7356 AliasSet
MMapObjectHasNonBigInt::getAliasSet() const {
7357 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7360 AliasSet
MMapObjectHasBigInt::getAliasSet() const {
7361 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7364 AliasSet
MMapObjectHasValue::getAliasSet() const {
7365 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7368 AliasSet
MMapObjectHasValueVMCall::getAliasSet() const {
7369 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7372 AliasSet
MMapObjectGetNonBigInt::getAliasSet() const {
7373 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7376 AliasSet
MMapObjectGetBigInt::getAliasSet() const {
7377 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7380 AliasSet
MMapObjectGetValue::getAliasSet() const {
7381 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7384 AliasSet
MMapObjectGetValueVMCall::getAliasSet() const {
7385 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7388 AliasSet
MMapObjectSize::getAliasSet() const {
7389 return AliasSet::Load(AliasSet::MapOrSetHashTable
);
7392 MIonToWasmCall
* MIonToWasmCall::New(TempAllocator
& alloc
,
7393 WasmInstanceObject
* instanceObj
,
7394 const wasm::FuncExport
& funcExport
) {
7395 const wasm::FuncType
& funcType
=
7396 instanceObj
->instance().metadata().getFuncExportType(funcExport
);
7397 const wasm::ValTypeVector
& results
= funcType
.results();
7398 MIRType resultType
= MIRType::Value
;
7399 // At the JS boundary some wasm types must be represented as a Value, and in
7400 // addition a void return requires an Undefined value.
7401 if (results
.length() > 0 && !results
[0].isEncodedAsJSValueOnEscape()) {
7402 MOZ_ASSERT(results
.length() == 1,
7403 "multiple returns not implemented for inlined Wasm calls");
7404 resultType
= results
[0].toMIRType();
7407 auto* ins
= new (alloc
) MIonToWasmCall(instanceObj
, resultType
, funcExport
);
7408 if (!ins
->init(alloc
, funcType
.args().length())) {
7414 MBindFunction
* MBindFunction::New(TempAllocator
& alloc
, MDefinition
* target
,
7415 uint32_t argc
, JSObject
* templateObj
) {
7416 auto* ins
= new (alloc
) MBindFunction(templateObj
);
7417 if (!ins
->init(alloc
, NumNonArgumentOperands
+ argc
)) {
7420 ins
->initOperand(0, target
);
7425 bool MIonToWasmCall::isConsistentFloat32Use(MUse
* use
) const {
7426 const wasm::FuncType
& funcType
=
7427 instance()->metadata().getFuncExportType(funcExport_
);
7428 return funcType
.args()[use
->index()].kind() == wasm::ValType::F32
;
7432 MCreateInlinedArgumentsObject
* MCreateInlinedArgumentsObject::New(
7433 TempAllocator
& alloc
, MDefinition
* callObj
, MDefinition
* callee
,
7434 MDefinitionVector
& args
, ArgumentsObject
* templateObj
) {
7435 MCreateInlinedArgumentsObject
* ins
=
7436 new (alloc
) MCreateInlinedArgumentsObject(templateObj
);
7438 uint32_t argc
= args
.length();
7439 MOZ_ASSERT(argc
<= ArgumentsObject::MaxInlinedArgs
);
7441 if (!ins
->init(alloc
, argc
+ NumNonArgumentOperands
)) {
7445 ins
->initOperand(0, callObj
);
7446 ins
->initOperand(1, callee
);
7447 for (uint32_t i
= 0; i
< argc
; i
++) {
7448 ins
->initOperand(i
+ NumNonArgumentOperands
, args
[i
]);
7454 MGetInlinedArgument
* MGetInlinedArgument::New(
7455 TempAllocator
& alloc
, MDefinition
* index
,
7456 MCreateInlinedArgumentsObject
* args
) {
7457 MGetInlinedArgument
* ins
= new (alloc
) MGetInlinedArgument();
7459 uint32_t argc
= args
->numActuals();
7460 MOZ_ASSERT(argc
<= ArgumentsObject::MaxInlinedArgs
);
7462 if (!ins
->init(alloc
, argc
+ NumNonArgumentOperands
)) {
7466 ins
->initOperand(0, index
);
7467 for (uint32_t i
= 0; i
< argc
; i
++) {
7468 ins
->initOperand(i
+ NumNonArgumentOperands
, args
->getArg(i
));
7474 MGetInlinedArgument
* MGetInlinedArgument::New(TempAllocator
& alloc
,
7476 const CallInfo
& callInfo
) {
7477 MGetInlinedArgument
* ins
= new (alloc
) MGetInlinedArgument();
7479 uint32_t argc
= callInfo
.argc();
7480 MOZ_ASSERT(argc
<= ArgumentsObject::MaxInlinedArgs
);
7482 if (!ins
->init(alloc
, argc
+ NumNonArgumentOperands
)) {
7486 ins
->initOperand(0, index
);
7487 for (uint32_t i
= 0; i
< argc
; i
++) {
7488 ins
->initOperand(i
+ NumNonArgumentOperands
, callInfo
.getArg(i
));
7494 MDefinition
* MGetInlinedArgument::foldsTo(TempAllocator
& alloc
) {
7495 MDefinition
* indexDef
= SkipUninterestingInstructions(index());
7496 if (!indexDef
->isConstant() || indexDef
->type() != MIRType::Int32
) {
7500 int32_t indexConst
= indexDef
->toConstant()->toInt32();
7501 if (indexConst
< 0 || uint32_t(indexConst
) >= numActuals()) {
7505 MDefinition
* arg
= getArg(indexConst
);
7506 if (arg
->type() != MIRType::Value
) {
7507 arg
= MBox::New(alloc
, arg
);
7513 MGetInlinedArgumentHole
* MGetInlinedArgumentHole::New(
7514 TempAllocator
& alloc
, MDefinition
* index
,
7515 MCreateInlinedArgumentsObject
* args
) {
7516 auto* ins
= new (alloc
) MGetInlinedArgumentHole();
7518 uint32_t argc
= args
->numActuals();
7519 MOZ_ASSERT(argc
<= ArgumentsObject::MaxInlinedArgs
);
7521 if (!ins
->init(alloc
, argc
+ NumNonArgumentOperands
)) {
7525 ins
->initOperand(0, index
);
7526 for (uint32_t i
= 0; i
< argc
; i
++) {
7527 ins
->initOperand(i
+ NumNonArgumentOperands
, args
->getArg(i
));
7533 MDefinition
* MGetInlinedArgumentHole::foldsTo(TempAllocator
& alloc
) {
7534 MDefinition
* indexDef
= SkipUninterestingInstructions(index());
7535 if (!indexDef
->isConstant() || indexDef
->type() != MIRType::Int32
) {
7539 int32_t indexConst
= indexDef
->toConstant()->toInt32();
7540 if (indexConst
< 0) {
7545 if (uint32_t(indexConst
) < numActuals()) {
7546 arg
= getArg(indexConst
);
7548 if (arg
->type() != MIRType::Value
) {
7549 arg
= MBox::New(alloc
, arg
);
7552 auto* undefined
= MConstant::New(alloc
, UndefinedValue());
7553 block()->insertBefore(this, undefined
);
7555 arg
= MBox::New(alloc
, undefined
);
7561 MInlineArgumentsSlice
* MInlineArgumentsSlice::New(
7562 TempAllocator
& alloc
, MDefinition
* begin
, MDefinition
* count
,
7563 MCreateInlinedArgumentsObject
* args
, JSObject
* templateObj
,
7564 gc::Heap initialHeap
) {
7565 auto* ins
= new (alloc
) MInlineArgumentsSlice(templateObj
, initialHeap
);
7567 uint32_t argc
= args
->numActuals();
7568 MOZ_ASSERT(argc
<= ArgumentsObject::MaxInlinedArgs
);
7570 if (!ins
->init(alloc
, argc
+ NumNonArgumentOperands
)) {
7574 ins
->initOperand(0, begin
);
7575 ins
->initOperand(1, count
);
7576 for (uint32_t i
= 0; i
< argc
; i
++) {
7577 ins
->initOperand(i
+ NumNonArgumentOperands
, args
->getArg(i
));
7583 MDefinition
* MArrayLength::foldsTo(TempAllocator
& alloc
) {
7584 // Object.keys() is potentially effectful, in case of Proxies. Otherwise, when
7585 // it is only computed for its length property, there is no need to
7586 // materialize the Array which results from it and it can be marked as
7587 // recovered on bailout as long as no properties are added to / removed from
7589 MDefinition
* elems
= elements();
7590 if (!elems
->isElements()) {
7594 MDefinition
* guardshape
= elems
->toElements()->object();
7595 if (!guardshape
->isGuardShape()) {
7599 // The Guard shape is guarding the shape of the object returned by
7600 // Object.keys, this guard can be removed as knowing the function is good
7601 // enough to infer that we are returning an array.
7602 MDefinition
* keys
= guardshape
->toGuardShape()->object();
7603 if (!keys
->isObjectKeys()) {
7607 // Object.keys() inline cache guards against proxies when creating the IC. We
7608 // rely on this here as we are looking to elide `Object.keys(...)` call, which
7609 // is only possible if we know for sure that no side-effect might have
7611 MDefinition
* noproxy
= keys
->toObjectKeys()->object();
7612 if (!noproxy
->isGuardIsNotProxy()) {
7613 // The guard might have been replaced by an assertion, in case the class is
7614 // known at compile time. IF the guard has been removed check whether check
7615 // has been removed.
7616 MOZ_RELEASE_ASSERT(GetObjectKnownClass(noproxy
) != KnownClass::None
);
7617 MOZ_RELEASE_ASSERT(!GetObjectKnownJSClass(noproxy
)->isProxyObject());
7620 // Check if both the elements and the Object.keys() have a single use. We only
7621 // check for live uses, and are ok if a branch which was previously using the
7622 // keys array has been removed since.
7623 if (!elems
->hasOneLiveDefUse() || !guardshape
->hasOneLiveDefUse() ||
7624 !keys
->hasOneLiveDefUse()) {
7628 // Check that the latest active resume point is the one from Object.keys(), in
7629 // order to steal it. If this is not the latest active resume point then some
7630 // side-effect might happen which updates the content of the object, making
7631 // any recovery of the keys exhibit a different behavior than expected.
7632 if (keys
->toObjectKeys()->resumePoint() != block()->activeResumePoint(this)) {
7636 // Verify whether any resume point captures the keys array after any aliasing
7637 // mutations. If this were to be the case the recovery of ObjectKeys on
7638 // bailout might compute a version which might not match with the elided
7641 // Iterate over the resume point uses of ObjectKeys, and check whether the
7642 // instructions they are attached to are aliasing Object fields. If so, skip
7643 // this optimization.
7644 AliasSet enumKeysAliasSet
= AliasSet::Load(AliasSet::Flag::ObjectFields
);
7645 for (auto* use
: UsesIterator(keys
)) {
7646 if (!use
->consumer()->isResumePoint()) {
7647 // There is only a single use, and this is the length computation as
7648 // asserted with `hasOneLiveDefUse`.
7652 MResumePoint
* rp
= use
->consumer()->toResumePoint();
7653 if (!rp
->instruction()) {
7654 // If there is no instruction, this is a resume point which is attached to
7655 // the entry of a block. Thus no risk of mutating the object on which the
7656 // keys are queried.
7660 MInstruction
* ins
= rp
->instruction();
7665 // Check whether the instruction can potentially alias the object fields of
7666 // the object from which we are querying the keys.
7667 AliasSet mightAlias
= ins
->getAliasSet() & enumKeysAliasSet
;
7668 if (!mightAlias
.isNone()) {
7673 // Flag every instructions since Object.keys(..) as recovered on bailout, and
7674 // make Object.keys(..) be the recovered value in-place of the shape guard.
7675 setRecoveredOnBailout();
7676 elems
->setRecoveredOnBailout();
7677 guardshape
->replaceAllUsesWith(keys
);
7678 guardshape
->block()->discard(guardshape
->toGuardShape());
7679 keys
->setRecoveredOnBailout();
7681 // Steal the resume point from Object.keys, which is ok as we confirmed that
7682 // there is no other resume point in-between.
7683 MObjectKeysLength
* keysLength
= MObjectKeysLength::New(alloc
, noproxy
);
7684 keysLength
->stealResumePoint(keys
->toObjectKeys());
7689 MDefinition
* MNormalizeSliceTerm::foldsTo(TempAllocator
& alloc
) {
7690 auto* length
= this->length();
7691 if (!length
->isConstant() && !length
->isArgumentsLength()) {
7695 if (length
->isConstant()) {
7696 int32_t lengthConst
= length
->toConstant()->toInt32();
7697 MOZ_ASSERT(lengthConst
>= 0);
7699 // Result is always zero when |length| is zero.
7700 if (lengthConst
== 0) {
7704 auto* value
= this->value();
7705 if (value
->isConstant()) {
7706 int32_t valueConst
= value
->toConstant()->toInt32();
7709 if (valueConst
< 0) {
7710 normalized
= std::max(valueConst
+ lengthConst
, 0);
7712 normalized
= std::min(valueConst
, lengthConst
);
7715 if (normalized
== valueConst
) {
7718 if (normalized
== lengthConst
) {
7721 return MConstant::New(alloc
, Int32Value(normalized
));
7727 auto* value
= this->value();
7728 if (value
->isConstant()) {
7729 int32_t valueConst
= value
->toConstant()->toInt32();
7731 // Minimum of |value| and |length|.
7732 if (valueConst
> 0) {
7734 return MMinMax::New(alloc
, value
, length
, MIRType::Int32
, isMax
);
7737 // Maximum of |value + length| and zero.
7738 if (valueConst
< 0) {
7739 // Safe to truncate because |length| is never negative.
7740 auto* add
= MAdd::New(alloc
, value
, length
, TruncateKind::Truncate
);
7741 block()->insertBefore(this, add
);
7743 auto* zero
= MConstant::New(alloc
, Int32Value(0));
7744 block()->insertBefore(this, zero
);
7747 return MMinMax::New(alloc
, add
, zero
, MIRType::Int32
, isMax
);
7750 // Directly return the value when it's zero.
7754 // Normalizing MArgumentsLength is a no-op.
7755 if (value
->isArgumentsLength()) {
7762 bool MInt32ToStringWithBase::congruentTo(const MDefinition
* ins
) const {
7763 if (!ins
->isInt32ToStringWithBase()) {
7766 if (ins
->toInt32ToStringWithBase()->lowerCase() != lowerCase()) {
7769 return congruentIfOperandsEqual(ins
);
7772 bool MWasmShiftSimd128::congruentTo(const MDefinition
* ins
) const {
7773 if (!ins
->isWasmShiftSimd128()) {
7776 return ins
->toWasmShiftSimd128()->simdOp() == simdOp_
&&
7777 congruentIfOperandsEqual(ins
);
7780 bool MWasmShuffleSimd128::congruentTo(const MDefinition
* ins
) const {
7781 if (!ins
->isWasmShuffleSimd128()) {
7784 return ins
->toWasmShuffleSimd128()->shuffle().equals(&shuffle_
) &&
7785 congruentIfOperandsEqual(ins
);
7788 bool MWasmUnarySimd128::congruentTo(const MDefinition
* ins
) const {
7789 if (!ins
->isWasmUnarySimd128()) {
7792 return ins
->toWasmUnarySimd128()->simdOp() == simdOp_
&&
7793 congruentIfOperandsEqual(ins
);
7796 #ifdef ENABLE_WASM_SIMD
7797 MWasmShuffleSimd128
* jit::BuildWasmShuffleSimd128(TempAllocator
& alloc
,
7798 const int8_t* control
,
7802 AnalyzeSimdShuffle(SimdConstant::CreateX16(control
), lhs
, rhs
);
7804 case SimdShuffle::Operand::LEFT
:
7805 // When SimdShuffle::Operand is LEFT the right operand is not used,
7806 // lose reference to rhs.
7809 case SimdShuffle::Operand::RIGHT
:
7810 // When SimdShuffle::Operand is RIGHT the left operand is not used,
7811 // lose reference to lhs.
7817 return MWasmShuffleSimd128::New(alloc
, lhs
, rhs
, s
);
7819 #endif // ENABLE_WASM_SIMD
7821 static MDefinition
* FoldTrivialWasmCasts(TempAllocator
& alloc
,
7822 wasm::RefType sourceType
,
7823 wasm::RefType destType
) {
7824 // Upcasts are trivially valid.
7825 if (wasm::RefType::isSubTypeOf(sourceType
, destType
)) {
7826 return MConstant::New(alloc
, Int32Value(1), MIRType::Int32
);
7829 // If two types are completely disjoint, then all casts between them are
7831 if (!wasm::RefType::castPossible(destType
, sourceType
)) {
7832 return MConstant::New(alloc
, Int32Value(0), MIRType::Int32
);
7838 MDefinition
* MWasmRefIsSubtypeOfAbstract::foldsTo(TempAllocator
& alloc
) {
7839 MDefinition
* folded
= FoldTrivialWasmCasts(alloc
, sourceType(), destType());
7846 MDefinition
* MWasmRefIsSubtypeOfConcrete::foldsTo(TempAllocator
& alloc
) {
7847 MDefinition
* folded
= FoldTrivialWasmCasts(alloc
, sourceType(), destType());