Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / js / src / jit / MIR.cpp
blob2471839d82acdd14e17725d4e8bc8249217c1343
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 MDefinition* MTest::foldsTo(TempAllocator& alloc) {
693 if (MDefinition* def = foldsDoubleNegation(alloc)) {
694 return def;
697 if (MDefinition* def = foldsConstant(alloc)) {
698 return def;
701 if (MDefinition* def = foldsTypes(alloc)) {
702 return def;
705 if (MDefinition* def = foldsNeedlessControlFlow(alloc)) {
706 return def;
709 return this;
712 AliasSet MThrow::getAliasSet() const {
713 return AliasSet::Store(AliasSet::ExceptionState);
716 AliasSet MThrowWithStack::getAliasSet() const {
717 return AliasSet::Store(AliasSet::ExceptionState);
720 AliasSet MNewArrayDynamicLength::getAliasSet() const {
721 return AliasSet::Store(AliasSet::ExceptionState);
724 AliasSet MNewTypedArrayDynamicLength::getAliasSet() const {
725 return AliasSet::Store(AliasSet::ExceptionState);
728 #ifdef JS_JITSPEW
729 void MDefinition::printOpcode(GenericPrinter& out) const {
730 PrintOpcodeName(out, op());
731 for (size_t j = 0, e = numOperands(); j < e; j++) {
732 out.printf(" ");
733 if (getUseFor(j)->hasProducer()) {
734 getOperand(j)->printName(out);
735 out.printf(":%s", StringFromMIRType(getOperand(j)->type()));
736 } else {
737 out.printf("(null)");
742 void MDefinition::dump(GenericPrinter& out) const {
743 printName(out);
744 out.printf(":%s", StringFromMIRType(type()));
745 out.printf(" = ");
746 printOpcode(out);
747 out.printf("\n");
749 if (isInstruction()) {
750 if (MResumePoint* resume = toInstruction()->resumePoint()) {
751 resume->dump(out);
756 void MDefinition::dump() const {
757 Fprinter out(stderr);
758 dump(out);
759 out.finish();
762 void MDefinition::dumpLocation(GenericPrinter& out) const {
763 MResumePoint* rp = nullptr;
764 const char* linkWord = nullptr;
765 if (isInstruction() && toInstruction()->resumePoint()) {
766 rp = toInstruction()->resumePoint();
767 linkWord = "at";
768 } else {
769 rp = block()->entryResumePoint();
770 linkWord = "after";
773 while (rp) {
774 JSScript* script = rp->block()->info().script();
775 uint32_t lineno = PCToLineNumber(rp->block()->info().script(), rp->pc());
776 out.printf(" %s %s:%u\n", linkWord, script->filename(), lineno);
777 rp = rp->caller();
778 linkWord = "in";
782 void MDefinition::dumpLocation() const {
783 Fprinter out(stderr);
784 dumpLocation(out);
785 out.finish();
787 #endif
789 #if defined(DEBUG) || defined(JS_JITSPEW)
790 size_t MDefinition::useCount() const {
791 size_t count = 0;
792 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
793 count++;
795 return count;
798 size_t MDefinition::defUseCount() const {
799 size_t count = 0;
800 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
801 if ((*i)->consumer()->isDefinition()) {
802 count++;
805 return count;
807 #endif
809 bool MDefinition::hasOneUse() const {
810 MUseIterator i(uses_.begin());
811 if (i == uses_.end()) {
812 return false;
814 i++;
815 return i == uses_.end();
818 bool MDefinition::hasOneDefUse() const {
819 bool hasOneDefUse = false;
820 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
821 if (!(*i)->consumer()->isDefinition()) {
822 continue;
825 // We already have a definition use. So 1+
826 if (hasOneDefUse) {
827 return false;
830 // We saw one definition. Loop to test if there is another.
831 hasOneDefUse = true;
834 return hasOneDefUse;
837 bool MDefinition::hasOneLiveDefUse() const {
838 bool hasOneDefUse = false;
839 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
840 if (!(*i)->consumer()->isDefinition()) {
841 continue;
844 MDefinition* def = (*i)->consumer()->toDefinition();
845 if (def->isRecoveredOnBailout()) {
846 continue;
849 // We already have a definition use. So 1+
850 if (hasOneDefUse) {
851 return false;
854 // We saw one definition. Loop to test if there is another.
855 hasOneDefUse = true;
858 return hasOneDefUse;
861 bool MDefinition::hasDefUses() const {
862 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
863 if ((*i)->consumer()->isDefinition()) {
864 return true;
868 return false;
871 bool MDefinition::hasLiveDefUses() const {
872 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
873 MNode* ins = (*i)->consumer();
874 if (ins->isDefinition()) {
875 if (!ins->toDefinition()->isRecoveredOnBailout()) {
876 return true;
878 } else {
879 MOZ_ASSERT(ins->isResumePoint());
880 if (!ins->toResumePoint()->isRecoverableOperand(*i)) {
881 return true;
886 return false;
889 MDefinition* MDefinition::maybeSingleDefUse() const {
890 MUseDefIterator use(this);
891 if (!use) {
892 // No def-uses.
893 return nullptr;
896 MDefinition* useDef = use.def();
898 use++;
899 if (use) {
900 // More than one def-use.
901 return nullptr;
904 return useDef;
907 MDefinition* MDefinition::maybeMostRecentlyAddedDefUse() const {
908 MUseDefIterator use(this);
909 if (!use) {
910 // No def-uses.
911 return nullptr;
914 MDefinition* mostRecentUse = use.def();
916 #ifdef DEBUG
917 // This function relies on addUse adding new uses to the front of the list.
918 // Check this invariant by asserting the next few uses are 'older'. Skip this
919 // for phis because setBackedge can add a new use for a loop phi even if the
920 // loop body has a use with an id greater than the loop phi's id.
921 if (!mostRecentUse->isPhi()) {
922 static constexpr size_t NumUsesToCheck = 3;
923 use++;
924 for (size_t i = 0; use && i < NumUsesToCheck; i++, use++) {
925 MOZ_ASSERT(use.def()->id() <= mostRecentUse->id());
928 #endif
930 return mostRecentUse;
933 void MDefinition::replaceAllUsesWith(MDefinition* dom) {
934 for (size_t i = 0, e = numOperands(); i < e; ++i) {
935 getOperand(i)->setImplicitlyUsedUnchecked();
938 justReplaceAllUsesWith(dom);
941 void MDefinition::justReplaceAllUsesWith(MDefinition* dom) {
942 MOZ_ASSERT(dom != nullptr);
943 MOZ_ASSERT(dom != this);
945 // Carry over the fact the value has uses which are no longer inspectable
946 // with the graph.
947 if (isImplicitlyUsed()) {
948 dom->setImplicitlyUsedUnchecked();
951 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i) {
952 i->setProducerUnchecked(dom);
954 dom->uses_.takeElements(uses_);
957 bool MDefinition::optimizeOutAllUses(TempAllocator& alloc) {
958 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) {
959 MUse* use = *i++;
960 MConstant* constant = use->consumer()->block()->optimizedOutConstant(alloc);
961 if (!alloc.ensureBallast()) {
962 return false;
965 // Update the resume point operand to use the optimized-out constant.
966 use->setProducerUnchecked(constant);
967 constant->addUseUnchecked(use);
970 // Remove dangling pointers.
971 this->uses_.clear();
972 return true;
975 void MDefinition::replaceAllLiveUsesWith(MDefinition* dom) {
976 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) {
977 MUse* use = *i++;
978 MNode* consumer = use->consumer();
979 if (consumer->isResumePoint()) {
980 continue;
982 if (consumer->isDefinition() &&
983 consumer->toDefinition()->isRecoveredOnBailout()) {
984 continue;
987 // Update the operand to use the dominating definition.
988 use->replaceProducer(dom);
992 MConstant* MConstant::New(TempAllocator& alloc, const Value& v) {
993 return new (alloc) MConstant(alloc, v);
996 MConstant* MConstant::New(TempAllocator::Fallible alloc, const Value& v) {
997 return new (alloc) MConstant(alloc.alloc, v);
1000 MConstant* MConstant::NewFloat32(TempAllocator& alloc, double d) {
1001 MOZ_ASSERT(std::isnan(d) || d == double(float(d)));
1002 return new (alloc) MConstant(float(d));
1005 MConstant* MConstant::NewInt64(TempAllocator& alloc, int64_t i) {
1006 return new (alloc) MConstant(MIRType::Int64, i);
1009 MConstant* MConstant::NewIntPtr(TempAllocator& alloc, intptr_t i) {
1010 return new (alloc) MConstant(MIRType::IntPtr, i);
1013 MConstant* MConstant::New(TempAllocator& alloc, const Value& v, MIRType type) {
1014 if (type == MIRType::Float32) {
1015 return NewFloat32(alloc, v.toNumber());
1017 MConstant* res = New(alloc, v);
1018 MOZ_ASSERT(res->type() == type);
1019 return res;
1022 MConstant* MConstant::NewObject(TempAllocator& alloc, JSObject* v) {
1023 return new (alloc) MConstant(v);
1026 MConstant* MConstant::NewShape(TempAllocator& alloc, Shape* s) {
1027 return new (alloc) MConstant(s);
1030 static MIRType MIRTypeFromValue(const js::Value& vp) {
1031 if (vp.isDouble()) {
1032 return MIRType::Double;
1034 if (vp.isMagic()) {
1035 switch (vp.whyMagic()) {
1036 case JS_OPTIMIZED_OUT:
1037 return MIRType::MagicOptimizedOut;
1038 case JS_ELEMENTS_HOLE:
1039 return MIRType::MagicHole;
1040 case JS_IS_CONSTRUCTING:
1041 return MIRType::MagicIsConstructing;
1042 case JS_UNINITIALIZED_LEXICAL:
1043 return MIRType::MagicUninitializedLexical;
1044 default:
1045 MOZ_ASSERT_UNREACHABLE("Unexpected magic constant");
1048 return MIRTypeFromValueType(vp.extractNonDoubleType());
1051 MConstant::MConstant(TempAllocator& alloc, const js::Value& vp)
1052 : MNullaryInstruction(classOpcode) {
1053 setResultType(MIRTypeFromValue(vp));
1055 MOZ_ASSERT(payload_.asBits == 0);
1057 switch (type()) {
1058 case MIRType::Undefined:
1059 case MIRType::Null:
1060 break;
1061 case MIRType::Boolean:
1062 payload_.b = vp.toBoolean();
1063 break;
1064 case MIRType::Int32:
1065 payload_.i32 = vp.toInt32();
1066 break;
1067 case MIRType::Double:
1068 payload_.d = vp.toDouble();
1069 break;
1070 case MIRType::String:
1071 MOZ_ASSERT(!IsInsideNursery(vp.toString()));
1072 MOZ_ASSERT(vp.toString()->isLinear());
1073 payload_.str = vp.toString();
1074 break;
1075 case MIRType::Symbol:
1076 payload_.sym = vp.toSymbol();
1077 break;
1078 case MIRType::BigInt:
1079 MOZ_ASSERT(!IsInsideNursery(vp.toBigInt()));
1080 payload_.bi = vp.toBigInt();
1081 break;
1082 case MIRType::Object:
1083 MOZ_ASSERT(!IsInsideNursery(&vp.toObject()));
1084 payload_.obj = &vp.toObject();
1085 break;
1086 case MIRType::MagicOptimizedOut:
1087 case MIRType::MagicHole:
1088 case MIRType::MagicIsConstructing:
1089 case MIRType::MagicUninitializedLexical:
1090 break;
1091 default:
1092 MOZ_CRASH("Unexpected type");
1095 setMovable();
1098 MConstant::MConstant(JSObject* obj) : MNullaryInstruction(classOpcode) {
1099 MOZ_ASSERT(!IsInsideNursery(obj));
1100 setResultType(MIRType::Object);
1101 payload_.obj = obj;
1102 setMovable();
1105 MConstant::MConstant(Shape* shape) : MNullaryInstruction(classOpcode) {
1106 setResultType(MIRType::Shape);
1107 payload_.shape = shape;
1108 setMovable();
1111 MConstant::MConstant(float f) : MNullaryInstruction(classOpcode) {
1112 setResultType(MIRType::Float32);
1113 payload_.f = f;
1114 setMovable();
1117 MConstant::MConstant(MIRType type, int64_t i)
1118 : MNullaryInstruction(classOpcode) {
1119 MOZ_ASSERT(type == MIRType::Int64 || type == MIRType::IntPtr);
1120 setResultType(type);
1121 if (type == MIRType::Int64) {
1122 payload_.i64 = i;
1123 } else {
1124 payload_.iptr = i;
1126 setMovable();
1129 #ifdef DEBUG
1130 void MConstant::assertInitializedPayload() const {
1131 // valueHash() and equals() expect the unused payload bits to be
1132 // initialized to zero. Assert this in debug builds.
1134 switch (type()) {
1135 case MIRType::Int32:
1136 case MIRType::Float32:
1137 # if MOZ_LITTLE_ENDIAN()
1138 MOZ_ASSERT((payload_.asBits >> 32) == 0);
1139 # else
1140 MOZ_ASSERT((payload_.asBits << 32) == 0);
1141 # endif
1142 break;
1143 case MIRType::Boolean:
1144 # if MOZ_LITTLE_ENDIAN()
1145 MOZ_ASSERT((payload_.asBits >> 1) == 0);
1146 # else
1147 MOZ_ASSERT((payload_.asBits & ~(1ULL << 56)) == 0);
1148 # endif
1149 break;
1150 case MIRType::Double:
1151 case MIRType::Int64:
1152 break;
1153 case MIRType::String:
1154 case MIRType::Object:
1155 case MIRType::Symbol:
1156 case MIRType::BigInt:
1157 case MIRType::IntPtr:
1158 case MIRType::Shape:
1159 # if MOZ_LITTLE_ENDIAN()
1160 MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits >> 32) == 0);
1161 # else
1162 MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits << 32) == 0);
1163 # endif
1164 break;
1165 default:
1166 MOZ_ASSERT(IsNullOrUndefined(type()) || IsMagicType(type()));
1167 MOZ_ASSERT(payload_.asBits == 0);
1168 break;
1171 #endif
1173 static HashNumber ConstantValueHash(MIRType type, uint64_t payload) {
1174 // Build a 64-bit value holding both the payload and the type.
1175 static const size_t TypeBits = 8;
1176 static const size_t TypeShift = 64 - TypeBits;
1177 MOZ_ASSERT(uintptr_t(type) <= (1 << TypeBits) - 1);
1178 uint64_t bits = (uint64_t(type) << TypeShift) ^ payload;
1180 // Fold all 64 bits into the 32-bit result. It's tempting to just discard
1181 // half of the bits, as this is just a hash, however there are many common
1182 // patterns of values where only the low or the high bits vary, so
1183 // discarding either side would lead to excessive hash collisions.
1184 return (HashNumber)bits ^ (HashNumber)(bits >> 32);
1187 HashNumber MConstant::valueHash() const {
1188 static_assert(sizeof(Payload) == sizeof(uint64_t),
1189 "Code below assumes payload fits in 64 bits");
1191 assertInitializedPayload();
1192 return ConstantValueHash(type(), payload_.asBits);
1195 HashNumber MConstantProto::valueHash() const {
1196 HashNumber hash = protoObject()->valueHash();
1197 const MDefinition* receiverObject = getReceiverObject();
1198 if (receiverObject) {
1199 hash = addU32ToHash(hash, receiverObject->id());
1201 return hash;
1204 bool MConstant::congruentTo(const MDefinition* ins) const {
1205 return ins->isConstant() && equals(ins->toConstant());
1208 #ifdef JS_JITSPEW
1209 void MConstant::printOpcode(GenericPrinter& out) const {
1210 PrintOpcodeName(out, op());
1211 out.printf(" ");
1212 switch (type()) {
1213 case MIRType::Undefined:
1214 out.printf("undefined");
1215 break;
1216 case MIRType::Null:
1217 out.printf("null");
1218 break;
1219 case MIRType::Boolean:
1220 out.printf(toBoolean() ? "true" : "false");
1221 break;
1222 case MIRType::Int32:
1223 out.printf("0x%x", uint32_t(toInt32()));
1224 break;
1225 case MIRType::Int64:
1226 out.printf("0x%" PRIx64, uint64_t(toInt64()));
1227 break;
1228 case MIRType::IntPtr:
1229 out.printf("0x%" PRIxPTR, uintptr_t(toIntPtr()));
1230 break;
1231 case MIRType::Double:
1232 out.printf("%.16g", toDouble());
1233 break;
1234 case MIRType::Float32: {
1235 float val = toFloat32();
1236 out.printf("%.16g", val);
1237 break;
1239 case MIRType::Object:
1240 if (toObject().is<JSFunction>()) {
1241 JSFunction* fun = &toObject().as<JSFunction>();
1242 if (fun->maybePartialDisplayAtom()) {
1243 out.put("function ");
1244 EscapedStringPrinter(out, fun->maybePartialDisplayAtom(), 0);
1245 } else {
1246 out.put("unnamed function");
1248 if (fun->hasBaseScript()) {
1249 BaseScript* script = fun->baseScript();
1250 out.printf(" (%s:%u)", script->filename() ? script->filename() : "",
1251 script->lineno());
1253 out.printf(" at %p", (void*)fun);
1254 break;
1256 out.printf("object %p (%s)", (void*)&toObject(),
1257 toObject().getClass()->name);
1258 break;
1259 case MIRType::Symbol:
1260 out.printf("symbol at %p", (void*)toSymbol());
1261 break;
1262 case MIRType::BigInt:
1263 out.printf("BigInt at %p", (void*)toBigInt());
1264 break;
1265 case MIRType::String:
1266 out.printf("string %p", (void*)toString());
1267 break;
1268 case MIRType::Shape:
1269 out.printf("shape at %p", (void*)toShape());
1270 break;
1271 case MIRType::MagicHole:
1272 out.printf("magic hole");
1273 break;
1274 case MIRType::MagicIsConstructing:
1275 out.printf("magic is-constructing");
1276 break;
1277 case MIRType::MagicOptimizedOut:
1278 out.printf("magic optimized-out");
1279 break;
1280 case MIRType::MagicUninitializedLexical:
1281 out.printf("magic uninitialized-lexical");
1282 break;
1283 default:
1284 MOZ_CRASH("unexpected type");
1287 #endif
1289 bool MConstant::canProduceFloat32() const {
1290 if (!isTypeRepresentableAsDouble()) {
1291 return false;
1294 if (type() == MIRType::Int32) {
1295 return IsFloat32Representable(static_cast<double>(toInt32()));
1297 if (type() == MIRType::Double) {
1298 return IsFloat32Representable(toDouble());
1300 MOZ_ASSERT(type() == MIRType::Float32);
1301 return true;
1304 Value MConstant::toJSValue() const {
1305 // Wasm has types like int64 that cannot be stored as js::Value. It also
1306 // doesn't want the NaN canonicalization enforced by js::Value.
1307 MOZ_ASSERT(!IsCompilingWasm());
1309 switch (type()) {
1310 case MIRType::Undefined:
1311 return UndefinedValue();
1312 case MIRType::Null:
1313 return NullValue();
1314 case MIRType::Boolean:
1315 return BooleanValue(toBoolean());
1316 case MIRType::Int32:
1317 return Int32Value(toInt32());
1318 case MIRType::Double:
1319 return DoubleValue(toDouble());
1320 case MIRType::Float32:
1321 return Float32Value(toFloat32());
1322 case MIRType::String:
1323 return StringValue(toString());
1324 case MIRType::Symbol:
1325 return SymbolValue(toSymbol());
1326 case MIRType::BigInt:
1327 return BigIntValue(toBigInt());
1328 case MIRType::Object:
1329 return ObjectValue(toObject());
1330 case MIRType::Shape:
1331 return PrivateGCThingValue(toShape());
1332 case MIRType::MagicOptimizedOut:
1333 return MagicValue(JS_OPTIMIZED_OUT);
1334 case MIRType::MagicHole:
1335 return MagicValue(JS_ELEMENTS_HOLE);
1336 case MIRType::MagicIsConstructing:
1337 return MagicValue(JS_IS_CONSTRUCTING);
1338 case MIRType::MagicUninitializedLexical:
1339 return MagicValue(JS_UNINITIALIZED_LEXICAL);
1340 default:
1341 MOZ_CRASH("Unexpected type");
1345 bool MConstant::valueToBoolean(bool* res) const {
1346 switch (type()) {
1347 case MIRType::Boolean:
1348 *res = toBoolean();
1349 return true;
1350 case MIRType::Int32:
1351 *res = toInt32() != 0;
1352 return true;
1353 case MIRType::Int64:
1354 *res = toInt64() != 0;
1355 return true;
1356 case MIRType::Double:
1357 *res = !std::isnan(toDouble()) && toDouble() != 0.0;
1358 return true;
1359 case MIRType::Float32:
1360 *res = !std::isnan(toFloat32()) && toFloat32() != 0.0f;
1361 return true;
1362 case MIRType::Null:
1363 case MIRType::Undefined:
1364 *res = false;
1365 return true;
1366 case MIRType::Symbol:
1367 *res = true;
1368 return true;
1369 case MIRType::BigInt:
1370 *res = !toBigInt()->isZero();
1371 return true;
1372 case MIRType::String:
1373 *res = toString()->length() != 0;
1374 return true;
1375 case MIRType::Object:
1376 // TODO(Warp): Lazy groups have been removed.
1377 // We have to call EmulatesUndefined but that reads obj->group->clasp
1378 // and so it's racy when the object has a lazy group. The main callers
1379 // of this (MTest, MNot) already know how to fold the object case, so
1380 // just give up.
1381 return false;
1382 default:
1383 MOZ_ASSERT(IsMagicType(type()));
1384 return false;
1388 HashNumber MWasmFloatConstant::valueHash() const {
1389 #ifdef ENABLE_WASM_SIMD
1390 return ConstantValueHash(type(), u.bits_[0] ^ u.bits_[1]);
1391 #else
1392 return ConstantValueHash(type(), u.bits_[0]);
1393 #endif
1396 bool MWasmFloatConstant::congruentTo(const MDefinition* ins) const {
1397 return ins->isWasmFloatConstant() && type() == ins->type() &&
1398 #ifdef ENABLE_WASM_SIMD
1399 u.bits_[1] == ins->toWasmFloatConstant()->u.bits_[1] &&
1400 #endif
1401 u.bits_[0] == ins->toWasmFloatConstant()->u.bits_[0];
1404 HashNumber MWasmNullConstant::valueHash() const {
1405 return ConstantValueHash(MIRType::WasmAnyRef, 0);
1408 #ifdef JS_JITSPEW
1409 void MControlInstruction::printOpcode(GenericPrinter& out) const {
1410 MDefinition::printOpcode(out);
1411 for (size_t j = 0; j < numSuccessors(); j++) {
1412 if (getSuccessor(j)) {
1413 out.printf(" block%u", getSuccessor(j)->id());
1414 } else {
1415 out.printf(" (null-to-be-patched)");
1420 void MCompare::printOpcode(GenericPrinter& out) const {
1421 MDefinition::printOpcode(out);
1422 out.printf(" %s", CodeName(jsop()));
1425 void MTypeOfIs::printOpcode(GenericPrinter& out) const {
1426 MDefinition::printOpcode(out);
1427 out.printf(" %s", CodeName(jsop()));
1429 const char* name = "";
1430 switch (jstype()) {
1431 case JSTYPE_UNDEFINED:
1432 name = "undefined";
1433 break;
1434 case JSTYPE_OBJECT:
1435 name = "object";
1436 break;
1437 case JSTYPE_FUNCTION:
1438 name = "function";
1439 break;
1440 case JSTYPE_STRING:
1441 name = "string";
1442 break;
1443 case JSTYPE_NUMBER:
1444 name = "number";
1445 break;
1446 case JSTYPE_BOOLEAN:
1447 name = "boolean";
1448 break;
1449 case JSTYPE_SYMBOL:
1450 name = "symbol";
1451 break;
1452 case JSTYPE_BIGINT:
1453 name = "bigint";
1454 break;
1455 # ifdef ENABLE_RECORD_TUPLE
1456 case JSTYPE_RECORD:
1457 case JSTYPE_TUPLE:
1458 # endif
1459 case JSTYPE_LIMIT:
1460 MOZ_CRASH("Unexpected type");
1462 out.printf(" '%s'", name);
1465 void MLoadUnboxedScalar::printOpcode(GenericPrinter& out) const {
1466 MDefinition::printOpcode(out);
1467 out.printf(" %s", Scalar::name(storageType()));
1470 void MLoadDataViewElement::printOpcode(GenericPrinter& out) const {
1471 MDefinition::printOpcode(out);
1472 out.printf(" %s", Scalar::name(storageType()));
1475 void MAssertRange::printOpcode(GenericPrinter& out) const {
1476 MDefinition::printOpcode(out);
1477 out.put(" ");
1478 assertedRange()->dump(out);
1481 void MNearbyInt::printOpcode(GenericPrinter& out) const {
1482 MDefinition::printOpcode(out);
1483 const char* roundingModeStr = nullptr;
1484 switch (roundingMode_) {
1485 case RoundingMode::Up:
1486 roundingModeStr = "(up)";
1487 break;
1488 case RoundingMode::Down:
1489 roundingModeStr = "(down)";
1490 break;
1491 case RoundingMode::NearestTiesToEven:
1492 roundingModeStr = "(nearest ties even)";
1493 break;
1494 case RoundingMode::TowardsZero:
1495 roundingModeStr = "(towards zero)";
1496 break;
1498 out.printf(" %s", roundingModeStr);
1500 #endif
1502 AliasSet MRandom::getAliasSet() const { return AliasSet::Store(AliasSet::RNG); }
1504 MDefinition* MSign::foldsTo(TempAllocator& alloc) {
1505 MDefinition* input = getOperand(0);
1506 if (!input->isConstant() ||
1507 !input->toConstant()->isTypeRepresentableAsDouble()) {
1508 return this;
1511 double in = input->toConstant()->numberToDouble();
1512 double out = js::math_sign_impl(in);
1514 if (type() == MIRType::Int32) {
1515 // Decline folding if this is an int32 operation, but the result type
1516 // isn't an int32.
1517 Value outValue = NumberValue(out);
1518 if (!outValue.isInt32()) {
1519 return this;
1522 return MConstant::New(alloc, outValue);
1525 return MConstant::New(alloc, DoubleValue(out));
1528 const char* MMathFunction::FunctionName(UnaryMathFunction function) {
1529 return GetUnaryMathFunctionName(function);
1532 #ifdef JS_JITSPEW
1533 void MMathFunction::printOpcode(GenericPrinter& out) const {
1534 MDefinition::printOpcode(out);
1535 out.printf(" %s", FunctionName(function()));
1537 #endif
1539 MDefinition* MMathFunction::foldsTo(TempAllocator& alloc) {
1540 MDefinition* input = getOperand(0);
1541 if (!input->isConstant() ||
1542 !input->toConstant()->isTypeRepresentableAsDouble()) {
1543 return this;
1546 UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(function());
1548 double in = input->toConstant()->numberToDouble();
1550 // The function pointer call can't GC.
1551 JS::AutoSuppressGCAnalysis nogc;
1552 double out = funPtr(in);
1554 if (input->type() == MIRType::Float32) {
1555 return MConstant::NewFloat32(alloc, out);
1557 return MConstant::New(alloc, DoubleValue(out));
1560 MDefinition* MAtomicIsLockFree::foldsTo(TempAllocator& alloc) {
1561 MDefinition* input = getOperand(0);
1562 if (!input->isConstant() || input->type() != MIRType::Int32) {
1563 return this;
1566 int32_t i = input->toConstant()->toInt32();
1567 return MConstant::New(alloc, BooleanValue(AtomicOperations::isLockfreeJS(i)));
1570 // Define |THIS_SLOT| as part of this translation unit, as it is used to
1571 // specialized the parameterized |New| function calls introduced by
1572 // TRIVIAL_NEW_WRAPPERS.
1573 const int32_t MParameter::THIS_SLOT;
1575 #ifdef JS_JITSPEW
1576 void MParameter::printOpcode(GenericPrinter& out) const {
1577 PrintOpcodeName(out, op());
1578 if (index() == THIS_SLOT) {
1579 out.printf(" THIS_SLOT");
1580 } else {
1581 out.printf(" %d", index());
1584 #endif
1586 HashNumber MParameter::valueHash() const {
1587 HashNumber hash = MDefinition::valueHash();
1588 hash = addU32ToHash(hash, index_);
1589 return hash;
1592 bool MParameter::congruentTo(const MDefinition* ins) const {
1593 if (!ins->isParameter()) {
1594 return false;
1597 return ins->toParameter()->index() == index_;
1600 WrappedFunction::WrappedFunction(JSFunction* nativeFun, uint16_t nargs,
1601 FunctionFlags flags)
1602 : nativeFun_(nativeFun), nargs_(nargs), flags_(flags) {
1603 MOZ_ASSERT_IF(nativeFun, isNativeWithoutJitEntry());
1605 #ifdef DEBUG
1606 // If we are not running off-main thread we can assert that the
1607 // metadata is consistent.
1608 if (!CanUseExtraThreads() && nativeFun) {
1609 MOZ_ASSERT(nativeFun->nargs() == nargs);
1611 MOZ_ASSERT(nativeFun->isNativeWithoutJitEntry() ==
1612 isNativeWithoutJitEntry());
1613 MOZ_ASSERT(nativeFun->hasJitEntry() == hasJitEntry());
1614 MOZ_ASSERT(nativeFun->isConstructor() == isConstructor());
1615 MOZ_ASSERT(nativeFun->isClassConstructor() == isClassConstructor());
1617 #endif
1620 MCall* MCall::New(TempAllocator& alloc, WrappedFunction* target, size_t maxArgc,
1621 size_t numActualArgs, bool construct, bool ignoresReturnValue,
1622 bool isDOMCall, mozilla::Maybe<DOMObjectKind> objectKind) {
1623 MOZ_ASSERT(isDOMCall == objectKind.isSome());
1624 MOZ_ASSERT(maxArgc >= numActualArgs);
1625 MCall* ins;
1626 if (isDOMCall) {
1627 MOZ_ASSERT(!construct);
1628 ins = new (alloc) MCallDOMNative(target, numActualArgs, *objectKind);
1629 } else {
1630 ins =
1631 new (alloc) MCall(target, numActualArgs, construct, ignoresReturnValue);
1633 if (!ins->init(alloc, maxArgc + NumNonArgumentOperands)) {
1634 return nullptr;
1636 return ins;
1639 AliasSet MCallDOMNative::getAliasSet() const {
1640 const JSJitInfo* jitInfo = getJitInfo();
1642 // If we don't know anything about the types of our arguments, we have to
1643 // assume that type-coercions can have side-effects, so we need to alias
1644 // everything.
1645 if (jitInfo->aliasSet() == JSJitInfo::AliasEverything ||
1646 !jitInfo->isTypedMethodJitInfo()) {
1647 return AliasSet::Store(AliasSet::Any);
1650 uint32_t argIndex = 0;
1651 const JSTypedMethodJitInfo* methodInfo =
1652 reinterpret_cast<const JSTypedMethodJitInfo*>(jitInfo);
1653 for (const JSJitInfo::ArgType* argType = methodInfo->argTypes;
1654 *argType != JSJitInfo::ArgTypeListEnd; ++argType, ++argIndex) {
1655 if (argIndex >= numActualArgs()) {
1656 // Passing through undefined can't have side-effects
1657 continue;
1659 // getArg(0) is "this", so skip it
1660 MDefinition* arg = getArg(argIndex + 1);
1661 MIRType actualType = arg->type();
1662 // The only way to reliably avoid side-effects given the information we
1663 // have here is if we're passing in a known primitive value to an
1664 // argument that expects a primitive value.
1666 // XXXbz maybe we need to communicate better information. For example,
1667 // a sequence argument will sort of unavoidably have side effects, while
1668 // a typed array argument won't have any, but both are claimed to be
1669 // JSJitInfo::Object. But if we do that, we need to watch out for our
1670 // movability/DCE-ability bits: if we have an arg type that can reliably
1671 // throw an exception on conversion, that might not affect our alias set
1672 // per se, but it should prevent us being moved or DCE-ed, unless we
1673 // know the incoming things match that arg type and won't throw.
1675 if ((actualType == MIRType::Value || actualType == MIRType::Object) ||
1676 (*argType & JSJitInfo::Object)) {
1677 return AliasSet::Store(AliasSet::Any);
1681 // We checked all the args, and they check out. So we only alias DOM
1682 // mutations or alias nothing, depending on the alias set in the jitinfo.
1683 if (jitInfo->aliasSet() == JSJitInfo::AliasNone) {
1684 return AliasSet::None();
1687 MOZ_ASSERT(jitInfo->aliasSet() == JSJitInfo::AliasDOMSets);
1688 return AliasSet::Load(AliasSet::DOMProperty);
1691 void MCallDOMNative::computeMovable() {
1692 // We are movable if the jitinfo says we can be and if we're also not
1693 // effectful. The jitinfo can't check for the latter, since it depends on
1694 // the types of our arguments.
1695 const JSJitInfo* jitInfo = getJitInfo();
1697 MOZ_ASSERT_IF(jitInfo->isMovable,
1698 jitInfo->aliasSet() != JSJitInfo::AliasEverything);
1700 if (jitInfo->isMovable && !isEffectful()) {
1701 setMovable();
1705 bool MCallDOMNative::congruentTo(const MDefinition* ins) const {
1706 if (!isMovable()) {
1707 return false;
1710 if (!ins->isCall()) {
1711 return false;
1714 const MCall* call = ins->toCall();
1716 if (!call->isCallDOMNative()) {
1717 return false;
1720 if (getSingleTarget() != call->getSingleTarget()) {
1721 return false;
1724 if (isConstructing() != call->isConstructing()) {
1725 return false;
1728 if (numActualArgs() != call->numActualArgs()) {
1729 return false;
1732 if (!congruentIfOperandsEqual(call)) {
1733 return false;
1736 // The other call had better be movable at this point!
1737 MOZ_ASSERT(call->isMovable());
1739 return true;
1742 const JSJitInfo* MCallDOMNative::getJitInfo() const {
1743 MOZ_ASSERT(getSingleTarget()->hasJitInfo());
1744 return getSingleTarget()->jitInfo();
1747 MCallClassHook* MCallClassHook::New(TempAllocator& alloc, JSNative target,
1748 uint32_t argc, bool constructing) {
1749 auto* ins = new (alloc) MCallClassHook(target, constructing);
1751 // Add callee + |this| + (if constructing) newTarget.
1752 uint32_t numOperands = 2 + argc + constructing;
1754 if (!ins->init(alloc, numOperands)) {
1755 return nullptr;
1758 return ins;
1761 MDefinition* MStringLength::foldsTo(TempAllocator& alloc) {
1762 if (string()->isConstant()) {
1763 JSString* str = string()->toConstant()->toString();
1764 return MConstant::New(alloc, Int32Value(str->length()));
1767 // MFromCharCode returns a one-element string.
1768 if (string()->isFromCharCode()) {
1769 return MConstant::New(alloc, Int32Value(1));
1772 return this;
1775 MDefinition* MConcat::foldsTo(TempAllocator& alloc) {
1776 if (lhs()->isConstant() && lhs()->toConstant()->toString()->empty()) {
1777 return rhs();
1780 if (rhs()->isConstant() && rhs()->toConstant()->toString()->empty()) {
1781 return lhs();
1784 return this;
1787 MDefinition* MStringConvertCase::foldsTo(TempAllocator& alloc) {
1788 MDefinition* string = this->string();
1790 // Handle the pattern |str[idx].toUpperCase()| and simplify it from
1791 // |StringConvertCase(FromCharCode(CharCodeAt(str, idx)))| to just
1792 // |CharCodeConvertCase(CharCodeAt(str, idx))|.
1793 if (string->isFromCharCode()) {
1794 auto* charCode = string->toFromCharCode()->code();
1795 auto mode = mode_ == Mode::LowerCase ? MCharCodeConvertCase::LowerCase
1796 : MCharCodeConvertCase::UpperCase;
1797 return MCharCodeConvertCase::New(alloc, charCode, mode);
1800 // Handle the pattern |num.toString(base).toUpperCase()| and simplify it to
1801 // directly return the string representation in the correct case.
1802 if (string->isInt32ToStringWithBase()) {
1803 auto* toString = string->toInt32ToStringWithBase();
1805 bool lowerCase = mode_ == Mode::LowerCase;
1806 if (toString->lowerCase() == lowerCase) {
1807 return toString;
1809 return MInt32ToStringWithBase::New(alloc, toString->input(),
1810 toString->base(), lowerCase);
1813 return this;
1816 static bool IsSubstrTo(MSubstr* substr, int32_t len) {
1817 // We want to match this pattern:
1819 // Substr(string, Constant(0), Min(Constant(length), StringLength(string)))
1821 // which is generated for the self-hosted `String.p.{substring,slice,substr}`
1822 // functions when called with constants `start` and `end` parameters.
1824 auto isConstantZero = [](auto* def) {
1825 return def->isConstant() && def->toConstant()->isInt32(0);
1828 if (!isConstantZero(substr->begin())) {
1829 return false;
1832 auto* length = substr->length();
1833 if (length->isBitOr()) {
1834 // Unnecessary bit-ops haven't yet been removed.
1835 auto* bitOr = length->toBitOr();
1836 if (isConstantZero(bitOr->lhs())) {
1837 length = bitOr->rhs();
1838 } else if (isConstantZero(bitOr->rhs())) {
1839 length = bitOr->lhs();
1842 if (!length->isMinMax() || length->toMinMax()->isMax()) {
1843 return false;
1846 auto* min = length->toMinMax();
1847 if (!min->lhs()->isConstant() && !min->rhs()->isConstant()) {
1848 return false;
1851 auto* minConstant = min->lhs()->isConstant() ? min->lhs()->toConstant()
1852 : min->rhs()->toConstant();
1854 auto* minOperand = min->lhs()->isConstant() ? min->rhs() : min->lhs();
1855 if (!minOperand->isStringLength() ||
1856 minOperand->toStringLength()->string() != substr->string()) {
1857 return false;
1860 // Ensure |len| matches the substring's length.
1861 return minConstant->isInt32(len);
1864 MDefinition* MSubstr::foldsTo(TempAllocator& alloc) {
1865 // Fold |str.substring(0, 1)| to |str.charAt(0)|.
1866 if (!IsSubstrTo(this, 1)) {
1867 return this;
1870 auto* charCode = MCharCodeAtOrNegative::New(alloc, string(), begin());
1871 block()->insertBefore(this, charCode);
1873 return MFromCharCodeEmptyIfNegative::New(alloc, charCode);
1876 MDefinition* MCharCodeAt::foldsTo(TempAllocator& alloc) {
1877 MDefinition* string = this->string();
1878 if (!string->isConstant() && !string->isFromCharCode()) {
1879 return this;
1882 MDefinition* index = this->index();
1883 if (index->isSpectreMaskIndex()) {
1884 index = index->toSpectreMaskIndex()->index();
1886 if (!index->isConstant()) {
1887 return this;
1889 int32_t idx = index->toConstant()->toInt32();
1891 // Handle the pattern |s[idx].charCodeAt(0)|.
1892 if (string->isFromCharCode()) {
1893 if (idx != 0) {
1894 return this;
1897 // Simplify |CharCodeAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
1898 // |CharCodeAt(s, idx)|.
1899 auto* charCode = string->toFromCharCode()->code();
1900 if (!charCode->isCharCodeAt()) {
1901 return this;
1904 return charCode;
1907 JSLinearString* str = &string->toConstant()->toString()->asLinear();
1908 if (idx < 0 || uint32_t(idx) >= str->length()) {
1909 return this;
1912 char16_t ch = str->latin1OrTwoByteChar(idx);
1913 return MConstant::New(alloc, Int32Value(ch));
1916 MDefinition* MCodePointAt::foldsTo(TempAllocator& alloc) {
1917 MDefinition* string = this->string();
1918 if (!string->isConstant() && !string->isFromCharCode()) {
1919 return this;
1922 MDefinition* index = this->index();
1923 if (index->isSpectreMaskIndex()) {
1924 index = index->toSpectreMaskIndex()->index();
1926 if (!index->isConstant()) {
1927 return this;
1929 int32_t idx = index->toConstant()->toInt32();
1931 // Handle the pattern |s[idx].codePointAt(0)|.
1932 if (string->isFromCharCode()) {
1933 if (idx != 0) {
1934 return this;
1937 // Simplify |CodePointAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
1938 // |CharCodeAt(s, idx)|.
1939 auto* charCode = string->toFromCharCode()->code();
1940 if (!charCode->isCharCodeAt()) {
1941 return this;
1944 return charCode;
1947 JSLinearString* str = &string->toConstant()->toString()->asLinear();
1948 if (idx < 0 || uint32_t(idx) >= str->length()) {
1949 return this;
1952 char32_t first = str->latin1OrTwoByteChar(idx);
1953 if (unicode::IsLeadSurrogate(first) && uint32_t(idx) + 1 < str->length()) {
1954 char32_t second = str->latin1OrTwoByteChar(idx + 1);
1955 if (unicode::IsTrailSurrogate(second)) {
1956 first = unicode::UTF16Decode(first, second);
1959 return MConstant::New(alloc, Int32Value(first));
1962 MDefinition* MToRelativeStringIndex::foldsTo(TempAllocator& alloc) {
1963 MDefinition* index = this->index();
1964 MDefinition* length = this->length();
1966 if (!index->isConstant()) {
1967 return this;
1969 if (!length->isStringLength() && !length->isConstant()) {
1970 return this;
1972 MOZ_ASSERT_IF(length->isConstant(), length->toConstant()->toInt32() >= 0);
1974 int32_t relativeIndex = index->toConstant()->toInt32();
1975 if (relativeIndex >= 0) {
1976 return index;
1979 // Safe to truncate because |length| is never negative.
1980 return MAdd::New(alloc, index, length, TruncateKind::Truncate);
1983 template <size_t Arity>
1984 [[nodiscard]] static bool EnsureFloatInputOrConvert(
1985 MAryInstruction<Arity>* owner, TempAllocator& alloc) {
1986 MOZ_ASSERT(!IsFloatingPointType(owner->type()),
1987 "Floating point types must check consumers");
1989 if (AllOperandsCanProduceFloat32(owner)) {
1990 return true;
1992 ConvertOperandsToDouble(owner, alloc);
1993 return false;
1996 template <size_t Arity>
1997 [[nodiscard]] static bool EnsureFloatConsumersAndInputOrConvert(
1998 MAryInstruction<Arity>* owner, TempAllocator& alloc) {
1999 MOZ_ASSERT(IsFloatingPointType(owner->type()),
2000 "Integer types don't need to check consumers");
2002 if (AllOperandsCanProduceFloat32(owner) &&
2003 CheckUsesAreFloat32Consumers(owner)) {
2004 return true;
2006 ConvertOperandsToDouble(owner, alloc);
2007 return false;
2010 void MFloor::trySpecializeFloat32(TempAllocator& alloc) {
2011 MOZ_ASSERT(type() == MIRType::Int32);
2012 if (EnsureFloatInputOrConvert(this, alloc)) {
2013 specialization_ = MIRType::Float32;
2017 void MCeil::trySpecializeFloat32(TempAllocator& alloc) {
2018 MOZ_ASSERT(type() == MIRType::Int32);
2019 if (EnsureFloatInputOrConvert(this, alloc)) {
2020 specialization_ = MIRType::Float32;
2024 void MRound::trySpecializeFloat32(TempAllocator& alloc) {
2025 MOZ_ASSERT(type() == MIRType::Int32);
2026 if (EnsureFloatInputOrConvert(this, alloc)) {
2027 specialization_ = MIRType::Float32;
2031 void MTrunc::trySpecializeFloat32(TempAllocator& alloc) {
2032 MOZ_ASSERT(type() == MIRType::Int32);
2033 if (EnsureFloatInputOrConvert(this, alloc)) {
2034 specialization_ = MIRType::Float32;
2038 void MNearbyInt::trySpecializeFloat32(TempAllocator& alloc) {
2039 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
2040 specialization_ = MIRType::Float32;
2041 setResultType(MIRType::Float32);
2045 MGoto* MGoto::New(TempAllocator& alloc, MBasicBlock* target) {
2046 return new (alloc) MGoto(target);
2049 MGoto* MGoto::New(TempAllocator::Fallible alloc, MBasicBlock* target) {
2050 MOZ_ASSERT(target);
2051 return new (alloc) MGoto(target);
2054 MGoto* MGoto::New(TempAllocator& alloc) { return new (alloc) MGoto(nullptr); }
2056 MDefinition* MBox::foldsTo(TempAllocator& alloc) {
2057 if (input()->isUnbox()) {
2058 return input()->toUnbox()->input();
2060 return this;
2063 #ifdef JS_JITSPEW
2064 void MUnbox::printOpcode(GenericPrinter& out) const {
2065 PrintOpcodeName(out, op());
2066 out.printf(" ");
2067 getOperand(0)->printName(out);
2068 out.printf(" ");
2070 switch (type()) {
2071 case MIRType::Int32:
2072 out.printf("to Int32");
2073 break;
2074 case MIRType::Double:
2075 out.printf("to Double");
2076 break;
2077 case MIRType::Boolean:
2078 out.printf("to Boolean");
2079 break;
2080 case MIRType::String:
2081 out.printf("to String");
2082 break;
2083 case MIRType::Symbol:
2084 out.printf("to Symbol");
2085 break;
2086 case MIRType::BigInt:
2087 out.printf("to BigInt");
2088 break;
2089 case MIRType::Object:
2090 out.printf("to Object");
2091 break;
2092 default:
2093 break;
2096 switch (mode()) {
2097 case Fallible:
2098 out.printf(" (fallible)");
2099 break;
2100 case Infallible:
2101 out.printf(" (infallible)");
2102 break;
2103 default:
2104 break;
2107 #endif
2109 MDefinition* MUnbox::foldsTo(TempAllocator& alloc) {
2110 if (input()->isBox()) {
2111 MDefinition* unboxed = input()->toBox()->input();
2113 // Fold MUnbox(MBox(x)) => x if types match.
2114 if (unboxed->type() == type()) {
2115 if (fallible()) {
2116 unboxed->setImplicitlyUsedUnchecked();
2118 return unboxed;
2121 // Fold MUnbox(MBox(x)) => MToDouble(x) if possible.
2122 if (type() == MIRType::Double &&
2123 IsTypeRepresentableAsDouble(unboxed->type())) {
2124 if (unboxed->isConstant()) {
2125 return MConstant::New(
2126 alloc, DoubleValue(unboxed->toConstant()->numberToDouble()));
2129 return MToDouble::New(alloc, unboxed);
2132 // MUnbox<Int32>(MBox<Double>(x)) will always fail, even if x can be
2133 // represented as an Int32. Fold to avoid unnecessary bailouts.
2134 if (type() == MIRType::Int32 && unboxed->type() == MIRType::Double) {
2135 auto* folded = MToNumberInt32::New(alloc, unboxed,
2136 IntConversionInputKind::NumbersOnly);
2137 folded->setGuard();
2138 return folded;
2142 return this;
2145 #ifdef DEBUG
2146 void MPhi::assertLoopPhi() const {
2147 // getLoopPredecessorOperand and getLoopBackedgeOperand rely on these
2148 // predecessors being at known indices.
2149 if (block()->numPredecessors() == 2) {
2150 MBasicBlock* pred = block()->getPredecessor(0);
2151 MBasicBlock* back = block()->getPredecessor(1);
2152 MOZ_ASSERT(pred == block()->loopPredecessor());
2153 MOZ_ASSERT(pred->successorWithPhis() == block());
2154 MOZ_ASSERT(pred->positionInPhiSuccessor() == 0);
2155 MOZ_ASSERT(back == block()->backedge());
2156 MOZ_ASSERT(back->successorWithPhis() == block());
2157 MOZ_ASSERT(back->positionInPhiSuccessor() == 1);
2158 } else {
2159 // After we remove fake loop predecessors for loop headers that
2160 // are only reachable via OSR, the only predecessor is the
2161 // loop backedge.
2162 MOZ_ASSERT(block()->numPredecessors() == 1);
2163 MOZ_ASSERT(block()->graph().osrBlock());
2164 MOZ_ASSERT(!block()->graph().canBuildDominators());
2165 MBasicBlock* back = block()->getPredecessor(0);
2166 MOZ_ASSERT(back == block()->backedge());
2167 MOZ_ASSERT(back->successorWithPhis() == block());
2168 MOZ_ASSERT(back->positionInPhiSuccessor() == 0);
2171 #endif
2173 MDefinition* MPhi::getLoopPredecessorOperand() const {
2174 // This should not be called after removing fake loop predecessors.
2175 MOZ_ASSERT(block()->numPredecessors() == 2);
2176 assertLoopPhi();
2177 return getOperand(0);
2180 MDefinition* MPhi::getLoopBackedgeOperand() const {
2181 assertLoopPhi();
2182 uint32_t idx = block()->numPredecessors() == 2 ? 1 : 0;
2183 return getOperand(idx);
2186 void MPhi::removeOperand(size_t index) {
2187 MOZ_ASSERT(index < numOperands());
2188 MOZ_ASSERT(getUseFor(index)->index() == index);
2189 MOZ_ASSERT(getUseFor(index)->consumer() == this);
2191 // If we have phi(..., a, b, c, d, ..., z) and we plan
2192 // on removing a, then first shift downward so that we have
2193 // phi(..., b, c, d, ..., z, z):
2194 MUse* p = inputs_.begin() + index;
2195 MUse* e = inputs_.end();
2196 p->producer()->removeUse(p);
2197 for (; p < e - 1; ++p) {
2198 MDefinition* producer = (p + 1)->producer();
2199 p->setProducerUnchecked(producer);
2200 producer->replaceUse(p + 1, p);
2203 // truncate the inputs_ list:
2204 inputs_.popBack();
2207 void MPhi::removeAllOperands() {
2208 for (MUse& p : inputs_) {
2209 p.producer()->removeUse(&p);
2211 inputs_.clear();
2214 MDefinition* MPhi::foldsTernary(TempAllocator& alloc) {
2215 /* Look if this MPhi is a ternary construct.
2216 * This is a very loose term as it actually only checks for
2218 * MTest X
2219 * / \
2220 * ... ...
2221 * \ /
2222 * MPhi X Y
2224 * Which we will simply call:
2225 * x ? x : y or x ? y : x
2228 if (numOperands() != 2) {
2229 return nullptr;
2232 MOZ_ASSERT(block()->numPredecessors() == 2);
2234 MBasicBlock* pred = block()->immediateDominator();
2235 if (!pred || !pred->lastIns()->isTest()) {
2236 return nullptr;
2239 MTest* test = pred->lastIns()->toTest();
2241 // True branch may only dominate one edge of MPhi.
2242 if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
2243 test->ifTrue()->dominates(block()->getPredecessor(1))) {
2244 return nullptr;
2247 // False branch may only dominate one edge of MPhi.
2248 if (test->ifFalse()->dominates(block()->getPredecessor(0)) ==
2249 test->ifFalse()->dominates(block()->getPredecessor(1))) {
2250 return nullptr;
2253 // True and false branch must dominate different edges of MPhi.
2254 if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
2255 test->ifFalse()->dominates(block()->getPredecessor(0))) {
2256 return nullptr;
2259 // We found a ternary construct.
2260 bool firstIsTrueBranch =
2261 test->ifTrue()->dominates(block()->getPredecessor(0));
2262 MDefinition* trueDef = firstIsTrueBranch ? getOperand(0) : getOperand(1);
2263 MDefinition* falseDef = firstIsTrueBranch ? getOperand(1) : getOperand(0);
2265 // Accept either
2266 // testArg ? testArg : constant or
2267 // testArg ? constant : testArg
2268 if (!trueDef->isConstant() && !falseDef->isConstant()) {
2269 return nullptr;
2272 MConstant* c =
2273 trueDef->isConstant() ? trueDef->toConstant() : falseDef->toConstant();
2274 MDefinition* testArg = (trueDef == c) ? falseDef : trueDef;
2275 if (testArg != test->input()) {
2276 return nullptr;
2279 // This check should be a tautology, except that the constant might be the
2280 // result of the removal of a branch. In such case the domination scope of
2281 // the block which is holding the constant might be incomplete. This
2282 // condition is used to prevent doing this optimization based on incomplete
2283 // information.
2285 // As GVN removed a branch, it will update the dominations rules before
2286 // trying to fold this MPhi again. Thus, this condition does not inhibit
2287 // this optimization.
2288 MBasicBlock* truePred = block()->getPredecessor(firstIsTrueBranch ? 0 : 1);
2289 MBasicBlock* falsePred = block()->getPredecessor(firstIsTrueBranch ? 1 : 0);
2290 if (!trueDef->block()->dominates(truePred) ||
2291 !falseDef->block()->dominates(falsePred)) {
2292 return nullptr;
2295 // If testArg is an int32 type we can:
2296 // - fold testArg ? testArg : 0 to testArg
2297 // - fold testArg ? 0 : testArg to 0
2298 if (testArg->type() == MIRType::Int32 && c->numberToDouble() == 0) {
2299 testArg->setGuardRangeBailoutsUnchecked();
2301 // When folding to the constant we need to hoist it.
2302 if (trueDef == c && !c->block()->dominates(block())) {
2303 c->block()->moveBefore(pred->lastIns(), c);
2305 return trueDef;
2308 // If testArg is an double type we can:
2309 // - fold testArg ? testArg : 0.0 to MNaNToZero(testArg)
2310 if (testArg->type() == MIRType::Double &&
2311 mozilla::IsPositiveZero(c->numberToDouble()) && c != trueDef) {
2312 MNaNToZero* replace = MNaNToZero::New(alloc, testArg);
2313 test->block()->insertBefore(test, replace);
2314 return replace;
2317 // If testArg is a string type we can:
2318 // - fold testArg ? testArg : "" to testArg
2319 // - fold testArg ? "" : testArg to ""
2320 if (testArg->type() == MIRType::String &&
2321 c->toString() == GetJitContext()->runtime->emptyString()) {
2322 // When folding to the constant we need to hoist it.
2323 if (trueDef == c && !c->block()->dominates(block())) {
2324 c->block()->moveBefore(pred->lastIns(), c);
2326 return trueDef;
2329 return nullptr;
2332 MDefinition* MPhi::operandIfRedundant() {
2333 if (inputs_.length() == 0) {
2334 return nullptr;
2337 // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
2338 // returns the operand that it will always be equal to (a, in
2339 // those two cases).
2340 MDefinition* first = getOperand(0);
2341 for (size_t i = 1, e = numOperands(); i < e; i++) {
2342 MDefinition* op = getOperand(i);
2343 if (op != first && op != this) {
2344 return nullptr;
2347 return first;
2350 MDefinition* MPhi::foldsTo(TempAllocator& alloc) {
2351 if (MDefinition* def = operandIfRedundant()) {
2352 return def;
2355 if (MDefinition* def = foldsTernary(alloc)) {
2356 return def;
2359 return this;
2362 bool MPhi::congruentTo(const MDefinition* ins) const {
2363 if (!ins->isPhi()) {
2364 return false;
2367 // Phis in different blocks may have different control conditions.
2368 // For example, these phis:
2370 // if (p)
2371 // goto a
2372 // a:
2373 // t = phi(x, y)
2375 // if (q)
2376 // goto b
2377 // b:
2378 // s = phi(x, y)
2380 // have identical operands, but they are not equvalent because t is
2381 // effectively p?x:y and s is effectively q?x:y.
2383 // For now, consider phis in different blocks incongruent.
2384 if (ins->block() != block()) {
2385 return false;
2388 return congruentIfOperandsEqual(ins);
2391 void MPhi::updateForReplacement(MPhi* other) {
2392 // This function is called to fix the current Phi flags using it as a
2393 // replacement of the other Phi instruction |other|.
2395 // When dealing with usage analysis, any Use will replace all other values,
2396 // such as Unused and Unknown. Unless both are Unused, the merge would be
2397 // Unknown.
2398 if (usageAnalysis_ == PhiUsage::Used ||
2399 other->usageAnalysis_ == PhiUsage::Used) {
2400 usageAnalysis_ = PhiUsage::Used;
2401 } else if (usageAnalysis_ != other->usageAnalysis_) {
2402 // this == unused && other == unknown
2403 // or this == unknown && other == unused
2404 usageAnalysis_ = PhiUsage::Unknown;
2405 } else {
2406 // this == unused && other == unused
2407 // or this == unknown && other = unknown
2408 MOZ_ASSERT(usageAnalysis_ == PhiUsage::Unused ||
2409 usageAnalysis_ == PhiUsage::Unknown);
2410 MOZ_ASSERT(usageAnalysis_ == other->usageAnalysis_);
2414 /* static */
2415 bool MPhi::markIteratorPhis(const PhiVector& iterators) {
2416 // Find and mark phis that must transitively hold an iterator live.
2418 Vector<MPhi*, 8, SystemAllocPolicy> worklist;
2420 for (MPhi* iter : iterators) {
2421 if (!iter->isInWorklist()) {
2422 if (!worklist.append(iter)) {
2423 return false;
2425 iter->setInWorklist();
2429 while (!worklist.empty()) {
2430 MPhi* phi = worklist.popCopy();
2431 phi->setNotInWorklist();
2433 phi->setIterator();
2434 phi->setImplicitlyUsedUnchecked();
2436 for (MUseDefIterator iter(phi); iter; iter++) {
2437 MDefinition* use = iter.def();
2438 if (!use->isInWorklist() && use->isPhi() && !use->toPhi()->isIterator()) {
2439 if (!worklist.append(use->toPhi())) {
2440 return false;
2442 use->setInWorklist();
2447 return true;
2450 bool MPhi::typeIncludes(MDefinition* def) {
2451 MOZ_ASSERT(!IsMagicType(def->type()));
2453 if (def->type() == MIRType::Int32 && this->type() == MIRType::Double) {
2454 return true;
2457 if (def->type() == MIRType::Value) {
2458 // This phi must be able to be any value.
2459 return this->type() == MIRType::Value;
2462 return this->mightBeType(def->type());
2465 void MCallBase::addArg(size_t argnum, MDefinition* arg) {
2466 // The operand vector is initialized in reverse order by WarpBuilder.
2467 // It cannot be checked for consistency until all arguments are added.
2468 // FixedList doesn't initialize its elements, so do an unchecked init.
2469 initOperand(argnum + NumNonArgumentOperands, arg);
2472 static inline bool IsConstant(MDefinition* def, double v) {
2473 if (!def->isConstant()) {
2474 return false;
2477 return NumbersAreIdentical(def->toConstant()->numberToDouble(), v);
2480 MDefinition* MBinaryBitwiseInstruction::foldsTo(TempAllocator& alloc) {
2481 // Identity operations are removed (for int32 only) in foldUnnecessaryBitop.
2483 if (type() == MIRType::Int32) {
2484 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) {
2485 return folded;
2487 } else if (type() == MIRType::Int64) {
2488 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
2489 return folded;
2493 return this;
2496 MDefinition* MBinaryBitwiseInstruction::foldUnnecessaryBitop() {
2497 // It's probably OK to perform this optimization only for int32, as it will
2498 // have the greatest effect for asm.js code that is compiled with the JS
2499 // pipeline, and that code will not see int64 values.
2501 if (type() != MIRType::Int32) {
2502 return this;
2505 // Fold unsigned shift right operator when the second operand is zero and
2506 // the only use is an unsigned modulo. Thus, the expression
2507 // |(x >>> 0) % y| becomes |x % y|.
2508 if (isUrsh() && IsUint32Type(this)) {
2509 MDefinition* defUse = maybeSingleDefUse();
2510 if (defUse && defUse->isMod() && defUse->toMod()->isUnsigned()) {
2511 return getOperand(0);
2515 // Eliminate bitwise operations that are no-ops when used on integer
2516 // inputs, such as (x | 0).
2518 MDefinition* lhs = getOperand(0);
2519 MDefinition* rhs = getOperand(1);
2521 if (IsConstant(lhs, 0)) {
2522 return foldIfZero(0);
2525 if (IsConstant(rhs, 0)) {
2526 return foldIfZero(1);
2529 if (IsConstant(lhs, -1)) {
2530 return foldIfNegOne(0);
2533 if (IsConstant(rhs, -1)) {
2534 return foldIfNegOne(1);
2537 if (lhs == rhs) {
2538 return foldIfEqual();
2541 if (maskMatchesRightRange) {
2542 MOZ_ASSERT(lhs->isConstant());
2543 MOZ_ASSERT(lhs->type() == MIRType::Int32);
2544 return foldIfAllBitsSet(0);
2547 if (maskMatchesLeftRange) {
2548 MOZ_ASSERT(rhs->isConstant());
2549 MOZ_ASSERT(rhs->type() == MIRType::Int32);
2550 return foldIfAllBitsSet(1);
2553 return this;
2556 static inline bool CanProduceNegativeZero(MDefinition* def) {
2557 // Test if this instruction can produce negative zero even when bailing out
2558 // and changing types.
2559 switch (def->op()) {
2560 case MDefinition::Opcode::Constant:
2561 if (def->type() == MIRType::Double &&
2562 def->toConstant()->toDouble() == -0.0) {
2563 return true;
2565 [[fallthrough]];
2566 case MDefinition::Opcode::BitAnd:
2567 case MDefinition::Opcode::BitOr:
2568 case MDefinition::Opcode::BitXor:
2569 case MDefinition::Opcode::BitNot:
2570 case MDefinition::Opcode::Lsh:
2571 case MDefinition::Opcode::Rsh:
2572 return false;
2573 default:
2574 return true;
2578 static inline bool NeedNegativeZeroCheck(MDefinition* def) {
2579 if (def->isGuard() || def->isGuardRangeBailouts()) {
2580 return true;
2583 // Test if all uses have the same semantics for -0 and 0
2584 for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) {
2585 if (use->consumer()->isResumePoint()) {
2586 return true;
2589 MDefinition* use_def = use->consumer()->toDefinition();
2590 switch (use_def->op()) {
2591 case MDefinition::Opcode::Add: {
2592 // If add is truncating -0 and 0 are observed as the same.
2593 if (use_def->toAdd()->isTruncated()) {
2594 break;
2597 // x + y gives -0, when both x and y are -0
2599 // Figure out the order in which the addition's operands will
2600 // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR
2601 // definitions for us so that this just requires comparing ids.
2602 MDefinition* first = use_def->toAdd()->lhs();
2603 MDefinition* second = use_def->toAdd()->rhs();
2604 if (first->id() > second->id()) {
2605 std::swap(first, second);
2607 // Negative zero checks can be removed on the first executed
2608 // operand only if it is guaranteed the second executed operand
2609 // will produce a value other than -0. While the second is
2610 // typed as an int32, a bailout taken between execution of the
2611 // operands may change that type and cause a -0 to flow to the
2612 // second.
2614 // There is no way to test whether there are any bailouts
2615 // between execution of the operands, so remove negative
2616 // zero checks from the first only if the second's type is
2617 // independent from type changes that may occur after bailing.
2618 if (def == first && CanProduceNegativeZero(second)) {
2619 return true;
2622 // The negative zero check can always be removed on the second
2623 // executed operand; by the time this executes the first will have
2624 // been evaluated as int32 and the addition's result cannot be -0.
2625 break;
2627 case MDefinition::Opcode::Sub: {
2628 // If sub is truncating -0 and 0 are observed as the same
2629 if (use_def->toSub()->isTruncated()) {
2630 break;
2633 // x + y gives -0, when x is -0 and y is 0
2635 // We can remove the negative zero check on the rhs, only if we
2636 // are sure the lhs isn't negative zero.
2638 // The lhs is typed as integer (i.e. not -0.0), but it can bailout
2639 // and change type. This should be fine if the lhs is executed
2640 // first. However if the rhs is executed first, the lhs can bail,
2641 // change type and become -0.0 while the rhs has already been
2642 // optimized to not make a difference between zero and negative zero.
2643 MDefinition* lhs = use_def->toSub()->lhs();
2644 MDefinition* rhs = use_def->toSub()->rhs();
2645 if (rhs->id() < lhs->id() && CanProduceNegativeZero(lhs)) {
2646 return true;
2649 [[fallthrough]];
2651 case MDefinition::Opcode::StoreElement:
2652 case MDefinition::Opcode::StoreHoleValueElement:
2653 case MDefinition::Opcode::LoadElement:
2654 case MDefinition::Opcode::LoadElementHole:
2655 case MDefinition::Opcode::LoadUnboxedScalar:
2656 case MDefinition::Opcode::LoadDataViewElement:
2657 case MDefinition::Opcode::LoadTypedArrayElementHole:
2658 case MDefinition::Opcode::CharCodeAt:
2659 case MDefinition::Opcode::Mod:
2660 case MDefinition::Opcode::InArray:
2661 // Only allowed to remove check when definition is the second operand
2662 if (use_def->getOperand(0) == def) {
2663 return true;
2665 for (size_t i = 2, e = use_def->numOperands(); i < e; i++) {
2666 if (use_def->getOperand(i) == def) {
2667 return true;
2670 break;
2671 case MDefinition::Opcode::BoundsCheck:
2672 // Only allowed to remove check when definition is the first operand
2673 if (use_def->toBoundsCheck()->getOperand(1) == def) {
2674 return true;
2676 break;
2677 case MDefinition::Opcode::ToString:
2678 case MDefinition::Opcode::FromCharCode:
2679 case MDefinition::Opcode::FromCodePoint:
2680 case MDefinition::Opcode::TableSwitch:
2681 case MDefinition::Opcode::Compare:
2682 case MDefinition::Opcode::BitAnd:
2683 case MDefinition::Opcode::BitOr:
2684 case MDefinition::Opcode::BitXor:
2685 case MDefinition::Opcode::Abs:
2686 case MDefinition::Opcode::TruncateToInt32:
2687 // Always allowed to remove check. No matter which operand.
2688 break;
2689 case MDefinition::Opcode::StoreElementHole:
2690 case MDefinition::Opcode::StoreTypedArrayElementHole:
2691 case MDefinition::Opcode::PostWriteElementBarrier:
2692 // Only allowed to remove check when definition is the third operand.
2693 for (size_t i = 0, e = use_def->numOperands(); i < e; i++) {
2694 if (i == 2) {
2695 continue;
2697 if (use_def->getOperand(i) == def) {
2698 return true;
2701 break;
2702 default:
2703 return true;
2706 return false;
2709 #ifdef JS_JITSPEW
2710 void MBinaryArithInstruction::printOpcode(GenericPrinter& out) const {
2711 MDefinition::printOpcode(out);
2713 switch (type()) {
2714 case MIRType::Int32:
2715 if (isDiv()) {
2716 out.printf(" [%s]", toDiv()->isUnsigned() ? "uint32" : "int32");
2717 } else if (isMod()) {
2718 out.printf(" [%s]", toMod()->isUnsigned() ? "uint32" : "int32");
2719 } else {
2720 out.printf(" [int32]");
2722 break;
2723 case MIRType::Int64:
2724 if (isDiv()) {
2725 out.printf(" [%s]", toDiv()->isUnsigned() ? "uint64" : "int64");
2726 } else if (isMod()) {
2727 out.printf(" [%s]", toMod()->isUnsigned() ? "uint64" : "int64");
2728 } else {
2729 out.printf(" [int64]");
2731 break;
2732 case MIRType::Float32:
2733 out.printf(" [float]");
2734 break;
2735 case MIRType::Double:
2736 out.printf(" [double]");
2737 break;
2738 default:
2739 break;
2742 #endif
2744 MDefinition* MRsh::foldsTo(TempAllocator& alloc) {
2745 MDefinition* f = MBinaryBitwiseInstruction::foldsTo(alloc);
2747 if (f != this) {
2748 return f;
2751 MDefinition* lhs = getOperand(0);
2752 MDefinition* rhs = getOperand(1);
2754 // It's probably OK to perform this optimization only for int32, as it will
2755 // have the greatest effect for asm.js code that is compiled with the JS
2756 // pipeline, and that code will not see int64 values.
2758 if (!lhs->isLsh() || !rhs->isConstant() || rhs->type() != MIRType::Int32) {
2759 return this;
2762 if (!lhs->getOperand(1)->isConstant() ||
2763 lhs->getOperand(1)->type() != MIRType::Int32) {
2764 return this;
2767 uint32_t shift = rhs->toConstant()->toInt32();
2768 uint32_t shift_lhs = lhs->getOperand(1)->toConstant()->toInt32();
2769 if (shift != shift_lhs) {
2770 return this;
2773 switch (shift) {
2774 case 16:
2775 return MSignExtendInt32::New(alloc, lhs->getOperand(0),
2776 MSignExtendInt32::Half);
2777 case 24:
2778 return MSignExtendInt32::New(alloc, lhs->getOperand(0),
2779 MSignExtendInt32::Byte);
2782 return this;
2785 MDefinition* MBinaryArithInstruction::foldsTo(TempAllocator& alloc) {
2786 MOZ_ASSERT(IsNumberType(type()));
2788 MDefinition* lhs = getOperand(0);
2789 MDefinition* rhs = getOperand(1);
2791 if (type() == MIRType::Int64) {
2792 MOZ_ASSERT(!isTruncated());
2794 if (MConstant* folded = EvaluateInt64ConstantOperands(alloc, this)) {
2795 if (!folded->block()) {
2796 block()->insertBefore(this, folded);
2798 return folded;
2800 if (isSub() || isDiv() || isMod()) {
2801 return this;
2803 if (rhs->isConstant() &&
2804 rhs->toConstant()->toInt64() == int64_t(getIdentity())) {
2805 return lhs;
2807 if (lhs->isConstant() &&
2808 lhs->toConstant()->toInt64() == int64_t(getIdentity())) {
2809 return rhs;
2811 return this;
2814 if (MConstant* folded = EvaluateConstantOperands(alloc, this)) {
2815 if (isTruncated()) {
2816 if (!folded->block()) {
2817 block()->insertBefore(this, folded);
2819 if (folded->type() != MIRType::Int32) {
2820 return MTruncateToInt32::New(alloc, folded);
2823 return folded;
2826 if (mustPreserveNaN_) {
2827 return this;
2830 // 0 + -0 = 0. So we can't remove addition
2831 if (isAdd() && type() != MIRType::Int32) {
2832 return this;
2835 if (IsConstant(rhs, getIdentity())) {
2836 if (isTruncated()) {
2837 return MTruncateToInt32::New(alloc, lhs);
2839 return lhs;
2842 // subtraction isn't commutative. So we can't remove subtraction when lhs
2843 // equals 0
2844 if (isSub()) {
2845 return this;
2848 if (IsConstant(lhs, getIdentity())) {
2849 if (isTruncated()) {
2850 return MTruncateToInt32::New(alloc, rhs);
2852 return rhs; // id op x => x
2855 return this;
2858 void MBinaryArithInstruction::trySpecializeFloat32(TempAllocator& alloc) {
2859 MOZ_ASSERT(IsNumberType(type()));
2861 // Do not use Float32 if we can use int32.
2862 if (type() == MIRType::Int32) {
2863 return;
2866 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
2867 setResultType(MIRType::Float32);
2871 void MMinMax::trySpecializeFloat32(TempAllocator& alloc) {
2872 if (type() == MIRType::Int32) {
2873 return;
2876 MDefinition* left = lhs();
2877 MDefinition* right = rhs();
2879 if ((left->canProduceFloat32() ||
2880 (left->isMinMax() && left->type() == MIRType::Float32)) &&
2881 (right->canProduceFloat32() ||
2882 (right->isMinMax() && right->type() == MIRType::Float32))) {
2883 setResultType(MIRType::Float32);
2884 } else {
2885 ConvertOperandsToDouble(this, alloc);
2889 MDefinition* MMinMax::foldsTo(TempAllocator& alloc) {
2890 MOZ_ASSERT(lhs()->type() == type());
2891 MOZ_ASSERT(rhs()->type() == type());
2893 if (lhs() == rhs()) {
2894 return lhs();
2897 auto foldConstants = [&alloc](MDefinition* lhs, MDefinition* rhs,
2898 bool isMax) -> MConstant* {
2899 MOZ_ASSERT(lhs->type() == rhs->type());
2900 MOZ_ASSERT(lhs->toConstant()->isTypeRepresentableAsDouble());
2901 MOZ_ASSERT(rhs->toConstant()->isTypeRepresentableAsDouble());
2903 double lnum = lhs->toConstant()->numberToDouble();
2904 double rnum = rhs->toConstant()->numberToDouble();
2906 double result;
2907 if (isMax) {
2908 result = js::math_max_impl(lnum, rnum);
2909 } else {
2910 result = js::math_min_impl(lnum, rnum);
2913 // The folded MConstant should maintain the same MIRType with the original
2914 // inputs.
2915 if (lhs->type() == MIRType::Int32) {
2916 int32_t cast;
2917 if (mozilla::NumberEqualsInt32(result, &cast)) {
2918 return MConstant::New(alloc, Int32Value(cast));
2920 return nullptr;
2922 if (lhs->type() == MIRType::Float32) {
2923 return MConstant::NewFloat32(alloc, result);
2925 MOZ_ASSERT(lhs->type() == MIRType::Double);
2926 return MConstant::New(alloc, DoubleValue(result));
2929 // Try to fold the following patterns when |x| and |y| are constants.
2931 // min(min(x, z), min(y, z)) = min(min(x, y), z)
2932 // max(max(x, z), max(y, z)) = max(max(x, y), z)
2933 // max(min(x, z), min(y, z)) = min(max(x, y), z)
2934 // min(max(x, z), max(y, z)) = max(min(x, y), z)
2935 if (lhs()->isMinMax() && rhs()->isMinMax()) {
2936 do {
2937 auto* left = lhs()->toMinMax();
2938 auto* right = rhs()->toMinMax();
2939 if (left->isMax() != right->isMax()) {
2940 break;
2943 MDefinition* x;
2944 MDefinition* y;
2945 MDefinition* z;
2946 if (left->lhs() == right->lhs()) {
2947 std::tie(x, y, z) = std::tuple{left->rhs(), right->rhs(), left->lhs()};
2948 } else if (left->lhs() == right->rhs()) {
2949 std::tie(x, y, z) = std::tuple{left->rhs(), right->lhs(), left->lhs()};
2950 } else if (left->rhs() == right->lhs()) {
2951 std::tie(x, y, z) = std::tuple{left->lhs(), right->rhs(), left->rhs()};
2952 } else if (left->rhs() == right->rhs()) {
2953 std::tie(x, y, z) = std::tuple{left->lhs(), right->lhs(), left->rhs()};
2954 } else {
2955 break;
2958 if (!x->isConstant() || !x->toConstant()->isTypeRepresentableAsDouble() ||
2959 !y->isConstant() || !y->toConstant()->isTypeRepresentableAsDouble()) {
2960 break;
2963 if (auto* folded = foldConstants(x, y, isMax())) {
2964 block()->insertBefore(this, folded);
2965 return MMinMax::New(alloc, folded, z, type(), left->isMax());
2967 } while (false);
2970 // Fold min/max operations with same inputs.
2971 if (lhs()->isMinMax() || rhs()->isMinMax()) {
2972 auto* other = lhs()->isMinMax() ? lhs()->toMinMax() : rhs()->toMinMax();
2973 auto* operand = lhs()->isMinMax() ? rhs() : lhs();
2975 if (operand == other->lhs() || operand == other->rhs()) {
2976 if (isMax() == other->isMax()) {
2977 // min(x, min(x, y)) = min(x, y)
2978 // max(x, max(x, y)) = max(x, y)
2979 return other;
2981 if (!IsFloatingPointType(type())) {
2982 // When neither value is NaN:
2983 // max(x, min(x, y)) = x
2984 // min(x, max(x, y)) = x
2986 // Ensure that any bailouts that we depend on to guarantee that |y| is
2987 // Int32 are not removed.
2988 auto* otherOp = operand == other->lhs() ? other->rhs() : other->lhs();
2989 otherOp->setGuardRangeBailoutsUnchecked();
2991 return operand;
2996 if (!lhs()->isConstant() && !rhs()->isConstant()) {
2997 return this;
3000 // Directly apply math utility to compare the rhs() and lhs() when
3001 // they are both constants.
3002 if (lhs()->isConstant() && rhs()->isConstant()) {
3003 if (!lhs()->toConstant()->isTypeRepresentableAsDouble() ||
3004 !rhs()->toConstant()->isTypeRepresentableAsDouble()) {
3005 return this;
3008 if (auto* folded = foldConstants(lhs(), rhs(), isMax())) {
3009 return folded;
3013 MDefinition* operand = lhs()->isConstant() ? rhs() : lhs();
3014 MConstant* constant =
3015 lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant();
3017 if (operand->isToDouble() &&
3018 operand->getOperand(0)->type() == MIRType::Int32) {
3019 // min(int32, cte >= INT32_MAX) = int32
3020 if (!isMax() && constant->isTypeRepresentableAsDouble() &&
3021 constant->numberToDouble() >= INT32_MAX) {
3022 MLimitedTruncate* limit = MLimitedTruncate::New(
3023 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
3024 block()->insertBefore(this, limit);
3025 MToDouble* toDouble = MToDouble::New(alloc, limit);
3026 return toDouble;
3029 // max(int32, cte <= INT32_MIN) = int32
3030 if (isMax() && constant->isTypeRepresentableAsDouble() &&
3031 constant->numberToDouble() <= INT32_MIN) {
3032 MLimitedTruncate* limit = MLimitedTruncate::New(
3033 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
3034 block()->insertBefore(this, limit);
3035 MToDouble* toDouble = MToDouble::New(alloc, limit);
3036 return toDouble;
3040 auto foldLength = [](MDefinition* operand, MConstant* constant,
3041 bool isMax) -> MDefinition* {
3042 if ((operand->isArrayLength() || operand->isArrayBufferViewLength() ||
3043 operand->isArgumentsLength() || operand->isStringLength()) &&
3044 constant->type() == MIRType::Int32) {
3045 // (Array|ArrayBufferView|Arguments|String)Length is always >= 0.
3046 // max(array.length, cte <= 0) = array.length
3047 // min(array.length, cte <= 0) = cte
3048 if (constant->toInt32() <= 0) {
3049 return isMax ? operand : constant;
3052 return nullptr;
3055 if (auto* folded = foldLength(operand, constant, isMax())) {
3056 return folded;
3059 // Attempt to fold nested min/max operations which are produced by
3060 // self-hosted built-in functions.
3061 if (operand->isMinMax()) {
3062 auto* other = operand->toMinMax();
3063 MOZ_ASSERT(other->lhs()->type() == type());
3064 MOZ_ASSERT(other->rhs()->type() == type());
3066 MConstant* otherConstant = nullptr;
3067 MDefinition* otherOperand = nullptr;
3068 if (other->lhs()->isConstant()) {
3069 otherConstant = other->lhs()->toConstant();
3070 otherOperand = other->rhs();
3071 } else if (other->rhs()->isConstant()) {
3072 otherConstant = other->rhs()->toConstant();
3073 otherOperand = other->lhs();
3076 if (otherConstant && constant->isTypeRepresentableAsDouble() &&
3077 otherConstant->isTypeRepresentableAsDouble()) {
3078 if (isMax() == other->isMax()) {
3079 // Fold min(x, min(y, z)) to min(min(x, y), z) with constant min(x, y).
3080 // Fold max(x, max(y, z)) to max(max(x, y), z) with constant max(x, y).
3081 if (auto* left = foldConstants(constant, otherConstant, isMax())) {
3082 block()->insertBefore(this, left);
3083 return MMinMax::New(alloc, left, otherOperand, type(), isMax());
3085 } else {
3086 // Fold min(x, max(y, z)) to max(min(x, y), min(x, z)).
3087 // Fold max(x, min(y, z)) to min(max(x, y), max(x, z)).
3089 // But only do this when min(x, z) can also be simplified.
3090 if (auto* right = foldLength(otherOperand, constant, isMax())) {
3091 if (auto* left = foldConstants(constant, otherConstant, isMax())) {
3092 block()->insertBefore(this, left);
3093 return MMinMax::New(alloc, left, right, type(), !isMax());
3100 return this;
3103 #ifdef JS_JITSPEW
3104 void MMinMax::printOpcode(GenericPrinter& out) const {
3105 MDefinition::printOpcode(out);
3106 out.printf(" (%s)", isMax() ? "max" : "min");
3109 void MMinMaxArray::printOpcode(GenericPrinter& out) const {
3110 MDefinition::printOpcode(out);
3111 out.printf(" (%s)", isMax() ? "max" : "min");
3113 #endif
3115 MDefinition* MPow::foldsConstant(TempAllocator& alloc) {
3116 // Both `x` and `p` in `x^p` must be constants in order to precompute.
3117 if (!input()->isConstant() || !power()->isConstant()) {
3118 return nullptr;
3120 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3121 return nullptr;
3123 if (!input()->toConstant()->isTypeRepresentableAsDouble()) {
3124 return nullptr;
3127 double x = input()->toConstant()->numberToDouble();
3128 double p = power()->toConstant()->numberToDouble();
3129 double result = js::ecmaPow(x, p);
3130 if (type() == MIRType::Int32) {
3131 int32_t cast;
3132 if (!mozilla::NumberIsInt32(result, &cast)) {
3133 // Reject folding if the result isn't an int32, because we'll bail anyway.
3134 return nullptr;
3136 return MConstant::New(alloc, Int32Value(cast));
3138 return MConstant::New(alloc, DoubleValue(result));
3141 MDefinition* MPow::foldsConstantPower(TempAllocator& alloc) {
3142 // If `p` in `x^p` isn't constant, we can't apply these folds.
3143 if (!power()->isConstant()) {
3144 return nullptr;
3146 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3147 return nullptr;
3150 MOZ_ASSERT(type() == MIRType::Double || type() == MIRType::Int32);
3152 // NOTE: The optimizations must match the optimizations used in |js::ecmaPow|
3153 // resp. |js::powi| to avoid differential testing issues.
3155 double pow = power()->toConstant()->numberToDouble();
3157 // Math.pow(x, 0.5) is a sqrt with edge-case detection.
3158 if (pow == 0.5) {
3159 MOZ_ASSERT(type() == MIRType::Double);
3160 return MPowHalf::New(alloc, input());
3163 // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases.
3164 if (pow == -0.5) {
3165 MOZ_ASSERT(type() == MIRType::Double);
3166 MPowHalf* half = MPowHalf::New(alloc, input());
3167 block()->insertBefore(this, half);
3168 MConstant* one = MConstant::New(alloc, DoubleValue(1.0));
3169 block()->insertBefore(this, one);
3170 return MDiv::New(alloc, one, half, MIRType::Double);
3173 // Math.pow(x, 1) == x.
3174 if (pow == 1.0) {
3175 return input();
3178 auto multiply = [this, &alloc](MDefinition* lhs, MDefinition* rhs) {
3179 MMul* mul = MMul::New(alloc, lhs, rhs, type());
3180 mul->setBailoutKind(bailoutKind());
3182 // Multiplying the same number can't yield negative zero.
3183 mul->setCanBeNegativeZero(lhs != rhs && canBeNegativeZero());
3184 return mul;
3187 // Math.pow(x, 2) == x*x.
3188 if (pow == 2.0) {
3189 return multiply(input(), input());
3192 // Math.pow(x, 3) == x*x*x.
3193 if (pow == 3.0) {
3194 MMul* mul1 = multiply(input(), input());
3195 block()->insertBefore(this, mul1);
3196 return multiply(input(), mul1);
3199 // Math.pow(x, 4) == y*y, where y = x*x.
3200 if (pow == 4.0) {
3201 MMul* y = multiply(input(), input());
3202 block()->insertBefore(this, y);
3203 return multiply(y, y);
3206 // No optimization
3207 return nullptr;
3210 MDefinition* MPow::foldsTo(TempAllocator& alloc) {
3211 if (MDefinition* def = foldsConstant(alloc)) {
3212 return def;
3214 if (MDefinition* def = foldsConstantPower(alloc)) {
3215 return def;
3217 return this;
3220 MDefinition* MInt32ToIntPtr::foldsTo(TempAllocator& alloc) {
3221 MDefinition* def = input();
3222 if (def->isConstant()) {
3223 int32_t i = def->toConstant()->toInt32();
3224 return MConstant::NewIntPtr(alloc, intptr_t(i));
3227 if (def->isNonNegativeIntPtrToInt32()) {
3228 return def->toNonNegativeIntPtrToInt32()->input();
3231 return this;
3234 bool MAbs::fallible() const {
3235 return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds());
3238 void MAbs::trySpecializeFloat32(TempAllocator& alloc) {
3239 // Do not use Float32 if we can use int32.
3240 if (input()->type() == MIRType::Int32) {
3241 return;
3244 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
3245 setResultType(MIRType::Float32);
3249 MDefinition* MDiv::foldsTo(TempAllocator& alloc) {
3250 MOZ_ASSERT(IsNumberType(type()));
3252 if (type() == MIRType::Int64) {
3253 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
3254 return folded;
3256 return this;
3259 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) {
3260 return folded;
3263 if (MDefinition* folded = EvaluateExactReciprocal(alloc, this)) {
3264 return folded;
3267 return this;
3270 void MDiv::analyzeEdgeCasesForward() {
3271 // This is only meaningful when doing integer division.
3272 if (type() != MIRType::Int32) {
3273 return;
3276 MOZ_ASSERT(lhs()->type() == MIRType::Int32);
3277 MOZ_ASSERT(rhs()->type() == MIRType::Int32);
3279 // Try removing divide by zero check
3280 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
3281 canBeDivideByZero_ = false;
3284 // If lhs is a constant int != INT32_MIN, then
3285 // negative overflow check can be skipped.
3286 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(INT32_MIN)) {
3287 canBeNegativeOverflow_ = false;
3290 // If rhs is a constant int != -1, likewise.
3291 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(-1)) {
3292 canBeNegativeOverflow_ = false;
3295 // If lhs is != 0, then negative zero check can be skipped.
3296 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(0)) {
3297 setCanBeNegativeZero(false);
3300 // If rhs is >= 0, likewise.
3301 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) {
3302 if (rhs()->toConstant()->toInt32() >= 0) {
3303 setCanBeNegativeZero(false);
3308 void MDiv::analyzeEdgeCasesBackward() {
3309 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) {
3310 setCanBeNegativeZero(false);
3314 bool MDiv::fallible() const { return !isTruncated(); }
3316 MDefinition* MMod::foldsTo(TempAllocator& alloc) {
3317 MOZ_ASSERT(IsNumberType(type()));
3319 if (type() == MIRType::Int64) {
3320 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
3321 return folded;
3323 } else {
3324 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) {
3325 return folded;
3328 return this;
3331 void MMod::analyzeEdgeCasesForward() {
3332 // These optimizations make sense only for integer division
3333 if (type() != MIRType::Int32) {
3334 return;
3337 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
3338 canBeDivideByZero_ = false;
3341 if (rhs()->isConstant()) {
3342 int32_t n = rhs()->toConstant()->toInt32();
3343 if (n > 0 && !IsPowerOfTwo(uint32_t(n))) {
3344 canBePowerOfTwoDivisor_ = false;
3349 bool MMod::fallible() const {
3350 return !isTruncated() &&
3351 (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend());
3354 void MMathFunction::trySpecializeFloat32(TempAllocator& alloc) {
3355 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
3356 setResultType(MIRType::Float32);
3357 specialization_ = MIRType::Float32;
3361 bool MMathFunction::isFloat32Commutative() const {
3362 switch (function_) {
3363 case UnaryMathFunction::Floor:
3364 case UnaryMathFunction::Ceil:
3365 case UnaryMathFunction::Round:
3366 case UnaryMathFunction::Trunc:
3367 return true;
3368 default:
3369 return false;
3373 MHypot* MHypot::New(TempAllocator& alloc, const MDefinitionVector& vector) {
3374 uint32_t length = vector.length();
3375 MHypot* hypot = new (alloc) MHypot;
3376 if (!hypot->init(alloc, length)) {
3377 return nullptr;
3380 for (uint32_t i = 0; i < length; ++i) {
3381 hypot->initOperand(i, vector[i]);
3383 return hypot;
3386 bool MAdd::fallible() const {
3387 // the add is fallible if range analysis does not say that it is finite, AND
3388 // either the truncation analysis shows that there are non-truncated uses.
3389 if (truncateKind() >= TruncateKind::IndirectTruncate) {
3390 return false;
3392 if (range() && range()->hasInt32Bounds()) {
3393 return false;
3395 return true;
3398 bool MSub::fallible() const {
3399 // see comment in MAdd::fallible()
3400 if (truncateKind() >= TruncateKind::IndirectTruncate) {
3401 return false;
3403 if (range() && range()->hasInt32Bounds()) {
3404 return false;
3406 return true;
3409 MDefinition* MSub::foldsTo(TempAllocator& alloc) {
3410 MDefinition* out = MBinaryArithInstruction::foldsTo(alloc);
3411 if (out != this) {
3412 return out;
3415 if (type() != MIRType::Int32) {
3416 return this;
3419 // Optimize X - X to 0. This optimization is only valid for Int32
3420 // values. Subtracting a floating point value from itself returns
3421 // NaN when the operand is either Infinity or NaN.
3422 if (lhs() == rhs()) {
3423 // Ensure that any bailouts that we depend on to guarantee that X
3424 // is Int32 are not removed.
3425 lhs()->setGuardRangeBailoutsUnchecked();
3426 return MConstant::New(alloc, Int32Value(0));
3429 return this;
3432 MDefinition* MMul::foldsTo(TempAllocator& alloc) {
3433 MDefinition* out = MBinaryArithInstruction::foldsTo(alloc);
3434 if (out != this) {
3435 return out;
3438 if (type() != MIRType::Int32) {
3439 return this;
3442 if (lhs() == rhs()) {
3443 setCanBeNegativeZero(false);
3446 return this;
3449 void MMul::analyzeEdgeCasesForward() {
3450 // Try to remove the check for negative zero
3451 // This only makes sense when using the integer multiplication
3452 if (type() != MIRType::Int32) {
3453 return;
3456 // If lhs is > 0, no need for negative zero check.
3457 if (lhs()->isConstant() && lhs()->type() == MIRType::Int32) {
3458 if (lhs()->toConstant()->toInt32() > 0) {
3459 setCanBeNegativeZero(false);
3463 // If rhs is > 0, likewise.
3464 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) {
3465 if (rhs()->toConstant()->toInt32() > 0) {
3466 setCanBeNegativeZero(false);
3471 void MMul::analyzeEdgeCasesBackward() {
3472 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) {
3473 setCanBeNegativeZero(false);
3477 bool MMul::canOverflow() const {
3478 if (isTruncated()) {
3479 return false;
3481 return !range() || !range()->hasInt32Bounds();
3484 bool MUrsh::fallible() const {
3485 if (bailoutsDisabled()) {
3486 return false;
3488 return !range() || !range()->hasInt32Bounds();
3491 MIRType MCompare::inputType() {
3492 switch (compareType_) {
3493 case Compare_Undefined:
3494 return MIRType::Undefined;
3495 case Compare_Null:
3496 return MIRType::Null;
3497 case Compare_UInt32:
3498 case Compare_Int32:
3499 return MIRType::Int32;
3500 case Compare_UIntPtr:
3501 return MIRType::IntPtr;
3502 case Compare_Double:
3503 return MIRType::Double;
3504 case Compare_Float32:
3505 return MIRType::Float32;
3506 case Compare_String:
3507 return MIRType::String;
3508 case Compare_Symbol:
3509 return MIRType::Symbol;
3510 case Compare_Object:
3511 return MIRType::Object;
3512 case Compare_BigInt:
3513 case Compare_BigInt_Int32:
3514 case Compare_BigInt_Double:
3515 case Compare_BigInt_String:
3516 return MIRType::BigInt;
3517 default:
3518 MOZ_CRASH("No known conversion");
3522 static inline bool MustBeUInt32(MDefinition* def, MDefinition** pwrapped) {
3523 if (def->isUrsh()) {
3524 *pwrapped = def->toUrsh()->lhs();
3525 MDefinition* rhs = def->toUrsh()->rhs();
3526 return def->toUrsh()->bailoutsDisabled() && rhs->maybeConstantValue() &&
3527 rhs->maybeConstantValue()->isInt32(0);
3530 if (MConstant* defConst = def->maybeConstantValue()) {
3531 *pwrapped = defConst;
3532 return defConst->type() == MIRType::Int32 && defConst->toInt32() >= 0;
3535 *pwrapped = nullptr; // silence GCC warning
3536 return false;
3539 /* static */
3540 bool MBinaryInstruction::unsignedOperands(MDefinition* left,
3541 MDefinition* right) {
3542 MDefinition* replace;
3543 if (!MustBeUInt32(left, &replace)) {
3544 return false;
3546 if (replace->type() != MIRType::Int32) {
3547 return false;
3549 if (!MustBeUInt32(right, &replace)) {
3550 return false;
3552 if (replace->type() != MIRType::Int32) {
3553 return false;
3555 return true;
3558 bool MBinaryInstruction::unsignedOperands() {
3559 return unsignedOperands(getOperand(0), getOperand(1));
3562 void MBinaryInstruction::replaceWithUnsignedOperands() {
3563 MOZ_ASSERT(unsignedOperands());
3565 for (size_t i = 0; i < numOperands(); i++) {
3566 MDefinition* replace;
3567 MustBeUInt32(getOperand(i), &replace);
3568 if (replace == getOperand(i)) {
3569 continue;
3572 getOperand(i)->setImplicitlyUsedUnchecked();
3573 replaceOperand(i, replace);
3577 MDefinition* MBitNot::foldsTo(TempAllocator& alloc) {
3578 if (type() == MIRType::Int64) {
3579 return this;
3581 MOZ_ASSERT(type() == MIRType::Int32);
3583 MDefinition* input = getOperand(0);
3585 if (input->isConstant()) {
3586 js::Value v = Int32Value(~(input->toConstant()->toInt32()));
3587 return MConstant::New(alloc, v);
3590 if (input->isBitNot()) {
3591 MOZ_ASSERT(input->toBitNot()->type() == MIRType::Int32);
3592 MOZ_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType::Int32);
3593 return MTruncateToInt32::New(alloc,
3594 input->toBitNot()->input()); // ~~x => x | 0
3597 return this;
3600 static void AssertKnownClass(TempAllocator& alloc, MInstruction* ins,
3601 MDefinition* obj) {
3602 #ifdef DEBUG
3603 const JSClass* clasp = GetObjectKnownJSClass(obj);
3604 MOZ_ASSERT(clasp);
3606 auto* assert = MAssertClass::New(alloc, obj, clasp);
3607 ins->block()->insertBefore(ins, assert);
3608 #endif
3611 MDefinition* MBoxNonStrictThis::foldsTo(TempAllocator& alloc) {
3612 MDefinition* in = input();
3613 if (in->isBox()) {
3614 in = in->toBox()->input();
3617 if (in->type() == MIRType::Object) {
3618 return in;
3621 return this;
3624 AliasSet MLoadArgumentsObjectArg::getAliasSet() const {
3625 return AliasSet::Load(AliasSet::Any);
3628 AliasSet MLoadArgumentsObjectArgHole::getAliasSet() const {
3629 return AliasSet::Load(AliasSet::Any);
3632 AliasSet MInArgumentsObjectArg::getAliasSet() const {
3633 // Loads |arguments.length|, but not the actual element, so we can use the
3634 // same alias-set as MArgumentsObjectLength.
3635 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
3636 AliasSet::DynamicSlot);
3639 AliasSet MArgumentsObjectLength::getAliasSet() const {
3640 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
3641 AliasSet::DynamicSlot);
3644 bool MGuardArgumentsObjectFlags::congruentTo(const MDefinition* ins) const {
3645 if (!ins->isGuardArgumentsObjectFlags() ||
3646 ins->toGuardArgumentsObjectFlags()->flags() != flags()) {
3647 return false;
3649 return congruentIfOperandsEqual(ins);
3652 AliasSet MGuardArgumentsObjectFlags::getAliasSet() const {
3653 // The flags are packed with the length in a fixed private slot.
3654 return AliasSet::Load(AliasSet::FixedSlot);
3657 MDefinition* MIdToStringOrSymbol::foldsTo(TempAllocator& alloc) {
3658 if (idVal()->isBox()) {
3659 auto* input = idVal()->toBox()->input();
3660 MIRType idType = input->type();
3661 if (idType == MIRType::String || idType == MIRType::Symbol) {
3662 return idVal();
3664 if (idType == MIRType::Int32) {
3665 auto* toString =
3666 MToString::New(alloc, input, MToString::SideEffectHandling::Bailout);
3667 block()->insertBefore(this, toString);
3669 return MBox::New(alloc, toString);
3673 return this;
3676 MDefinition* MReturnFromCtor::foldsTo(TempAllocator& alloc) {
3677 MDefinition* rval = value();
3678 if (rval->isBox()) {
3679 rval = rval->toBox()->input();
3682 if (rval->type() == MIRType::Object) {
3683 return rval;
3686 if (rval->type() != MIRType::Value) {
3687 return object();
3690 return this;
3693 MDefinition* MTypeOf::foldsTo(TempAllocator& alloc) {
3694 MDefinition* unboxed = input();
3695 if (unboxed->isBox()) {
3696 unboxed = unboxed->toBox()->input();
3699 JSType type;
3700 switch (unboxed->type()) {
3701 case MIRType::Double:
3702 case MIRType::Float32:
3703 case MIRType::Int32:
3704 type = JSTYPE_NUMBER;
3705 break;
3706 case MIRType::String:
3707 type = JSTYPE_STRING;
3708 break;
3709 case MIRType::Symbol:
3710 type = JSTYPE_SYMBOL;
3711 break;
3712 case MIRType::BigInt:
3713 type = JSTYPE_BIGINT;
3714 break;
3715 case MIRType::Null:
3716 type = JSTYPE_OBJECT;
3717 break;
3718 case MIRType::Undefined:
3719 type = JSTYPE_UNDEFINED;
3720 break;
3721 case MIRType::Boolean:
3722 type = JSTYPE_BOOLEAN;
3723 break;
3724 case MIRType::Object: {
3725 KnownClass known = GetObjectKnownClass(unboxed);
3726 if (known != KnownClass::None) {
3727 if (known == KnownClass::Function) {
3728 type = JSTYPE_FUNCTION;
3729 } else {
3730 type = JSTYPE_OBJECT;
3733 AssertKnownClass(alloc, this, unboxed);
3734 break;
3736 [[fallthrough]];
3738 default:
3739 return this;
3742 return MConstant::New(alloc, Int32Value(static_cast<int32_t>(type)));
3745 MDefinition* MTypeOfName::foldsTo(TempAllocator& alloc) {
3746 MOZ_ASSERT(input()->type() == MIRType::Int32);
3748 if (!input()->isConstant()) {
3749 return this;
3752 static_assert(JSTYPE_UNDEFINED == 0);
3754 int32_t type = input()->toConstant()->toInt32();
3755 MOZ_ASSERT(JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT);
3757 JSString* name =
3758 TypeName(static_cast<JSType>(type), GetJitContext()->runtime->names());
3759 return MConstant::New(alloc, StringValue(name));
3762 MUrsh* MUrsh::NewWasm(TempAllocator& alloc, MDefinition* left,
3763 MDefinition* right, MIRType type) {
3764 MUrsh* ins = new (alloc) MUrsh(left, right, type);
3766 // Since Ion has no UInt32 type, we use Int32 and we have a special
3767 // exception to the type rules: we can return values in
3768 // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
3769 // without bailing out. This is necessary because Ion has no UInt32
3770 // type and we can't have bailouts in wasm code.
3771 ins->bailoutsDisabled_ = true;
3773 return ins;
3776 MResumePoint* MResumePoint::New(TempAllocator& alloc, MBasicBlock* block,
3777 jsbytecode* pc, ResumeMode mode) {
3778 MResumePoint* resume = new (alloc) MResumePoint(block, pc, mode);
3779 if (!resume->init(alloc)) {
3780 block->discardPreAllocatedResumePoint(resume);
3781 return nullptr;
3783 resume->inherit(block);
3784 return resume;
3787 MResumePoint::MResumePoint(MBasicBlock* block, jsbytecode* pc, ResumeMode mode)
3788 : MNode(block, Kind::ResumePoint),
3789 pc_(pc),
3790 instruction_(nullptr),
3791 mode_(mode) {
3792 block->addResumePoint(this);
3795 bool MResumePoint::init(TempAllocator& alloc) {
3796 return operands_.init(alloc, block()->stackDepth());
3799 MResumePoint* MResumePoint::caller() const {
3800 return block()->callerResumePoint();
3803 void MResumePoint::inherit(MBasicBlock* block) {
3804 // FixedList doesn't initialize its elements, so do unchecked inits.
3805 for (size_t i = 0; i < stackDepth(); i++) {
3806 initOperand(i, block->getSlot(i));
3810 void MResumePoint::addStore(TempAllocator& alloc, MDefinition* store,
3811 const MResumePoint* cache) {
3812 MOZ_ASSERT(block()->outerResumePoint() != this);
3813 MOZ_ASSERT_IF(cache, !cache->stores_.empty());
3815 if (cache && cache->stores_.begin()->operand == store) {
3816 // If the last resume point had the same side-effect stack, then we can
3817 // reuse the current side effect without cloning it. This is a simple
3818 // way to share common context by making a spaghetti stack.
3819 if (++cache->stores_.begin() == stores_.begin()) {
3820 stores_.copy(cache->stores_);
3821 return;
3825 // Ensure that the store would not be deleted by DCE.
3826 MOZ_ASSERT(store->isEffectful());
3828 MStoreToRecover* top = new (alloc) MStoreToRecover(store);
3829 stores_.push(top);
3832 #ifdef JS_JITSPEW
3833 void MResumePoint::dump(GenericPrinter& out) const {
3834 out.printf("resumepoint mode=");
3836 switch (mode()) {
3837 case ResumeMode::ResumeAt:
3838 if (instruction_) {
3839 out.printf("ResumeAt(%u)", instruction_->id());
3840 } else {
3841 out.printf("ResumeAt");
3843 break;
3844 default:
3845 out.put(ResumeModeToString(mode()));
3846 break;
3849 if (MResumePoint* c = caller()) {
3850 out.printf(" (caller in block%u)", c->block()->id());
3853 for (size_t i = 0; i < numOperands(); i++) {
3854 out.printf(" ");
3855 if (operands_[i].hasProducer()) {
3856 getOperand(i)->printName(out);
3857 } else {
3858 out.printf("(null)");
3861 out.printf("\n");
3864 void MResumePoint::dump() const {
3865 Fprinter out(stderr);
3866 dump(out);
3867 out.finish();
3869 #endif
3871 bool MResumePoint::isObservableOperand(MUse* u) const {
3872 return isObservableOperand(indexOf(u));
3875 bool MResumePoint::isObservableOperand(size_t index) const {
3876 return block()->info().isObservableSlot(index);
3879 bool MResumePoint::isRecoverableOperand(MUse* u) const {
3880 return block()->info().isRecoverableOperand(indexOf(u));
3883 MDefinition* MTruncateBigIntToInt64::foldsTo(TempAllocator& alloc) {
3884 MDefinition* input = getOperand(0);
3886 if (input->isBox()) {
3887 input = input->getOperand(0);
3890 // If the operand converts an I64 to BigInt, drop both conversions.
3891 if (input->isInt64ToBigInt()) {
3892 return input->getOperand(0);
3895 // Fold this operation if the input operand is constant.
3896 if (input->isConstant()) {
3897 return MConstant::NewInt64(
3898 alloc, BigInt::toInt64(input->toConstant()->toBigInt()));
3901 return this;
3904 MDefinition* MToInt64::foldsTo(TempAllocator& alloc) {
3905 MDefinition* input = getOperand(0);
3907 if (input->isBox()) {
3908 input = input->getOperand(0);
3911 // Unwrap MInt64ToBigInt: MToInt64(MInt64ToBigInt(int64)) = int64.
3912 if (input->isInt64ToBigInt()) {
3913 return input->getOperand(0);
3916 // When the input is an Int64 already, just return it.
3917 if (input->type() == MIRType::Int64) {
3918 return input;
3921 // Fold this operation if the input operand is constant.
3922 if (input->isConstant()) {
3923 switch (input->type()) {
3924 case MIRType::Boolean:
3925 return MConstant::NewInt64(alloc, input->toConstant()->toBoolean());
3926 default:
3927 break;
3931 return this;
3934 MDefinition* MToNumberInt32::foldsTo(TempAllocator& alloc) {
3935 // Fold this operation if the input operand is constant.
3936 if (MConstant* cst = input()->maybeConstantValue()) {
3937 switch (cst->type()) {
3938 case MIRType::Null:
3939 if (conversion() == IntConversionInputKind::Any) {
3940 return MConstant::New(alloc, Int32Value(0));
3942 break;
3943 case MIRType::Boolean:
3944 if (conversion() == IntConversionInputKind::Any ||
3945 conversion() == IntConversionInputKind::NumbersOrBoolsOnly) {
3946 return MConstant::New(alloc, Int32Value(cst->toBoolean()));
3948 break;
3949 case MIRType::Int32:
3950 return MConstant::New(alloc, Int32Value(cst->toInt32()));
3951 case MIRType::Float32:
3952 case MIRType::Double:
3953 int32_t ival;
3954 // Only the value within the range of Int32 can be substituted as
3955 // constant.
3956 if (mozilla::NumberIsInt32(cst->numberToDouble(), &ival)) {
3957 return MConstant::New(alloc, Int32Value(ival));
3959 break;
3960 default:
3961 break;
3965 MDefinition* input = getOperand(0);
3966 if (input->isBox()) {
3967 input = input->toBox()->input();
3970 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
3971 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
3972 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
3973 // is folded to a MTruncateToInt32 node, which will result in this MIR:
3974 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
3975 // the MUrsh node's type is int32 (since uint32 is not implemented), and
3976 // that would fold the MTruncateToInt32 node. This will make the modulo
3977 // unsigned, while is should have been signed.
3978 if (input->type() == MIRType::Int32 && !IsUint32Type(input)) {
3979 return input;
3982 return this;
3985 MDefinition* MBooleanToInt32::foldsTo(TempAllocator& alloc) {
3986 MDefinition* input = getOperand(0);
3987 MOZ_ASSERT(input->type() == MIRType::Boolean);
3989 if (input->isConstant()) {
3990 return MConstant::New(alloc, Int32Value(input->toConstant()->toBoolean()));
3993 return this;
3996 void MToNumberInt32::analyzeEdgeCasesBackward() {
3997 if (!NeedNegativeZeroCheck(this)) {
3998 setNeedsNegativeZeroCheck(false);
4002 MDefinition* MTruncateToInt32::foldsTo(TempAllocator& alloc) {
4003 MDefinition* input = getOperand(0);
4004 if (input->isBox()) {
4005 input = input->getOperand(0);
4008 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4009 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4010 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4011 // is folded to a MTruncateToInt32 node, which will result in this MIR:
4012 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4013 // the MUrsh node's type is int32 (since uint32 is not implemented), and
4014 // that would fold the MTruncateToInt32 node. This will make the modulo
4015 // unsigned, while is should have been signed.
4016 if (input->type() == MIRType::Int32 && !IsUint32Type(input)) {
4017 return input;
4020 if (input->type() == MIRType::Double && input->isConstant()) {
4021 int32_t ret = ToInt32(input->toConstant()->toDouble());
4022 return MConstant::New(alloc, Int32Value(ret));
4025 return this;
4028 MDefinition* MWasmTruncateToInt32::foldsTo(TempAllocator& alloc) {
4029 MDefinition* input = getOperand(0);
4030 if (input->type() == MIRType::Int32) {
4031 return input;
4034 if (input->type() == MIRType::Double && input->isConstant()) {
4035 double d = input->toConstant()->toDouble();
4036 if (std::isnan(d)) {
4037 return this;
4040 if (!isUnsigned() && d <= double(INT32_MAX) && d >= double(INT32_MIN)) {
4041 return MConstant::New(alloc, Int32Value(ToInt32(d)));
4044 if (isUnsigned() && d <= double(UINT32_MAX) && d >= 0) {
4045 return MConstant::New(alloc, Int32Value(ToInt32(d)));
4049 if (input->type() == MIRType::Float32 && input->isConstant()) {
4050 double f = double(input->toConstant()->toFloat32());
4051 if (std::isnan(f)) {
4052 return this;
4055 if (!isUnsigned() && f <= double(INT32_MAX) && f >= double(INT32_MIN)) {
4056 return MConstant::New(alloc, Int32Value(ToInt32(f)));
4059 if (isUnsigned() && f <= double(UINT32_MAX) && f >= 0) {
4060 return MConstant::New(alloc, Int32Value(ToInt32(f)));
4064 return this;
4067 MDefinition* MWrapInt64ToInt32::foldsTo(TempAllocator& alloc) {
4068 MDefinition* input = this->input();
4069 if (input->isConstant()) {
4070 uint64_t c = input->toConstant()->toInt64();
4071 int32_t output = bottomHalf() ? int32_t(c) : int32_t(c >> 32);
4072 return MConstant::New(alloc, Int32Value(output));
4075 return this;
4078 MDefinition* MExtendInt32ToInt64::foldsTo(TempAllocator& alloc) {
4079 MDefinition* input = this->input();
4080 if (input->isConstant()) {
4081 int32_t c = input->toConstant()->toInt32();
4082 int64_t res = isUnsigned() ? int64_t(uint32_t(c)) : int64_t(c);
4083 return MConstant::NewInt64(alloc, res);
4086 return this;
4089 MDefinition* MSignExtendInt32::foldsTo(TempAllocator& alloc) {
4090 MDefinition* input = this->input();
4091 if (input->isConstant()) {
4092 int32_t c = input->toConstant()->toInt32();
4093 int32_t res;
4094 switch (mode_) {
4095 case Byte:
4096 res = int32_t(int8_t(c & 0xFF));
4097 break;
4098 case Half:
4099 res = int32_t(int16_t(c & 0xFFFF));
4100 break;
4102 return MConstant::New(alloc, Int32Value(res));
4105 return this;
4108 MDefinition* MSignExtendInt64::foldsTo(TempAllocator& alloc) {
4109 MDefinition* input = this->input();
4110 if (input->isConstant()) {
4111 int64_t c = input->toConstant()->toInt64();
4112 int64_t res;
4113 switch (mode_) {
4114 case Byte:
4115 res = int64_t(int8_t(c & 0xFF));
4116 break;
4117 case Half:
4118 res = int64_t(int16_t(c & 0xFFFF));
4119 break;
4120 case Word:
4121 res = int64_t(int32_t(c & 0xFFFFFFFFU));
4122 break;
4124 return MConstant::NewInt64(alloc, res);
4127 return this;
4130 MDefinition* MToDouble::foldsTo(TempAllocator& alloc) {
4131 MDefinition* input = getOperand(0);
4132 if (input->isBox()) {
4133 input = input->getOperand(0);
4136 if (input->type() == MIRType::Double) {
4137 return input;
4140 if (input->isConstant() &&
4141 input->toConstant()->isTypeRepresentableAsDouble()) {
4142 return MConstant::New(alloc,
4143 DoubleValue(input->toConstant()->numberToDouble()));
4146 return this;
4149 MDefinition* MToFloat32::foldsTo(TempAllocator& alloc) {
4150 MDefinition* input = getOperand(0);
4151 if (input->isBox()) {
4152 input = input->getOperand(0);
4155 if (input->type() == MIRType::Float32) {
4156 return input;
4159 // If x is a Float32, Float32(Double(x)) == x
4160 if (!mustPreserveNaN_ && input->isToDouble() &&
4161 input->toToDouble()->input()->type() == MIRType::Float32) {
4162 return input->toToDouble()->input();
4165 if (input->isConstant() &&
4166 input->toConstant()->isTypeRepresentableAsDouble()) {
4167 return MConstant::NewFloat32(alloc,
4168 float(input->toConstant()->numberToDouble()));
4171 // Fold ToFloat32(ToDouble(int32)) to ToFloat32(int32).
4172 if (input->isToDouble() &&
4173 input->toToDouble()->input()->type() == MIRType::Int32) {
4174 return MToFloat32::New(alloc, input->toToDouble()->input());
4177 return this;
4180 MDefinition* MToString::foldsTo(TempAllocator& alloc) {
4181 MDefinition* in = input();
4182 if (in->isBox()) {
4183 in = in->getOperand(0);
4186 if (in->type() == MIRType::String) {
4187 return in;
4189 return this;
4192 MDefinition* MClampToUint8::foldsTo(TempAllocator& alloc) {
4193 if (MConstant* inputConst = input()->maybeConstantValue()) {
4194 if (inputConst->isTypeRepresentableAsDouble()) {
4195 int32_t clamped = ClampDoubleToUint8(inputConst->numberToDouble());
4196 return MConstant::New(alloc, Int32Value(clamped));
4199 return this;
4202 bool MCompare::tryFoldEqualOperands(bool* result) {
4203 if (lhs() != rhs()) {
4204 return false;
4207 // Intuitively somebody would think that if lhs === rhs,
4208 // then we can just return true. (Or false for !==)
4209 // However NaN !== NaN is true! So we spend some time trying
4210 // to eliminate this case.
4212 if (!IsStrictEqualityOp(jsop())) {
4213 return false;
4216 MOZ_ASSERT(
4217 compareType_ == Compare_Undefined || compareType_ == Compare_Null ||
4218 compareType_ == Compare_Int32 || compareType_ == Compare_UInt32 ||
4219 compareType_ == Compare_UInt64 || compareType_ == Compare_Double ||
4220 compareType_ == Compare_Float32 || compareType_ == Compare_UIntPtr ||
4221 compareType_ == Compare_String || compareType_ == Compare_Object ||
4222 compareType_ == Compare_Symbol || compareType_ == Compare_BigInt ||
4223 compareType_ == Compare_BigInt_Int32 ||
4224 compareType_ == Compare_BigInt_Double ||
4225 compareType_ == Compare_BigInt_String);
4227 if (isDoubleComparison() || isFloat32Comparison()) {
4228 if (!operandsAreNeverNaN()) {
4229 return false;
4233 lhs()->setGuardRangeBailoutsUnchecked();
4235 *result = (jsop() == JSOp::StrictEq);
4236 return true;
4239 static JSType TypeOfName(JSLinearString* str) {
4240 static constexpr std::array types = {
4241 JSTYPE_UNDEFINED, JSTYPE_OBJECT, JSTYPE_FUNCTION, JSTYPE_STRING,
4242 JSTYPE_NUMBER, JSTYPE_BOOLEAN, JSTYPE_SYMBOL, JSTYPE_BIGINT,
4243 #ifdef ENABLE_RECORD_TUPLE
4244 JSTYPE_RECORD, JSTYPE_TUPLE,
4245 #endif
4247 static_assert(types.size() == JSTYPE_LIMIT);
4249 const JSAtomState& names = GetJitContext()->runtime->names();
4250 for (auto type : types) {
4251 if (EqualStrings(str, TypeName(type, names))) {
4252 return type;
4255 return JSTYPE_LIMIT;
4258 static mozilla::Maybe<std::pair<MTypeOfName*, JSType>> IsTypeOfCompare(
4259 MCompare* ins) {
4260 if (!IsEqualityOp(ins->jsop())) {
4261 return mozilla::Nothing();
4263 if (ins->compareType() != MCompare::Compare_String) {
4264 return mozilla::Nothing();
4267 auto* lhs = ins->lhs();
4268 auto* rhs = ins->rhs();
4270 MOZ_ASSERT(ins->type() == MIRType::Boolean);
4271 MOZ_ASSERT(lhs->type() == MIRType::String);
4272 MOZ_ASSERT(rhs->type() == MIRType::String);
4274 if (!lhs->isTypeOfName() && !rhs->isTypeOfName()) {
4275 return mozilla::Nothing();
4277 if (!lhs->isConstant() && !rhs->isConstant()) {
4278 return mozilla::Nothing();
4281 auto* typeOfName =
4282 lhs->isTypeOfName() ? lhs->toTypeOfName() : rhs->toTypeOfName();
4283 MOZ_ASSERT(typeOfName->input()->isTypeOf());
4285 auto* constant = lhs->isConstant() ? lhs->toConstant() : rhs->toConstant();
4287 JSType type = TypeOfName(&constant->toString()->asLinear());
4288 return mozilla::Some(std::pair(typeOfName, type));
4291 bool MCompare::tryFoldTypeOf(bool* result) {
4292 auto typeOfPair = IsTypeOfCompare(this);
4293 if (!typeOfPair) {
4294 return false;
4296 auto [typeOfName, type] = *typeOfPair;
4297 auto* typeOf = typeOfName->input()->toTypeOf();
4299 switch (type) {
4300 case JSTYPE_BOOLEAN:
4301 if (!typeOf->input()->mightBeType(MIRType::Boolean)) {
4302 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4303 return true;
4305 break;
4306 case JSTYPE_NUMBER:
4307 if (!typeOf->input()->mightBeType(MIRType::Int32) &&
4308 !typeOf->input()->mightBeType(MIRType::Float32) &&
4309 !typeOf->input()->mightBeType(MIRType::Double)) {
4310 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4311 return true;
4313 break;
4314 case JSTYPE_STRING:
4315 if (!typeOf->input()->mightBeType(MIRType::String)) {
4316 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4317 return true;
4319 break;
4320 case JSTYPE_SYMBOL:
4321 if (!typeOf->input()->mightBeType(MIRType::Symbol)) {
4322 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4323 return true;
4325 break;
4326 case JSTYPE_BIGINT:
4327 if (!typeOf->input()->mightBeType(MIRType::BigInt)) {
4328 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4329 return true;
4331 break;
4332 case JSTYPE_OBJECT:
4333 if (!typeOf->input()->mightBeType(MIRType::Object) &&
4334 !typeOf->input()->mightBeType(MIRType::Null)) {
4335 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4336 return true;
4338 break;
4339 case JSTYPE_UNDEFINED:
4340 if (!typeOf->input()->mightBeType(MIRType::Object) &&
4341 !typeOf->input()->mightBeType(MIRType::Undefined)) {
4342 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4343 return true;
4345 break;
4346 case JSTYPE_FUNCTION:
4347 if (!typeOf->input()->mightBeType(MIRType::Object)) {
4348 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4349 return true;
4351 break;
4352 case JSTYPE_LIMIT:
4353 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4354 return true;
4355 #ifdef ENABLE_RECORD_TUPLE
4356 case JSTYPE_RECORD:
4357 case JSTYPE_TUPLE:
4358 MOZ_CRASH("Records and Tuples are not supported yet.");
4359 #endif
4362 return false;
4365 bool MCompare::tryFold(bool* result) {
4366 JSOp op = jsop();
4368 if (tryFoldEqualOperands(result)) {
4369 return true;
4372 if (tryFoldTypeOf(result)) {
4373 return true;
4376 if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) {
4377 // The LHS is the value we want to test against null or undefined.
4378 if (IsStrictEqualityOp(op)) {
4379 if (lhs()->type() == inputType()) {
4380 *result = (op == JSOp::StrictEq);
4381 return true;
4383 if (!lhs()->mightBeType(inputType())) {
4384 *result = (op == JSOp::StrictNe);
4385 return true;
4387 } else {
4388 MOZ_ASSERT(IsLooseEqualityOp(op));
4389 if (IsNullOrUndefined(lhs()->type())) {
4390 *result = (op == JSOp::Eq);
4391 return true;
4393 if (!lhs()->mightBeType(MIRType::Null) &&
4394 !lhs()->mightBeType(MIRType::Undefined) &&
4395 !lhs()->mightBeType(MIRType::Object)) {
4396 *result = (op == JSOp::Ne);
4397 return true;
4400 return false;
4403 return false;
4406 template <typename T>
4407 static bool FoldComparison(JSOp op, T left, T right) {
4408 switch (op) {
4409 case JSOp::Lt:
4410 return left < right;
4411 case JSOp::Le:
4412 return left <= right;
4413 case JSOp::Gt:
4414 return left > right;
4415 case JSOp::Ge:
4416 return left >= right;
4417 case JSOp::StrictEq:
4418 case JSOp::Eq:
4419 return left == right;
4420 case JSOp::StrictNe:
4421 case JSOp::Ne:
4422 return left != right;
4423 default:
4424 MOZ_CRASH("Unexpected op.");
4428 bool MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result) {
4429 if (type() != MIRType::Boolean && type() != MIRType::Int32) {
4430 return false;
4433 MDefinition* left = getOperand(0);
4434 MDefinition* right = getOperand(1);
4436 if (compareType() == Compare_Double) {
4437 // Optimize "MCompare MConstant (MToDouble SomethingInInt32Range).
4438 // In most cases the MToDouble was added, because the constant is
4439 // a double.
4440 // e.g. v < 9007199254740991, where v is an int32 is always true.
4441 if (!lhs()->isConstant() && !rhs()->isConstant()) {
4442 return false;
4445 MDefinition* operand = left->isConstant() ? right : left;
4446 MConstant* constant =
4447 left->isConstant() ? left->toConstant() : right->toConstant();
4448 MOZ_ASSERT(constant->type() == MIRType::Double);
4449 double cte = constant->toDouble();
4451 if (operand->isToDouble() &&
4452 operand->getOperand(0)->type() == MIRType::Int32) {
4453 bool replaced = false;
4454 switch (jsop_) {
4455 case JSOp::Lt:
4456 if (cte > INT32_MAX || cte < INT32_MIN) {
4457 *result = !((constant == lhs()) ^ (cte < INT32_MIN));
4458 replaced = true;
4460 break;
4461 case JSOp::Le:
4462 if (constant == lhs()) {
4463 if (cte > INT32_MAX || cte <= INT32_MIN) {
4464 *result = (cte <= INT32_MIN);
4465 replaced = true;
4467 } else {
4468 if (cte >= INT32_MAX || cte < INT32_MIN) {
4469 *result = (cte >= INT32_MIN);
4470 replaced = true;
4473 break;
4474 case JSOp::Gt:
4475 if (cte > INT32_MAX || cte < INT32_MIN) {
4476 *result = !((constant == rhs()) ^ (cte < INT32_MIN));
4477 replaced = true;
4479 break;
4480 case JSOp::Ge:
4481 if (constant == lhs()) {
4482 if (cte >= INT32_MAX || cte < INT32_MIN) {
4483 *result = (cte >= INT32_MAX);
4484 replaced = true;
4486 } else {
4487 if (cte > INT32_MAX || cte <= INT32_MIN) {
4488 *result = (cte <= INT32_MIN);
4489 replaced = true;
4492 break;
4493 case JSOp::StrictEq: // Fall through.
4494 case JSOp::Eq:
4495 if (cte > INT32_MAX || cte < INT32_MIN) {
4496 *result = false;
4497 replaced = true;
4499 break;
4500 case JSOp::StrictNe: // Fall through.
4501 case JSOp::Ne:
4502 if (cte > INT32_MAX || cte < INT32_MIN) {
4503 *result = true;
4504 replaced = true;
4506 break;
4507 default:
4508 MOZ_CRASH("Unexpected op.");
4510 if (replaced) {
4511 MLimitedTruncate* limit = MLimitedTruncate::New(
4512 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
4513 limit->setGuardUnchecked();
4514 block()->insertBefore(this, limit);
4515 return true;
4519 // Optimize comparison against NaN.
4520 if (std::isnan(cte)) {
4521 switch (jsop_) {
4522 case JSOp::Lt:
4523 case JSOp::Le:
4524 case JSOp::Gt:
4525 case JSOp::Ge:
4526 case JSOp::Eq:
4527 case JSOp::StrictEq:
4528 *result = false;
4529 break;
4530 case JSOp::Ne:
4531 case JSOp::StrictNe:
4532 *result = true;
4533 break;
4534 default:
4535 MOZ_CRASH("Unexpected op.");
4537 return true;
4541 if (!left->isConstant() || !right->isConstant()) {
4542 return false;
4545 MConstant* lhs = left->toConstant();
4546 MConstant* rhs = right->toConstant();
4548 // Fold away some String equality comparisons.
4549 if (lhs->type() == MIRType::String && rhs->type() == MIRType::String) {
4550 int32_t comp = 0; // Default to equal.
4551 if (left != right) {
4552 comp = CompareStrings(&lhs->toString()->asLinear(),
4553 &rhs->toString()->asLinear());
4555 *result = FoldComparison(jsop_, comp, 0);
4556 return true;
4559 if (compareType_ == Compare_UInt32) {
4560 *result = FoldComparison(jsop_, uint32_t(lhs->toInt32()),
4561 uint32_t(rhs->toInt32()));
4562 return true;
4565 if (compareType_ == Compare_Int64) {
4566 *result = FoldComparison(jsop_, lhs->toInt64(), rhs->toInt64());
4567 return true;
4570 if (compareType_ == Compare_UInt64) {
4571 *result = FoldComparison(jsop_, uint64_t(lhs->toInt64()),
4572 uint64_t(rhs->toInt64()));
4573 return true;
4576 if (lhs->isTypeRepresentableAsDouble() &&
4577 rhs->isTypeRepresentableAsDouble()) {
4578 *result =
4579 FoldComparison(jsop_, lhs->numberToDouble(), rhs->numberToDouble());
4580 return true;
4583 return false;
4586 MDefinition* MCompare::tryFoldTypeOf(TempAllocator& alloc) {
4587 auto typeOfPair = IsTypeOfCompare(this);
4588 if (!typeOfPair) {
4589 return this;
4591 auto [typeOfName, type] = *typeOfPair;
4592 auto* typeOf = typeOfName->input()->toTypeOf();
4594 auto* input = typeOf->input();
4595 MOZ_ASSERT(input->type() == MIRType::Value ||
4596 input->type() == MIRType::Object);
4598 // Constant typeof folding handles the other cases.
4599 MOZ_ASSERT_IF(input->type() == MIRType::Object, type == JSTYPE_UNDEFINED ||
4600 type == JSTYPE_OBJECT ||
4601 type == JSTYPE_FUNCTION);
4603 MOZ_ASSERT(type != JSTYPE_LIMIT, "unknown typeof strings folded earlier");
4605 // If there's only a single use, assume this |typeof| is used in a simple
4606 // comparison context.
4608 // if (typeof thing === "number") { ... }
4610 // It'll be compiled into something similar to:
4612 // if (IsNumber(thing)) { ... }
4614 // This heuristic can go wrong when repeated |typeof| are used in consecutive
4615 // if-statements.
4617 // if (typeof thing === "number") { ... }
4618 // else if (typeof thing === "string") { ... }
4619 // ... repeated for all possible types
4621 // In that case it'd more efficient to emit MTypeOf compared to MTypeOfIs. We
4622 // don't yet handle that case, because it'd require a separate optimization
4623 // pass to correctly detect it.
4624 if (typeOfName->hasOneUse()) {
4625 return MTypeOfIs::New(alloc, input, jsop(), type);
4628 MConstant* cst = MConstant::New(alloc, Int32Value(type));
4629 block()->insertBefore(this, cst);
4631 return MCompare::New(alloc, typeOf, cst, jsop(), MCompare::Compare_Int32);
4634 MDefinition* MCompare::tryFoldCharCompare(TempAllocator& alloc) {
4635 if (compareType() != Compare_String) {
4636 return this;
4639 MDefinition* left = lhs();
4640 MOZ_ASSERT(left->type() == MIRType::String);
4642 MDefinition* right = rhs();
4643 MOZ_ASSERT(right->type() == MIRType::String);
4645 // |str[i]| is compiled as |MFromCharCode(MCharCodeAt(str, i))|.
4646 // Out-of-bounds access is compiled as
4647 // |FromCharCodeEmptyIfNegative(CharCodeAtOrNegative(str, i))|.
4648 auto isCharAccess = [](MDefinition* ins) {
4649 if (ins->isFromCharCode()) {
4650 return ins->toFromCharCode()->code()->isCharCodeAt();
4652 if (ins->isFromCharCodeEmptyIfNegative()) {
4653 auto* fromCharCode = ins->toFromCharCodeEmptyIfNegative();
4654 return fromCharCode->code()->isCharCodeAtOrNegative();
4656 return false;
4659 auto charAccessCode = [](MDefinition* ins) {
4660 if (ins->isFromCharCode()) {
4661 return ins->toFromCharCode()->code();
4663 return ins->toFromCharCodeEmptyIfNegative()->code();
4666 if (left->isConstant() || right->isConstant()) {
4667 // Try to optimize |MConstant(string) <compare> (MFromCharCode MCharCodeAt)|
4668 // as |MConstant(charcode) <compare> MCharCodeAt|.
4669 MConstant* constant;
4670 MDefinition* operand;
4671 if (left->isConstant()) {
4672 constant = left->toConstant();
4673 operand = right;
4674 } else {
4675 constant = right->toConstant();
4676 operand = left;
4679 if (constant->toString()->length() != 1 || !isCharAccess(operand)) {
4680 return this;
4683 char16_t charCode = constant->toString()->asLinear().latin1OrTwoByteChar(0);
4684 MConstant* charCodeConst = MConstant::New(alloc, Int32Value(charCode));
4685 block()->insertBefore(this, charCodeConst);
4687 MDefinition* charCodeAt = charAccessCode(operand);
4689 if (left->isConstant()) {
4690 left = charCodeConst;
4691 right = charCodeAt;
4692 } else {
4693 left = charCodeAt;
4694 right = charCodeConst;
4696 } else if (isCharAccess(left) && isCharAccess(right)) {
4697 // Try to optimize |(MFromCharCode MCharCodeAt) <compare> (MFromCharCode
4698 // MCharCodeAt)| as |MCharCodeAt <compare> MCharCodeAt|.
4700 left = charAccessCode(left);
4701 right = charAccessCode(right);
4702 } else {
4703 return this;
4706 return MCompare::New(alloc, left, right, jsop(), MCompare::Compare_Int32);
4709 MDefinition* MCompare::tryFoldStringCompare(TempAllocator& alloc) {
4710 if (compareType() != Compare_String) {
4711 return this;
4714 MDefinition* left = lhs();
4715 MOZ_ASSERT(left->type() == MIRType::String);
4717 MDefinition* right = rhs();
4718 MOZ_ASSERT(right->type() == MIRType::String);
4720 if (!left->isConstant() && !right->isConstant()) {
4721 return this;
4724 // Try to optimize |string <compare> MConstant("")| as |MStringLength(string)
4725 // <compare> MConstant(0)|.
4727 MConstant* constant =
4728 left->isConstant() ? left->toConstant() : right->toConstant();
4729 if (!constant->toString()->empty()) {
4730 return this;
4733 MDefinition* operand = left->isConstant() ? right : left;
4735 auto* strLength = MStringLength::New(alloc, operand);
4736 block()->insertBefore(this, strLength);
4738 auto* zero = MConstant::New(alloc, Int32Value(0));
4739 block()->insertBefore(this, zero);
4741 if (left->isConstant()) {
4742 left = zero;
4743 right = strLength;
4744 } else {
4745 left = strLength;
4746 right = zero;
4749 return MCompare::New(alloc, left, right, jsop(), MCompare::Compare_Int32);
4752 MDefinition* MCompare::tryFoldStringSubstring(TempAllocator& alloc) {
4753 if (compareType() != Compare_String) {
4754 return this;
4756 if (!IsEqualityOp(jsop())) {
4757 return this;
4760 auto* left = lhs();
4761 MOZ_ASSERT(left->type() == MIRType::String);
4763 auto* right = rhs();
4764 MOZ_ASSERT(right->type() == MIRType::String);
4766 // One operand must be a constant string.
4767 if (!left->isConstant() && !right->isConstant()) {
4768 return this;
4771 // The constant string must be non-empty.
4772 auto* constant =
4773 left->isConstant() ? left->toConstant() : right->toConstant();
4774 if (constant->toString()->empty()) {
4775 return this;
4778 // The other operand must be a substring operation.
4779 auto* operand = left->isConstant() ? right : left;
4780 if (!operand->isSubstr()) {
4781 return this;
4783 auto* substr = operand->toSubstr();
4785 static_assert(JSString::MAX_LENGTH < INT32_MAX,
4786 "string length can be casted to int32_t");
4788 if (!IsSubstrTo(substr, int32_t(constant->toString()->length()))) {
4789 return this;
4792 // Now fold code like |str.substring(0, 2) == "aa"| to |str.startsWith("aa")|.
4794 auto* startsWith = MStringStartsWith::New(alloc, substr->string(), constant);
4795 if (jsop() == JSOp::Eq || jsop() == JSOp::StrictEq) {
4796 return startsWith;
4799 // Invert for inequality.
4800 MOZ_ASSERT(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe);
4802 block()->insertBefore(this, startsWith);
4803 return MNot::New(alloc, startsWith);
4806 MDefinition* MCompare::tryFoldStringIndexOf(TempAllocator& alloc) {
4807 if (compareType() != Compare_Int32) {
4808 return this;
4810 if (!IsEqualityOp(jsop())) {
4811 return this;
4814 auto* left = lhs();
4815 MOZ_ASSERT(left->type() == MIRType::Int32);
4817 auto* right = rhs();
4818 MOZ_ASSERT(right->type() == MIRType::Int32);
4820 // One operand must be a constant integer.
4821 if (!left->isConstant() && !right->isConstant()) {
4822 return this;
4825 // The constant must be zero.
4826 auto* constant =
4827 left->isConstant() ? left->toConstant() : right->toConstant();
4828 if (!constant->isInt32(0)) {
4829 return this;
4832 // The other operand must be an indexOf operation.
4833 auto* operand = left->isConstant() ? right : left;
4834 if (!operand->isStringIndexOf()) {
4835 return this;
4838 // Fold |str.indexOf(searchStr) == 0| to |str.startsWith(searchStr)|.
4840 auto* indexOf = operand->toStringIndexOf();
4841 auto* startsWith =
4842 MStringStartsWith::New(alloc, indexOf->string(), indexOf->searchString());
4843 if (jsop() == JSOp::Eq || jsop() == JSOp::StrictEq) {
4844 return startsWith;
4847 // Invert for inequality.
4848 MOZ_ASSERT(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe);
4850 block()->insertBefore(this, startsWith);
4851 return MNot::New(alloc, startsWith);
4854 MDefinition* MCompare::foldsTo(TempAllocator& alloc) {
4855 bool result;
4857 if (tryFold(&result) || evaluateConstantOperands(alloc, &result)) {
4858 if (type() == MIRType::Int32) {
4859 return MConstant::New(alloc, Int32Value(result));
4862 MOZ_ASSERT(type() == MIRType::Boolean);
4863 return MConstant::New(alloc, BooleanValue(result));
4866 if (MDefinition* folded = tryFoldTypeOf(alloc); folded != this) {
4867 return folded;
4870 if (MDefinition* folded = tryFoldCharCompare(alloc); folded != this) {
4871 return folded;
4874 if (MDefinition* folded = tryFoldStringCompare(alloc); folded != this) {
4875 return folded;
4878 if (MDefinition* folded = tryFoldStringSubstring(alloc); folded != this) {
4879 return folded;
4882 if (MDefinition* folded = tryFoldStringIndexOf(alloc); folded != this) {
4883 return folded;
4886 return this;
4889 void MCompare::trySpecializeFloat32(TempAllocator& alloc) {
4890 if (AllOperandsCanProduceFloat32(this) && compareType_ == Compare_Double) {
4891 compareType_ = Compare_Float32;
4892 } else {
4893 ConvertOperandsToDouble(this, alloc);
4897 MDefinition* MNot::foldsTo(TempAllocator& alloc) {
4898 // Fold if the input is constant
4899 if (MConstant* inputConst = input()->maybeConstantValue()) {
4900 bool b;
4901 if (inputConst->valueToBoolean(&b)) {
4902 if (type() == MIRType::Int32 || type() == MIRType::Int64) {
4903 return MConstant::New(alloc, Int32Value(!b));
4905 return MConstant::New(alloc, BooleanValue(!b));
4909 // If the operand of the Not is itself a Not, they cancel out. But we can't
4910 // always convert Not(Not(x)) to x because that may loose the conversion to
4911 // boolean. We can simplify Not(Not(Not(x))) to Not(x) though.
4912 MDefinition* op = getOperand(0);
4913 if (op->isNot()) {
4914 MDefinition* opop = op->getOperand(0);
4915 if (opop->isNot()) {
4916 return opop;
4920 // Not of an undefined or null value is always true
4921 if (input()->type() == MIRType::Undefined ||
4922 input()->type() == MIRType::Null) {
4923 return MConstant::New(alloc, BooleanValue(true));
4926 // Not of a symbol is always false.
4927 if (input()->type() == MIRType::Symbol) {
4928 return MConstant::New(alloc, BooleanValue(false));
4931 return this;
4934 void MNot::trySpecializeFloat32(TempAllocator& alloc) {
4935 (void)EnsureFloatInputOrConvert(this, alloc);
4938 #ifdef JS_JITSPEW
4939 void MBeta::printOpcode(GenericPrinter& out) const {
4940 MDefinition::printOpcode(out);
4942 out.printf(" ");
4943 comparison_->dump(out);
4945 #endif
4947 AliasSet MCreateThis::getAliasSet() const {
4948 return AliasSet::Load(AliasSet::Any);
4951 bool MGetArgumentsObjectArg::congruentTo(const MDefinition* ins) const {
4952 if (!ins->isGetArgumentsObjectArg()) {
4953 return false;
4955 if (ins->toGetArgumentsObjectArg()->argno() != argno()) {
4956 return false;
4958 return congruentIfOperandsEqual(ins);
4961 AliasSet MGetArgumentsObjectArg::getAliasSet() const {
4962 return AliasSet::Load(AliasSet::Any);
4965 AliasSet MSetArgumentsObjectArg::getAliasSet() const {
4966 return AliasSet::Store(AliasSet::Any);
4969 MObjectState::MObjectState(MObjectState* state)
4970 : MVariadicInstruction(classOpcode),
4971 numSlots_(state->numSlots_),
4972 numFixedSlots_(state->numFixedSlots_) {
4973 // This instruction is only used as a summary for bailout paths.
4974 setResultType(MIRType::Object);
4975 setRecoveredOnBailout();
4978 MObjectState::MObjectState(JSObject* templateObject)
4979 : MObjectState(templateObject->as<NativeObject>().shape()) {}
4981 MObjectState::MObjectState(const Shape* shape)
4982 : MVariadicInstruction(classOpcode) {
4983 // This instruction is only used as a summary for bailout paths.
4984 setResultType(MIRType::Object);
4985 setRecoveredOnBailout();
4987 numSlots_ = shape->asShared().slotSpan();
4988 numFixedSlots_ = shape->asShared().numFixedSlots();
4991 /* static */
4992 JSObject* MObjectState::templateObjectOf(MDefinition* obj) {
4993 // MNewPlainObject uses a shape constant, not an object.
4994 MOZ_ASSERT(!obj->isNewPlainObject());
4996 if (obj->isNewObject()) {
4997 return obj->toNewObject()->templateObject();
4998 } else if (obj->isNewCallObject()) {
4999 return obj->toNewCallObject()->templateObject();
5000 } else if (obj->isNewIterator()) {
5001 return obj->toNewIterator()->templateObject();
5004 MOZ_CRASH("unreachable");
5007 bool MObjectState::init(TempAllocator& alloc, MDefinition* obj) {
5008 if (!MVariadicInstruction::init(alloc, numSlots() + 1)) {
5009 return false;
5011 // +1, for the Object.
5012 initOperand(0, obj);
5013 return true;
5016 void MObjectState::initFromTemplateObject(TempAllocator& alloc,
5017 MDefinition* undefinedVal) {
5018 if (object()->isNewPlainObject()) {
5019 MOZ_ASSERT(object()->toNewPlainObject()->shape()->asShared().slotSpan() ==
5020 numSlots());
5021 for (size_t i = 0; i < numSlots(); i++) {
5022 initSlot(i, undefinedVal);
5024 return;
5027 JSObject* templateObject = templateObjectOf(object());
5029 // Initialize all the slots of the object state with the value contained in
5030 // the template object. This is needed to account values which are baked in
5031 // the template objects and not visible in IonMonkey, such as the
5032 // uninitialized-lexical magic value of call objects.
5034 MOZ_ASSERT(templateObject->is<NativeObject>());
5035 NativeObject& nativeObject = templateObject->as<NativeObject>();
5036 MOZ_ASSERT(nativeObject.slotSpan() == numSlots());
5038 for (size_t i = 0; i < numSlots(); i++) {
5039 Value val = nativeObject.getSlot(i);
5040 MDefinition* def = undefinedVal;
5041 if (!val.isUndefined()) {
5042 MConstant* ins = MConstant::New(alloc, val);
5043 block()->insertBefore(this, ins);
5044 def = ins;
5046 initSlot(i, def);
5050 MObjectState* MObjectState::New(TempAllocator& alloc, MDefinition* obj) {
5051 MObjectState* res;
5052 if (obj->isNewPlainObject()) {
5053 const Shape* shape = obj->toNewPlainObject()->shape();
5054 res = new (alloc) MObjectState(shape);
5055 } else {
5056 JSObject* templateObject = templateObjectOf(obj);
5057 MOZ_ASSERT(templateObject, "Unexpected object creation.");
5058 res = new (alloc) MObjectState(templateObject);
5061 if (!res || !res->init(alloc, obj)) {
5062 return nullptr;
5064 return res;
5067 MObjectState* MObjectState::Copy(TempAllocator& alloc, MObjectState* state) {
5068 MObjectState* res = new (alloc) MObjectState(state);
5069 if (!res || !res->init(alloc, state->object())) {
5070 return nullptr;
5072 for (size_t i = 0; i < res->numSlots(); i++) {
5073 res->initSlot(i, state->getSlot(i));
5075 return res;
5078 MArrayState::MArrayState(MDefinition* arr) : MVariadicInstruction(classOpcode) {
5079 // This instruction is only used as a summary for bailout paths.
5080 setResultType(MIRType::Object);
5081 setRecoveredOnBailout();
5082 if (arr->isNewArrayObject()) {
5083 numElements_ = arr->toNewArrayObject()->length();
5084 } else {
5085 numElements_ = arr->toNewArray()->length();
5089 bool MArrayState::init(TempAllocator& alloc, MDefinition* obj,
5090 MDefinition* len) {
5091 if (!MVariadicInstruction::init(alloc, numElements() + 2)) {
5092 return false;
5094 // +1, for the Array object.
5095 initOperand(0, obj);
5096 // +1, for the length value of the array.
5097 initOperand(1, len);
5098 return true;
5101 void MArrayState::initFromTemplateObject(TempAllocator& alloc,
5102 MDefinition* undefinedVal) {
5103 for (size_t i = 0; i < numElements(); i++) {
5104 initElement(i, undefinedVal);
5108 MArrayState* MArrayState::New(TempAllocator& alloc, MDefinition* arr,
5109 MDefinition* initLength) {
5110 MArrayState* res = new (alloc) MArrayState(arr);
5111 if (!res || !res->init(alloc, arr, initLength)) {
5112 return nullptr;
5114 return res;
5117 MArrayState* MArrayState::Copy(TempAllocator& alloc, MArrayState* state) {
5118 MDefinition* arr = state->array();
5119 MDefinition* len = state->initializedLength();
5120 MArrayState* res = new (alloc) MArrayState(arr);
5121 if (!res || !res->init(alloc, arr, len)) {
5122 return nullptr;
5124 for (size_t i = 0; i < res->numElements(); i++) {
5125 res->initElement(i, state->getElement(i));
5127 return res;
5130 MNewArray::MNewArray(uint32_t length, MConstant* templateConst,
5131 gc::Heap initialHeap, bool vmCall)
5132 : MUnaryInstruction(classOpcode, templateConst),
5133 length_(length),
5134 initialHeap_(initialHeap),
5135 vmCall_(vmCall) {
5136 setResultType(MIRType::Object);
5139 MDefinition::AliasType MLoadFixedSlot::mightAlias(
5140 const MDefinition* def) const {
5141 if (def->isStoreFixedSlot()) {
5142 const MStoreFixedSlot* store = def->toStoreFixedSlot();
5143 if (store->slot() != slot()) {
5144 return AliasType::NoAlias;
5146 if (store->object() != object()) {
5147 return AliasType::MayAlias;
5149 return AliasType::MustAlias;
5151 return AliasType::MayAlias;
5154 MDefinition* MLoadFixedSlot::foldsTo(TempAllocator& alloc) {
5155 if (MDefinition* def = foldsToStore(alloc)) {
5156 return def;
5159 return this;
5162 MDefinition::AliasType MLoadFixedSlotAndUnbox::mightAlias(
5163 const MDefinition* def) const {
5164 if (def->isStoreFixedSlot()) {
5165 const MStoreFixedSlot* store = def->toStoreFixedSlot();
5166 if (store->slot() != slot()) {
5167 return AliasType::NoAlias;
5169 if (store->object() != object()) {
5170 return AliasType::MayAlias;
5172 return AliasType::MustAlias;
5174 return AliasType::MayAlias;
5177 MDefinition* MLoadFixedSlotAndUnbox::foldsTo(TempAllocator& alloc) {
5178 if (MDefinition* def = foldsToStore(alloc)) {
5179 return def;
5182 return this;
5185 MDefinition* MWasmExtendU32Index::foldsTo(TempAllocator& alloc) {
5186 MDefinition* input = this->input();
5187 if (input->isConstant()) {
5188 return MConstant::NewInt64(
5189 alloc, int64_t(uint32_t(input->toConstant()->toInt32())));
5192 return this;
5195 MDefinition* MWasmWrapU32Index::foldsTo(TempAllocator& alloc) {
5196 MDefinition* input = this->input();
5197 if (input->isConstant()) {
5198 return MConstant::New(
5199 alloc, Int32Value(int32_t(uint32_t(input->toConstant()->toInt64()))));
5202 return this;
5205 // Some helpers for folding wasm and/or/xor on int32/64 values. Rather than
5206 // duplicating these for 32 and 64-bit values, all folding is done on 64-bit
5207 // values and masked for the 32-bit case.
5209 const uint64_t Low32Mask = uint64_t(0xFFFFFFFFULL);
5211 // Routines to check and disassemble values.
5213 static bool IsIntegralConstant(const MDefinition* def) {
5214 return def->isConstant() &&
5215 (def->type() == MIRType::Int32 || def->type() == MIRType::Int64);
5218 static uint64_t GetIntegralConstant(const MDefinition* def) {
5219 if (def->type() == MIRType::Int32) {
5220 return uint64_t(def->toConstant()->toInt32()) & Low32Mask;
5222 return uint64_t(def->toConstant()->toInt64());
5225 static bool IsIntegralConstantZero(const MDefinition* def) {
5226 return IsIntegralConstant(def) && GetIntegralConstant(def) == 0;
5229 static bool IsIntegralConstantOnes(const MDefinition* def) {
5230 uint64_t ones = def->type() == MIRType::Int32 ? Low32Mask : ~uint64_t(0);
5231 return IsIntegralConstant(def) && GetIntegralConstant(def) == ones;
5234 // Routines to create values.
5235 static MDefinition* ToIntegralConstant(TempAllocator& alloc, MIRType ty,
5236 uint64_t val) {
5237 switch (ty) {
5238 case MIRType::Int32:
5239 return MConstant::New(alloc,
5240 Int32Value(int32_t(uint32_t(val & Low32Mask))));
5241 case MIRType::Int64:
5242 return MConstant::NewInt64(alloc, int64_t(val));
5243 default:
5244 MOZ_CRASH();
5248 static MDefinition* ZeroOfType(TempAllocator& alloc, MIRType ty) {
5249 return ToIntegralConstant(alloc, ty, 0);
5252 static MDefinition* OnesOfType(TempAllocator& alloc, MIRType ty) {
5253 return ToIntegralConstant(alloc, ty, ~uint64_t(0));
5256 MDefinition* MWasmBinaryBitwise::foldsTo(TempAllocator& alloc) {
5257 MOZ_ASSERT(op() == Opcode::WasmBinaryBitwise);
5258 MOZ_ASSERT(type() == MIRType::Int32 || type() == MIRType::Int64);
5260 MDefinition* argL = getOperand(0);
5261 MDefinition* argR = getOperand(1);
5262 MOZ_ASSERT(argL->type() == type() && argR->type() == type());
5264 // The args are the same (SSA name)
5265 if (argL == argR) {
5266 switch (subOpcode()) {
5267 case SubOpcode::And:
5268 case SubOpcode::Or:
5269 return argL;
5270 case SubOpcode::Xor:
5271 return ZeroOfType(alloc, type());
5272 default:
5273 MOZ_CRASH();
5277 // Both args constant
5278 if (IsIntegralConstant(argL) && IsIntegralConstant(argR)) {
5279 uint64_t valL = GetIntegralConstant(argL);
5280 uint64_t valR = GetIntegralConstant(argR);
5281 uint64_t val = valL;
5282 switch (subOpcode()) {
5283 case SubOpcode::And:
5284 val &= valR;
5285 break;
5286 case SubOpcode::Or:
5287 val |= valR;
5288 break;
5289 case SubOpcode::Xor:
5290 val ^= valR;
5291 break;
5292 default:
5293 MOZ_CRASH();
5295 return ToIntegralConstant(alloc, type(), val);
5298 // Left arg is zero
5299 if (IsIntegralConstantZero(argL)) {
5300 switch (subOpcode()) {
5301 case SubOpcode::And:
5302 return ZeroOfType(alloc, type());
5303 case SubOpcode::Or:
5304 case SubOpcode::Xor:
5305 return argR;
5306 default:
5307 MOZ_CRASH();
5311 // Right arg is zero
5312 if (IsIntegralConstantZero(argR)) {
5313 switch (subOpcode()) {
5314 case SubOpcode::And:
5315 return ZeroOfType(alloc, type());
5316 case SubOpcode::Or:
5317 case SubOpcode::Xor:
5318 return argL;
5319 default:
5320 MOZ_CRASH();
5324 // Left arg is ones
5325 if (IsIntegralConstantOnes(argL)) {
5326 switch (subOpcode()) {
5327 case SubOpcode::And:
5328 return argR;
5329 case SubOpcode::Or:
5330 return OnesOfType(alloc, type());
5331 case SubOpcode::Xor:
5332 return MBitNot::New(alloc, argR);
5333 default:
5334 MOZ_CRASH();
5338 // Right arg is ones
5339 if (IsIntegralConstantOnes(argR)) {
5340 switch (subOpcode()) {
5341 case SubOpcode::And:
5342 return argL;
5343 case SubOpcode::Or:
5344 return OnesOfType(alloc, type());
5345 case SubOpcode::Xor:
5346 return MBitNot::New(alloc, argL);
5347 default:
5348 MOZ_CRASH();
5352 return this;
5355 MDefinition* MWasmAddOffset::foldsTo(TempAllocator& alloc) {
5356 MDefinition* baseArg = base();
5357 if (!baseArg->isConstant()) {
5358 return this;
5361 if (baseArg->type() == MIRType::Int32) {
5362 CheckedInt<uint32_t> ptr = baseArg->toConstant()->toInt32();
5363 ptr += offset();
5364 if (!ptr.isValid()) {
5365 return this;
5367 return MConstant::New(alloc, Int32Value(ptr.value()));
5370 MOZ_ASSERT(baseArg->type() == MIRType::Int64);
5371 CheckedInt<uint64_t> ptr = baseArg->toConstant()->toInt64();
5372 ptr += offset();
5373 if (!ptr.isValid()) {
5374 return this;
5376 return MConstant::NewInt64(alloc, ptr.value());
5379 bool MWasmAlignmentCheck::congruentTo(const MDefinition* ins) const {
5380 if (!ins->isWasmAlignmentCheck()) {
5381 return false;
5383 const MWasmAlignmentCheck* check = ins->toWasmAlignmentCheck();
5384 return byteSize_ == check->byteSize() && congruentIfOperandsEqual(check);
5387 MDefinition::AliasType MAsmJSLoadHeap::mightAlias(
5388 const MDefinition* def) const {
5389 if (def->isAsmJSStoreHeap()) {
5390 const MAsmJSStoreHeap* store = def->toAsmJSStoreHeap();
5391 if (store->accessType() != accessType()) {
5392 return AliasType::MayAlias;
5394 if (!base()->isConstant() || !store->base()->isConstant()) {
5395 return AliasType::MayAlias;
5397 const MConstant* otherBase = store->base()->toConstant();
5398 if (base()->toConstant()->equals(otherBase)) {
5399 return AliasType::MayAlias;
5401 return AliasType::NoAlias;
5403 return AliasType::MayAlias;
5406 bool MAsmJSLoadHeap::congruentTo(const MDefinition* ins) const {
5407 if (!ins->isAsmJSLoadHeap()) {
5408 return false;
5410 const MAsmJSLoadHeap* load = ins->toAsmJSLoadHeap();
5411 return load->accessType() == accessType() && congruentIfOperandsEqual(load);
5414 MDefinition::AliasType MWasmLoadInstanceDataField::mightAlias(
5415 const MDefinition* def) const {
5416 if (def->isWasmStoreInstanceDataField()) {
5417 const MWasmStoreInstanceDataField* store =
5418 def->toWasmStoreInstanceDataField();
5419 return store->instanceDataOffset() == instanceDataOffset_
5420 ? AliasType::MayAlias
5421 : AliasType::NoAlias;
5424 return AliasType::MayAlias;
5427 MDefinition::AliasType MWasmLoadGlobalCell::mightAlias(
5428 const MDefinition* def) const {
5429 if (def->isWasmStoreGlobalCell()) {
5430 // No globals of different type can alias. See bug 1467415 comment 3.
5431 if (type() != def->toWasmStoreGlobalCell()->value()->type()) {
5432 return AliasType::NoAlias;
5435 // We could do better here. We're dealing with two indirect globals.
5436 // If at at least one of them is created in this module, then they
5437 // can't alias -- in other words they can only alias if they are both
5438 // imported. That would require having a flag on globals to indicate
5439 // which are imported. See bug 1467415 comment 3, 4th rule.
5442 return AliasType::MayAlias;
5445 HashNumber MWasmLoadInstanceDataField::valueHash() const {
5446 // Same comment as in MWasmLoadInstanceDataField::congruentTo() applies here.
5447 HashNumber hash = MDefinition::valueHash();
5448 hash = addU32ToHash(hash, instanceDataOffset_);
5449 return hash;
5452 bool MWasmLoadInstanceDataField::congruentTo(const MDefinition* ins) const {
5453 if (!ins->isWasmLoadInstanceDataField()) {
5454 return false;
5457 const MWasmLoadInstanceDataField* other = ins->toWasmLoadInstanceDataField();
5459 // We don't need to consider the isConstant_ markings here, because
5460 // equivalence of offsets implies equivalence of constness.
5461 bool sameOffsets = instanceDataOffset_ == other->instanceDataOffset_;
5462 MOZ_ASSERT_IF(sameOffsets, isConstant_ == other->isConstant_);
5464 // We omit checking congruence of the operands. There is only one
5465 // operand, the instance pointer, and it only ever has one value within the
5466 // domain of optimization. If that should ever change then operand
5467 // congruence checking should be reinstated.
5468 return sameOffsets /* && congruentIfOperandsEqual(other) */;
5471 MDefinition* MWasmLoadInstanceDataField::foldsTo(TempAllocator& alloc) {
5472 if (!dependency() || !dependency()->isWasmStoreInstanceDataField()) {
5473 return this;
5476 MWasmStoreInstanceDataField* store =
5477 dependency()->toWasmStoreInstanceDataField();
5478 if (!store->block()->dominates(block())) {
5479 return this;
5482 if (store->instanceDataOffset() != instanceDataOffset()) {
5483 return this;
5486 if (store->value()->type() != type()) {
5487 return this;
5490 return store->value();
5493 bool MWasmLoadGlobalCell::congruentTo(const MDefinition* ins) const {
5494 if (!ins->isWasmLoadGlobalCell()) {
5495 return false;
5497 const MWasmLoadGlobalCell* other = ins->toWasmLoadGlobalCell();
5498 return congruentIfOperandsEqual(other);
5501 #ifdef ENABLE_WASM_SIMD
5502 MDefinition* MWasmTernarySimd128::foldsTo(TempAllocator& alloc) {
5503 if (simdOp() == wasm::SimdOp::V128Bitselect) {
5504 if (v2()->op() == MDefinition::Opcode::WasmFloatConstant) {
5505 int8_t shuffle[16];
5506 if (specializeBitselectConstantMaskAsShuffle(shuffle)) {
5507 return BuildWasmShuffleSimd128(alloc, shuffle, v0(), v1());
5509 } else if (canRelaxBitselect()) {
5510 return MWasmTernarySimd128::New(alloc, v0(), v1(), v2(),
5511 wasm::SimdOp::I8x16RelaxedLaneSelect);
5514 return this;
5517 inline static bool MatchSpecificShift(MDefinition* instr,
5518 wasm::SimdOp simdShiftOp,
5519 int shiftValue) {
5520 return instr->isWasmShiftSimd128() &&
5521 instr->toWasmShiftSimd128()->simdOp() == simdShiftOp &&
5522 instr->toWasmShiftSimd128()->rhs()->isConstant() &&
5523 instr->toWasmShiftSimd128()->rhs()->toConstant()->toInt32() ==
5524 shiftValue;
5527 // Matches MIR subtree that represents PMADDUBSW instruction generated by
5528 // emscripten. The a and b parameters return subtrees that correspond
5529 // operands of the instruction, if match is found.
5530 static bool MatchPmaddubswSequence(MWasmBinarySimd128* lhs,
5531 MWasmBinarySimd128* rhs, MDefinition** a,
5532 MDefinition** b) {
5533 MOZ_ASSERT(lhs->simdOp() == wasm::SimdOp::I16x8Mul &&
5534 rhs->simdOp() == wasm::SimdOp::I16x8Mul);
5535 // The emscripten/LLVM produced the following sequence for _mm_maddubs_epi16:
5537 // return _mm_adds_epi16(
5538 // _mm_mullo_epi16(
5539 // _mm_and_si128(__a, _mm_set1_epi16(0x00FF)),
5540 // _mm_srai_epi16(_mm_slli_epi16(__b, 8), 8)),
5541 // _mm_mullo_epi16(_mm_srli_epi16(__a, 8), _mm_srai_epi16(__b, 8)));
5543 // This will roughly correspond the following MIR:
5544 // MWasmBinarySimd128[I16x8AddSatS]
5545 // |-- lhs: MWasmBinarySimd128[I16x8Mul] (lhs)
5546 // | |-- lhs: MWasmBinarySimd128WithConstant[V128And] (op0)
5547 // | | |-- lhs: a
5548 // | | -- rhs: SimdConstant::SplatX8(0x00FF)
5549 // | -- rhs: MWasmShiftSimd128[I16x8ShrS] (op1)
5550 // | |-- lhs: MWasmShiftSimd128[I16x8Shl]
5551 // | | |-- lhs: b
5552 // | | -- rhs: MConstant[8]
5553 // | -- rhs: MConstant[8]
5554 // -- rhs: MWasmBinarySimd128[I16x8Mul] (rhs)
5555 // |-- lhs: MWasmShiftSimd128[I16x8ShrU] (op2)
5556 // | |-- lhs: a
5557 // | |-- rhs: MConstant[8]
5558 // -- rhs: MWasmShiftSimd128[I16x8ShrS] (op3)
5559 // |-- lhs: b
5560 // -- rhs: MConstant[8]
5562 // The I16x8AddSatS and I16x8Mul are commutative, so their operands
5563 // may be swapped. Rearrange op0, op1, op2, op3 to be in the order
5564 // noted above.
5565 MDefinition *op0 = lhs->lhs(), *op1 = lhs->rhs(), *op2 = rhs->lhs(),
5566 *op3 = rhs->rhs();
5567 if (op1->isWasmBinarySimd128WithConstant()) {
5568 // Move MWasmBinarySimd128WithConstant[V128And] as first operand in lhs.
5569 std::swap(op0, op1);
5570 } else if (op3->isWasmBinarySimd128WithConstant()) {
5571 // Move MWasmBinarySimd128WithConstant[V128And] as first operand in rhs.
5572 std::swap(op2, op3);
5574 if (op2->isWasmBinarySimd128WithConstant()) {
5575 // The lhs and rhs are swapped.
5576 // Make MWasmBinarySimd128WithConstant[V128And] to be op0.
5577 std::swap(op0, op2);
5578 std::swap(op1, op3);
5580 if (op2->isWasmShiftSimd128() &&
5581 op2->toWasmShiftSimd128()->simdOp() == wasm::SimdOp::I16x8ShrS) {
5582 // The op2 and op3 appears to be in wrong order, swap.
5583 std::swap(op2, op3);
5586 // Check all instructions SIMD code and constant values for assigned
5587 // names op0, op1, op2, op3 (see diagram above).
5588 const uint16_t const00FF[8] = {255, 255, 255, 255, 255, 255, 255, 255};
5589 if (!op0->isWasmBinarySimd128WithConstant() ||
5590 op0->toWasmBinarySimd128WithConstant()->simdOp() !=
5591 wasm::SimdOp::V128And ||
5592 memcmp(op0->toWasmBinarySimd128WithConstant()->rhs().bytes(), const00FF,
5593 16) != 0 ||
5594 !MatchSpecificShift(op1, wasm::SimdOp::I16x8ShrS, 8) ||
5595 !MatchSpecificShift(op2, wasm::SimdOp::I16x8ShrU, 8) ||
5596 !MatchSpecificShift(op3, wasm::SimdOp::I16x8ShrS, 8) ||
5597 !MatchSpecificShift(op1->toWasmShiftSimd128()->lhs(),
5598 wasm::SimdOp::I16x8Shl, 8)) {
5599 return false;
5602 // Check if the instructions arguments that are subtrees match the
5603 // a and b assignments. May depend on GVN behavior.
5604 MDefinition* maybeA = op0->toWasmBinarySimd128WithConstant()->lhs();
5605 MDefinition* maybeB = op3->toWasmShiftSimd128()->lhs();
5606 if (maybeA != op2->toWasmShiftSimd128()->lhs() ||
5607 maybeB != op1->toWasmShiftSimd128()->lhs()->toWasmShiftSimd128()->lhs()) {
5608 return false;
5611 *a = maybeA;
5612 *b = maybeB;
5613 return true;
5616 MDefinition* MWasmBinarySimd128::foldsTo(TempAllocator& alloc) {
5617 if (simdOp() == wasm::SimdOp::I8x16Swizzle && rhs()->isWasmFloatConstant()) {
5618 // Specialize swizzle(v, constant) as shuffle(mask, v, zero) to trigger all
5619 // our shuffle optimizations. We don't report this rewriting as the report
5620 // will be overwritten by the subsequent shuffle analysis.
5621 int8_t shuffleMask[16];
5622 memcpy(shuffleMask, rhs()->toWasmFloatConstant()->toSimd128().bytes(), 16);
5623 for (int i = 0; i < 16; i++) {
5624 // Out-of-bounds lanes reference the zero vector; in many cases, the zero
5625 // vector is removed by subsequent optimizations.
5626 if (shuffleMask[i] < 0 || shuffleMask[i] > 15) {
5627 shuffleMask[i] = 16;
5630 MWasmFloatConstant* zero =
5631 MWasmFloatConstant::NewSimd128(alloc, SimdConstant::SplatX4(0));
5632 if (!zero) {
5633 return nullptr;
5635 block()->insertBefore(this, zero);
5636 return BuildWasmShuffleSimd128(alloc, shuffleMask, lhs(), zero);
5639 // Specialize var OP const / const OP var when possible.
5641 // As the LIR layer can't directly handle v128 constants as part of its normal
5642 // machinery we specialize some nodes here if they have single-use v128
5643 // constant arguments. The purpose is to generate code that inlines the
5644 // constant in the instruction stream, using either a rip-relative load+op or
5645 // quickly-synthesized constant in a scratch on x64. There is a general
5646 // assumption here that that is better than generating the constant into an
5647 // allocatable register, since that register value could not be reused. (This
5648 // ignores the possibility that the constant load could be hoisted).
5650 if (lhs()->isWasmFloatConstant() != rhs()->isWasmFloatConstant() &&
5651 specializeForConstantRhs()) {
5652 if (isCommutative() && lhs()->isWasmFloatConstant() && lhs()->hasOneUse()) {
5653 return MWasmBinarySimd128WithConstant::New(
5654 alloc, rhs(), lhs()->toWasmFloatConstant()->toSimd128(), simdOp());
5657 if (rhs()->isWasmFloatConstant() && rhs()->hasOneUse()) {
5658 return MWasmBinarySimd128WithConstant::New(
5659 alloc, lhs(), rhs()->toWasmFloatConstant()->toSimd128(), simdOp());
5663 // Check special encoding for PMADDUBSW.
5664 if (canPmaddubsw() && simdOp() == wasm::SimdOp::I16x8AddSatS &&
5665 lhs()->isWasmBinarySimd128() && rhs()->isWasmBinarySimd128() &&
5666 lhs()->toWasmBinarySimd128()->simdOp() == wasm::SimdOp::I16x8Mul &&
5667 rhs()->toWasmBinarySimd128()->simdOp() == wasm::SimdOp::I16x8Mul) {
5668 MDefinition *a, *b;
5669 if (MatchPmaddubswSequence(lhs()->toWasmBinarySimd128(),
5670 rhs()->toWasmBinarySimd128(), &a, &b)) {
5671 return MWasmBinarySimd128::New(alloc, a, b, /* commutative = */ false,
5672 wasm::SimdOp::MozPMADDUBSW);
5676 return this;
5679 MDefinition* MWasmScalarToSimd128::foldsTo(TempAllocator& alloc) {
5680 # ifdef DEBUG
5681 auto logging = mozilla::MakeScopeExit([&] {
5682 js::wasm::ReportSimdAnalysis("scalar-to-simd128 -> constant folded");
5684 # endif
5685 if (input()->isConstant()) {
5686 MConstant* c = input()->toConstant();
5687 switch (simdOp()) {
5688 case wasm::SimdOp::I8x16Splat:
5689 return MWasmFloatConstant::NewSimd128(
5690 alloc, SimdConstant::SplatX16(c->toInt32()));
5691 case wasm::SimdOp::I16x8Splat:
5692 return MWasmFloatConstant::NewSimd128(
5693 alloc, SimdConstant::SplatX8(c->toInt32()));
5694 case wasm::SimdOp::I32x4Splat:
5695 return MWasmFloatConstant::NewSimd128(
5696 alloc, SimdConstant::SplatX4(c->toInt32()));
5697 case wasm::SimdOp::I64x2Splat:
5698 return MWasmFloatConstant::NewSimd128(
5699 alloc, SimdConstant::SplatX2(c->toInt64()));
5700 default:
5701 # ifdef DEBUG
5702 logging.release();
5703 # endif
5704 return this;
5707 if (input()->isWasmFloatConstant()) {
5708 MWasmFloatConstant* c = input()->toWasmFloatConstant();
5709 switch (simdOp()) {
5710 case wasm::SimdOp::F32x4Splat:
5711 return MWasmFloatConstant::NewSimd128(
5712 alloc, SimdConstant::SplatX4(c->toFloat32()));
5713 case wasm::SimdOp::F64x2Splat:
5714 return MWasmFloatConstant::NewSimd128(
5715 alloc, SimdConstant::SplatX2(c->toDouble()));
5716 default:
5717 # ifdef DEBUG
5718 logging.release();
5719 # endif
5720 return this;
5723 # ifdef DEBUG
5724 logging.release();
5725 # endif
5726 return this;
5729 template <typename T>
5730 static bool AllTrue(const T& v) {
5731 constexpr size_t count = sizeof(T) / sizeof(*v);
5732 static_assert(count == 16 || count == 8 || count == 4 || count == 2);
5733 bool result = true;
5734 for (unsigned i = 0; i < count; i++) {
5735 result = result && v[i] != 0;
5737 return result;
5740 template <typename T>
5741 static int32_t Bitmask(const T& v) {
5742 constexpr size_t count = sizeof(T) / sizeof(*v);
5743 constexpr size_t shift = 8 * sizeof(*v) - 1;
5744 static_assert(shift == 7 || shift == 15 || shift == 31 || shift == 63);
5745 int32_t result = 0;
5746 for (unsigned i = 0; i < count; i++) {
5747 result = result | int32_t(((v[i] >> shift) & 1) << i);
5749 return result;
5752 MDefinition* MWasmReduceSimd128::foldsTo(TempAllocator& alloc) {
5753 # ifdef DEBUG
5754 auto logging = mozilla::MakeScopeExit([&] {
5755 js::wasm::ReportSimdAnalysis("simd128-to-scalar -> constant folded");
5757 # endif
5758 if (input()->isWasmFloatConstant()) {
5759 SimdConstant c = input()->toWasmFloatConstant()->toSimd128();
5760 int32_t i32Result = 0;
5761 switch (simdOp()) {
5762 case wasm::SimdOp::V128AnyTrue:
5763 i32Result = !c.isZeroBits();
5764 break;
5765 case wasm::SimdOp::I8x16AllTrue:
5766 i32Result = AllTrue(
5767 SimdConstant::CreateSimd128((int8_t*)c.bytes()).asInt8x16());
5768 break;
5769 case wasm::SimdOp::I8x16Bitmask:
5770 i32Result = Bitmask(
5771 SimdConstant::CreateSimd128((int8_t*)c.bytes()).asInt8x16());
5772 break;
5773 case wasm::SimdOp::I16x8AllTrue:
5774 i32Result = AllTrue(
5775 SimdConstant::CreateSimd128((int16_t*)c.bytes()).asInt16x8());
5776 break;
5777 case wasm::SimdOp::I16x8Bitmask:
5778 i32Result = Bitmask(
5779 SimdConstant::CreateSimd128((int16_t*)c.bytes()).asInt16x8());
5780 break;
5781 case wasm::SimdOp::I32x4AllTrue:
5782 i32Result = AllTrue(
5783 SimdConstant::CreateSimd128((int32_t*)c.bytes()).asInt32x4());
5784 break;
5785 case wasm::SimdOp::I32x4Bitmask:
5786 i32Result = Bitmask(
5787 SimdConstant::CreateSimd128((int32_t*)c.bytes()).asInt32x4());
5788 break;
5789 case wasm::SimdOp::I64x2AllTrue:
5790 i32Result = AllTrue(
5791 SimdConstant::CreateSimd128((int64_t*)c.bytes()).asInt64x2());
5792 break;
5793 case wasm::SimdOp::I64x2Bitmask:
5794 i32Result = Bitmask(
5795 SimdConstant::CreateSimd128((int64_t*)c.bytes()).asInt64x2());
5796 break;
5797 case wasm::SimdOp::I8x16ExtractLaneS:
5798 i32Result =
5799 SimdConstant::CreateSimd128((int8_t*)c.bytes()).asInt8x16()[imm()];
5800 break;
5801 case wasm::SimdOp::I8x16ExtractLaneU:
5802 i32Result = int32_t(SimdConstant::CreateSimd128((int8_t*)c.bytes())
5803 .asInt8x16()[imm()]) &
5804 0xFF;
5805 break;
5806 case wasm::SimdOp::I16x8ExtractLaneS:
5807 i32Result =
5808 SimdConstant::CreateSimd128((int16_t*)c.bytes()).asInt16x8()[imm()];
5809 break;
5810 case wasm::SimdOp::I16x8ExtractLaneU:
5811 i32Result = int32_t(SimdConstant::CreateSimd128((int16_t*)c.bytes())
5812 .asInt16x8()[imm()]) &
5813 0xFFFF;
5814 break;
5815 case wasm::SimdOp::I32x4ExtractLane:
5816 i32Result =
5817 SimdConstant::CreateSimd128((int32_t*)c.bytes()).asInt32x4()[imm()];
5818 break;
5819 case wasm::SimdOp::I64x2ExtractLane:
5820 return MConstant::NewInt64(
5821 alloc, SimdConstant::CreateSimd128((int64_t*)c.bytes())
5822 .asInt64x2()[imm()]);
5823 case wasm::SimdOp::F32x4ExtractLane:
5824 return MWasmFloatConstant::NewFloat32(
5825 alloc, SimdConstant::CreateSimd128((float*)c.bytes())
5826 .asFloat32x4()[imm()]);
5827 case wasm::SimdOp::F64x2ExtractLane:
5828 return MWasmFloatConstant::NewDouble(
5829 alloc, SimdConstant::CreateSimd128((double*)c.bytes())
5830 .asFloat64x2()[imm()]);
5831 default:
5832 # ifdef DEBUG
5833 logging.release();
5834 # endif
5835 return this;
5837 return MConstant::New(alloc, Int32Value(i32Result), MIRType::Int32);
5839 # ifdef DEBUG
5840 logging.release();
5841 # endif
5842 return this;
5844 #endif // ENABLE_WASM_SIMD
5846 MDefinition::AliasType MLoadDynamicSlot::mightAlias(
5847 const MDefinition* def) const {
5848 if (def->isStoreDynamicSlot()) {
5849 const MStoreDynamicSlot* store = def->toStoreDynamicSlot();
5850 if (store->slot() != slot()) {
5851 return AliasType::NoAlias;
5854 if (store->slots() != slots()) {
5855 return AliasType::MayAlias;
5858 return AliasType::MustAlias;
5860 return AliasType::MayAlias;
5863 HashNumber MLoadDynamicSlot::valueHash() const {
5864 HashNumber hash = MDefinition::valueHash();
5865 hash = addU32ToHash(hash, slot_);
5866 return hash;
5869 MDefinition* MLoadDynamicSlot::foldsTo(TempAllocator& alloc) {
5870 if (MDefinition* def = foldsToStore(alloc)) {
5871 return def;
5874 return this;
5877 #ifdef JS_JITSPEW
5878 void MLoadDynamicSlot::printOpcode(GenericPrinter& out) const {
5879 MDefinition::printOpcode(out);
5880 out.printf(" (slot %u)", slot());
5883 void MLoadDynamicSlotAndUnbox::printOpcode(GenericPrinter& out) const {
5884 MDefinition::printOpcode(out);
5885 out.printf(" (slot %zu)", slot());
5888 void MStoreDynamicSlot::printOpcode(GenericPrinter& out) const {
5889 MDefinition::printOpcode(out);
5890 out.printf(" (slot %u)", slot());
5893 void MLoadFixedSlot::printOpcode(GenericPrinter& out) const {
5894 MDefinition::printOpcode(out);
5895 out.printf(" (slot %zu)", slot());
5898 void MLoadFixedSlotAndUnbox::printOpcode(GenericPrinter& out) const {
5899 MDefinition::printOpcode(out);
5900 out.printf(" (slot %zu)", slot());
5903 void MStoreFixedSlot::printOpcode(GenericPrinter& out) const {
5904 MDefinition::printOpcode(out);
5905 out.printf(" (slot %zu)", slot());
5907 #endif
5909 MDefinition* MGuardFunctionScript::foldsTo(TempAllocator& alloc) {
5910 MDefinition* in = input();
5911 if (in->isLambda() &&
5912 in->toLambda()->templateFunction()->baseScript() == expected()) {
5913 return in;
5915 return this;
5918 MDefinition* MFunctionEnvironment::foldsTo(TempAllocator& alloc) {
5919 if (input()->isLambda()) {
5920 return input()->toLambda()->environmentChain();
5922 if (input()->isFunctionWithProto()) {
5923 return input()->toFunctionWithProto()->environmentChain();
5925 return this;
5928 static bool AddIsANonZeroAdditionOf(MAdd* add, MDefinition* ins) {
5929 if (add->lhs() != ins && add->rhs() != ins) {
5930 return false;
5932 MDefinition* other = (add->lhs() == ins) ? add->rhs() : add->lhs();
5933 if (!IsNumberType(other->type())) {
5934 return false;
5936 if (!other->isConstant()) {
5937 return false;
5939 if (other->toConstant()->numberToDouble() == 0) {
5940 return false;
5942 return true;
5945 // Skip over instructions that usually appear between the actual index
5946 // value being used and the MLoadElement.
5947 // They don't modify the index value in a meaningful way.
5948 static MDefinition* SkipUninterestingInstructions(MDefinition* ins) {
5949 // Drop the MToNumberInt32 added by the TypePolicy for double and float
5950 // values.
5951 if (ins->isToNumberInt32()) {
5952 return SkipUninterestingInstructions(ins->toToNumberInt32()->input());
5955 // Ignore the bounds check, which don't modify the index.
5956 if (ins->isBoundsCheck()) {
5957 return SkipUninterestingInstructions(ins->toBoundsCheck()->index());
5960 // Masking the index for Spectre-mitigation is not observable.
5961 if (ins->isSpectreMaskIndex()) {
5962 return SkipUninterestingInstructions(ins->toSpectreMaskIndex()->index());
5965 return ins;
5968 static bool DefinitelyDifferentValue(MDefinition* ins1, MDefinition* ins2) {
5969 ins1 = SkipUninterestingInstructions(ins1);
5970 ins2 = SkipUninterestingInstructions(ins2);
5972 if (ins1 == ins2) {
5973 return false;
5976 // For constants check they are not equal.
5977 if (ins1->isConstant() && ins2->isConstant()) {
5978 MConstant* cst1 = ins1->toConstant();
5979 MConstant* cst2 = ins2->toConstant();
5981 if (!cst1->isTypeRepresentableAsDouble() ||
5982 !cst2->isTypeRepresentableAsDouble()) {
5983 return false;
5986 // Be conservative and only allow values that fit into int32.
5987 int32_t n1, n2;
5988 if (!mozilla::NumberIsInt32(cst1->numberToDouble(), &n1) ||
5989 !mozilla::NumberIsInt32(cst2->numberToDouble(), &n2)) {
5990 return false;
5993 return n1 != n2;
5996 // Check if "ins1 = ins2 + cte", which would make both instructions
5997 // have different values.
5998 if (ins1->isAdd()) {
5999 if (AddIsANonZeroAdditionOf(ins1->toAdd(), ins2)) {
6000 return true;
6003 if (ins2->isAdd()) {
6004 if (AddIsANonZeroAdditionOf(ins2->toAdd(), ins1)) {
6005 return true;
6009 return false;
6012 MDefinition::AliasType MLoadElement::mightAlias(const MDefinition* def) const {
6013 if (def->isStoreElement()) {
6014 const MStoreElement* store = def->toStoreElement();
6015 if (store->index() != index()) {
6016 if (DefinitelyDifferentValue(store->index(), index())) {
6017 return AliasType::NoAlias;
6019 return AliasType::MayAlias;
6022 if (store->elements() != elements()) {
6023 return AliasType::MayAlias;
6026 return AliasType::MustAlias;
6028 return AliasType::MayAlias;
6031 MDefinition* MLoadElement::foldsTo(TempAllocator& alloc) {
6032 if (MDefinition* def = foldsToStore(alloc)) {
6033 return def;
6036 return this;
6039 MDefinition* MWasmUnsignedToDouble::foldsTo(TempAllocator& alloc) {
6040 if (input()->isConstant()) {
6041 return MConstant::New(
6042 alloc, DoubleValue(uint32_t(input()->toConstant()->toInt32())));
6045 return this;
6048 MDefinition* MWasmUnsignedToFloat32::foldsTo(TempAllocator& alloc) {
6049 if (input()->isConstant()) {
6050 double dval = double(uint32_t(input()->toConstant()->toInt32()));
6051 if (IsFloat32Representable(dval)) {
6052 return MConstant::NewFloat32(alloc, float(dval));
6056 return this;
6059 MWasmCallCatchable* MWasmCallCatchable::New(TempAllocator& alloc,
6060 const wasm::CallSiteDesc& desc,
6061 const wasm::CalleeDesc& callee,
6062 const Args& args,
6063 uint32_t stackArgAreaSizeUnaligned,
6064 const MWasmCallTryDesc& tryDesc,
6065 MDefinition* tableIndexOrRef) {
6066 MOZ_ASSERT(tryDesc.inTry);
6068 MWasmCallCatchable* call = new (alloc) MWasmCallCatchable(
6069 desc, callee, stackArgAreaSizeUnaligned, tryDesc.tryNoteIndex);
6071 call->setSuccessor(FallthroughBranchIndex, tryDesc.fallthroughBlock);
6072 call->setSuccessor(PrePadBranchIndex, tryDesc.prePadBlock);
6074 MOZ_ASSERT_IF(callee.isTable() || callee.isFuncRef(), tableIndexOrRef);
6075 if (!call->initWithArgs(alloc, call, args, tableIndexOrRef)) {
6076 return nullptr;
6079 return call;
6082 MWasmCallCatchable* MWasmCallCatchable::NewBuiltinInstanceMethodCall(
6083 TempAllocator& alloc, const wasm::CallSiteDesc& desc,
6084 const wasm::SymbolicAddress builtin, wasm::FailureMode failureMode,
6085 const ABIArg& instanceArg, const Args& args,
6086 uint32_t stackArgAreaSizeUnaligned, const MWasmCallTryDesc& tryDesc) {
6087 auto callee = wasm::CalleeDesc::builtinInstanceMethod(builtin);
6088 MWasmCallCatchable* call = MWasmCallCatchable::New(
6089 alloc, desc, callee, args, stackArgAreaSizeUnaligned, tryDesc, nullptr);
6090 if (!call) {
6091 return nullptr;
6094 MOZ_ASSERT(instanceArg != ABIArg());
6095 call->instanceArg_ = instanceArg;
6096 call->builtinMethodFailureMode_ = failureMode;
6097 return call;
6100 MWasmCallUncatchable* MWasmCallUncatchable::New(
6101 TempAllocator& alloc, const wasm::CallSiteDesc& desc,
6102 const wasm::CalleeDesc& callee, const Args& args,
6103 uint32_t stackArgAreaSizeUnaligned, MDefinition* tableIndexOrRef) {
6104 MWasmCallUncatchable* call =
6105 new (alloc) MWasmCallUncatchable(desc, callee, stackArgAreaSizeUnaligned);
6107 MOZ_ASSERT_IF(callee.isTable() || callee.isFuncRef(), tableIndexOrRef);
6108 if (!call->initWithArgs(alloc, call, args, tableIndexOrRef)) {
6109 return nullptr;
6112 return call;
6115 MWasmCallUncatchable* MWasmCallUncatchable::NewBuiltinInstanceMethodCall(
6116 TempAllocator& alloc, const wasm::CallSiteDesc& desc,
6117 const wasm::SymbolicAddress builtin, wasm::FailureMode failureMode,
6118 const ABIArg& instanceArg, const Args& args,
6119 uint32_t stackArgAreaSizeUnaligned) {
6120 auto callee = wasm::CalleeDesc::builtinInstanceMethod(builtin);
6121 MWasmCallUncatchable* call = MWasmCallUncatchable::New(
6122 alloc, desc, callee, args, stackArgAreaSizeUnaligned, nullptr);
6123 if (!call) {
6124 return nullptr;
6127 MOZ_ASSERT(instanceArg != ABIArg());
6128 call->instanceArg_ = instanceArg;
6129 call->builtinMethodFailureMode_ = failureMode;
6130 return call;
6133 MWasmReturnCall* MWasmReturnCall::New(TempAllocator& alloc,
6134 const wasm::CallSiteDesc& desc,
6135 const wasm::CalleeDesc& callee,
6136 const Args& args,
6137 uint32_t stackArgAreaSizeUnaligned,
6138 MDefinition* tableIndexOrRef) {
6139 MWasmReturnCall* call =
6140 new (alloc) MWasmReturnCall(desc, callee, stackArgAreaSizeUnaligned);
6142 MOZ_ASSERT_IF(callee.isTable() || callee.isFuncRef(), tableIndexOrRef);
6143 if (!call->initWithArgs(alloc, call, args, tableIndexOrRef)) {
6144 return nullptr;
6147 return call;
6150 void MSqrt::trySpecializeFloat32(TempAllocator& alloc) {
6151 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
6152 setResultType(MIRType::Float32);
6153 specialization_ = MIRType::Float32;
6157 MDefinition* MClz::foldsTo(TempAllocator& alloc) {
6158 if (num()->isConstant()) {
6159 MConstant* c = num()->toConstant();
6160 if (type() == MIRType::Int32) {
6161 int32_t n = c->toInt32();
6162 if (n == 0) {
6163 return MConstant::New(alloc, Int32Value(32));
6165 return MConstant::New(alloc,
6166 Int32Value(mozilla::CountLeadingZeroes32(n)));
6168 int64_t n = c->toInt64();
6169 if (n == 0) {
6170 return MConstant::NewInt64(alloc, int64_t(64));
6172 return MConstant::NewInt64(alloc,
6173 int64_t(mozilla::CountLeadingZeroes64(n)));
6176 return this;
6179 MDefinition* MCtz::foldsTo(TempAllocator& alloc) {
6180 if (num()->isConstant()) {
6181 MConstant* c = num()->toConstant();
6182 if (type() == MIRType::Int32) {
6183 int32_t n = num()->toConstant()->toInt32();
6184 if (n == 0) {
6185 return MConstant::New(alloc, Int32Value(32));
6187 return MConstant::New(alloc,
6188 Int32Value(mozilla::CountTrailingZeroes32(n)));
6190 int64_t n = c->toInt64();
6191 if (n == 0) {
6192 return MConstant::NewInt64(alloc, int64_t(64));
6194 return MConstant::NewInt64(alloc,
6195 int64_t(mozilla::CountTrailingZeroes64(n)));
6198 return this;
6201 MDefinition* MPopcnt::foldsTo(TempAllocator& alloc) {
6202 if (num()->isConstant()) {
6203 MConstant* c = num()->toConstant();
6204 if (type() == MIRType::Int32) {
6205 int32_t n = num()->toConstant()->toInt32();
6206 return MConstant::New(alloc, Int32Value(mozilla::CountPopulation32(n)));
6208 int64_t n = c->toInt64();
6209 return MConstant::NewInt64(alloc, int64_t(mozilla::CountPopulation64(n)));
6212 return this;
6215 MDefinition* MBoundsCheck::foldsTo(TempAllocator& alloc) {
6216 if (type() == MIRType::Int32 && index()->isConstant() &&
6217 length()->isConstant()) {
6218 uint32_t len = length()->toConstant()->toInt32();
6219 uint32_t idx = index()->toConstant()->toInt32();
6220 if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len) {
6221 return index();
6225 return this;
6228 MDefinition* MTableSwitch::foldsTo(TempAllocator& alloc) {
6229 MDefinition* op = getOperand(0);
6231 // If we only have one successor, convert to a plain goto to the only
6232 // successor. TableSwitch indices are numeric; other types will always go to
6233 // the only successor.
6234 if (numSuccessors() == 1 ||
6235 (op->type() != MIRType::Value && !IsNumberType(op->type()))) {
6236 return MGoto::New(alloc, getDefault());
6239 if (MConstant* opConst = op->maybeConstantValue()) {
6240 if (op->type() == MIRType::Int32) {
6241 int32_t i = opConst->toInt32() - low_;
6242 MBasicBlock* target;
6243 if (size_t(i) < numCases()) {
6244 target = getCase(size_t(i));
6245 } else {
6246 target = getDefault();
6248 MOZ_ASSERT(target);
6249 return MGoto::New(alloc, target);
6253 return this;
6256 MDefinition* MArrayJoin::foldsTo(TempAllocator& alloc) {
6257 MDefinition* arr = array();
6259 if (!arr->isStringSplit()) {
6260 return this;
6263 setRecoveredOnBailout();
6264 if (arr->hasLiveDefUses()) {
6265 setNotRecoveredOnBailout();
6266 return this;
6269 // The MStringSplit won't generate any code.
6270 arr->setRecoveredOnBailout();
6272 // We're replacing foo.split(bar).join(baz) by
6273 // foo.replace(bar, baz). MStringSplit could be recovered by
6274 // a bailout. As we are removing its last use, and its result
6275 // could be captured by a resume point, this MStringSplit will
6276 // be executed on the bailout path.
6277 MDefinition* string = arr->toStringSplit()->string();
6278 MDefinition* pattern = arr->toStringSplit()->separator();
6279 MDefinition* replacement = sep();
6281 MStringReplace* substr =
6282 MStringReplace::New(alloc, string, pattern, replacement);
6283 substr->setFlatReplacement();
6284 return substr;
6287 MDefinition* MGetFirstDollarIndex::foldsTo(TempAllocator& alloc) {
6288 MDefinition* strArg = str();
6289 if (!strArg->isConstant()) {
6290 return this;
6293 JSLinearString* str = &strArg->toConstant()->toString()->asLinear();
6294 int32_t index = GetFirstDollarIndexRawFlat(str);
6295 return MConstant::New(alloc, Int32Value(index));
6298 AliasSet MThrowRuntimeLexicalError::getAliasSet() const {
6299 return AliasSet::Store(AliasSet::ExceptionState);
6302 AliasSet MSlots::getAliasSet() const {
6303 return AliasSet::Load(AliasSet::ObjectFields);
6306 MDefinition::AliasType MSlots::mightAlias(const MDefinition* store) const {
6307 // ArrayPush only modifies object elements, but not object slots.
6308 if (store->isArrayPush()) {
6309 return AliasType::NoAlias;
6311 return MInstruction::mightAlias(store);
6314 AliasSet MElements::getAliasSet() const {
6315 return AliasSet::Load(AliasSet::ObjectFields);
6318 AliasSet MInitializedLength::getAliasSet() const {
6319 return AliasSet::Load(AliasSet::ObjectFields);
6322 AliasSet MSetInitializedLength::getAliasSet() const {
6323 return AliasSet::Store(AliasSet::ObjectFields);
6326 AliasSet MObjectKeysLength::getAliasSet() const {
6327 return AliasSet::Load(AliasSet::ObjectFields);
6330 AliasSet MArrayLength::getAliasSet() const {
6331 return AliasSet::Load(AliasSet::ObjectFields);
6334 AliasSet MSetArrayLength::getAliasSet() const {
6335 return AliasSet::Store(AliasSet::ObjectFields);
6338 AliasSet MFunctionLength::getAliasSet() const {
6339 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6340 AliasSet::DynamicSlot);
6343 AliasSet MFunctionName::getAliasSet() const {
6344 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6345 AliasSet::DynamicSlot);
6348 AliasSet MArrayBufferByteLength::getAliasSet() const {
6349 return AliasSet::Load(AliasSet::FixedSlot);
6352 AliasSet MArrayBufferViewLength::getAliasSet() const {
6353 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset);
6356 AliasSet MArrayBufferViewByteOffset::getAliasSet() const {
6357 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset);
6360 AliasSet MArrayBufferViewElements::getAliasSet() const {
6361 return AliasSet::Load(AliasSet::ObjectFields);
6364 AliasSet MGuardHasAttachedArrayBuffer::getAliasSet() const {
6365 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot);
6368 AliasSet MArrayPush::getAliasSet() const {
6369 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
6372 MDefinition* MGuardNumberToIntPtrIndex::foldsTo(TempAllocator& alloc) {
6373 MDefinition* input = this->input();
6375 if (input->isToDouble() && input->getOperand(0)->type() == MIRType::Int32) {
6376 return MInt32ToIntPtr::New(alloc, input->getOperand(0));
6379 if (!input->isConstant()) {
6380 return this;
6383 // Fold constant double representable as intptr to intptr.
6384 int64_t ival;
6385 if (!mozilla::NumberEqualsInt64(input->toConstant()->toDouble(), &ival)) {
6386 // If not representable as an int64, this access is equal to an OOB access.
6387 // So replace it with a known int64/intptr value which also produces an OOB
6388 // access. If we don't support OOB accesses we have to bail out.
6389 if (!supportOOB()) {
6390 return this;
6392 ival = -1;
6395 if (ival < INTPTR_MIN || ival > INTPTR_MAX) {
6396 return this;
6399 return MConstant::NewIntPtr(alloc, intptr_t(ival));
6402 MDefinition* MIsObject::foldsTo(TempAllocator& alloc) {
6403 if (!object()->isBox()) {
6404 return this;
6407 MDefinition* unboxed = object()->getOperand(0);
6408 if (unboxed->type() == MIRType::Object) {
6409 return MConstant::New(alloc, BooleanValue(true));
6412 return this;
6415 MDefinition* MIsNullOrUndefined::foldsTo(TempAllocator& alloc) {
6416 MDefinition* input = value();
6417 if (input->isBox()) {
6418 input = input->toBox()->input();
6421 if (input->definitelyType({MIRType::Null, MIRType::Undefined})) {
6422 return MConstant::New(alloc, BooleanValue(true));
6425 if (!input->mightBeType(MIRType::Null) &&
6426 !input->mightBeType(MIRType::Undefined)) {
6427 return MConstant::New(alloc, BooleanValue(false));
6430 return this;
6433 AliasSet MHomeObjectSuperBase::getAliasSet() const {
6434 return AliasSet::Load(AliasSet::ObjectFields);
6437 MDefinition* MGuardValue::foldsTo(TempAllocator& alloc) {
6438 if (MConstant* cst = value()->maybeConstantValue()) {
6439 if (cst->toJSValue() == expected()) {
6440 return value();
6444 return this;
6447 MDefinition* MGuardNullOrUndefined::foldsTo(TempAllocator& alloc) {
6448 MDefinition* input = value();
6449 if (input->isBox()) {
6450 input = input->toBox()->input();
6453 if (input->definitelyType({MIRType::Null, MIRType::Undefined})) {
6454 return value();
6457 return this;
6460 MDefinition* MGuardIsNotObject::foldsTo(TempAllocator& alloc) {
6461 MDefinition* input = value();
6462 if (input->isBox()) {
6463 input = input->toBox()->input();
6466 if (!input->mightBeType(MIRType::Object)) {
6467 return value();
6470 return this;
6473 MDefinition* MGuardObjectIdentity::foldsTo(TempAllocator& alloc) {
6474 if (object()->isConstant() && expected()->isConstant()) {
6475 JSObject* obj = &object()->toConstant()->toObject();
6476 JSObject* other = &expected()->toConstant()->toObject();
6477 if (!bailOnEquality()) {
6478 if (obj == other) {
6479 return object();
6481 } else {
6482 if (obj != other) {
6483 return object();
6488 if (!bailOnEquality() && object()->isNurseryObject() &&
6489 expected()->isNurseryObject()) {
6490 uint32_t objIndex = object()->toNurseryObject()->nurseryIndex();
6491 uint32_t otherIndex = expected()->toNurseryObject()->nurseryIndex();
6492 if (objIndex == otherIndex) {
6493 return object();
6497 return this;
6500 MDefinition* MGuardSpecificFunction::foldsTo(TempAllocator& alloc) {
6501 if (function()->isConstant() && expected()->isConstant()) {
6502 JSObject* fun = &function()->toConstant()->toObject();
6503 JSObject* other = &expected()->toConstant()->toObject();
6504 if (fun == other) {
6505 return function();
6509 if (function()->isNurseryObject() && expected()->isNurseryObject()) {
6510 uint32_t funIndex = function()->toNurseryObject()->nurseryIndex();
6511 uint32_t otherIndex = expected()->toNurseryObject()->nurseryIndex();
6512 if (funIndex == otherIndex) {
6513 return function();
6517 return this;
6520 MDefinition* MGuardSpecificAtom::foldsTo(TempAllocator& alloc) {
6521 if (str()->isConstant()) {
6522 JSString* s = str()->toConstant()->toString();
6523 if (s->isAtom()) {
6524 JSAtom* cstAtom = &s->asAtom();
6525 if (cstAtom == atom()) {
6526 return str();
6531 return this;
6534 MDefinition* MGuardSpecificSymbol::foldsTo(TempAllocator& alloc) {
6535 if (symbol()->isConstant()) {
6536 if (symbol()->toConstant()->toSymbol() == expected()) {
6537 return symbol();
6541 return this;
6544 MDefinition* MGuardSpecificInt32::foldsTo(TempAllocator& alloc) {
6545 if (num()->isConstant() && num()->toConstant()->isInt32(expected())) {
6546 return num();
6548 return this;
6551 bool MCallBindVar::congruentTo(const MDefinition* ins) const {
6552 if (!ins->isCallBindVar()) {
6553 return false;
6555 return congruentIfOperandsEqual(ins);
6558 bool MGuardShape::congruentTo(const MDefinition* ins) const {
6559 if (!ins->isGuardShape()) {
6560 return false;
6562 if (shape() != ins->toGuardShape()->shape()) {
6563 return false;
6565 return congruentIfOperandsEqual(ins);
6568 AliasSet MGuardShape::getAliasSet() const {
6569 return AliasSet::Load(AliasSet::ObjectFields);
6572 MDefinition::AliasType MGuardShape::mightAlias(const MDefinition* store) const {
6573 // These instructions only modify object elements, but not the shape.
6574 if (store->isStoreElementHole() || store->isArrayPush()) {
6575 return AliasType::NoAlias;
6577 if (object()->isConstantProto()) {
6578 const MDefinition* receiverObject =
6579 object()->toConstantProto()->getReceiverObject();
6580 switch (store->op()) {
6581 case MDefinition::Opcode::StoreFixedSlot:
6582 if (store->toStoreFixedSlot()->object()->skipObjectGuards() ==
6583 receiverObject) {
6584 return AliasType::NoAlias;
6586 break;
6587 case MDefinition::Opcode::StoreDynamicSlot:
6588 if (store->toStoreDynamicSlot()
6589 ->slots()
6590 ->toSlots()
6591 ->object()
6592 ->skipObjectGuards() == receiverObject) {
6593 return AliasType::NoAlias;
6595 break;
6596 case MDefinition::Opcode::AddAndStoreSlot:
6597 if (store->toAddAndStoreSlot()->object()->skipObjectGuards() ==
6598 receiverObject) {
6599 return AliasType::NoAlias;
6601 break;
6602 case MDefinition::Opcode::AllocateAndStoreSlot:
6603 if (store->toAllocateAndStoreSlot()->object()->skipObjectGuards() ==
6604 receiverObject) {
6605 return AliasType::NoAlias;
6607 break;
6608 default:
6609 break;
6612 return MInstruction::mightAlias(store);
6615 bool MGuardFuse::congruentTo(const MDefinition* ins) const {
6616 if (!ins->isGuardFuse()) {
6617 return false;
6619 if (fuseIndex() != ins->toGuardFuse()->fuseIndex()) {
6620 return false;
6622 return congruentIfOperandsEqual(ins);
6625 AliasSet MGuardFuse::getAliasSet() const {
6626 // The alias set below reflects the set of operations which could cause a fuse
6627 // to be popped, and therefore MGuardFuse aliases with.
6628 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::DynamicSlot |
6629 AliasSet::FixedSlot |
6630 AliasSet::GlobalGenerationCounter);
6633 AliasSet MGuardMultipleShapes::getAliasSet() const {
6634 // Note: This instruction loads the elements of the ListObject used to
6635 // store the list of shapes, but that object is internal and not exposed
6636 // to script, so it doesn't have to be in the alias set.
6637 return AliasSet::Load(AliasSet::ObjectFields);
6640 AliasSet MGuardGlobalGeneration::getAliasSet() const {
6641 return AliasSet::Load(AliasSet::GlobalGenerationCounter);
6644 bool MGuardGlobalGeneration::congruentTo(const MDefinition* ins) const {
6645 return ins->isGuardGlobalGeneration() &&
6646 ins->toGuardGlobalGeneration()->expected() == expected() &&
6647 ins->toGuardGlobalGeneration()->generationAddr() == generationAddr();
6650 MDefinition* MGuardIsNotProxy::foldsTo(TempAllocator& alloc) {
6651 KnownClass known = GetObjectKnownClass(object());
6652 if (known == KnownClass::None) {
6653 return this;
6656 MOZ_ASSERT(!GetObjectKnownJSClass(object())->isProxyObject());
6657 AssertKnownClass(alloc, this, object());
6658 return object();
6661 AliasSet MMegamorphicLoadSlotByValue::getAliasSet() const {
6662 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6663 AliasSet::DynamicSlot);
6666 MDefinition* MMegamorphicLoadSlotByValue::foldsTo(TempAllocator& alloc) {
6667 MDefinition* input = idVal();
6668 if (input->isBox()) {
6669 input = input->toBox()->input();
6672 MDefinition* result = this;
6674 if (input->isConstant()) {
6675 MConstant* constant = input->toConstant();
6676 if (constant->type() == MIRType::Symbol) {
6677 PropertyKey id = PropertyKey::Symbol(constant->toSymbol());
6678 result = MMegamorphicLoadSlot::New(alloc, object(), id);
6681 if (constant->type() == MIRType::String) {
6682 JSString* str = constant->toString();
6683 if (str->isAtom() && !str->asAtom().isIndex()) {
6684 PropertyKey id = PropertyKey::NonIntAtom(str);
6685 result = MMegamorphicLoadSlot::New(alloc, object(), id);
6690 if (result != this) {
6691 result->setDependency(dependency());
6694 return result;
6697 bool MMegamorphicLoadSlot::congruentTo(const MDefinition* ins) const {
6698 if (!ins->isMegamorphicLoadSlot()) {
6699 return false;
6701 if (ins->toMegamorphicLoadSlot()->name() != name()) {
6702 return false;
6704 return congruentIfOperandsEqual(ins);
6707 AliasSet MMegamorphicLoadSlot::getAliasSet() const {
6708 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6709 AliasSet::DynamicSlot);
6712 bool MMegamorphicHasProp::congruentTo(const MDefinition* ins) const {
6713 if (!ins->isMegamorphicHasProp()) {
6714 return false;
6716 if (ins->toMegamorphicHasProp()->hasOwn() != hasOwn()) {
6717 return false;
6719 return congruentIfOperandsEqual(ins);
6722 AliasSet MMegamorphicHasProp::getAliasSet() const {
6723 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6724 AliasSet::DynamicSlot);
6727 bool MNurseryObject::congruentTo(const MDefinition* ins) const {
6728 if (!ins->isNurseryObject()) {
6729 return false;
6731 return nurseryIndex() == ins->toNurseryObject()->nurseryIndex();
6734 AliasSet MGuardFunctionIsNonBuiltinCtor::getAliasSet() const {
6735 return AliasSet::Load(AliasSet::ObjectFields);
6738 bool MGuardFunctionKind::congruentTo(const MDefinition* ins) const {
6739 if (!ins->isGuardFunctionKind()) {
6740 return false;
6742 if (expected() != ins->toGuardFunctionKind()->expected()) {
6743 return false;
6745 if (bailOnEquality() != ins->toGuardFunctionKind()->bailOnEquality()) {
6746 return false;
6748 return congruentIfOperandsEqual(ins);
6751 AliasSet MGuardFunctionKind::getAliasSet() const {
6752 return AliasSet::Load(AliasSet::ObjectFields);
6755 bool MGuardFunctionScript::congruentTo(const MDefinition* ins) const {
6756 if (!ins->isGuardFunctionScript()) {
6757 return false;
6759 if (expected() != ins->toGuardFunctionScript()->expected()) {
6760 return false;
6762 return congruentIfOperandsEqual(ins);
6765 AliasSet MGuardFunctionScript::getAliasSet() const {
6766 // A JSFunction's BaseScript pointer is immutable. Relazification of
6767 // top-level/named self-hosted functions is an exception to this, but we don't
6768 // use this guard for those self-hosted functions.
6769 // See IRGenerator::emitCalleeGuard.
6770 MOZ_ASSERT_IF(flags_.isSelfHostedOrIntrinsic(), flags_.isLambda());
6771 return AliasSet::None();
6774 bool MGuardSpecificAtom::congruentTo(const MDefinition* ins) const {
6775 if (!ins->isGuardSpecificAtom()) {
6776 return false;
6778 if (atom() != ins->toGuardSpecificAtom()->atom()) {
6779 return false;
6781 return congruentIfOperandsEqual(ins);
6784 MDefinition* MGuardStringToIndex::foldsTo(TempAllocator& alloc) {
6785 if (!string()->isConstant()) {
6786 return this;
6789 JSString* str = string()->toConstant()->toString();
6791 int32_t index = GetIndexFromString(str);
6792 if (index < 0) {
6793 return this;
6796 return MConstant::New(alloc, Int32Value(index));
6799 MDefinition* MGuardStringToInt32::foldsTo(TempAllocator& alloc) {
6800 if (!string()->isConstant()) {
6801 return this;
6804 JSLinearString* str = &string()->toConstant()->toString()->asLinear();
6805 double number = LinearStringToNumber(str);
6807 int32_t n;
6808 if (!mozilla::NumberIsInt32(number, &n)) {
6809 return this;
6812 return MConstant::New(alloc, Int32Value(n));
6815 MDefinition* MGuardStringToDouble::foldsTo(TempAllocator& alloc) {
6816 if (!string()->isConstant()) {
6817 return this;
6820 JSLinearString* str = &string()->toConstant()->toString()->asLinear();
6821 double number = LinearStringToNumber(str);
6822 return MConstant::New(alloc, DoubleValue(number));
6825 AliasSet MGuardNoDenseElements::getAliasSet() const {
6826 return AliasSet::Load(AliasSet::ObjectFields);
6829 AliasSet MIteratorHasIndices::getAliasSet() const {
6830 return AliasSet::Load(AliasSet::ObjectFields);
6833 AliasSet MAllocateAndStoreSlot::getAliasSet() const {
6834 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::DynamicSlot);
6837 AliasSet MLoadDOMExpandoValue::getAliasSet() const {
6838 return AliasSet::Load(AliasSet::DOMProxyExpando);
6841 AliasSet MLoadDOMExpandoValueIgnoreGeneration::getAliasSet() const {
6842 return AliasSet::Load(AliasSet::DOMProxyExpando);
6845 bool MGuardDOMExpandoMissingOrGuardShape::congruentTo(
6846 const MDefinition* ins) const {
6847 if (!ins->isGuardDOMExpandoMissingOrGuardShape()) {
6848 return false;
6850 if (shape() != ins->toGuardDOMExpandoMissingOrGuardShape()->shape()) {
6851 return false;
6853 return congruentIfOperandsEqual(ins);
6856 AliasSet MGuardDOMExpandoMissingOrGuardShape::getAliasSet() const {
6857 return AliasSet::Load(AliasSet::ObjectFields);
6860 MDefinition* MGuardToClass::foldsTo(TempAllocator& alloc) {
6861 const JSClass* clasp = GetObjectKnownJSClass(object());
6862 if (!clasp || getClass() != clasp) {
6863 return this;
6866 AssertKnownClass(alloc, this, object());
6867 return object();
6870 MDefinition* MGuardToFunction::foldsTo(TempAllocator& alloc) {
6871 if (GetObjectKnownClass(object()) != KnownClass::Function) {
6872 return this;
6875 AssertKnownClass(alloc, this, object());
6876 return object();
6879 MDefinition* MHasClass::foldsTo(TempAllocator& alloc) {
6880 const JSClass* clasp = GetObjectKnownJSClass(object());
6881 if (!clasp) {
6882 return this;
6885 AssertKnownClass(alloc, this, object());
6886 return MConstant::New(alloc, BooleanValue(getClass() == clasp));
6889 MDefinition* MIsCallable::foldsTo(TempAllocator& alloc) {
6890 if (input()->type() != MIRType::Object) {
6891 return this;
6894 KnownClass known = GetObjectKnownClass(input());
6895 if (known == KnownClass::None) {
6896 return this;
6899 AssertKnownClass(alloc, this, input());
6900 return MConstant::New(alloc, BooleanValue(known == KnownClass::Function));
6903 MDefinition* MIsArray::foldsTo(TempAllocator& alloc) {
6904 if (input()->type() != MIRType::Object) {
6905 return this;
6908 KnownClass known = GetObjectKnownClass(input());
6909 if (known == KnownClass::None) {
6910 return this;
6913 AssertKnownClass(alloc, this, input());
6914 return MConstant::New(alloc, BooleanValue(known == KnownClass::Array));
6917 AliasSet MObjectClassToString::getAliasSet() const {
6918 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6919 AliasSet::DynamicSlot);
6922 MDefinition* MGuardIsNotArrayBufferMaybeShared::foldsTo(TempAllocator& alloc) {
6923 switch (GetObjectKnownClass(object())) {
6924 case KnownClass::PlainObject:
6925 case KnownClass::Array:
6926 case KnownClass::Function:
6927 case KnownClass::RegExp:
6928 case KnownClass::ArrayIterator:
6929 case KnownClass::StringIterator:
6930 case KnownClass::RegExpStringIterator: {
6931 AssertKnownClass(alloc, this, object());
6932 return object();
6934 case KnownClass::None:
6935 break;
6938 return this;
6941 MDefinition* MCheckIsObj::foldsTo(TempAllocator& alloc) {
6942 if (!input()->isBox()) {
6943 return this;
6946 MDefinition* unboxed = input()->getOperand(0);
6947 if (unboxed->type() == MIRType::Object) {
6948 return unboxed;
6951 return this;
6954 AliasSet MCheckIsObj::getAliasSet() const {
6955 return AliasSet::Store(AliasSet::ExceptionState);
6958 #ifdef JS_PUNBOX64
6959 AliasSet MCheckScriptedProxyGetResult::getAliasSet() const {
6960 return AliasSet::Store(AliasSet::ExceptionState);
6962 #endif
6964 static bool IsBoxedObject(MDefinition* def) {
6965 MOZ_ASSERT(def->type() == MIRType::Value);
6967 if (def->isBox()) {
6968 return def->toBox()->input()->type() == MIRType::Object;
6971 // Construct calls are always returning a boxed object.
6973 // TODO: We should consider encoding this directly in the graph instead of
6974 // having to special case it here.
6975 if (def->isCall()) {
6976 return def->toCall()->isConstructing();
6978 if (def->isConstructArray()) {
6979 return true;
6981 if (def->isConstructArgs()) {
6982 return true;
6985 return false;
6988 MDefinition* MCheckReturn::foldsTo(TempAllocator& alloc) {
6989 auto* returnVal = returnValue();
6990 if (!returnVal->isBox()) {
6991 return this;
6994 auto* unboxedReturnVal = returnVal->toBox()->input();
6995 if (unboxedReturnVal->type() == MIRType::Object) {
6996 return returnVal;
6999 if (unboxedReturnVal->type() != MIRType::Undefined) {
7000 return this;
7003 auto* thisVal = thisValue();
7004 if (IsBoxedObject(thisVal)) {
7005 return thisVal;
7008 return this;
7011 MDefinition* MCheckThis::foldsTo(TempAllocator& alloc) {
7012 MDefinition* input = thisValue();
7013 if (!input->isBox()) {
7014 return this;
7017 MDefinition* unboxed = input->getOperand(0);
7018 if (unboxed->mightBeMagicType()) {
7019 return this;
7022 return input;
7025 MDefinition* MCheckThisReinit::foldsTo(TempAllocator& alloc) {
7026 MDefinition* input = thisValue();
7027 if (!input->isBox()) {
7028 return this;
7031 MDefinition* unboxed = input->getOperand(0);
7032 if (unboxed->type() != MIRType::MagicUninitializedLexical) {
7033 return this;
7036 return input;
7039 MDefinition* MCheckObjCoercible::foldsTo(TempAllocator& alloc) {
7040 MDefinition* input = checkValue();
7041 if (!input->isBox()) {
7042 return this;
7045 MDefinition* unboxed = input->getOperand(0);
7046 if (unboxed->mightBeType(MIRType::Null) ||
7047 unboxed->mightBeType(MIRType::Undefined)) {
7048 return this;
7051 return input;
7054 AliasSet MCheckObjCoercible::getAliasSet() const {
7055 return AliasSet::Store(AliasSet::ExceptionState);
7058 AliasSet MCheckReturn::getAliasSet() const {
7059 return AliasSet::Store(AliasSet::ExceptionState);
7062 AliasSet MCheckThis::getAliasSet() const {
7063 return AliasSet::Store(AliasSet::ExceptionState);
7066 AliasSet MCheckThisReinit::getAliasSet() const {
7067 return AliasSet::Store(AliasSet::ExceptionState);
7070 AliasSet MIsPackedArray::getAliasSet() const {
7071 return AliasSet::Load(AliasSet::ObjectFields);
7074 AliasSet MGuardArrayIsPacked::getAliasSet() const {
7075 return AliasSet::Load(AliasSet::ObjectFields);
7078 AliasSet MSuperFunction::getAliasSet() const {
7079 return AliasSet::Load(AliasSet::ObjectFields);
7082 AliasSet MInitHomeObject::getAliasSet() const {
7083 return AliasSet::Store(AliasSet::ObjectFields);
7086 AliasSet MLoadWrapperTarget::getAliasSet() const {
7087 return AliasSet::Load(AliasSet::Any);
7090 AliasSet MGuardHasGetterSetter::getAliasSet() const {
7091 return AliasSet::Load(AliasSet::ObjectFields);
7094 bool MGuardHasGetterSetter::congruentTo(const MDefinition* ins) const {
7095 if (!ins->isGuardHasGetterSetter()) {
7096 return false;
7098 if (ins->toGuardHasGetterSetter()->propId() != propId()) {
7099 return false;
7101 if (ins->toGuardHasGetterSetter()->getterSetter() != getterSetter()) {
7102 return false;
7104 return congruentIfOperandsEqual(ins);
7107 AliasSet MGuardIsExtensible::getAliasSet() const {
7108 return AliasSet::Load(AliasSet::ObjectFields);
7111 AliasSet MGuardIndexIsNotDenseElement::getAliasSet() const {
7112 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::Element);
7115 AliasSet MGuardIndexIsValidUpdateOrAdd::getAliasSet() const {
7116 return AliasSet::Load(AliasSet::ObjectFields);
7119 AliasSet MCallObjectHasSparseElement::getAliasSet() const {
7120 return AliasSet::Load(AliasSet::Element | AliasSet::ObjectFields |
7121 AliasSet::FixedSlot | AliasSet::DynamicSlot);
7124 AliasSet MLoadSlotByIteratorIndex::getAliasSet() const {
7125 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
7126 AliasSet::DynamicSlot | AliasSet::Element);
7129 AliasSet MStoreSlotByIteratorIndex::getAliasSet() const {
7130 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot |
7131 AliasSet::DynamicSlot | AliasSet::Element);
7134 MDefinition* MGuardInt32IsNonNegative::foldsTo(TempAllocator& alloc) {
7135 MOZ_ASSERT(index()->type() == MIRType::Int32);
7137 MDefinition* input = index();
7138 if (!input->isConstant() || input->toConstant()->toInt32() < 0) {
7139 return this;
7141 return input;
7144 MDefinition* MGuardInt32Range::foldsTo(TempAllocator& alloc) {
7145 MOZ_ASSERT(input()->type() == MIRType::Int32);
7146 MOZ_ASSERT(minimum() <= maximum());
7148 MDefinition* in = input();
7149 if (!in->isConstant()) {
7150 return this;
7152 int32_t cst = in->toConstant()->toInt32();
7153 if (cst < minimum() || cst > maximum()) {
7154 return this;
7156 return in;
7159 MDefinition* MGuardNonGCThing::foldsTo(TempAllocator& alloc) {
7160 if (!input()->isBox()) {
7161 return this;
7164 MDefinition* unboxed = input()->getOperand(0);
7165 if (!IsNonGCThing(unboxed->type())) {
7166 return this;
7168 return input();
7171 AliasSet MSetObjectHasNonBigInt::getAliasSet() const {
7172 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7175 AliasSet MSetObjectHasBigInt::getAliasSet() const {
7176 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7179 AliasSet MSetObjectHasValue::getAliasSet() const {
7180 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7183 AliasSet MSetObjectHasValueVMCall::getAliasSet() const {
7184 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7187 AliasSet MSetObjectSize::getAliasSet() const {
7188 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7191 AliasSet MMapObjectHasNonBigInt::getAliasSet() const {
7192 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7195 AliasSet MMapObjectHasBigInt::getAliasSet() const {
7196 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7199 AliasSet MMapObjectHasValue::getAliasSet() const {
7200 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7203 AliasSet MMapObjectHasValueVMCall::getAliasSet() const {
7204 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7207 AliasSet MMapObjectGetNonBigInt::getAliasSet() const {
7208 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7211 AliasSet MMapObjectGetBigInt::getAliasSet() const {
7212 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7215 AliasSet MMapObjectGetValue::getAliasSet() const {
7216 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7219 AliasSet MMapObjectGetValueVMCall::getAliasSet() const {
7220 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7223 AliasSet MMapObjectSize::getAliasSet() const {
7224 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7227 MIonToWasmCall* MIonToWasmCall::New(TempAllocator& alloc,
7228 WasmInstanceObject* instanceObj,
7229 const wasm::FuncExport& funcExport) {
7230 const wasm::FuncType& funcType =
7231 instanceObj->instance().metadata().getFuncExportType(funcExport);
7232 const wasm::ValTypeVector& results = funcType.results();
7233 MIRType resultType = MIRType::Value;
7234 // At the JS boundary some wasm types must be represented as a Value, and in
7235 // addition a void return requires an Undefined value.
7236 if (results.length() > 0 && !results[0].isEncodedAsJSValueOnEscape()) {
7237 MOZ_ASSERT(results.length() == 1,
7238 "multiple returns not implemented for inlined Wasm calls");
7239 resultType = results[0].toMIRType();
7242 auto* ins = new (alloc) MIonToWasmCall(instanceObj, resultType, funcExport);
7243 if (!ins->init(alloc, funcType.args().length())) {
7244 return nullptr;
7246 return ins;
7249 MBindFunction* MBindFunction::New(TempAllocator& alloc, MDefinition* target,
7250 uint32_t argc, JSObject* templateObj) {
7251 auto* ins = new (alloc) MBindFunction(templateObj);
7252 if (!ins->init(alloc, NumNonArgumentOperands + argc)) {
7253 return nullptr;
7255 ins->initOperand(0, target);
7256 return ins;
7259 #ifdef DEBUG
7260 bool MIonToWasmCall::isConsistentFloat32Use(MUse* use) const {
7261 const wasm::FuncType& funcType =
7262 instance()->metadata().getFuncExportType(funcExport_);
7263 return funcType.args()[use->index()].kind() == wasm::ValType::F32;
7265 #endif
7267 MCreateInlinedArgumentsObject* MCreateInlinedArgumentsObject::New(
7268 TempAllocator& alloc, MDefinition* callObj, MDefinition* callee,
7269 MDefinitionVector& args, ArgumentsObject* templateObj) {
7270 MCreateInlinedArgumentsObject* ins =
7271 new (alloc) MCreateInlinedArgumentsObject(templateObj);
7273 uint32_t argc = args.length();
7274 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs);
7276 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7277 return nullptr;
7280 ins->initOperand(0, callObj);
7281 ins->initOperand(1, callee);
7282 for (uint32_t i = 0; i < argc; i++) {
7283 ins->initOperand(i + NumNonArgumentOperands, args[i]);
7286 return ins;
7289 MGetInlinedArgument* MGetInlinedArgument::New(
7290 TempAllocator& alloc, MDefinition* index,
7291 MCreateInlinedArgumentsObject* args) {
7292 MGetInlinedArgument* ins = new (alloc) MGetInlinedArgument();
7294 uint32_t argc = args->numActuals();
7295 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs);
7297 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7298 return nullptr;
7301 ins->initOperand(0, index);
7302 for (uint32_t i = 0; i < argc; i++) {
7303 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7306 return ins;
7309 MGetInlinedArgument* MGetInlinedArgument::New(TempAllocator& alloc,
7310 MDefinition* index,
7311 const CallInfo& callInfo) {
7312 MGetInlinedArgument* ins = new (alloc) MGetInlinedArgument();
7314 uint32_t argc = callInfo.argc();
7315 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs);
7317 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7318 return nullptr;
7321 ins->initOperand(0, index);
7322 for (uint32_t i = 0; i < argc; i++) {
7323 ins->initOperand(i + NumNonArgumentOperands, callInfo.getArg(i));
7326 return ins;
7329 MDefinition* MGetInlinedArgument::foldsTo(TempAllocator& alloc) {
7330 MDefinition* indexDef = SkipUninterestingInstructions(index());
7331 if (!indexDef->isConstant() || indexDef->type() != MIRType::Int32) {
7332 return this;
7335 int32_t indexConst = indexDef->toConstant()->toInt32();
7336 if (indexConst < 0 || uint32_t(indexConst) >= numActuals()) {
7337 return this;
7340 MDefinition* arg = getArg(indexConst);
7341 if (arg->type() != MIRType::Value) {
7342 arg = MBox::New(alloc, arg);
7345 return arg;
7348 MGetInlinedArgumentHole* MGetInlinedArgumentHole::New(
7349 TempAllocator& alloc, MDefinition* index,
7350 MCreateInlinedArgumentsObject* args) {
7351 auto* ins = new (alloc) MGetInlinedArgumentHole();
7353 uint32_t argc = args->numActuals();
7354 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs);
7356 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7357 return nullptr;
7360 ins->initOperand(0, index);
7361 for (uint32_t i = 0; i < argc; i++) {
7362 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7365 return ins;
7368 MDefinition* MGetInlinedArgumentHole::foldsTo(TempAllocator& alloc) {
7369 MDefinition* indexDef = SkipUninterestingInstructions(index());
7370 if (!indexDef->isConstant() || indexDef->type() != MIRType::Int32) {
7371 return this;
7374 int32_t indexConst = indexDef->toConstant()->toInt32();
7375 if (indexConst < 0) {
7376 return this;
7379 MDefinition* arg;
7380 if (uint32_t(indexConst) < numActuals()) {
7381 arg = getArg(indexConst);
7383 if (arg->type() != MIRType::Value) {
7384 arg = MBox::New(alloc, arg);
7386 } else {
7387 auto* undefined = MConstant::New(alloc, UndefinedValue());
7388 block()->insertBefore(this, undefined);
7390 arg = MBox::New(alloc, undefined);
7393 return arg;
7396 MInlineArgumentsSlice* MInlineArgumentsSlice::New(
7397 TempAllocator& alloc, MDefinition* begin, MDefinition* count,
7398 MCreateInlinedArgumentsObject* args, JSObject* templateObj,
7399 gc::Heap initialHeap) {
7400 auto* ins = new (alloc) MInlineArgumentsSlice(templateObj, initialHeap);
7402 uint32_t argc = args->numActuals();
7403 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs);
7405 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7406 return nullptr;
7409 ins->initOperand(0, begin);
7410 ins->initOperand(1, count);
7411 for (uint32_t i = 0; i < argc; i++) {
7412 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7415 return ins;
7418 MDefinition* MArrayLength::foldsTo(TempAllocator& alloc) {
7419 // Object.keys() is potentially effectful, in case of Proxies. Otherwise, when
7420 // it is only computed for its length property, there is no need to
7421 // materialize the Array which results from it and it can be marked as
7422 // recovered on bailout as long as no properties are added to / removed from
7423 // the object.
7424 MDefinition* elems = elements();
7425 if (!elems->isElements()) {
7426 return this;
7429 MDefinition* guardshape = elems->toElements()->object();
7430 if (!guardshape->isGuardShape()) {
7431 return this;
7434 // The Guard shape is guarding the shape of the object returned by
7435 // Object.keys, this guard can be removed as knowing the function is good
7436 // enough to infer that we are returning an array.
7437 MDefinition* keys = guardshape->toGuardShape()->object();
7438 if (!keys->isObjectKeys()) {
7439 return this;
7442 // Object.keys() inline cache guards against proxies when creating the IC. We
7443 // rely on this here as we are looking to elide `Object.keys(...)` call, which
7444 // is only possible if we know for sure that no side-effect might have
7445 // happened.
7446 MDefinition* noproxy = keys->toObjectKeys()->object();
7447 if (!noproxy->isGuardIsNotProxy()) {
7448 // The guard might have been replaced by an assertion, in case the class is
7449 // known at compile time. IF the guard has been removed check whether check
7450 // has been removed.
7451 MOZ_RELEASE_ASSERT(GetObjectKnownClass(noproxy) != KnownClass::None);
7452 MOZ_RELEASE_ASSERT(!GetObjectKnownJSClass(noproxy)->isProxyObject());
7455 // Check if both the elements and the Object.keys() have a single use. We only
7456 // check for live uses, and are ok if a branch which was previously using the
7457 // keys array has been removed since.
7458 if (!elems->hasOneLiveDefUse() || !guardshape->hasOneLiveDefUse() ||
7459 !keys->hasOneLiveDefUse()) {
7460 return this;
7463 // Check that the latest active resume point is the one from Object.keys(), in
7464 // order to steal it. If this is not the latest active resume point then some
7465 // side-effect might happen which updates the content of the object, making
7466 // any recovery of the keys exhibit a different behavior than expected.
7467 if (keys->toObjectKeys()->resumePoint() != block()->activeResumePoint(this)) {
7468 return this;
7471 // Verify whether any resume point captures the keys array after any aliasing
7472 // mutations. If this were to be the case the recovery of ObjectKeys on
7473 // bailout might compute a version which might not match with the elided
7474 // result.
7476 // Iterate over the resume point uses of ObjectKeys, and check whether the
7477 // instructions they are attached to are aliasing Object fields. If so, skip
7478 // this optimization.
7479 AliasSet enumKeysAliasSet = AliasSet::Load(AliasSet::Flag::ObjectFields);
7480 for (auto* use : UsesIterator(keys)) {
7481 if (!use->consumer()->isResumePoint()) {
7482 // There is only a single use, and this is the length computation as
7483 // asserted with `hasOneLiveDefUse`.
7484 continue;
7487 MResumePoint* rp = use->consumer()->toResumePoint();
7488 if (!rp->instruction()) {
7489 // If there is no instruction, this is a resume point which is attached to
7490 // the entry of a block. Thus no risk of mutating the object on which the
7491 // keys are queried.
7492 continue;
7495 MInstruction* ins = rp->instruction();
7496 if (ins == keys) {
7497 continue;
7500 // Check whether the instruction can potentially alias the object fields of
7501 // the object from which we are querying the keys.
7502 AliasSet mightAlias = ins->getAliasSet() & enumKeysAliasSet;
7503 if (!mightAlias.isNone()) {
7504 return this;
7508 // Flag every instructions since Object.keys(..) as recovered on bailout, and
7509 // make Object.keys(..) be the recovered value in-place of the shape guard.
7510 setRecoveredOnBailout();
7511 elems->setRecoveredOnBailout();
7512 guardshape->replaceAllUsesWith(keys);
7513 guardshape->block()->discard(guardshape->toGuardShape());
7514 keys->setRecoveredOnBailout();
7516 // Steal the resume point from Object.keys, which is ok as we confirmed that
7517 // there is no other resume point in-between.
7518 MObjectKeysLength* keysLength = MObjectKeysLength::New(alloc, noproxy);
7519 keysLength->stealResumePoint(keys->toObjectKeys());
7521 return keysLength;
7524 MDefinition* MNormalizeSliceTerm::foldsTo(TempAllocator& alloc) {
7525 auto* length = this->length();
7526 if (!length->isConstant() && !length->isArgumentsLength()) {
7527 return this;
7530 if (length->isConstant()) {
7531 int32_t lengthConst = length->toConstant()->toInt32();
7532 MOZ_ASSERT(lengthConst >= 0);
7534 // Result is always zero when |length| is zero.
7535 if (lengthConst == 0) {
7536 return length;
7539 auto* value = this->value();
7540 if (value->isConstant()) {
7541 int32_t valueConst = value->toConstant()->toInt32();
7543 int32_t normalized;
7544 if (valueConst < 0) {
7545 normalized = std::max(valueConst + lengthConst, 0);
7546 } else {
7547 normalized = std::min(valueConst, lengthConst);
7550 if (normalized == valueConst) {
7551 return value;
7553 if (normalized == lengthConst) {
7554 return length;
7556 return MConstant::New(alloc, Int32Value(normalized));
7559 return this;
7562 auto* value = this->value();
7563 if (value->isConstant()) {
7564 int32_t valueConst = value->toConstant()->toInt32();
7566 // Minimum of |value| and |length|.
7567 if (valueConst > 0) {
7568 bool isMax = false;
7569 return MMinMax::New(alloc, value, length, MIRType::Int32, isMax);
7572 // Maximum of |value + length| and zero.
7573 if (valueConst < 0) {
7574 // Safe to truncate because |length| is never negative.
7575 auto* add = MAdd::New(alloc, value, length, TruncateKind::Truncate);
7576 block()->insertBefore(this, add);
7578 auto* zero = MConstant::New(alloc, Int32Value(0));
7579 block()->insertBefore(this, zero);
7581 bool isMax = true;
7582 return MMinMax::New(alloc, add, zero, MIRType::Int32, isMax);
7585 // Directly return the value when it's zero.
7586 return value;
7589 // Normalizing MArgumentsLength is a no-op.
7590 if (value->isArgumentsLength()) {
7591 return value;
7594 return this;
7597 bool MInt32ToStringWithBase::congruentTo(const MDefinition* ins) const {
7598 if (!ins->isInt32ToStringWithBase()) {
7599 return false;
7601 if (ins->toInt32ToStringWithBase()->lowerCase() != lowerCase()) {
7602 return false;
7604 return congruentIfOperandsEqual(ins);
7607 bool MWasmShiftSimd128::congruentTo(const MDefinition* ins) const {
7608 if (!ins->isWasmShiftSimd128()) {
7609 return false;
7611 return ins->toWasmShiftSimd128()->simdOp() == simdOp_ &&
7612 congruentIfOperandsEqual(ins);
7615 bool MWasmShuffleSimd128::congruentTo(const MDefinition* ins) const {
7616 if (!ins->isWasmShuffleSimd128()) {
7617 return false;
7619 return ins->toWasmShuffleSimd128()->shuffle().equals(&shuffle_) &&
7620 congruentIfOperandsEqual(ins);
7623 bool MWasmUnarySimd128::congruentTo(const MDefinition* ins) const {
7624 if (!ins->isWasmUnarySimd128()) {
7625 return false;
7627 return ins->toWasmUnarySimd128()->simdOp() == simdOp_ &&
7628 congruentIfOperandsEqual(ins);
7631 #ifdef ENABLE_WASM_SIMD
7632 MWasmShuffleSimd128* jit::BuildWasmShuffleSimd128(TempAllocator& alloc,
7633 const int8_t* control,
7634 MDefinition* lhs,
7635 MDefinition* rhs) {
7636 SimdShuffle s =
7637 AnalyzeSimdShuffle(SimdConstant::CreateX16(control), lhs, rhs);
7638 switch (s.opd) {
7639 case SimdShuffle::Operand::LEFT:
7640 // When SimdShuffle::Operand is LEFT the right operand is not used,
7641 // lose reference to rhs.
7642 rhs = lhs;
7643 break;
7644 case SimdShuffle::Operand::RIGHT:
7645 // When SimdShuffle::Operand is RIGHT the left operand is not used,
7646 // lose reference to lhs.
7647 lhs = rhs;
7648 break;
7649 default:
7650 break;
7652 return MWasmShuffleSimd128::New(alloc, lhs, rhs, s);
7654 #endif // ENABLE_WASM_SIMD
7656 static MDefinition* FoldTrivialWasmCasts(TempAllocator& alloc,
7657 wasm::RefType sourceType,
7658 wasm::RefType destType) {
7659 // Upcasts are trivially valid.
7660 if (wasm::RefType::isSubTypeOf(sourceType, destType)) {
7661 return MConstant::New(alloc, Int32Value(1), MIRType::Int32);
7664 // If two types are completely disjoint, then all casts between them are
7665 // impossible.
7666 if (!wasm::RefType::castPossible(destType, sourceType)) {
7667 return MConstant::New(alloc, Int32Value(0), MIRType::Int32);
7670 return nullptr;
7673 MDefinition* MWasmRefIsSubtypeOfAbstract::foldsTo(TempAllocator& alloc) {
7674 MDefinition* folded = FoldTrivialWasmCasts(alloc, sourceType(), destType());
7675 if (folded) {
7676 return folded;
7678 return this;
7681 MDefinition* MWasmRefIsSubtypeOfConcrete::foldsTo(TempAllocator& alloc) {
7682 MDefinition* folded = FoldTrivialWasmCasts(alloc, sourceType(), destType());
7683 if (folded) {
7684 return folded;
7686 return this;