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