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