Bug 1874683 - Part 7: Transpile LoadStringCodePoint. r=jandem,nbp
[gecko.git] / js / src / jit / MIR.cpp
blobe5ae436dd1b35fffefbc7ad07db3485f43b81029
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 MNewArrayDynamicLength::getAliasSet() const {
717 return AliasSet::Store(AliasSet::ExceptionState);
720 AliasSet MNewTypedArrayDynamicLength::getAliasSet() const {
721 return AliasSet::Store(AliasSet::ExceptionState);
724 #ifdef JS_JITSPEW
725 void MDefinition::printOpcode(GenericPrinter& out) const {
726 PrintOpcodeName(out, op());
727 for (size_t j = 0, e = numOperands(); j < e; j++) {
728 out.printf(" ");
729 if (getUseFor(j)->hasProducer()) {
730 getOperand(j)->printName(out);
731 out.printf(":%s", StringFromMIRType(getOperand(j)->type()));
732 } else {
733 out.printf("(null)");
738 void MDefinition::dump(GenericPrinter& out) const {
739 printName(out);
740 out.printf(":%s", StringFromMIRType(type()));
741 out.printf(" = ");
742 printOpcode(out);
743 out.printf("\n");
745 if (isInstruction()) {
746 if (MResumePoint* resume = toInstruction()->resumePoint()) {
747 resume->dump(out);
752 void MDefinition::dump() const {
753 Fprinter out(stderr);
754 dump(out);
755 out.finish();
758 void MDefinition::dumpLocation(GenericPrinter& out) const {
759 MResumePoint* rp = nullptr;
760 const char* linkWord = nullptr;
761 if (isInstruction() && toInstruction()->resumePoint()) {
762 rp = toInstruction()->resumePoint();
763 linkWord = "at";
764 } else {
765 rp = block()->entryResumePoint();
766 linkWord = "after";
769 while (rp) {
770 JSScript* script = rp->block()->info().script();
771 uint32_t lineno = PCToLineNumber(rp->block()->info().script(), rp->pc());
772 out.printf(" %s %s:%u\n", linkWord, script->filename(), lineno);
773 rp = rp->caller();
774 linkWord = "in";
778 void MDefinition::dumpLocation() const {
779 Fprinter out(stderr);
780 dumpLocation(out);
781 out.finish();
783 #endif
785 #if defined(DEBUG) || defined(JS_JITSPEW)
786 size_t MDefinition::useCount() const {
787 size_t count = 0;
788 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
789 count++;
791 return count;
794 size_t MDefinition::defUseCount() const {
795 size_t count = 0;
796 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
797 if ((*i)->consumer()->isDefinition()) {
798 count++;
801 return count;
803 #endif
805 bool MDefinition::hasOneUse() const {
806 MUseIterator i(uses_.begin());
807 if (i == uses_.end()) {
808 return false;
810 i++;
811 return i == uses_.end();
814 bool MDefinition::hasOneDefUse() const {
815 bool hasOneDefUse = false;
816 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
817 if (!(*i)->consumer()->isDefinition()) {
818 continue;
821 // We already have a definition use. So 1+
822 if (hasOneDefUse) {
823 return false;
826 // We saw one definition. Loop to test if there is another.
827 hasOneDefUse = true;
830 return hasOneDefUse;
833 bool MDefinition::hasOneLiveDefUse() const {
834 bool hasOneDefUse = false;
835 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
836 if (!(*i)->consumer()->isDefinition()) {
837 continue;
840 MDefinition* def = (*i)->consumer()->toDefinition();
841 if (def->isRecoveredOnBailout()) {
842 continue;
845 // We already have a definition use. So 1+
846 if (hasOneDefUse) {
847 return false;
850 // We saw one definition. Loop to test if there is another.
851 hasOneDefUse = true;
854 return hasOneDefUse;
857 bool MDefinition::hasDefUses() const {
858 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
859 if ((*i)->consumer()->isDefinition()) {
860 return true;
864 return false;
867 bool MDefinition::hasLiveDefUses() const {
868 for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
869 MNode* ins = (*i)->consumer();
870 if (ins->isDefinition()) {
871 if (!ins->toDefinition()->isRecoveredOnBailout()) {
872 return true;
874 } else {
875 MOZ_ASSERT(ins->isResumePoint());
876 if (!ins->toResumePoint()->isRecoverableOperand(*i)) {
877 return true;
882 return false;
885 MDefinition* MDefinition::maybeSingleDefUse() const {
886 MUseDefIterator use(this);
887 if (!use) {
888 // No def-uses.
889 return nullptr;
892 MDefinition* useDef = use.def();
894 use++;
895 if (use) {
896 // More than one def-use.
897 return nullptr;
900 return useDef;
903 MDefinition* MDefinition::maybeMostRecentlyAddedDefUse() const {
904 MUseDefIterator use(this);
905 if (!use) {
906 // No def-uses.
907 return nullptr;
910 MDefinition* mostRecentUse = use.def();
912 #ifdef DEBUG
913 // This function relies on addUse adding new uses to the front of the list.
914 // Check this invariant by asserting the next few uses are 'older'. Skip this
915 // for phis because setBackedge can add a new use for a loop phi even if the
916 // loop body has a use with an id greater than the loop phi's id.
917 if (!mostRecentUse->isPhi()) {
918 static constexpr size_t NumUsesToCheck = 3;
919 use++;
920 for (size_t i = 0; use && i < NumUsesToCheck; i++, use++) {
921 MOZ_ASSERT(use.def()->id() <= mostRecentUse->id());
924 #endif
926 return mostRecentUse;
929 void MDefinition::replaceAllUsesWith(MDefinition* dom) {
930 for (size_t i = 0, e = numOperands(); i < e; ++i) {
931 getOperand(i)->setImplicitlyUsedUnchecked();
934 justReplaceAllUsesWith(dom);
937 void MDefinition::justReplaceAllUsesWith(MDefinition* dom) {
938 MOZ_ASSERT(dom != nullptr);
939 MOZ_ASSERT(dom != this);
941 // Carry over the fact the value has uses which are no longer inspectable
942 // with the graph.
943 if (isImplicitlyUsed()) {
944 dom->setImplicitlyUsedUnchecked();
947 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i) {
948 i->setProducerUnchecked(dom);
950 dom->uses_.takeElements(uses_);
953 bool MDefinition::optimizeOutAllUses(TempAllocator& alloc) {
954 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) {
955 MUse* use = *i++;
956 MConstant* constant = use->consumer()->block()->optimizedOutConstant(alloc);
957 if (!alloc.ensureBallast()) {
958 return false;
961 // Update the resume point operand to use the optimized-out constant.
962 use->setProducerUnchecked(constant);
963 constant->addUseUnchecked(use);
966 // Remove dangling pointers.
967 this->uses_.clear();
968 return true;
971 void MDefinition::replaceAllLiveUsesWith(MDefinition* dom) {
972 for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) {
973 MUse* use = *i++;
974 MNode* consumer = use->consumer();
975 if (consumer->isResumePoint()) {
976 continue;
978 if (consumer->isDefinition() &&
979 consumer->toDefinition()->isRecoveredOnBailout()) {
980 continue;
983 // Update the operand to use the dominating definition.
984 use->replaceProducer(dom);
988 MConstant* MConstant::New(TempAllocator& alloc, const Value& v) {
989 return new (alloc) MConstant(alloc, v);
992 MConstant* MConstant::New(TempAllocator::Fallible alloc, const Value& v) {
993 return new (alloc) MConstant(alloc.alloc, v);
996 MConstant* MConstant::NewFloat32(TempAllocator& alloc, double d) {
997 MOZ_ASSERT(std::isnan(d) || d == double(float(d)));
998 return new (alloc) MConstant(float(d));
1001 MConstant* MConstant::NewInt64(TempAllocator& alloc, int64_t i) {
1002 return new (alloc) MConstant(MIRType::Int64, i);
1005 MConstant* MConstant::NewIntPtr(TempAllocator& alloc, intptr_t i) {
1006 return new (alloc) MConstant(MIRType::IntPtr, i);
1009 MConstant* MConstant::New(TempAllocator& alloc, const Value& v, MIRType type) {
1010 if (type == MIRType::Float32) {
1011 return NewFloat32(alloc, v.toNumber());
1013 MConstant* res = New(alloc, v);
1014 MOZ_ASSERT(res->type() == type);
1015 return res;
1018 MConstant* MConstant::NewObject(TempAllocator& alloc, JSObject* v) {
1019 return new (alloc) MConstant(v);
1022 MConstant* MConstant::NewShape(TempAllocator& alloc, Shape* s) {
1023 return new (alloc) MConstant(s);
1026 static MIRType MIRTypeFromValue(const js::Value& vp) {
1027 if (vp.isDouble()) {
1028 return MIRType::Double;
1030 if (vp.isMagic()) {
1031 switch (vp.whyMagic()) {
1032 case JS_OPTIMIZED_OUT:
1033 return MIRType::MagicOptimizedOut;
1034 case JS_ELEMENTS_HOLE:
1035 return MIRType::MagicHole;
1036 case JS_IS_CONSTRUCTING:
1037 return MIRType::MagicIsConstructing;
1038 case JS_UNINITIALIZED_LEXICAL:
1039 return MIRType::MagicUninitializedLexical;
1040 default:
1041 MOZ_ASSERT_UNREACHABLE("Unexpected magic constant");
1044 return MIRTypeFromValueType(vp.extractNonDoubleType());
1047 MConstant::MConstant(TempAllocator& alloc, const js::Value& vp)
1048 : MNullaryInstruction(classOpcode) {
1049 setResultType(MIRTypeFromValue(vp));
1051 MOZ_ASSERT(payload_.asBits == 0);
1053 switch (type()) {
1054 case MIRType::Undefined:
1055 case MIRType::Null:
1056 break;
1057 case MIRType::Boolean:
1058 payload_.b = vp.toBoolean();
1059 break;
1060 case MIRType::Int32:
1061 payload_.i32 = vp.toInt32();
1062 break;
1063 case MIRType::Double:
1064 payload_.d = vp.toDouble();
1065 break;
1066 case MIRType::String:
1067 MOZ_ASSERT(!IsInsideNursery(vp.toString()));
1068 MOZ_ASSERT(vp.toString()->isLinear());
1069 payload_.str = vp.toString();
1070 break;
1071 case MIRType::Symbol:
1072 payload_.sym = vp.toSymbol();
1073 break;
1074 case MIRType::BigInt:
1075 MOZ_ASSERT(!IsInsideNursery(vp.toBigInt()));
1076 payload_.bi = vp.toBigInt();
1077 break;
1078 case MIRType::Object:
1079 MOZ_ASSERT(!IsInsideNursery(&vp.toObject()));
1080 payload_.obj = &vp.toObject();
1081 break;
1082 case MIRType::MagicOptimizedOut:
1083 case MIRType::MagicHole:
1084 case MIRType::MagicIsConstructing:
1085 case MIRType::MagicUninitializedLexical:
1086 break;
1087 default:
1088 MOZ_CRASH("Unexpected type");
1091 setMovable();
1094 MConstant::MConstant(JSObject* obj) : MNullaryInstruction(classOpcode) {
1095 MOZ_ASSERT(!IsInsideNursery(obj));
1096 setResultType(MIRType::Object);
1097 payload_.obj = obj;
1098 setMovable();
1101 MConstant::MConstant(Shape* shape) : MNullaryInstruction(classOpcode) {
1102 setResultType(MIRType::Shape);
1103 payload_.shape = shape;
1104 setMovable();
1107 MConstant::MConstant(float f) : MNullaryInstruction(classOpcode) {
1108 setResultType(MIRType::Float32);
1109 payload_.f = f;
1110 setMovable();
1113 MConstant::MConstant(MIRType type, int64_t i)
1114 : MNullaryInstruction(classOpcode) {
1115 MOZ_ASSERT(type == MIRType::Int64 || type == MIRType::IntPtr);
1116 setResultType(type);
1117 if (type == MIRType::Int64) {
1118 payload_.i64 = i;
1119 } else {
1120 payload_.iptr = i;
1122 setMovable();
1125 #ifdef DEBUG
1126 void MConstant::assertInitializedPayload() const {
1127 // valueHash() and equals() expect the unused payload bits to be
1128 // initialized to zero. Assert this in debug builds.
1130 switch (type()) {
1131 case MIRType::Int32:
1132 case MIRType::Float32:
1133 # if MOZ_LITTLE_ENDIAN()
1134 MOZ_ASSERT((payload_.asBits >> 32) == 0);
1135 # else
1136 MOZ_ASSERT((payload_.asBits << 32) == 0);
1137 # endif
1138 break;
1139 case MIRType::Boolean:
1140 # if MOZ_LITTLE_ENDIAN()
1141 MOZ_ASSERT((payload_.asBits >> 1) == 0);
1142 # else
1143 MOZ_ASSERT((payload_.asBits & ~(1ULL << 56)) == 0);
1144 # endif
1145 break;
1146 case MIRType::Double:
1147 case MIRType::Int64:
1148 break;
1149 case MIRType::String:
1150 case MIRType::Object:
1151 case MIRType::Symbol:
1152 case MIRType::BigInt:
1153 case MIRType::IntPtr:
1154 case MIRType::Shape:
1155 # if MOZ_LITTLE_ENDIAN()
1156 MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits >> 32) == 0);
1157 # else
1158 MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits << 32) == 0);
1159 # endif
1160 break;
1161 default:
1162 MOZ_ASSERT(IsNullOrUndefined(type()) || IsMagicType(type()));
1163 MOZ_ASSERT(payload_.asBits == 0);
1164 break;
1167 #endif
1169 static HashNumber ConstantValueHash(MIRType type, uint64_t payload) {
1170 // Build a 64-bit value holding both the payload and the type.
1171 static const size_t TypeBits = 8;
1172 static const size_t TypeShift = 64 - TypeBits;
1173 MOZ_ASSERT(uintptr_t(type) <= (1 << TypeBits) - 1);
1174 uint64_t bits = (uint64_t(type) << TypeShift) ^ payload;
1176 // Fold all 64 bits into the 32-bit result. It's tempting to just discard
1177 // half of the bits, as this is just a hash, however there are many common
1178 // patterns of values where only the low or the high bits vary, so
1179 // discarding either side would lead to excessive hash collisions.
1180 return (HashNumber)bits ^ (HashNumber)(bits >> 32);
1183 HashNumber MConstant::valueHash() const {
1184 static_assert(sizeof(Payload) == sizeof(uint64_t),
1185 "Code below assumes payload fits in 64 bits");
1187 assertInitializedPayload();
1188 return ConstantValueHash(type(), payload_.asBits);
1191 HashNumber MConstantProto::valueHash() const {
1192 HashNumber hash = protoObject()->valueHash();
1193 const MDefinition* receiverObject = getReceiverObject();
1194 if (receiverObject) {
1195 hash = addU32ToHash(hash, receiverObject->id());
1197 return hash;
1200 bool MConstant::congruentTo(const MDefinition* ins) const {
1201 return ins->isConstant() && equals(ins->toConstant());
1204 #ifdef JS_JITSPEW
1205 void MConstant::printOpcode(GenericPrinter& out) const {
1206 PrintOpcodeName(out, op());
1207 out.printf(" ");
1208 switch (type()) {
1209 case MIRType::Undefined:
1210 out.printf("undefined");
1211 break;
1212 case MIRType::Null:
1213 out.printf("null");
1214 break;
1215 case MIRType::Boolean:
1216 out.printf(toBoolean() ? "true" : "false");
1217 break;
1218 case MIRType::Int32:
1219 out.printf("0x%x", uint32_t(toInt32()));
1220 break;
1221 case MIRType::Int64:
1222 out.printf("0x%" PRIx64, uint64_t(toInt64()));
1223 break;
1224 case MIRType::IntPtr:
1225 out.printf("0x%" PRIxPTR, uintptr_t(toIntPtr()));
1226 break;
1227 case MIRType::Double:
1228 out.printf("%.16g", toDouble());
1229 break;
1230 case MIRType::Float32: {
1231 float val = toFloat32();
1232 out.printf("%.16g", val);
1233 break;
1235 case MIRType::Object:
1236 if (toObject().is<JSFunction>()) {
1237 JSFunction* fun = &toObject().as<JSFunction>();
1238 if (fun->maybePartialDisplayAtom()) {
1239 out.put("function ");
1240 EscapedStringPrinter(out, fun->maybePartialDisplayAtom(), 0);
1241 } else {
1242 out.put("unnamed function");
1244 if (fun->hasBaseScript()) {
1245 BaseScript* script = fun->baseScript();
1246 out.printf(" (%s:%u)", script->filename() ? script->filename() : "",
1247 script->lineno());
1249 out.printf(" at %p", (void*)fun);
1250 break;
1252 out.printf("object %p (%s)", (void*)&toObject(),
1253 toObject().getClass()->name);
1254 break;
1255 case MIRType::Symbol:
1256 out.printf("symbol at %p", (void*)toSymbol());
1257 break;
1258 case MIRType::BigInt:
1259 out.printf("BigInt at %p", (void*)toBigInt());
1260 break;
1261 case MIRType::String:
1262 out.printf("string %p", (void*)toString());
1263 break;
1264 case MIRType::Shape:
1265 out.printf("shape at %p", (void*)toShape());
1266 break;
1267 case MIRType::MagicHole:
1268 out.printf("magic hole");
1269 break;
1270 case MIRType::MagicIsConstructing:
1271 out.printf("magic is-constructing");
1272 break;
1273 case MIRType::MagicOptimizedOut:
1274 out.printf("magic optimized-out");
1275 break;
1276 case MIRType::MagicUninitializedLexical:
1277 out.printf("magic uninitialized-lexical");
1278 break;
1279 default:
1280 MOZ_CRASH("unexpected type");
1283 #endif
1285 bool MConstant::canProduceFloat32() const {
1286 if (!isTypeRepresentableAsDouble()) {
1287 return false;
1290 if (type() == MIRType::Int32) {
1291 return IsFloat32Representable(static_cast<double>(toInt32()));
1293 if (type() == MIRType::Double) {
1294 return IsFloat32Representable(toDouble());
1296 MOZ_ASSERT(type() == MIRType::Float32);
1297 return true;
1300 Value MConstant::toJSValue() const {
1301 // Wasm has types like int64 that cannot be stored as js::Value. It also
1302 // doesn't want the NaN canonicalization enforced by js::Value.
1303 MOZ_ASSERT(!IsCompilingWasm());
1305 switch (type()) {
1306 case MIRType::Undefined:
1307 return UndefinedValue();
1308 case MIRType::Null:
1309 return NullValue();
1310 case MIRType::Boolean:
1311 return BooleanValue(toBoolean());
1312 case MIRType::Int32:
1313 return Int32Value(toInt32());
1314 case MIRType::Double:
1315 return DoubleValue(toDouble());
1316 case MIRType::Float32:
1317 return Float32Value(toFloat32());
1318 case MIRType::String:
1319 return StringValue(toString());
1320 case MIRType::Symbol:
1321 return SymbolValue(toSymbol());
1322 case MIRType::BigInt:
1323 return BigIntValue(toBigInt());
1324 case MIRType::Object:
1325 return ObjectValue(toObject());
1326 case MIRType::Shape:
1327 return PrivateGCThingValue(toShape());
1328 case MIRType::MagicOptimizedOut:
1329 return MagicValue(JS_OPTIMIZED_OUT);
1330 case MIRType::MagicHole:
1331 return MagicValue(JS_ELEMENTS_HOLE);
1332 case MIRType::MagicIsConstructing:
1333 return MagicValue(JS_IS_CONSTRUCTING);
1334 case MIRType::MagicUninitializedLexical:
1335 return MagicValue(JS_UNINITIALIZED_LEXICAL);
1336 default:
1337 MOZ_CRASH("Unexpected type");
1341 bool MConstant::valueToBoolean(bool* res) const {
1342 switch (type()) {
1343 case MIRType::Boolean:
1344 *res = toBoolean();
1345 return true;
1346 case MIRType::Int32:
1347 *res = toInt32() != 0;
1348 return true;
1349 case MIRType::Int64:
1350 *res = toInt64() != 0;
1351 return true;
1352 case MIRType::Double:
1353 *res = !std::isnan(toDouble()) && toDouble() != 0.0;
1354 return true;
1355 case MIRType::Float32:
1356 *res = !std::isnan(toFloat32()) && toFloat32() != 0.0f;
1357 return true;
1358 case MIRType::Null:
1359 case MIRType::Undefined:
1360 *res = false;
1361 return true;
1362 case MIRType::Symbol:
1363 *res = true;
1364 return true;
1365 case MIRType::BigInt:
1366 *res = !toBigInt()->isZero();
1367 return true;
1368 case MIRType::String:
1369 *res = toString()->length() != 0;
1370 return true;
1371 case MIRType::Object:
1372 // TODO(Warp): Lazy groups have been removed.
1373 // We have to call EmulatesUndefined but that reads obj->group->clasp
1374 // and so it's racy when the object has a lazy group. The main callers
1375 // of this (MTest, MNot) already know how to fold the object case, so
1376 // just give up.
1377 return false;
1378 default:
1379 MOZ_ASSERT(IsMagicType(type()));
1380 return false;
1384 HashNumber MWasmFloatConstant::valueHash() const {
1385 #ifdef ENABLE_WASM_SIMD
1386 return ConstantValueHash(type(), u.bits_[0] ^ u.bits_[1]);
1387 #else
1388 return ConstantValueHash(type(), u.bits_[0]);
1389 #endif
1392 bool MWasmFloatConstant::congruentTo(const MDefinition* ins) const {
1393 return ins->isWasmFloatConstant() && type() == ins->type() &&
1394 #ifdef ENABLE_WASM_SIMD
1395 u.bits_[1] == ins->toWasmFloatConstant()->u.bits_[1] &&
1396 #endif
1397 u.bits_[0] == ins->toWasmFloatConstant()->u.bits_[0];
1400 HashNumber MWasmNullConstant::valueHash() const {
1401 return ConstantValueHash(MIRType::WasmAnyRef, 0);
1404 #ifdef JS_JITSPEW
1405 void MControlInstruction::printOpcode(GenericPrinter& out) const {
1406 MDefinition::printOpcode(out);
1407 for (size_t j = 0; j < numSuccessors(); j++) {
1408 if (getSuccessor(j)) {
1409 out.printf(" block%u", getSuccessor(j)->id());
1410 } else {
1411 out.printf(" (null-to-be-patched)");
1416 void MCompare::printOpcode(GenericPrinter& out) const {
1417 MDefinition::printOpcode(out);
1418 out.printf(" %s", CodeName(jsop()));
1421 void MTypeOfIs::printOpcode(GenericPrinter& out) const {
1422 MDefinition::printOpcode(out);
1423 out.printf(" %s", CodeName(jsop()));
1425 const char* name = "";
1426 switch (jstype()) {
1427 case JSTYPE_UNDEFINED:
1428 name = "undefined";
1429 break;
1430 case JSTYPE_OBJECT:
1431 name = "object";
1432 break;
1433 case JSTYPE_FUNCTION:
1434 name = "function";
1435 break;
1436 case JSTYPE_STRING:
1437 name = "string";
1438 break;
1439 case JSTYPE_NUMBER:
1440 name = "number";
1441 break;
1442 case JSTYPE_BOOLEAN:
1443 name = "boolean";
1444 break;
1445 case JSTYPE_SYMBOL:
1446 name = "symbol";
1447 break;
1448 case JSTYPE_BIGINT:
1449 name = "bigint";
1450 break;
1451 # ifdef ENABLE_RECORD_TUPLE
1452 case JSTYPE_RECORD:
1453 case JSTYPE_TUPLE:
1454 # endif
1455 case JSTYPE_LIMIT:
1456 MOZ_CRASH("Unexpected type");
1458 out.printf(" '%s'", name);
1461 void MLoadUnboxedScalar::printOpcode(GenericPrinter& out) const {
1462 MDefinition::printOpcode(out);
1463 out.printf(" %s", Scalar::name(storageType()));
1466 void MLoadDataViewElement::printOpcode(GenericPrinter& out) const {
1467 MDefinition::printOpcode(out);
1468 out.printf(" %s", Scalar::name(storageType()));
1471 void MAssertRange::printOpcode(GenericPrinter& out) const {
1472 MDefinition::printOpcode(out);
1473 out.put(" ");
1474 assertedRange()->dump(out);
1477 void MNearbyInt::printOpcode(GenericPrinter& out) const {
1478 MDefinition::printOpcode(out);
1479 const char* roundingModeStr = nullptr;
1480 switch (roundingMode_) {
1481 case RoundingMode::Up:
1482 roundingModeStr = "(up)";
1483 break;
1484 case RoundingMode::Down:
1485 roundingModeStr = "(down)";
1486 break;
1487 case RoundingMode::NearestTiesToEven:
1488 roundingModeStr = "(nearest ties even)";
1489 break;
1490 case RoundingMode::TowardsZero:
1491 roundingModeStr = "(towards zero)";
1492 break;
1494 out.printf(" %s", roundingModeStr);
1496 #endif
1498 AliasSet MRandom::getAliasSet() const { return AliasSet::Store(AliasSet::RNG); }
1500 MDefinition* MSign::foldsTo(TempAllocator& alloc) {
1501 MDefinition* input = getOperand(0);
1502 if (!input->isConstant() ||
1503 !input->toConstant()->isTypeRepresentableAsDouble()) {
1504 return this;
1507 double in = input->toConstant()->numberToDouble();
1508 double out = js::math_sign_impl(in);
1510 if (type() == MIRType::Int32) {
1511 // Decline folding if this is an int32 operation, but the result type
1512 // isn't an int32.
1513 Value outValue = NumberValue(out);
1514 if (!outValue.isInt32()) {
1515 return this;
1518 return MConstant::New(alloc, outValue);
1521 return MConstant::New(alloc, DoubleValue(out));
1524 const char* MMathFunction::FunctionName(UnaryMathFunction function) {
1525 return GetUnaryMathFunctionName(function);
1528 #ifdef JS_JITSPEW
1529 void MMathFunction::printOpcode(GenericPrinter& out) const {
1530 MDefinition::printOpcode(out);
1531 out.printf(" %s", FunctionName(function()));
1533 #endif
1535 MDefinition* MMathFunction::foldsTo(TempAllocator& alloc) {
1536 MDefinition* input = getOperand(0);
1537 if (!input->isConstant() ||
1538 !input->toConstant()->isTypeRepresentableAsDouble()) {
1539 return this;
1542 UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(function());
1544 double in = input->toConstant()->numberToDouble();
1546 // The function pointer call can't GC.
1547 JS::AutoSuppressGCAnalysis nogc;
1548 double out = funPtr(in);
1550 if (input->type() == MIRType::Float32) {
1551 return MConstant::NewFloat32(alloc, out);
1553 return MConstant::New(alloc, DoubleValue(out));
1556 MDefinition* MAtomicIsLockFree::foldsTo(TempAllocator& alloc) {
1557 MDefinition* input = getOperand(0);
1558 if (!input->isConstant() || input->type() != MIRType::Int32) {
1559 return this;
1562 int32_t i = input->toConstant()->toInt32();
1563 return MConstant::New(alloc, BooleanValue(AtomicOperations::isLockfreeJS(i)));
1566 // Define |THIS_SLOT| as part of this translation unit, as it is used to
1567 // specialized the parameterized |New| function calls introduced by
1568 // TRIVIAL_NEW_WRAPPERS.
1569 const int32_t MParameter::THIS_SLOT;
1571 #ifdef JS_JITSPEW
1572 void MParameter::printOpcode(GenericPrinter& out) const {
1573 PrintOpcodeName(out, op());
1574 if (index() == THIS_SLOT) {
1575 out.printf(" THIS_SLOT");
1576 } else {
1577 out.printf(" %d", index());
1580 #endif
1582 HashNumber MParameter::valueHash() const {
1583 HashNumber hash = MDefinition::valueHash();
1584 hash = addU32ToHash(hash, index_);
1585 return hash;
1588 bool MParameter::congruentTo(const MDefinition* ins) const {
1589 if (!ins->isParameter()) {
1590 return false;
1593 return ins->toParameter()->index() == index_;
1596 WrappedFunction::WrappedFunction(JSFunction* nativeFun, uint16_t nargs,
1597 FunctionFlags flags)
1598 : nativeFun_(nativeFun), nargs_(nargs), flags_(flags) {
1599 MOZ_ASSERT_IF(nativeFun, isNativeWithoutJitEntry());
1601 #ifdef DEBUG
1602 // If we are not running off-main thread we can assert that the
1603 // metadata is consistent.
1604 if (!CanUseExtraThreads() && nativeFun) {
1605 MOZ_ASSERT(nativeFun->nargs() == nargs);
1607 MOZ_ASSERT(nativeFun->isNativeWithoutJitEntry() ==
1608 isNativeWithoutJitEntry());
1609 MOZ_ASSERT(nativeFun->hasJitEntry() == hasJitEntry());
1610 MOZ_ASSERT(nativeFun->isConstructor() == isConstructor());
1611 MOZ_ASSERT(nativeFun->isClassConstructor() == isClassConstructor());
1613 #endif
1616 MCall* MCall::New(TempAllocator& alloc, WrappedFunction* target, size_t maxArgc,
1617 size_t numActualArgs, bool construct, bool ignoresReturnValue,
1618 bool isDOMCall, mozilla::Maybe<DOMObjectKind> objectKind) {
1619 MOZ_ASSERT(isDOMCall == objectKind.isSome());
1620 MOZ_ASSERT(maxArgc >= numActualArgs);
1621 MCall* ins;
1622 if (isDOMCall) {
1623 MOZ_ASSERT(!construct);
1624 ins = new (alloc) MCallDOMNative(target, numActualArgs, *objectKind);
1625 } else {
1626 ins =
1627 new (alloc) MCall(target, numActualArgs, construct, ignoresReturnValue);
1629 if (!ins->init(alloc, maxArgc + NumNonArgumentOperands)) {
1630 return nullptr;
1632 return ins;
1635 AliasSet MCallDOMNative::getAliasSet() const {
1636 const JSJitInfo* jitInfo = getJitInfo();
1638 // If we don't know anything about the types of our arguments, we have to
1639 // assume that type-coercions can have side-effects, so we need to alias
1640 // everything.
1641 if (jitInfo->aliasSet() == JSJitInfo::AliasEverything ||
1642 !jitInfo->isTypedMethodJitInfo()) {
1643 return AliasSet::Store(AliasSet::Any);
1646 uint32_t argIndex = 0;
1647 const JSTypedMethodJitInfo* methodInfo =
1648 reinterpret_cast<const JSTypedMethodJitInfo*>(jitInfo);
1649 for (const JSJitInfo::ArgType* argType = methodInfo->argTypes;
1650 *argType != JSJitInfo::ArgTypeListEnd; ++argType, ++argIndex) {
1651 if (argIndex >= numActualArgs()) {
1652 // Passing through undefined can't have side-effects
1653 continue;
1655 // getArg(0) is "this", so skip it
1656 MDefinition* arg = getArg(argIndex + 1);
1657 MIRType actualType = arg->type();
1658 // The only way to reliably avoid side-effects given the information we
1659 // have here is if we're passing in a known primitive value to an
1660 // argument that expects a primitive value.
1662 // XXXbz maybe we need to communicate better information. For example,
1663 // a sequence argument will sort of unavoidably have side effects, while
1664 // a typed array argument won't have any, but both are claimed to be
1665 // JSJitInfo::Object. But if we do that, we need to watch out for our
1666 // movability/DCE-ability bits: if we have an arg type that can reliably
1667 // throw an exception on conversion, that might not affect our alias set
1668 // per se, but it should prevent us being moved or DCE-ed, unless we
1669 // know the incoming things match that arg type and won't throw.
1671 if ((actualType == MIRType::Value || actualType == MIRType::Object) ||
1672 (*argType & JSJitInfo::Object)) {
1673 return AliasSet::Store(AliasSet::Any);
1677 // We checked all the args, and they check out. So we only alias DOM
1678 // mutations or alias nothing, depending on the alias set in the jitinfo.
1679 if (jitInfo->aliasSet() == JSJitInfo::AliasNone) {
1680 return AliasSet::None();
1683 MOZ_ASSERT(jitInfo->aliasSet() == JSJitInfo::AliasDOMSets);
1684 return AliasSet::Load(AliasSet::DOMProperty);
1687 void MCallDOMNative::computeMovable() {
1688 // We are movable if the jitinfo says we can be and if we're also not
1689 // effectful. The jitinfo can't check for the latter, since it depends on
1690 // the types of our arguments.
1691 const JSJitInfo* jitInfo = getJitInfo();
1693 MOZ_ASSERT_IF(jitInfo->isMovable,
1694 jitInfo->aliasSet() != JSJitInfo::AliasEverything);
1696 if (jitInfo->isMovable && !isEffectful()) {
1697 setMovable();
1701 bool MCallDOMNative::congruentTo(const MDefinition* ins) const {
1702 if (!isMovable()) {
1703 return false;
1706 if (!ins->isCall()) {
1707 return false;
1710 const MCall* call = ins->toCall();
1712 if (!call->isCallDOMNative()) {
1713 return false;
1716 if (getSingleTarget() != call->getSingleTarget()) {
1717 return false;
1720 if (isConstructing() != call->isConstructing()) {
1721 return false;
1724 if (numActualArgs() != call->numActualArgs()) {
1725 return false;
1728 if (!congruentIfOperandsEqual(call)) {
1729 return false;
1732 // The other call had better be movable at this point!
1733 MOZ_ASSERT(call->isMovable());
1735 return true;
1738 const JSJitInfo* MCallDOMNative::getJitInfo() const {
1739 MOZ_ASSERT(getSingleTarget()->hasJitInfo());
1740 return getSingleTarget()->jitInfo();
1743 MCallClassHook* MCallClassHook::New(TempAllocator& alloc, JSNative target,
1744 uint32_t argc, bool constructing) {
1745 auto* ins = new (alloc) MCallClassHook(target, constructing);
1747 // Add callee + |this| + (if constructing) newTarget.
1748 uint32_t numOperands = 2 + argc + constructing;
1750 if (!ins->init(alloc, numOperands)) {
1751 return nullptr;
1754 return ins;
1757 MDefinition* MStringLength::foldsTo(TempAllocator& alloc) {
1758 if (string()->isConstant()) {
1759 JSString* str = string()->toConstant()->toString();
1760 return MConstant::New(alloc, Int32Value(str->length()));
1763 // MFromCharCode returns a one-element string.
1764 if (string()->isFromCharCode()) {
1765 return MConstant::New(alloc, Int32Value(1));
1768 return this;
1771 MDefinition* MConcat::foldsTo(TempAllocator& alloc) {
1772 if (lhs()->isConstant() && lhs()->toConstant()->toString()->empty()) {
1773 return rhs();
1776 if (rhs()->isConstant() && rhs()->toConstant()->toString()->empty()) {
1777 return lhs();
1780 return this;
1783 MDefinition* MStringConvertCase::foldsTo(TempAllocator& alloc) {
1784 MDefinition* string = this->string();
1786 // Handle the pattern |str[idx].toUpperCase()| and simplify it from
1787 // |StringConvertCase(FromCharCode(CharCodeAt(str, idx)))| to just
1788 // |CharCodeConvertCase(CharCodeAt(str, idx))|.
1789 if (string->isFromCharCode()) {
1790 auto* charCode = string->toFromCharCode()->code();
1791 auto mode = mode_ == Mode::LowerCase ? MCharCodeConvertCase::LowerCase
1792 : MCharCodeConvertCase::UpperCase;
1793 return MCharCodeConvertCase::New(alloc, charCode, mode);
1796 // Handle the pattern |num.toString(base).toUpperCase()| and simplify it to
1797 // directly return the string representation in the correct case.
1798 if (string->isInt32ToStringWithBase()) {
1799 auto* toString = string->toInt32ToStringWithBase();
1801 bool lowerCase = mode_ == Mode::LowerCase;
1802 if (toString->lowerCase() == lowerCase) {
1803 return toString;
1805 return MInt32ToStringWithBase::New(alloc, toString->input(),
1806 toString->base(), lowerCase);
1809 return this;
1812 static bool IsSubstrTo(MSubstr* substr, int32_t len) {
1813 // We want to match this pattern:
1815 // Substr(string, Constant(0), Min(Constant(length), StringLength(string)))
1817 // which is generated for the self-hosted `String.p.{substring,slice,substr}`
1818 // functions when called with constants `start` and `end` parameters.
1820 auto isConstantZero = [](auto* def) {
1821 return def->isConstant() && def->toConstant()->isInt32(0);
1824 if (!isConstantZero(substr->begin())) {
1825 return false;
1828 auto* length = substr->length();
1829 if (length->isBitOr()) {
1830 // Unnecessary bit-ops haven't yet been removed.
1831 auto* bitOr = length->toBitOr();
1832 if (isConstantZero(bitOr->lhs())) {
1833 length = bitOr->rhs();
1834 } else if (isConstantZero(bitOr->rhs())) {
1835 length = bitOr->lhs();
1838 if (!length->isMinMax() || length->toMinMax()->isMax()) {
1839 return false;
1842 auto* min = length->toMinMax();
1843 if (!min->lhs()->isConstant() && !min->rhs()->isConstant()) {
1844 return false;
1847 auto* minConstant = min->lhs()->isConstant() ? min->lhs()->toConstant()
1848 : min->rhs()->toConstant();
1850 auto* minOperand = min->lhs()->isConstant() ? min->rhs() : min->lhs();
1851 if (!minOperand->isStringLength() ||
1852 minOperand->toStringLength()->string() != substr->string()) {
1853 return false;
1856 // Ensure |len| matches the substring's length.
1857 return minConstant->isInt32(len);
1860 MDefinition* MSubstr::foldsTo(TempAllocator& alloc) {
1861 // Fold |str.substring(0, 1)| to |str.charAt(0)|.
1862 if (!IsSubstrTo(this, 1)) {
1863 return this;
1866 auto* charCode = MCharCodeAtOrNegative::New(alloc, string(), begin());
1867 block()->insertBefore(this, charCode);
1869 return MFromCharCodeEmptyIfNegative::New(alloc, charCode);
1872 MDefinition* MCharCodeAt::foldsTo(TempAllocator& alloc) {
1873 MDefinition* string = this->string();
1874 if (!string->isConstant() && !string->isFromCharCode()) {
1875 return this;
1878 MDefinition* index = this->index();
1879 if (index->isSpectreMaskIndex()) {
1880 index = index->toSpectreMaskIndex()->index();
1882 if (!index->isConstant()) {
1883 return this;
1885 int32_t idx = index->toConstant()->toInt32();
1887 // Handle the pattern |s[idx].charCodeAt(0)|.
1888 if (string->isFromCharCode()) {
1889 if (idx != 0) {
1890 return this;
1893 // Simplify |CharCodeAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
1894 // |CharCodeAt(s, idx)|.
1895 auto* charCode = string->toFromCharCode()->code();
1896 if (!charCode->isCharCodeAt()) {
1897 return this;
1900 return charCode;
1903 JSLinearString* str = &string->toConstant()->toString()->asLinear();
1904 if (idx < 0 || uint32_t(idx) >= str->length()) {
1905 return this;
1908 char16_t ch = str->latin1OrTwoByteChar(idx);
1909 return MConstant::New(alloc, Int32Value(ch));
1912 MDefinition* MCodePointAt::foldsTo(TempAllocator& alloc) {
1913 MDefinition* string = this->string();
1914 if (!string->isConstant() && !string->isFromCharCode()) {
1915 return this;
1918 MDefinition* index = this->index();
1919 if (index->isSpectreMaskIndex()) {
1920 index = index->toSpectreMaskIndex()->index();
1922 if (!index->isConstant()) {
1923 return this;
1925 int32_t idx = index->toConstant()->toInt32();
1927 // Handle the pattern |s[idx].codePointAt(0)|.
1928 if (string->isFromCharCode()) {
1929 if (idx != 0) {
1930 return this;
1933 // Simplify |CodePointAt(FromCharCode(CharCodeAt(s, idx)), 0)| to just
1934 // |CharCodeAt(s, idx)|.
1935 auto* charCode = string->toFromCharCode()->code();
1936 if (!charCode->isCharCodeAt()) {
1937 return this;
1940 return charCode;
1943 JSLinearString* str = &string->toConstant()->toString()->asLinear();
1944 if (idx < 0 || uint32_t(idx) >= str->length()) {
1945 return this;
1948 char32_t first = str->latin1OrTwoByteChar(idx);
1949 if (unicode::IsLeadSurrogate(first) && uint32_t(idx) + 1 < str->length()) {
1950 char32_t second = str->latin1OrTwoByteChar(idx + 1);
1951 if (unicode::IsTrailSurrogate(second)) {
1952 first = unicode::UTF16Decode(first, second);
1955 return MConstant::New(alloc, Int32Value(first));
1958 template <size_t Arity>
1959 [[nodiscard]] static bool EnsureFloatInputOrConvert(
1960 MAryInstruction<Arity>* owner, TempAllocator& alloc) {
1961 MOZ_ASSERT(!IsFloatingPointType(owner->type()),
1962 "Floating point types must check consumers");
1964 if (AllOperandsCanProduceFloat32(owner)) {
1965 return true;
1967 ConvertOperandsToDouble(owner, alloc);
1968 return false;
1971 template <size_t Arity>
1972 [[nodiscard]] static bool EnsureFloatConsumersAndInputOrConvert(
1973 MAryInstruction<Arity>* owner, TempAllocator& alloc) {
1974 MOZ_ASSERT(IsFloatingPointType(owner->type()),
1975 "Integer types don't need to check consumers");
1977 if (AllOperandsCanProduceFloat32(owner) &&
1978 CheckUsesAreFloat32Consumers(owner)) {
1979 return true;
1981 ConvertOperandsToDouble(owner, alloc);
1982 return false;
1985 void MFloor::trySpecializeFloat32(TempAllocator& alloc) {
1986 MOZ_ASSERT(type() == MIRType::Int32);
1987 if (EnsureFloatInputOrConvert(this, alloc)) {
1988 specialization_ = MIRType::Float32;
1992 void MCeil::trySpecializeFloat32(TempAllocator& alloc) {
1993 MOZ_ASSERT(type() == MIRType::Int32);
1994 if (EnsureFloatInputOrConvert(this, alloc)) {
1995 specialization_ = MIRType::Float32;
1999 void MRound::trySpecializeFloat32(TempAllocator& alloc) {
2000 MOZ_ASSERT(type() == MIRType::Int32);
2001 if (EnsureFloatInputOrConvert(this, alloc)) {
2002 specialization_ = MIRType::Float32;
2006 void MTrunc::trySpecializeFloat32(TempAllocator& alloc) {
2007 MOZ_ASSERT(type() == MIRType::Int32);
2008 if (EnsureFloatInputOrConvert(this, alloc)) {
2009 specialization_ = MIRType::Float32;
2013 void MNearbyInt::trySpecializeFloat32(TempAllocator& alloc) {
2014 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
2015 specialization_ = MIRType::Float32;
2016 setResultType(MIRType::Float32);
2020 MGoto* MGoto::New(TempAllocator& alloc, MBasicBlock* target) {
2021 return new (alloc) MGoto(target);
2024 MGoto* MGoto::New(TempAllocator::Fallible alloc, MBasicBlock* target) {
2025 MOZ_ASSERT(target);
2026 return new (alloc) MGoto(target);
2029 MGoto* MGoto::New(TempAllocator& alloc) { return new (alloc) MGoto(nullptr); }
2031 MDefinition* MBox::foldsTo(TempAllocator& alloc) {
2032 if (input()->isUnbox()) {
2033 return input()->toUnbox()->input();
2035 return this;
2038 #ifdef JS_JITSPEW
2039 void MUnbox::printOpcode(GenericPrinter& out) const {
2040 PrintOpcodeName(out, op());
2041 out.printf(" ");
2042 getOperand(0)->printName(out);
2043 out.printf(" ");
2045 switch (type()) {
2046 case MIRType::Int32:
2047 out.printf("to Int32");
2048 break;
2049 case MIRType::Double:
2050 out.printf("to Double");
2051 break;
2052 case MIRType::Boolean:
2053 out.printf("to Boolean");
2054 break;
2055 case MIRType::String:
2056 out.printf("to String");
2057 break;
2058 case MIRType::Symbol:
2059 out.printf("to Symbol");
2060 break;
2061 case MIRType::BigInt:
2062 out.printf("to BigInt");
2063 break;
2064 case MIRType::Object:
2065 out.printf("to Object");
2066 break;
2067 default:
2068 break;
2071 switch (mode()) {
2072 case Fallible:
2073 out.printf(" (fallible)");
2074 break;
2075 case Infallible:
2076 out.printf(" (infallible)");
2077 break;
2078 default:
2079 break;
2082 #endif
2084 MDefinition* MUnbox::foldsTo(TempAllocator& alloc) {
2085 if (input()->isBox()) {
2086 MDefinition* unboxed = input()->toBox()->input();
2088 // Fold MUnbox(MBox(x)) => x if types match.
2089 if (unboxed->type() == type()) {
2090 if (fallible()) {
2091 unboxed->setImplicitlyUsedUnchecked();
2093 return unboxed;
2096 // Fold MUnbox(MBox(x)) => MToDouble(x) if possible.
2097 if (type() == MIRType::Double &&
2098 IsTypeRepresentableAsDouble(unboxed->type())) {
2099 if (unboxed->isConstant()) {
2100 return MConstant::New(
2101 alloc, DoubleValue(unboxed->toConstant()->numberToDouble()));
2104 return MToDouble::New(alloc, unboxed);
2107 // MUnbox<Int32>(MBox<Double>(x)) will always fail, even if x can be
2108 // represented as an Int32. Fold to avoid unnecessary bailouts.
2109 if (type() == MIRType::Int32 && unboxed->type() == MIRType::Double) {
2110 auto* folded = MToNumberInt32::New(alloc, unboxed,
2111 IntConversionInputKind::NumbersOnly);
2112 folded->setGuard();
2113 return folded;
2117 return this;
2120 #ifdef DEBUG
2121 void MPhi::assertLoopPhi() const {
2122 // getLoopPredecessorOperand and getLoopBackedgeOperand rely on these
2123 // predecessors being at known indices.
2124 if (block()->numPredecessors() == 2) {
2125 MBasicBlock* pred = block()->getPredecessor(0);
2126 MBasicBlock* back = block()->getPredecessor(1);
2127 MOZ_ASSERT(pred == block()->loopPredecessor());
2128 MOZ_ASSERT(pred->successorWithPhis() == block());
2129 MOZ_ASSERT(pred->positionInPhiSuccessor() == 0);
2130 MOZ_ASSERT(back == block()->backedge());
2131 MOZ_ASSERT(back->successorWithPhis() == block());
2132 MOZ_ASSERT(back->positionInPhiSuccessor() == 1);
2133 } else {
2134 // After we remove fake loop predecessors for loop headers that
2135 // are only reachable via OSR, the only predecessor is the
2136 // loop backedge.
2137 MOZ_ASSERT(block()->numPredecessors() == 1);
2138 MOZ_ASSERT(block()->graph().osrBlock());
2139 MOZ_ASSERT(!block()->graph().canBuildDominators());
2140 MBasicBlock* back = block()->getPredecessor(0);
2141 MOZ_ASSERT(back == block()->backedge());
2142 MOZ_ASSERT(back->successorWithPhis() == block());
2143 MOZ_ASSERT(back->positionInPhiSuccessor() == 0);
2146 #endif
2148 MDefinition* MPhi::getLoopPredecessorOperand() const {
2149 // This should not be called after removing fake loop predecessors.
2150 MOZ_ASSERT(block()->numPredecessors() == 2);
2151 assertLoopPhi();
2152 return getOperand(0);
2155 MDefinition* MPhi::getLoopBackedgeOperand() const {
2156 assertLoopPhi();
2157 uint32_t idx = block()->numPredecessors() == 2 ? 1 : 0;
2158 return getOperand(idx);
2161 void MPhi::removeOperand(size_t index) {
2162 MOZ_ASSERT(index < numOperands());
2163 MOZ_ASSERT(getUseFor(index)->index() == index);
2164 MOZ_ASSERT(getUseFor(index)->consumer() == this);
2166 // If we have phi(..., a, b, c, d, ..., z) and we plan
2167 // on removing a, then first shift downward so that we have
2168 // phi(..., b, c, d, ..., z, z):
2169 MUse* p = inputs_.begin() + index;
2170 MUse* e = inputs_.end();
2171 p->producer()->removeUse(p);
2172 for (; p < e - 1; ++p) {
2173 MDefinition* producer = (p + 1)->producer();
2174 p->setProducerUnchecked(producer);
2175 producer->replaceUse(p + 1, p);
2178 // truncate the inputs_ list:
2179 inputs_.popBack();
2182 void MPhi::removeAllOperands() {
2183 for (MUse& p : inputs_) {
2184 p.producer()->removeUse(&p);
2186 inputs_.clear();
2189 MDefinition* MPhi::foldsTernary(TempAllocator& alloc) {
2190 /* Look if this MPhi is a ternary construct.
2191 * This is a very loose term as it actually only checks for
2193 * MTest X
2194 * / \
2195 * ... ...
2196 * \ /
2197 * MPhi X Y
2199 * Which we will simply call:
2200 * x ? x : y or x ? y : x
2203 if (numOperands() != 2) {
2204 return nullptr;
2207 MOZ_ASSERT(block()->numPredecessors() == 2);
2209 MBasicBlock* pred = block()->immediateDominator();
2210 if (!pred || !pred->lastIns()->isTest()) {
2211 return nullptr;
2214 MTest* test = pred->lastIns()->toTest();
2216 // True branch may only dominate one edge of MPhi.
2217 if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
2218 test->ifTrue()->dominates(block()->getPredecessor(1))) {
2219 return nullptr;
2222 // False branch may only dominate one edge of MPhi.
2223 if (test->ifFalse()->dominates(block()->getPredecessor(0)) ==
2224 test->ifFalse()->dominates(block()->getPredecessor(1))) {
2225 return nullptr;
2228 // True and false branch must dominate different edges of MPhi.
2229 if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
2230 test->ifFalse()->dominates(block()->getPredecessor(0))) {
2231 return nullptr;
2234 // We found a ternary construct.
2235 bool firstIsTrueBranch =
2236 test->ifTrue()->dominates(block()->getPredecessor(0));
2237 MDefinition* trueDef = firstIsTrueBranch ? getOperand(0) : getOperand(1);
2238 MDefinition* falseDef = firstIsTrueBranch ? getOperand(1) : getOperand(0);
2240 // Accept either
2241 // testArg ? testArg : constant or
2242 // testArg ? constant : testArg
2243 if (!trueDef->isConstant() && !falseDef->isConstant()) {
2244 return nullptr;
2247 MConstant* c =
2248 trueDef->isConstant() ? trueDef->toConstant() : falseDef->toConstant();
2249 MDefinition* testArg = (trueDef == c) ? falseDef : trueDef;
2250 if (testArg != test->input()) {
2251 return nullptr;
2254 // This check should be a tautology, except that the constant might be the
2255 // result of the removal of a branch. In such case the domination scope of
2256 // the block which is holding the constant might be incomplete. This
2257 // condition is used to prevent doing this optimization based on incomplete
2258 // information.
2260 // As GVN removed a branch, it will update the dominations rules before
2261 // trying to fold this MPhi again. Thus, this condition does not inhibit
2262 // this optimization.
2263 MBasicBlock* truePred = block()->getPredecessor(firstIsTrueBranch ? 0 : 1);
2264 MBasicBlock* falsePred = block()->getPredecessor(firstIsTrueBranch ? 1 : 0);
2265 if (!trueDef->block()->dominates(truePred) ||
2266 !falseDef->block()->dominates(falsePred)) {
2267 return nullptr;
2270 // If testArg is an int32 type we can:
2271 // - fold testArg ? testArg : 0 to testArg
2272 // - fold testArg ? 0 : testArg to 0
2273 if (testArg->type() == MIRType::Int32 && c->numberToDouble() == 0) {
2274 testArg->setGuardRangeBailoutsUnchecked();
2276 // When folding to the constant we need to hoist it.
2277 if (trueDef == c && !c->block()->dominates(block())) {
2278 c->block()->moveBefore(pred->lastIns(), c);
2280 return trueDef;
2283 // If testArg is an double type we can:
2284 // - fold testArg ? testArg : 0.0 to MNaNToZero(testArg)
2285 if (testArg->type() == MIRType::Double &&
2286 mozilla::IsPositiveZero(c->numberToDouble()) && c != trueDef) {
2287 MNaNToZero* replace = MNaNToZero::New(alloc, testArg);
2288 test->block()->insertBefore(test, replace);
2289 return replace;
2292 // If testArg is a string type we can:
2293 // - fold testArg ? testArg : "" to testArg
2294 // - fold testArg ? "" : testArg to ""
2295 if (testArg->type() == MIRType::String &&
2296 c->toString() == GetJitContext()->runtime->emptyString()) {
2297 // When folding to the constant we need to hoist it.
2298 if (trueDef == c && !c->block()->dominates(block())) {
2299 c->block()->moveBefore(pred->lastIns(), c);
2301 return trueDef;
2304 return nullptr;
2307 MDefinition* MPhi::operandIfRedundant() {
2308 if (inputs_.length() == 0) {
2309 return nullptr;
2312 // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
2313 // returns the operand that it will always be equal to (a, in
2314 // those two cases).
2315 MDefinition* first = getOperand(0);
2316 for (size_t i = 1, e = numOperands(); i < e; i++) {
2317 MDefinition* op = getOperand(i);
2318 if (op != first && op != this) {
2319 return nullptr;
2322 return first;
2325 MDefinition* MPhi::foldsTo(TempAllocator& alloc) {
2326 if (MDefinition* def = operandIfRedundant()) {
2327 return def;
2330 if (MDefinition* def = foldsTernary(alloc)) {
2331 return def;
2334 return this;
2337 bool MPhi::congruentTo(const MDefinition* ins) const {
2338 if (!ins->isPhi()) {
2339 return false;
2342 // Phis in different blocks may have different control conditions.
2343 // For example, these phis:
2345 // if (p)
2346 // goto a
2347 // a:
2348 // t = phi(x, y)
2350 // if (q)
2351 // goto b
2352 // b:
2353 // s = phi(x, y)
2355 // have identical operands, but they are not equvalent because t is
2356 // effectively p?x:y and s is effectively q?x:y.
2358 // For now, consider phis in different blocks incongruent.
2359 if (ins->block() != block()) {
2360 return false;
2363 return congruentIfOperandsEqual(ins);
2366 void MPhi::updateForReplacement(MPhi* other) {
2367 // This function is called to fix the current Phi flags using it as a
2368 // replacement of the other Phi instruction |other|.
2370 // When dealing with usage analysis, any Use will replace all other values,
2371 // such as Unused and Unknown. Unless both are Unused, the merge would be
2372 // Unknown.
2373 if (usageAnalysis_ == PhiUsage::Used ||
2374 other->usageAnalysis_ == PhiUsage::Used) {
2375 usageAnalysis_ = PhiUsage::Used;
2376 } else if (usageAnalysis_ != other->usageAnalysis_) {
2377 // this == unused && other == unknown
2378 // or this == unknown && other == unused
2379 usageAnalysis_ = PhiUsage::Unknown;
2380 } else {
2381 // this == unused && other == unused
2382 // or this == unknown && other = unknown
2383 MOZ_ASSERT(usageAnalysis_ == PhiUsage::Unused ||
2384 usageAnalysis_ == PhiUsage::Unknown);
2385 MOZ_ASSERT(usageAnalysis_ == other->usageAnalysis_);
2389 /* static */
2390 bool MPhi::markIteratorPhis(const PhiVector& iterators) {
2391 // Find and mark phis that must transitively hold an iterator live.
2393 Vector<MPhi*, 8, SystemAllocPolicy> worklist;
2395 for (MPhi* iter : iterators) {
2396 if (!iter->isInWorklist()) {
2397 if (!worklist.append(iter)) {
2398 return false;
2400 iter->setInWorklist();
2404 while (!worklist.empty()) {
2405 MPhi* phi = worklist.popCopy();
2406 phi->setNotInWorklist();
2408 phi->setIterator();
2409 phi->setImplicitlyUsedUnchecked();
2411 for (MUseDefIterator iter(phi); iter; iter++) {
2412 MDefinition* use = iter.def();
2413 if (!use->isInWorklist() && use->isPhi() && !use->toPhi()->isIterator()) {
2414 if (!worklist.append(use->toPhi())) {
2415 return false;
2417 use->setInWorklist();
2422 return true;
2425 bool MPhi::typeIncludes(MDefinition* def) {
2426 MOZ_ASSERT(!IsMagicType(def->type()));
2428 if (def->type() == MIRType::Int32 && this->type() == MIRType::Double) {
2429 return true;
2432 if (def->type() == MIRType::Value) {
2433 // This phi must be able to be any value.
2434 return this->type() == MIRType::Value;
2437 return this->mightBeType(def->type());
2440 void MCallBase::addArg(size_t argnum, MDefinition* arg) {
2441 // The operand vector is initialized in reverse order by WarpBuilder.
2442 // It cannot be checked for consistency until all arguments are added.
2443 // FixedList doesn't initialize its elements, so do an unchecked init.
2444 initOperand(argnum + NumNonArgumentOperands, arg);
2447 static inline bool IsConstant(MDefinition* def, double v) {
2448 if (!def->isConstant()) {
2449 return false;
2452 return NumbersAreIdentical(def->toConstant()->numberToDouble(), v);
2455 MDefinition* MBinaryBitwiseInstruction::foldsTo(TempAllocator& alloc) {
2456 // Identity operations are removed (for int32 only) in foldUnnecessaryBitop.
2458 if (type() == MIRType::Int32) {
2459 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) {
2460 return folded;
2462 } else if (type() == MIRType::Int64) {
2463 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
2464 return folded;
2468 return this;
2471 MDefinition* MBinaryBitwiseInstruction::foldUnnecessaryBitop() {
2472 // It's probably OK to perform this optimization only for int32, as it will
2473 // have the greatest effect for asm.js code that is compiled with the JS
2474 // pipeline, and that code will not see int64 values.
2476 if (type() != MIRType::Int32) {
2477 return this;
2480 // Fold unsigned shift right operator when the second operand is zero and
2481 // the only use is an unsigned modulo. Thus, the expression
2482 // |(x >>> 0) % y| becomes |x % y|.
2483 if (isUrsh() && IsUint32Type(this)) {
2484 MDefinition* defUse = maybeSingleDefUse();
2485 if (defUse && defUse->isMod() && defUse->toMod()->isUnsigned()) {
2486 return getOperand(0);
2490 // Eliminate bitwise operations that are no-ops when used on integer
2491 // inputs, such as (x | 0).
2493 MDefinition* lhs = getOperand(0);
2494 MDefinition* rhs = getOperand(1);
2496 if (IsConstant(lhs, 0)) {
2497 return foldIfZero(0);
2500 if (IsConstant(rhs, 0)) {
2501 return foldIfZero(1);
2504 if (IsConstant(lhs, -1)) {
2505 return foldIfNegOne(0);
2508 if (IsConstant(rhs, -1)) {
2509 return foldIfNegOne(1);
2512 if (lhs == rhs) {
2513 return foldIfEqual();
2516 if (maskMatchesRightRange) {
2517 MOZ_ASSERT(lhs->isConstant());
2518 MOZ_ASSERT(lhs->type() == MIRType::Int32);
2519 return foldIfAllBitsSet(0);
2522 if (maskMatchesLeftRange) {
2523 MOZ_ASSERT(rhs->isConstant());
2524 MOZ_ASSERT(rhs->type() == MIRType::Int32);
2525 return foldIfAllBitsSet(1);
2528 return this;
2531 static inline bool CanProduceNegativeZero(MDefinition* def) {
2532 // Test if this instruction can produce negative zero even when bailing out
2533 // and changing types.
2534 switch (def->op()) {
2535 case MDefinition::Opcode::Constant:
2536 if (def->type() == MIRType::Double &&
2537 def->toConstant()->toDouble() == -0.0) {
2538 return true;
2540 [[fallthrough]];
2541 case MDefinition::Opcode::BitAnd:
2542 case MDefinition::Opcode::BitOr:
2543 case MDefinition::Opcode::BitXor:
2544 case MDefinition::Opcode::BitNot:
2545 case MDefinition::Opcode::Lsh:
2546 case MDefinition::Opcode::Rsh:
2547 return false;
2548 default:
2549 return true;
2553 static inline bool NeedNegativeZeroCheck(MDefinition* def) {
2554 if (def->isGuard() || def->isGuardRangeBailouts()) {
2555 return true;
2558 // Test if all uses have the same semantics for -0 and 0
2559 for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) {
2560 if (use->consumer()->isResumePoint()) {
2561 return true;
2564 MDefinition* use_def = use->consumer()->toDefinition();
2565 switch (use_def->op()) {
2566 case MDefinition::Opcode::Add: {
2567 // If add is truncating -0 and 0 are observed as the same.
2568 if (use_def->toAdd()->isTruncated()) {
2569 break;
2572 // x + y gives -0, when both x and y are -0
2574 // Figure out the order in which the addition's operands will
2575 // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR
2576 // definitions for us so that this just requires comparing ids.
2577 MDefinition* first = use_def->toAdd()->lhs();
2578 MDefinition* second = use_def->toAdd()->rhs();
2579 if (first->id() > second->id()) {
2580 std::swap(first, second);
2582 // Negative zero checks can be removed on the first executed
2583 // operand only if it is guaranteed the second executed operand
2584 // will produce a value other than -0. While the second is
2585 // typed as an int32, a bailout taken between execution of the
2586 // operands may change that type and cause a -0 to flow to the
2587 // second.
2589 // There is no way to test whether there are any bailouts
2590 // between execution of the operands, so remove negative
2591 // zero checks from the first only if the second's type is
2592 // independent from type changes that may occur after bailing.
2593 if (def == first && CanProduceNegativeZero(second)) {
2594 return true;
2597 // The negative zero check can always be removed on the second
2598 // executed operand; by the time this executes the first will have
2599 // been evaluated as int32 and the addition's result cannot be -0.
2600 break;
2602 case MDefinition::Opcode::Sub: {
2603 // If sub is truncating -0 and 0 are observed as the same
2604 if (use_def->toSub()->isTruncated()) {
2605 break;
2608 // x + y gives -0, when x is -0 and y is 0
2610 // We can remove the negative zero check on the rhs, only if we
2611 // are sure the lhs isn't negative zero.
2613 // The lhs is typed as integer (i.e. not -0.0), but it can bailout
2614 // and change type. This should be fine if the lhs is executed
2615 // first. However if the rhs is executed first, the lhs can bail,
2616 // change type and become -0.0 while the rhs has already been
2617 // optimized to not make a difference between zero and negative zero.
2618 MDefinition* lhs = use_def->toSub()->lhs();
2619 MDefinition* rhs = use_def->toSub()->rhs();
2620 if (rhs->id() < lhs->id() && CanProduceNegativeZero(lhs)) {
2621 return true;
2624 [[fallthrough]];
2626 case MDefinition::Opcode::StoreElement:
2627 case MDefinition::Opcode::StoreHoleValueElement:
2628 case MDefinition::Opcode::LoadElement:
2629 case MDefinition::Opcode::LoadElementHole:
2630 case MDefinition::Opcode::LoadUnboxedScalar:
2631 case MDefinition::Opcode::LoadDataViewElement:
2632 case MDefinition::Opcode::LoadTypedArrayElementHole:
2633 case MDefinition::Opcode::CharCodeAt:
2634 case MDefinition::Opcode::Mod:
2635 case MDefinition::Opcode::InArray:
2636 // Only allowed to remove check when definition is the second operand
2637 if (use_def->getOperand(0) == def) {
2638 return true;
2640 for (size_t i = 2, e = use_def->numOperands(); i < e; i++) {
2641 if (use_def->getOperand(i) == def) {
2642 return true;
2645 break;
2646 case MDefinition::Opcode::BoundsCheck:
2647 // Only allowed to remove check when definition is the first operand
2648 if (use_def->toBoundsCheck()->getOperand(1) == def) {
2649 return true;
2651 break;
2652 case MDefinition::Opcode::ToString:
2653 case MDefinition::Opcode::FromCharCode:
2654 case MDefinition::Opcode::FromCodePoint:
2655 case MDefinition::Opcode::TableSwitch:
2656 case MDefinition::Opcode::Compare:
2657 case MDefinition::Opcode::BitAnd:
2658 case MDefinition::Opcode::BitOr:
2659 case MDefinition::Opcode::BitXor:
2660 case MDefinition::Opcode::Abs:
2661 case MDefinition::Opcode::TruncateToInt32:
2662 // Always allowed to remove check. No matter which operand.
2663 break;
2664 case MDefinition::Opcode::StoreElementHole:
2665 case MDefinition::Opcode::StoreTypedArrayElementHole:
2666 case MDefinition::Opcode::PostWriteElementBarrier:
2667 // Only allowed to remove check when definition is the third operand.
2668 for (size_t i = 0, e = use_def->numOperands(); i < e; i++) {
2669 if (i == 2) {
2670 continue;
2672 if (use_def->getOperand(i) == def) {
2673 return true;
2676 break;
2677 default:
2678 return true;
2681 return false;
2684 #ifdef JS_JITSPEW
2685 void MBinaryArithInstruction::printOpcode(GenericPrinter& out) const {
2686 MDefinition::printOpcode(out);
2688 switch (type()) {
2689 case MIRType::Int32:
2690 if (isDiv()) {
2691 out.printf(" [%s]", toDiv()->isUnsigned() ? "uint32" : "int32");
2692 } else if (isMod()) {
2693 out.printf(" [%s]", toMod()->isUnsigned() ? "uint32" : "int32");
2694 } else {
2695 out.printf(" [int32]");
2697 break;
2698 case MIRType::Int64:
2699 if (isDiv()) {
2700 out.printf(" [%s]", toDiv()->isUnsigned() ? "uint64" : "int64");
2701 } else if (isMod()) {
2702 out.printf(" [%s]", toMod()->isUnsigned() ? "uint64" : "int64");
2703 } else {
2704 out.printf(" [int64]");
2706 break;
2707 case MIRType::Float32:
2708 out.printf(" [float]");
2709 break;
2710 case MIRType::Double:
2711 out.printf(" [double]");
2712 break;
2713 default:
2714 break;
2717 #endif
2719 MDefinition* MRsh::foldsTo(TempAllocator& alloc) {
2720 MDefinition* f = MBinaryBitwiseInstruction::foldsTo(alloc);
2722 if (f != this) {
2723 return f;
2726 MDefinition* lhs = getOperand(0);
2727 MDefinition* rhs = getOperand(1);
2729 // It's probably OK to perform this optimization only for int32, as it will
2730 // have the greatest effect for asm.js code that is compiled with the JS
2731 // pipeline, and that code will not see int64 values.
2733 if (!lhs->isLsh() || !rhs->isConstant() || rhs->type() != MIRType::Int32) {
2734 return this;
2737 if (!lhs->getOperand(1)->isConstant() ||
2738 lhs->getOperand(1)->type() != MIRType::Int32) {
2739 return this;
2742 uint32_t shift = rhs->toConstant()->toInt32();
2743 uint32_t shift_lhs = lhs->getOperand(1)->toConstant()->toInt32();
2744 if (shift != shift_lhs) {
2745 return this;
2748 switch (shift) {
2749 case 16:
2750 return MSignExtendInt32::New(alloc, lhs->getOperand(0),
2751 MSignExtendInt32::Half);
2752 case 24:
2753 return MSignExtendInt32::New(alloc, lhs->getOperand(0),
2754 MSignExtendInt32::Byte);
2757 return this;
2760 MDefinition* MBinaryArithInstruction::foldsTo(TempAllocator& alloc) {
2761 MOZ_ASSERT(IsNumberType(type()));
2763 MDefinition* lhs = getOperand(0);
2764 MDefinition* rhs = getOperand(1);
2766 if (type() == MIRType::Int64) {
2767 MOZ_ASSERT(!isTruncated());
2769 if (MConstant* folded = EvaluateInt64ConstantOperands(alloc, this)) {
2770 if (!folded->block()) {
2771 block()->insertBefore(this, folded);
2773 return folded;
2775 if (isSub() || isDiv() || isMod()) {
2776 return this;
2778 if (rhs->isConstant() &&
2779 rhs->toConstant()->toInt64() == int64_t(getIdentity())) {
2780 return lhs;
2782 if (lhs->isConstant() &&
2783 lhs->toConstant()->toInt64() == int64_t(getIdentity())) {
2784 return rhs;
2786 return this;
2789 if (MConstant* folded = EvaluateConstantOperands(alloc, this)) {
2790 if (isTruncated()) {
2791 if (!folded->block()) {
2792 block()->insertBefore(this, folded);
2794 if (folded->type() != MIRType::Int32) {
2795 return MTruncateToInt32::New(alloc, folded);
2798 return folded;
2801 if (mustPreserveNaN_) {
2802 return this;
2805 // 0 + -0 = 0. So we can't remove addition
2806 if (isAdd() && type() != MIRType::Int32) {
2807 return this;
2810 if (IsConstant(rhs, getIdentity())) {
2811 if (isTruncated()) {
2812 return MTruncateToInt32::New(alloc, lhs);
2814 return lhs;
2817 // subtraction isn't commutative. So we can't remove subtraction when lhs
2818 // equals 0
2819 if (isSub()) {
2820 return this;
2823 if (IsConstant(lhs, getIdentity())) {
2824 if (isTruncated()) {
2825 return MTruncateToInt32::New(alloc, rhs);
2827 return rhs; // id op x => x
2830 return this;
2833 void MBinaryArithInstruction::trySpecializeFloat32(TempAllocator& alloc) {
2834 MOZ_ASSERT(IsNumberType(type()));
2836 // Do not use Float32 if we can use int32.
2837 if (type() == MIRType::Int32) {
2838 return;
2841 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
2842 setResultType(MIRType::Float32);
2846 void MMinMax::trySpecializeFloat32(TempAllocator& alloc) {
2847 if (type() == MIRType::Int32) {
2848 return;
2851 MDefinition* left = lhs();
2852 MDefinition* right = rhs();
2854 if ((left->canProduceFloat32() ||
2855 (left->isMinMax() && left->type() == MIRType::Float32)) &&
2856 (right->canProduceFloat32() ||
2857 (right->isMinMax() && right->type() == MIRType::Float32))) {
2858 setResultType(MIRType::Float32);
2859 } else {
2860 ConvertOperandsToDouble(this, alloc);
2864 MDefinition* MMinMax::foldsTo(TempAllocator& alloc) {
2865 MOZ_ASSERT(lhs()->type() == type());
2866 MOZ_ASSERT(rhs()->type() == type());
2868 if (lhs() == rhs()) {
2869 return lhs();
2872 auto foldConstants = [&alloc](MDefinition* lhs, MDefinition* rhs,
2873 bool isMax) -> MConstant* {
2874 MOZ_ASSERT(lhs->type() == rhs->type());
2875 MOZ_ASSERT(lhs->toConstant()->isTypeRepresentableAsDouble());
2876 MOZ_ASSERT(rhs->toConstant()->isTypeRepresentableAsDouble());
2878 double lnum = lhs->toConstant()->numberToDouble();
2879 double rnum = rhs->toConstant()->numberToDouble();
2881 double result;
2882 if (isMax) {
2883 result = js::math_max_impl(lnum, rnum);
2884 } else {
2885 result = js::math_min_impl(lnum, rnum);
2888 // The folded MConstant should maintain the same MIRType with the original
2889 // inputs.
2890 if (lhs->type() == MIRType::Int32) {
2891 int32_t cast;
2892 if (mozilla::NumberEqualsInt32(result, &cast)) {
2893 return MConstant::New(alloc, Int32Value(cast));
2895 return nullptr;
2897 if (lhs->type() == MIRType::Float32) {
2898 return MConstant::NewFloat32(alloc, result);
2900 MOZ_ASSERT(lhs->type() == MIRType::Double);
2901 return MConstant::New(alloc, DoubleValue(result));
2904 // Try to fold the following patterns when |x| and |y| are constants.
2906 // min(min(x, z), min(y, z)) = min(min(x, y), z)
2907 // max(max(x, z), max(y, z)) = max(max(x, y), z)
2908 // max(min(x, z), min(y, z)) = min(max(x, y), z)
2909 // min(max(x, z), max(y, z)) = max(min(x, y), z)
2910 if (lhs()->isMinMax() && rhs()->isMinMax()) {
2911 do {
2912 auto* left = lhs()->toMinMax();
2913 auto* right = rhs()->toMinMax();
2914 if (left->isMax() != right->isMax()) {
2915 break;
2918 MDefinition* x;
2919 MDefinition* y;
2920 MDefinition* z;
2921 if (left->lhs() == right->lhs()) {
2922 std::tie(x, y, z) = std::tuple{left->rhs(), right->rhs(), left->lhs()};
2923 } else if (left->lhs() == right->rhs()) {
2924 std::tie(x, y, z) = std::tuple{left->rhs(), right->lhs(), left->lhs()};
2925 } else if (left->rhs() == right->lhs()) {
2926 std::tie(x, y, z) = std::tuple{left->lhs(), right->rhs(), left->rhs()};
2927 } else if (left->rhs() == right->rhs()) {
2928 std::tie(x, y, z) = std::tuple{left->lhs(), right->lhs(), left->rhs()};
2929 } else {
2930 break;
2933 if (!x->isConstant() || !x->toConstant()->isTypeRepresentableAsDouble() ||
2934 !y->isConstant() || !y->toConstant()->isTypeRepresentableAsDouble()) {
2935 break;
2938 if (auto* folded = foldConstants(x, y, isMax())) {
2939 block()->insertBefore(this, folded);
2940 return MMinMax::New(alloc, folded, z, type(), left->isMax());
2942 } while (false);
2945 // Fold min/max operations with same inputs.
2946 if (lhs()->isMinMax() || rhs()->isMinMax()) {
2947 auto* other = lhs()->isMinMax() ? lhs()->toMinMax() : rhs()->toMinMax();
2948 auto* operand = lhs()->isMinMax() ? rhs() : lhs();
2950 if (operand == other->lhs() || operand == other->rhs()) {
2951 if (isMax() == other->isMax()) {
2952 // min(x, min(x, y)) = min(x, y)
2953 // max(x, max(x, y)) = max(x, y)
2954 return other;
2956 if (!IsFloatingPointType(type())) {
2957 // When neither value is NaN:
2958 // max(x, min(x, y)) = x
2959 // min(x, max(x, y)) = x
2961 // Ensure that any bailouts that we depend on to guarantee that |y| is
2962 // Int32 are not removed.
2963 auto* otherOp = operand == other->lhs() ? other->rhs() : other->lhs();
2964 otherOp->setGuardRangeBailoutsUnchecked();
2966 return operand;
2971 if (!lhs()->isConstant() && !rhs()->isConstant()) {
2972 return this;
2975 // Directly apply math utility to compare the rhs() and lhs() when
2976 // they are both constants.
2977 if (lhs()->isConstant() && rhs()->isConstant()) {
2978 if (!lhs()->toConstant()->isTypeRepresentableAsDouble() ||
2979 !rhs()->toConstant()->isTypeRepresentableAsDouble()) {
2980 return this;
2983 if (auto* folded = foldConstants(lhs(), rhs(), isMax())) {
2984 return folded;
2988 MDefinition* operand = lhs()->isConstant() ? rhs() : lhs();
2989 MConstant* constant =
2990 lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant();
2992 if (operand->isToDouble() &&
2993 operand->getOperand(0)->type() == MIRType::Int32) {
2994 // min(int32, cte >= INT32_MAX) = int32
2995 if (!isMax() && constant->isTypeRepresentableAsDouble() &&
2996 constant->numberToDouble() >= INT32_MAX) {
2997 MLimitedTruncate* limit = MLimitedTruncate::New(
2998 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
2999 block()->insertBefore(this, limit);
3000 MToDouble* toDouble = MToDouble::New(alloc, limit);
3001 return toDouble;
3004 // max(int32, cte <= INT32_MIN) = int32
3005 if (isMax() && constant->isTypeRepresentableAsDouble() &&
3006 constant->numberToDouble() <= INT32_MIN) {
3007 MLimitedTruncate* limit = MLimitedTruncate::New(
3008 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
3009 block()->insertBefore(this, limit);
3010 MToDouble* toDouble = MToDouble::New(alloc, limit);
3011 return toDouble;
3015 auto foldLength = [](MDefinition* operand, MConstant* constant,
3016 bool isMax) -> MDefinition* {
3017 if ((operand->isArrayLength() || operand->isArrayBufferViewLength() ||
3018 operand->isArgumentsLength() || operand->isStringLength()) &&
3019 constant->type() == MIRType::Int32) {
3020 // (Array|ArrayBufferView|Arguments|String)Length is always >= 0.
3021 // max(array.length, cte <= 0) = array.length
3022 // min(array.length, cte <= 0) = cte
3023 if (constant->toInt32() <= 0) {
3024 return isMax ? operand : constant;
3027 return nullptr;
3030 if (auto* folded = foldLength(operand, constant, isMax())) {
3031 return folded;
3034 // Attempt to fold nested min/max operations which are produced by
3035 // self-hosted built-in functions.
3036 if (operand->isMinMax()) {
3037 auto* other = operand->toMinMax();
3038 MOZ_ASSERT(other->lhs()->type() == type());
3039 MOZ_ASSERT(other->rhs()->type() == type());
3041 MConstant* otherConstant = nullptr;
3042 MDefinition* otherOperand = nullptr;
3043 if (other->lhs()->isConstant()) {
3044 otherConstant = other->lhs()->toConstant();
3045 otherOperand = other->rhs();
3046 } else if (other->rhs()->isConstant()) {
3047 otherConstant = other->rhs()->toConstant();
3048 otherOperand = other->lhs();
3051 if (otherConstant && constant->isTypeRepresentableAsDouble() &&
3052 otherConstant->isTypeRepresentableAsDouble()) {
3053 if (isMax() == other->isMax()) {
3054 // Fold min(x, min(y, z)) to min(min(x, y), z) with constant min(x, y).
3055 // Fold max(x, max(y, z)) to max(max(x, y), z) with constant max(x, y).
3056 if (auto* left = foldConstants(constant, otherConstant, isMax())) {
3057 block()->insertBefore(this, left);
3058 return MMinMax::New(alloc, left, otherOperand, type(), isMax());
3060 } else {
3061 // Fold min(x, max(y, z)) to max(min(x, y), min(x, z)).
3062 // Fold max(x, min(y, z)) to min(max(x, y), max(x, z)).
3064 // But only do this when min(x, z) can also be simplified.
3065 if (auto* right = foldLength(otherOperand, constant, isMax())) {
3066 if (auto* left = foldConstants(constant, otherConstant, isMax())) {
3067 block()->insertBefore(this, left);
3068 return MMinMax::New(alloc, left, right, type(), !isMax());
3075 return this;
3078 #ifdef JS_JITSPEW
3079 void MMinMax::printOpcode(GenericPrinter& out) const {
3080 MDefinition::printOpcode(out);
3081 out.printf(" (%s)", isMax() ? "max" : "min");
3084 void MMinMaxArray::printOpcode(GenericPrinter& out) const {
3085 MDefinition::printOpcode(out);
3086 out.printf(" (%s)", isMax() ? "max" : "min");
3088 #endif
3090 MDefinition* MPow::foldsConstant(TempAllocator& alloc) {
3091 // Both `x` and `p` in `x^p` must be constants in order to precompute.
3092 if (!input()->isConstant() || !power()->isConstant()) {
3093 return nullptr;
3095 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3096 return nullptr;
3098 if (!input()->toConstant()->isTypeRepresentableAsDouble()) {
3099 return nullptr;
3102 double x = input()->toConstant()->numberToDouble();
3103 double p = power()->toConstant()->numberToDouble();
3104 double result = js::ecmaPow(x, p);
3105 if (type() == MIRType::Int32) {
3106 int32_t cast;
3107 if (!mozilla::NumberIsInt32(result, &cast)) {
3108 // Reject folding if the result isn't an int32, because we'll bail anyway.
3109 return nullptr;
3111 return MConstant::New(alloc, Int32Value(cast));
3113 return MConstant::New(alloc, DoubleValue(result));
3116 MDefinition* MPow::foldsConstantPower(TempAllocator& alloc) {
3117 // If `p` in `x^p` isn't constant, we can't apply these folds.
3118 if (!power()->isConstant()) {
3119 return nullptr;
3121 if (!power()->toConstant()->isTypeRepresentableAsDouble()) {
3122 return nullptr;
3125 MOZ_ASSERT(type() == MIRType::Double || type() == MIRType::Int32);
3127 // NOTE: The optimizations must match the optimizations used in |js::ecmaPow|
3128 // resp. |js::powi| to avoid differential testing issues.
3130 double pow = power()->toConstant()->numberToDouble();
3132 // Math.pow(x, 0.5) is a sqrt with edge-case detection.
3133 if (pow == 0.5) {
3134 MOZ_ASSERT(type() == MIRType::Double);
3135 return MPowHalf::New(alloc, input());
3138 // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases.
3139 if (pow == -0.5) {
3140 MOZ_ASSERT(type() == MIRType::Double);
3141 MPowHalf* half = MPowHalf::New(alloc, input());
3142 block()->insertBefore(this, half);
3143 MConstant* one = MConstant::New(alloc, DoubleValue(1.0));
3144 block()->insertBefore(this, one);
3145 return MDiv::New(alloc, one, half, MIRType::Double);
3148 // Math.pow(x, 1) == x.
3149 if (pow == 1.0) {
3150 return input();
3153 auto multiply = [this, &alloc](MDefinition* lhs, MDefinition* rhs) {
3154 MMul* mul = MMul::New(alloc, lhs, rhs, type());
3155 mul->setBailoutKind(bailoutKind());
3157 // Multiplying the same number can't yield negative zero.
3158 mul->setCanBeNegativeZero(lhs != rhs && canBeNegativeZero());
3159 return mul;
3162 // Math.pow(x, 2) == x*x.
3163 if (pow == 2.0) {
3164 return multiply(input(), input());
3167 // Math.pow(x, 3) == x*x*x.
3168 if (pow == 3.0) {
3169 MMul* mul1 = multiply(input(), input());
3170 block()->insertBefore(this, mul1);
3171 return multiply(input(), mul1);
3174 // Math.pow(x, 4) == y*y, where y = x*x.
3175 if (pow == 4.0) {
3176 MMul* y = multiply(input(), input());
3177 block()->insertBefore(this, y);
3178 return multiply(y, y);
3181 // No optimization
3182 return nullptr;
3185 MDefinition* MPow::foldsTo(TempAllocator& alloc) {
3186 if (MDefinition* def = foldsConstant(alloc)) {
3187 return def;
3189 if (MDefinition* def = foldsConstantPower(alloc)) {
3190 return def;
3192 return this;
3195 MDefinition* MInt32ToIntPtr::foldsTo(TempAllocator& alloc) {
3196 MDefinition* def = input();
3197 if (def->isConstant()) {
3198 int32_t i = def->toConstant()->toInt32();
3199 return MConstant::NewIntPtr(alloc, intptr_t(i));
3202 if (def->isNonNegativeIntPtrToInt32()) {
3203 return def->toNonNegativeIntPtrToInt32()->input();
3206 return this;
3209 bool MAbs::fallible() const {
3210 return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds());
3213 void MAbs::trySpecializeFloat32(TempAllocator& alloc) {
3214 // Do not use Float32 if we can use int32.
3215 if (input()->type() == MIRType::Int32) {
3216 return;
3219 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
3220 setResultType(MIRType::Float32);
3224 MDefinition* MDiv::foldsTo(TempAllocator& alloc) {
3225 MOZ_ASSERT(IsNumberType(type()));
3227 if (type() == MIRType::Int64) {
3228 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
3229 return folded;
3231 return this;
3234 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) {
3235 return folded;
3238 if (MDefinition* folded = EvaluateExactReciprocal(alloc, this)) {
3239 return folded;
3242 return this;
3245 void MDiv::analyzeEdgeCasesForward() {
3246 // This is only meaningful when doing integer division.
3247 if (type() != MIRType::Int32) {
3248 return;
3251 MOZ_ASSERT(lhs()->type() == MIRType::Int32);
3252 MOZ_ASSERT(rhs()->type() == MIRType::Int32);
3254 // Try removing divide by zero check
3255 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
3256 canBeDivideByZero_ = false;
3259 // If lhs is a constant int != INT32_MIN, then
3260 // negative overflow check can be skipped.
3261 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(INT32_MIN)) {
3262 canBeNegativeOverflow_ = false;
3265 // If rhs is a constant int != -1, likewise.
3266 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(-1)) {
3267 canBeNegativeOverflow_ = false;
3270 // If lhs is != 0, then negative zero check can be skipped.
3271 if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(0)) {
3272 setCanBeNegativeZero(false);
3275 // If rhs is >= 0, likewise.
3276 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) {
3277 if (rhs()->toConstant()->toInt32() >= 0) {
3278 setCanBeNegativeZero(false);
3283 void MDiv::analyzeEdgeCasesBackward() {
3284 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) {
3285 setCanBeNegativeZero(false);
3289 bool MDiv::fallible() const { return !isTruncated(); }
3291 MDefinition* MMod::foldsTo(TempAllocator& alloc) {
3292 MOZ_ASSERT(IsNumberType(type()));
3294 if (type() == MIRType::Int64) {
3295 if (MDefinition* folded = EvaluateInt64ConstantOperands(alloc, this)) {
3296 return folded;
3298 } else {
3299 if (MDefinition* folded = EvaluateConstantOperands(alloc, this)) {
3300 return folded;
3303 return this;
3306 void MMod::analyzeEdgeCasesForward() {
3307 // These optimizations make sense only for integer division
3308 if (type() != MIRType::Int32) {
3309 return;
3312 if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0)) {
3313 canBeDivideByZero_ = false;
3316 if (rhs()->isConstant()) {
3317 int32_t n = rhs()->toConstant()->toInt32();
3318 if (n > 0 && !IsPowerOfTwo(uint32_t(n))) {
3319 canBePowerOfTwoDivisor_ = false;
3324 bool MMod::fallible() const {
3325 return !isTruncated() &&
3326 (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend());
3329 void MMathFunction::trySpecializeFloat32(TempAllocator& alloc) {
3330 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
3331 setResultType(MIRType::Float32);
3332 specialization_ = MIRType::Float32;
3336 bool MMathFunction::isFloat32Commutative() const {
3337 switch (function_) {
3338 case UnaryMathFunction::Floor:
3339 case UnaryMathFunction::Ceil:
3340 case UnaryMathFunction::Round:
3341 case UnaryMathFunction::Trunc:
3342 return true;
3343 default:
3344 return false;
3348 MHypot* MHypot::New(TempAllocator& alloc, const MDefinitionVector& vector) {
3349 uint32_t length = vector.length();
3350 MHypot* hypot = new (alloc) MHypot;
3351 if (!hypot->init(alloc, length)) {
3352 return nullptr;
3355 for (uint32_t i = 0; i < length; ++i) {
3356 hypot->initOperand(i, vector[i]);
3358 return hypot;
3361 bool MAdd::fallible() const {
3362 // the add is fallible if range analysis does not say that it is finite, AND
3363 // either the truncation analysis shows that there are non-truncated uses.
3364 if (truncateKind() >= TruncateKind::IndirectTruncate) {
3365 return false;
3367 if (range() && range()->hasInt32Bounds()) {
3368 return false;
3370 return true;
3373 bool MSub::fallible() const {
3374 // see comment in MAdd::fallible()
3375 if (truncateKind() >= TruncateKind::IndirectTruncate) {
3376 return false;
3378 if (range() && range()->hasInt32Bounds()) {
3379 return false;
3381 return true;
3384 MDefinition* MSub::foldsTo(TempAllocator& alloc) {
3385 MDefinition* out = MBinaryArithInstruction::foldsTo(alloc);
3386 if (out != this) {
3387 return out;
3390 if (type() != MIRType::Int32) {
3391 return this;
3394 // Optimize X - X to 0. This optimization is only valid for Int32
3395 // values. Subtracting a floating point value from itself returns
3396 // NaN when the operand is either Infinity or NaN.
3397 if (lhs() == rhs()) {
3398 // Ensure that any bailouts that we depend on to guarantee that X
3399 // is Int32 are not removed.
3400 lhs()->setGuardRangeBailoutsUnchecked();
3401 return MConstant::New(alloc, Int32Value(0));
3404 return this;
3407 MDefinition* MMul::foldsTo(TempAllocator& alloc) {
3408 MDefinition* out = MBinaryArithInstruction::foldsTo(alloc);
3409 if (out != this) {
3410 return out;
3413 if (type() != MIRType::Int32) {
3414 return this;
3417 if (lhs() == rhs()) {
3418 setCanBeNegativeZero(false);
3421 return this;
3424 void MMul::analyzeEdgeCasesForward() {
3425 // Try to remove the check for negative zero
3426 // This only makes sense when using the integer multiplication
3427 if (type() != MIRType::Int32) {
3428 return;
3431 // If lhs is > 0, no need for negative zero check.
3432 if (lhs()->isConstant() && lhs()->type() == MIRType::Int32) {
3433 if (lhs()->toConstant()->toInt32() > 0) {
3434 setCanBeNegativeZero(false);
3438 // If rhs is > 0, likewise.
3439 if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) {
3440 if (rhs()->toConstant()->toInt32() > 0) {
3441 setCanBeNegativeZero(false);
3446 void MMul::analyzeEdgeCasesBackward() {
3447 if (canBeNegativeZero() && !NeedNegativeZeroCheck(this)) {
3448 setCanBeNegativeZero(false);
3452 bool MMul::canOverflow() const {
3453 if (isTruncated()) {
3454 return false;
3456 return !range() || !range()->hasInt32Bounds();
3459 bool MUrsh::fallible() const {
3460 if (bailoutsDisabled()) {
3461 return false;
3463 return !range() || !range()->hasInt32Bounds();
3466 MIRType MCompare::inputType() {
3467 switch (compareType_) {
3468 case Compare_Undefined:
3469 return MIRType::Undefined;
3470 case Compare_Null:
3471 return MIRType::Null;
3472 case Compare_UInt32:
3473 case Compare_Int32:
3474 return MIRType::Int32;
3475 case Compare_UIntPtr:
3476 return MIRType::IntPtr;
3477 case Compare_Double:
3478 return MIRType::Double;
3479 case Compare_Float32:
3480 return MIRType::Float32;
3481 case Compare_String:
3482 return MIRType::String;
3483 case Compare_Symbol:
3484 return MIRType::Symbol;
3485 case Compare_Object:
3486 return MIRType::Object;
3487 case Compare_BigInt:
3488 case Compare_BigInt_Int32:
3489 case Compare_BigInt_Double:
3490 case Compare_BigInt_String:
3491 return MIRType::BigInt;
3492 default:
3493 MOZ_CRASH("No known conversion");
3497 static inline bool MustBeUInt32(MDefinition* def, MDefinition** pwrapped) {
3498 if (def->isUrsh()) {
3499 *pwrapped = def->toUrsh()->lhs();
3500 MDefinition* rhs = def->toUrsh()->rhs();
3501 return def->toUrsh()->bailoutsDisabled() && rhs->maybeConstantValue() &&
3502 rhs->maybeConstantValue()->isInt32(0);
3505 if (MConstant* defConst = def->maybeConstantValue()) {
3506 *pwrapped = defConst;
3507 return defConst->type() == MIRType::Int32 && defConst->toInt32() >= 0;
3510 *pwrapped = nullptr; // silence GCC warning
3511 return false;
3514 /* static */
3515 bool MBinaryInstruction::unsignedOperands(MDefinition* left,
3516 MDefinition* right) {
3517 MDefinition* replace;
3518 if (!MustBeUInt32(left, &replace)) {
3519 return false;
3521 if (replace->type() != MIRType::Int32) {
3522 return false;
3524 if (!MustBeUInt32(right, &replace)) {
3525 return false;
3527 if (replace->type() != MIRType::Int32) {
3528 return false;
3530 return true;
3533 bool MBinaryInstruction::unsignedOperands() {
3534 return unsignedOperands(getOperand(0), getOperand(1));
3537 void MBinaryInstruction::replaceWithUnsignedOperands() {
3538 MOZ_ASSERT(unsignedOperands());
3540 for (size_t i = 0; i < numOperands(); i++) {
3541 MDefinition* replace;
3542 MustBeUInt32(getOperand(i), &replace);
3543 if (replace == getOperand(i)) {
3544 continue;
3547 getOperand(i)->setImplicitlyUsedUnchecked();
3548 replaceOperand(i, replace);
3552 MDefinition* MBitNot::foldsTo(TempAllocator& alloc) {
3553 if (type() == MIRType::Int64) {
3554 return this;
3556 MOZ_ASSERT(type() == MIRType::Int32);
3558 MDefinition* input = getOperand(0);
3560 if (input->isConstant()) {
3561 js::Value v = Int32Value(~(input->toConstant()->toInt32()));
3562 return MConstant::New(alloc, v);
3565 if (input->isBitNot()) {
3566 MOZ_ASSERT(input->toBitNot()->type() == MIRType::Int32);
3567 MOZ_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType::Int32);
3568 return MTruncateToInt32::New(alloc,
3569 input->toBitNot()->input()); // ~~x => x | 0
3572 return this;
3575 static void AssertKnownClass(TempAllocator& alloc, MInstruction* ins,
3576 MDefinition* obj) {
3577 #ifdef DEBUG
3578 const JSClass* clasp = GetObjectKnownJSClass(obj);
3579 MOZ_ASSERT(clasp);
3581 auto* assert = MAssertClass::New(alloc, obj, clasp);
3582 ins->block()->insertBefore(ins, assert);
3583 #endif
3586 MDefinition* MBoxNonStrictThis::foldsTo(TempAllocator& alloc) {
3587 MDefinition* in = input();
3588 if (in->isBox()) {
3589 in = in->toBox()->input();
3592 if (in->type() == MIRType::Object) {
3593 return in;
3596 return this;
3599 AliasSet MLoadArgumentsObjectArg::getAliasSet() const {
3600 return AliasSet::Load(AliasSet::Any);
3603 AliasSet MLoadArgumentsObjectArgHole::getAliasSet() const {
3604 return AliasSet::Load(AliasSet::Any);
3607 AliasSet MInArgumentsObjectArg::getAliasSet() const {
3608 // Loads |arguments.length|, but not the actual element, so we can use the
3609 // same alias-set as MArgumentsObjectLength.
3610 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
3611 AliasSet::DynamicSlot);
3614 AliasSet MArgumentsObjectLength::getAliasSet() const {
3615 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
3616 AliasSet::DynamicSlot);
3619 bool MGuardArgumentsObjectFlags::congruentTo(const MDefinition* ins) const {
3620 if (!ins->isGuardArgumentsObjectFlags() ||
3621 ins->toGuardArgumentsObjectFlags()->flags() != flags()) {
3622 return false;
3624 return congruentIfOperandsEqual(ins);
3627 AliasSet MGuardArgumentsObjectFlags::getAliasSet() const {
3628 // The flags are packed with the length in a fixed private slot.
3629 return AliasSet::Load(AliasSet::FixedSlot);
3632 MDefinition* MIdToStringOrSymbol::foldsTo(TempAllocator& alloc) {
3633 if (idVal()->isBox()) {
3634 auto* input = idVal()->toBox()->input();
3635 MIRType idType = input->type();
3636 if (idType == MIRType::String || idType == MIRType::Symbol) {
3637 return idVal();
3639 if (idType == MIRType::Int32) {
3640 auto* toString =
3641 MToString::New(alloc, input, MToString::SideEffectHandling::Bailout);
3642 block()->insertBefore(this, toString);
3644 return MBox::New(alloc, toString);
3648 return this;
3651 MDefinition* MReturnFromCtor::foldsTo(TempAllocator& alloc) {
3652 MDefinition* rval = value();
3653 if (rval->isBox()) {
3654 rval = rval->toBox()->input();
3657 if (rval->type() == MIRType::Object) {
3658 return rval;
3661 if (rval->type() != MIRType::Value) {
3662 return object();
3665 return this;
3668 MDefinition* MTypeOf::foldsTo(TempAllocator& alloc) {
3669 MDefinition* unboxed = input();
3670 if (unboxed->isBox()) {
3671 unboxed = unboxed->toBox()->input();
3674 JSType type;
3675 switch (unboxed->type()) {
3676 case MIRType::Double:
3677 case MIRType::Float32:
3678 case MIRType::Int32:
3679 type = JSTYPE_NUMBER;
3680 break;
3681 case MIRType::String:
3682 type = JSTYPE_STRING;
3683 break;
3684 case MIRType::Symbol:
3685 type = JSTYPE_SYMBOL;
3686 break;
3687 case MIRType::BigInt:
3688 type = JSTYPE_BIGINT;
3689 break;
3690 case MIRType::Null:
3691 type = JSTYPE_OBJECT;
3692 break;
3693 case MIRType::Undefined:
3694 type = JSTYPE_UNDEFINED;
3695 break;
3696 case MIRType::Boolean:
3697 type = JSTYPE_BOOLEAN;
3698 break;
3699 case MIRType::Object: {
3700 KnownClass known = GetObjectKnownClass(unboxed);
3701 if (known != KnownClass::None) {
3702 if (known == KnownClass::Function) {
3703 type = JSTYPE_FUNCTION;
3704 } else {
3705 type = JSTYPE_OBJECT;
3708 AssertKnownClass(alloc, this, unboxed);
3709 break;
3711 [[fallthrough]];
3713 default:
3714 return this;
3717 return MConstant::New(alloc, Int32Value(static_cast<int32_t>(type)));
3720 MDefinition* MTypeOfName::foldsTo(TempAllocator& alloc) {
3721 MOZ_ASSERT(input()->type() == MIRType::Int32);
3723 if (!input()->isConstant()) {
3724 return this;
3727 static_assert(JSTYPE_UNDEFINED == 0);
3729 int32_t type = input()->toConstant()->toInt32();
3730 MOZ_ASSERT(JSTYPE_UNDEFINED <= type && type < JSTYPE_LIMIT);
3732 JSString* name =
3733 TypeName(static_cast<JSType>(type), GetJitContext()->runtime->names());
3734 return MConstant::New(alloc, StringValue(name));
3737 MUrsh* MUrsh::NewWasm(TempAllocator& alloc, MDefinition* left,
3738 MDefinition* right, MIRType type) {
3739 MUrsh* ins = new (alloc) MUrsh(left, right, type);
3741 // Since Ion has no UInt32 type, we use Int32 and we have a special
3742 // exception to the type rules: we can return values in
3743 // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
3744 // without bailing out. This is necessary because Ion has no UInt32
3745 // type and we can't have bailouts in wasm code.
3746 ins->bailoutsDisabled_ = true;
3748 return ins;
3751 MResumePoint* MResumePoint::New(TempAllocator& alloc, MBasicBlock* block,
3752 jsbytecode* pc, ResumeMode mode) {
3753 MResumePoint* resume = new (alloc) MResumePoint(block, pc, mode);
3754 if (!resume->init(alloc)) {
3755 block->discardPreAllocatedResumePoint(resume);
3756 return nullptr;
3758 resume->inherit(block);
3759 return resume;
3762 MResumePoint::MResumePoint(MBasicBlock* block, jsbytecode* pc, ResumeMode mode)
3763 : MNode(block, Kind::ResumePoint),
3764 pc_(pc),
3765 instruction_(nullptr),
3766 mode_(mode) {
3767 block->addResumePoint(this);
3770 bool MResumePoint::init(TempAllocator& alloc) {
3771 return operands_.init(alloc, block()->stackDepth());
3774 MResumePoint* MResumePoint::caller() const {
3775 return block()->callerResumePoint();
3778 void MResumePoint::inherit(MBasicBlock* block) {
3779 // FixedList doesn't initialize its elements, so do unchecked inits.
3780 for (size_t i = 0; i < stackDepth(); i++) {
3781 initOperand(i, block->getSlot(i));
3785 void MResumePoint::addStore(TempAllocator& alloc, MDefinition* store,
3786 const MResumePoint* cache) {
3787 MOZ_ASSERT(block()->outerResumePoint() != this);
3788 MOZ_ASSERT_IF(cache, !cache->stores_.empty());
3790 if (cache && cache->stores_.begin()->operand == store) {
3791 // If the last resume point had the same side-effect stack, then we can
3792 // reuse the current side effect without cloning it. This is a simple
3793 // way to share common context by making a spaghetti stack.
3794 if (++cache->stores_.begin() == stores_.begin()) {
3795 stores_.copy(cache->stores_);
3796 return;
3800 // Ensure that the store would not be deleted by DCE.
3801 MOZ_ASSERT(store->isEffectful());
3803 MStoreToRecover* top = new (alloc) MStoreToRecover(store);
3804 stores_.push(top);
3807 #ifdef JS_JITSPEW
3808 void MResumePoint::dump(GenericPrinter& out) const {
3809 out.printf("resumepoint mode=");
3811 switch (mode()) {
3812 case ResumeMode::ResumeAt:
3813 if (instruction_) {
3814 out.printf("ResumeAt(%u)", instruction_->id());
3815 } else {
3816 out.printf("ResumeAt");
3818 break;
3819 default:
3820 out.put(ResumeModeToString(mode()));
3821 break;
3824 if (MResumePoint* c = caller()) {
3825 out.printf(" (caller in block%u)", c->block()->id());
3828 for (size_t i = 0; i < numOperands(); i++) {
3829 out.printf(" ");
3830 if (operands_[i].hasProducer()) {
3831 getOperand(i)->printName(out);
3832 } else {
3833 out.printf("(null)");
3836 out.printf("\n");
3839 void MResumePoint::dump() const {
3840 Fprinter out(stderr);
3841 dump(out);
3842 out.finish();
3844 #endif
3846 bool MResumePoint::isObservableOperand(MUse* u) const {
3847 return isObservableOperand(indexOf(u));
3850 bool MResumePoint::isObservableOperand(size_t index) const {
3851 return block()->info().isObservableSlot(index);
3854 bool MResumePoint::isRecoverableOperand(MUse* u) const {
3855 return block()->info().isRecoverableOperand(indexOf(u));
3858 MDefinition* MTruncateBigIntToInt64::foldsTo(TempAllocator& alloc) {
3859 MDefinition* input = getOperand(0);
3861 if (input->isBox()) {
3862 input = input->getOperand(0);
3865 // If the operand converts an I64 to BigInt, drop both conversions.
3866 if (input->isInt64ToBigInt()) {
3867 return input->getOperand(0);
3870 // Fold this operation if the input operand is constant.
3871 if (input->isConstant()) {
3872 return MConstant::NewInt64(
3873 alloc, BigInt::toInt64(input->toConstant()->toBigInt()));
3876 return this;
3879 MDefinition* MToInt64::foldsTo(TempAllocator& alloc) {
3880 MDefinition* input = getOperand(0);
3882 if (input->isBox()) {
3883 input = input->getOperand(0);
3886 // Unwrap MInt64ToBigInt: MToInt64(MInt64ToBigInt(int64)) = int64.
3887 if (input->isInt64ToBigInt()) {
3888 return input->getOperand(0);
3891 // When the input is an Int64 already, just return it.
3892 if (input->type() == MIRType::Int64) {
3893 return input;
3896 // Fold this operation if the input operand is constant.
3897 if (input->isConstant()) {
3898 switch (input->type()) {
3899 case MIRType::Boolean:
3900 return MConstant::NewInt64(alloc, input->toConstant()->toBoolean());
3901 default:
3902 break;
3906 return this;
3909 MDefinition* MToNumberInt32::foldsTo(TempAllocator& alloc) {
3910 // Fold this operation if the input operand is constant.
3911 if (MConstant* cst = input()->maybeConstantValue()) {
3912 switch (cst->type()) {
3913 case MIRType::Null:
3914 if (conversion() == IntConversionInputKind::Any) {
3915 return MConstant::New(alloc, Int32Value(0));
3917 break;
3918 case MIRType::Boolean:
3919 if (conversion() == IntConversionInputKind::Any ||
3920 conversion() == IntConversionInputKind::NumbersOrBoolsOnly) {
3921 return MConstant::New(alloc, Int32Value(cst->toBoolean()));
3923 break;
3924 case MIRType::Int32:
3925 return MConstant::New(alloc, Int32Value(cst->toInt32()));
3926 case MIRType::Float32:
3927 case MIRType::Double:
3928 int32_t ival;
3929 // Only the value within the range of Int32 can be substituted as
3930 // constant.
3931 if (mozilla::NumberIsInt32(cst->numberToDouble(), &ival)) {
3932 return MConstant::New(alloc, Int32Value(ival));
3934 break;
3935 default:
3936 break;
3940 MDefinition* input = getOperand(0);
3941 if (input->isBox()) {
3942 input = input->toBox()->input();
3945 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
3946 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
3947 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
3948 // is folded to a MTruncateToInt32 node, which will result in this MIR:
3949 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
3950 // the MUrsh node's type is int32 (since uint32 is not implemented), and
3951 // that would fold the MTruncateToInt32 node. This will make the modulo
3952 // unsigned, while is should have been signed.
3953 if (input->type() == MIRType::Int32 && !IsUint32Type(input)) {
3954 return input;
3957 return this;
3960 MDefinition* MBooleanToInt32::foldsTo(TempAllocator& alloc) {
3961 MDefinition* input = getOperand(0);
3962 MOZ_ASSERT(input->type() == MIRType::Boolean);
3964 if (input->isConstant()) {
3965 return MConstant::New(alloc, Int32Value(input->toConstant()->toBoolean()));
3968 return this;
3971 void MToNumberInt32::analyzeEdgeCasesBackward() {
3972 if (!NeedNegativeZeroCheck(this)) {
3973 setNeedsNegativeZeroCheck(false);
3977 MDefinition* MTruncateToInt32::foldsTo(TempAllocator& alloc) {
3978 MDefinition* input = getOperand(0);
3979 if (input->isBox()) {
3980 input = input->getOperand(0);
3983 // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
3984 // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
3985 // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
3986 // is folded to a MTruncateToInt32 node, which will result in this MIR:
3987 // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
3988 // the MUrsh node's type is int32 (since uint32 is not implemented), and
3989 // that would fold the MTruncateToInt32 node. This will make the modulo
3990 // unsigned, while is should have been signed.
3991 if (input->type() == MIRType::Int32 && !IsUint32Type(input)) {
3992 return input;
3995 if (input->type() == MIRType::Double && input->isConstant()) {
3996 int32_t ret = ToInt32(input->toConstant()->toDouble());
3997 return MConstant::New(alloc, Int32Value(ret));
4000 return this;
4003 MDefinition* MWasmTruncateToInt32::foldsTo(TempAllocator& alloc) {
4004 MDefinition* input = getOperand(0);
4005 if (input->type() == MIRType::Int32) {
4006 return input;
4009 if (input->type() == MIRType::Double && input->isConstant()) {
4010 double d = input->toConstant()->toDouble();
4011 if (std::isnan(d)) {
4012 return this;
4015 if (!isUnsigned() && d <= double(INT32_MAX) && d >= double(INT32_MIN)) {
4016 return MConstant::New(alloc, Int32Value(ToInt32(d)));
4019 if (isUnsigned() && d <= double(UINT32_MAX) && d >= 0) {
4020 return MConstant::New(alloc, Int32Value(ToInt32(d)));
4024 if (input->type() == MIRType::Float32 && input->isConstant()) {
4025 double f = double(input->toConstant()->toFloat32());
4026 if (std::isnan(f)) {
4027 return this;
4030 if (!isUnsigned() && f <= double(INT32_MAX) && f >= double(INT32_MIN)) {
4031 return MConstant::New(alloc, Int32Value(ToInt32(f)));
4034 if (isUnsigned() && f <= double(UINT32_MAX) && f >= 0) {
4035 return MConstant::New(alloc, Int32Value(ToInt32(f)));
4039 return this;
4042 MDefinition* MWrapInt64ToInt32::foldsTo(TempAllocator& alloc) {
4043 MDefinition* input = this->input();
4044 if (input->isConstant()) {
4045 uint64_t c = input->toConstant()->toInt64();
4046 int32_t output = bottomHalf() ? int32_t(c) : int32_t(c >> 32);
4047 return MConstant::New(alloc, Int32Value(output));
4050 return this;
4053 MDefinition* MExtendInt32ToInt64::foldsTo(TempAllocator& alloc) {
4054 MDefinition* input = this->input();
4055 if (input->isConstant()) {
4056 int32_t c = input->toConstant()->toInt32();
4057 int64_t res = isUnsigned() ? int64_t(uint32_t(c)) : int64_t(c);
4058 return MConstant::NewInt64(alloc, res);
4061 return this;
4064 MDefinition* MSignExtendInt32::foldsTo(TempAllocator& alloc) {
4065 MDefinition* input = this->input();
4066 if (input->isConstant()) {
4067 int32_t c = input->toConstant()->toInt32();
4068 int32_t res;
4069 switch (mode_) {
4070 case Byte:
4071 res = int32_t(int8_t(c & 0xFF));
4072 break;
4073 case Half:
4074 res = int32_t(int16_t(c & 0xFFFF));
4075 break;
4077 return MConstant::New(alloc, Int32Value(res));
4080 return this;
4083 MDefinition* MSignExtendInt64::foldsTo(TempAllocator& alloc) {
4084 MDefinition* input = this->input();
4085 if (input->isConstant()) {
4086 int64_t c = input->toConstant()->toInt64();
4087 int64_t res;
4088 switch (mode_) {
4089 case Byte:
4090 res = int64_t(int8_t(c & 0xFF));
4091 break;
4092 case Half:
4093 res = int64_t(int16_t(c & 0xFFFF));
4094 break;
4095 case Word:
4096 res = int64_t(int32_t(c & 0xFFFFFFFFU));
4097 break;
4099 return MConstant::NewInt64(alloc, res);
4102 return this;
4105 MDefinition* MToDouble::foldsTo(TempAllocator& alloc) {
4106 MDefinition* input = getOperand(0);
4107 if (input->isBox()) {
4108 input = input->getOperand(0);
4111 if (input->type() == MIRType::Double) {
4112 return input;
4115 if (input->isConstant() &&
4116 input->toConstant()->isTypeRepresentableAsDouble()) {
4117 return MConstant::New(alloc,
4118 DoubleValue(input->toConstant()->numberToDouble()));
4121 return this;
4124 MDefinition* MToFloat32::foldsTo(TempAllocator& alloc) {
4125 MDefinition* input = getOperand(0);
4126 if (input->isBox()) {
4127 input = input->getOperand(0);
4130 if (input->type() == MIRType::Float32) {
4131 return input;
4134 // If x is a Float32, Float32(Double(x)) == x
4135 if (!mustPreserveNaN_ && input->isToDouble() &&
4136 input->toToDouble()->input()->type() == MIRType::Float32) {
4137 return input->toToDouble()->input();
4140 if (input->isConstant() &&
4141 input->toConstant()->isTypeRepresentableAsDouble()) {
4142 return MConstant::NewFloat32(alloc,
4143 float(input->toConstant()->numberToDouble()));
4146 // Fold ToFloat32(ToDouble(int32)) to ToFloat32(int32).
4147 if (input->isToDouble() &&
4148 input->toToDouble()->input()->type() == MIRType::Int32) {
4149 return MToFloat32::New(alloc, input->toToDouble()->input());
4152 return this;
4155 MDefinition* MToString::foldsTo(TempAllocator& alloc) {
4156 MDefinition* in = input();
4157 if (in->isBox()) {
4158 in = in->getOperand(0);
4161 if (in->type() == MIRType::String) {
4162 return in;
4164 return this;
4167 MDefinition* MClampToUint8::foldsTo(TempAllocator& alloc) {
4168 if (MConstant* inputConst = input()->maybeConstantValue()) {
4169 if (inputConst->isTypeRepresentableAsDouble()) {
4170 int32_t clamped = ClampDoubleToUint8(inputConst->numberToDouble());
4171 return MConstant::New(alloc, Int32Value(clamped));
4174 return this;
4177 bool MCompare::tryFoldEqualOperands(bool* result) {
4178 if (lhs() != rhs()) {
4179 return false;
4182 // Intuitively somebody would think that if lhs === rhs,
4183 // then we can just return true. (Or false for !==)
4184 // However NaN !== NaN is true! So we spend some time trying
4185 // to eliminate this case.
4187 if (!IsStrictEqualityOp(jsop())) {
4188 return false;
4191 MOZ_ASSERT(
4192 compareType_ == Compare_Undefined || compareType_ == Compare_Null ||
4193 compareType_ == Compare_Int32 || compareType_ == Compare_UInt32 ||
4194 compareType_ == Compare_UInt64 || compareType_ == Compare_Double ||
4195 compareType_ == Compare_Float32 || compareType_ == Compare_UIntPtr ||
4196 compareType_ == Compare_String || compareType_ == Compare_Object ||
4197 compareType_ == Compare_Symbol || compareType_ == Compare_BigInt ||
4198 compareType_ == Compare_BigInt_Int32 ||
4199 compareType_ == Compare_BigInt_Double ||
4200 compareType_ == Compare_BigInt_String);
4202 if (isDoubleComparison() || isFloat32Comparison()) {
4203 if (!operandsAreNeverNaN()) {
4204 return false;
4208 lhs()->setGuardRangeBailoutsUnchecked();
4210 *result = (jsop() == JSOp::StrictEq);
4211 return true;
4214 static JSType TypeOfName(JSLinearString* str) {
4215 static constexpr std::array types = {
4216 JSTYPE_UNDEFINED, JSTYPE_OBJECT, JSTYPE_FUNCTION, JSTYPE_STRING,
4217 JSTYPE_NUMBER, JSTYPE_BOOLEAN, JSTYPE_SYMBOL, JSTYPE_BIGINT,
4218 #ifdef ENABLE_RECORD_TUPLE
4219 JSTYPE_RECORD, JSTYPE_TUPLE,
4220 #endif
4222 static_assert(types.size() == JSTYPE_LIMIT);
4224 const JSAtomState& names = GetJitContext()->runtime->names();
4225 for (auto type : types) {
4226 if (EqualStrings(str, TypeName(type, names))) {
4227 return type;
4230 return JSTYPE_LIMIT;
4233 static mozilla::Maybe<std::pair<MTypeOfName*, JSType>> IsTypeOfCompare(
4234 MCompare* ins) {
4235 if (!IsEqualityOp(ins->jsop())) {
4236 return mozilla::Nothing();
4238 if (ins->compareType() != MCompare::Compare_String) {
4239 return mozilla::Nothing();
4242 auto* lhs = ins->lhs();
4243 auto* rhs = ins->rhs();
4245 MOZ_ASSERT(ins->type() == MIRType::Boolean);
4246 MOZ_ASSERT(lhs->type() == MIRType::String);
4247 MOZ_ASSERT(rhs->type() == MIRType::String);
4249 if (!lhs->isTypeOfName() && !rhs->isTypeOfName()) {
4250 return mozilla::Nothing();
4252 if (!lhs->isConstant() && !rhs->isConstant()) {
4253 return mozilla::Nothing();
4256 auto* typeOfName =
4257 lhs->isTypeOfName() ? lhs->toTypeOfName() : rhs->toTypeOfName();
4258 MOZ_ASSERT(typeOfName->input()->isTypeOf());
4260 auto* constant = lhs->isConstant() ? lhs->toConstant() : rhs->toConstant();
4262 JSType type = TypeOfName(&constant->toString()->asLinear());
4263 return mozilla::Some(std::pair(typeOfName, type));
4266 bool MCompare::tryFoldTypeOf(bool* result) {
4267 auto typeOfPair = IsTypeOfCompare(this);
4268 if (!typeOfPair) {
4269 return false;
4271 auto [typeOfName, type] = *typeOfPair;
4272 auto* typeOf = typeOfName->input()->toTypeOf();
4274 switch (type) {
4275 case JSTYPE_BOOLEAN:
4276 if (!typeOf->input()->mightBeType(MIRType::Boolean)) {
4277 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4278 return true;
4280 break;
4281 case JSTYPE_NUMBER:
4282 if (!typeOf->input()->mightBeType(MIRType::Int32) &&
4283 !typeOf->input()->mightBeType(MIRType::Float32) &&
4284 !typeOf->input()->mightBeType(MIRType::Double)) {
4285 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4286 return true;
4288 break;
4289 case JSTYPE_STRING:
4290 if (!typeOf->input()->mightBeType(MIRType::String)) {
4291 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4292 return true;
4294 break;
4295 case JSTYPE_SYMBOL:
4296 if (!typeOf->input()->mightBeType(MIRType::Symbol)) {
4297 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4298 return true;
4300 break;
4301 case JSTYPE_BIGINT:
4302 if (!typeOf->input()->mightBeType(MIRType::BigInt)) {
4303 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4304 return true;
4306 break;
4307 case JSTYPE_OBJECT:
4308 if (!typeOf->input()->mightBeType(MIRType::Object) &&
4309 !typeOf->input()->mightBeType(MIRType::Null)) {
4310 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4311 return true;
4313 break;
4314 case JSTYPE_UNDEFINED:
4315 if (!typeOf->input()->mightBeType(MIRType::Object) &&
4316 !typeOf->input()->mightBeType(MIRType::Undefined)) {
4317 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4318 return true;
4320 break;
4321 case JSTYPE_FUNCTION:
4322 if (!typeOf->input()->mightBeType(MIRType::Object)) {
4323 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4324 return true;
4326 break;
4327 case JSTYPE_LIMIT:
4328 *result = (jsop() == JSOp::StrictNe || jsop() == JSOp::Ne);
4329 return true;
4330 #ifdef ENABLE_RECORD_TUPLE
4331 case JSTYPE_RECORD:
4332 case JSTYPE_TUPLE:
4333 MOZ_CRASH("Records and Tuples are not supported yet.");
4334 #endif
4337 return false;
4340 bool MCompare::tryFold(bool* result) {
4341 JSOp op = jsop();
4343 if (tryFoldEqualOperands(result)) {
4344 return true;
4347 if (tryFoldTypeOf(result)) {
4348 return true;
4351 if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) {
4352 // The LHS is the value we want to test against null or undefined.
4353 if (IsStrictEqualityOp(op)) {
4354 if (lhs()->type() == inputType()) {
4355 *result = (op == JSOp::StrictEq);
4356 return true;
4358 if (!lhs()->mightBeType(inputType())) {
4359 *result = (op == JSOp::StrictNe);
4360 return true;
4362 } else {
4363 MOZ_ASSERT(IsLooseEqualityOp(op));
4364 if (IsNullOrUndefined(lhs()->type())) {
4365 *result = (op == JSOp::Eq);
4366 return true;
4368 if (!lhs()->mightBeType(MIRType::Null) &&
4369 !lhs()->mightBeType(MIRType::Undefined) &&
4370 !lhs()->mightBeType(MIRType::Object)) {
4371 *result = (op == JSOp::Ne);
4372 return true;
4375 return false;
4378 return false;
4381 template <typename T>
4382 static bool FoldComparison(JSOp op, T left, T right) {
4383 switch (op) {
4384 case JSOp::Lt:
4385 return left < right;
4386 case JSOp::Le:
4387 return left <= right;
4388 case JSOp::Gt:
4389 return left > right;
4390 case JSOp::Ge:
4391 return left >= right;
4392 case JSOp::StrictEq:
4393 case JSOp::Eq:
4394 return left == right;
4395 case JSOp::StrictNe:
4396 case JSOp::Ne:
4397 return left != right;
4398 default:
4399 MOZ_CRASH("Unexpected op.");
4403 bool MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result) {
4404 if (type() != MIRType::Boolean && type() != MIRType::Int32) {
4405 return false;
4408 MDefinition* left = getOperand(0);
4409 MDefinition* right = getOperand(1);
4411 if (compareType() == Compare_Double) {
4412 // Optimize "MCompare MConstant (MToDouble SomethingInInt32Range).
4413 // In most cases the MToDouble was added, because the constant is
4414 // a double.
4415 // e.g. v < 9007199254740991, where v is an int32 is always true.
4416 if (!lhs()->isConstant() && !rhs()->isConstant()) {
4417 return false;
4420 MDefinition* operand = left->isConstant() ? right : left;
4421 MConstant* constant =
4422 left->isConstant() ? left->toConstant() : right->toConstant();
4423 MOZ_ASSERT(constant->type() == MIRType::Double);
4424 double cte = constant->toDouble();
4426 if (operand->isToDouble() &&
4427 operand->getOperand(0)->type() == MIRType::Int32) {
4428 bool replaced = false;
4429 switch (jsop_) {
4430 case JSOp::Lt:
4431 if (cte > INT32_MAX || cte < INT32_MIN) {
4432 *result = !((constant == lhs()) ^ (cte < INT32_MIN));
4433 replaced = true;
4435 break;
4436 case JSOp::Le:
4437 if (constant == lhs()) {
4438 if (cte > INT32_MAX || cte <= INT32_MIN) {
4439 *result = (cte <= INT32_MIN);
4440 replaced = true;
4442 } else {
4443 if (cte >= INT32_MAX || cte < INT32_MIN) {
4444 *result = (cte >= INT32_MIN);
4445 replaced = true;
4448 break;
4449 case JSOp::Gt:
4450 if (cte > INT32_MAX || cte < INT32_MIN) {
4451 *result = !((constant == rhs()) ^ (cte < INT32_MIN));
4452 replaced = true;
4454 break;
4455 case JSOp::Ge:
4456 if (constant == lhs()) {
4457 if (cte >= INT32_MAX || cte < INT32_MIN) {
4458 *result = (cte >= INT32_MAX);
4459 replaced = true;
4461 } else {
4462 if (cte > INT32_MAX || cte <= INT32_MIN) {
4463 *result = (cte <= INT32_MIN);
4464 replaced = true;
4467 break;
4468 case JSOp::StrictEq: // Fall through.
4469 case JSOp::Eq:
4470 if (cte > INT32_MAX || cte < INT32_MIN) {
4471 *result = false;
4472 replaced = true;
4474 break;
4475 case JSOp::StrictNe: // Fall through.
4476 case JSOp::Ne:
4477 if (cte > INT32_MAX || cte < INT32_MIN) {
4478 *result = true;
4479 replaced = true;
4481 break;
4482 default:
4483 MOZ_CRASH("Unexpected op.");
4485 if (replaced) {
4486 MLimitedTruncate* limit = MLimitedTruncate::New(
4487 alloc, operand->getOperand(0), TruncateKind::NoTruncate);
4488 limit->setGuardUnchecked();
4489 block()->insertBefore(this, limit);
4490 return true;
4494 // Optimize comparison against NaN.
4495 if (std::isnan(cte)) {
4496 switch (jsop_) {
4497 case JSOp::Lt:
4498 case JSOp::Le:
4499 case JSOp::Gt:
4500 case JSOp::Ge:
4501 case JSOp::Eq:
4502 case JSOp::StrictEq:
4503 *result = false;
4504 break;
4505 case JSOp::Ne:
4506 case JSOp::StrictNe:
4507 *result = true;
4508 break;
4509 default:
4510 MOZ_CRASH("Unexpected op.");
4512 return true;
4516 if (!left->isConstant() || !right->isConstant()) {
4517 return false;
4520 MConstant* lhs = left->toConstant();
4521 MConstant* rhs = right->toConstant();
4523 // Fold away some String equality comparisons.
4524 if (lhs->type() == MIRType::String && rhs->type() == MIRType::String) {
4525 int32_t comp = 0; // Default to equal.
4526 if (left != right) {
4527 comp = CompareStrings(&lhs->toString()->asLinear(),
4528 &rhs->toString()->asLinear());
4530 *result = FoldComparison(jsop_, comp, 0);
4531 return true;
4534 if (compareType_ == Compare_UInt32) {
4535 *result = FoldComparison(jsop_, uint32_t(lhs->toInt32()),
4536 uint32_t(rhs->toInt32()));
4537 return true;
4540 if (compareType_ == Compare_Int64) {
4541 *result = FoldComparison(jsop_, lhs->toInt64(), rhs->toInt64());
4542 return true;
4545 if (compareType_ == Compare_UInt64) {
4546 *result = FoldComparison(jsop_, uint64_t(lhs->toInt64()),
4547 uint64_t(rhs->toInt64()));
4548 return true;
4551 if (lhs->isTypeRepresentableAsDouble() &&
4552 rhs->isTypeRepresentableAsDouble()) {
4553 *result =
4554 FoldComparison(jsop_, lhs->numberToDouble(), rhs->numberToDouble());
4555 return true;
4558 return false;
4561 MDefinition* MCompare::tryFoldTypeOf(TempAllocator& alloc) {
4562 auto typeOfPair = IsTypeOfCompare(this);
4563 if (!typeOfPair) {
4564 return this;
4566 auto [typeOfName, type] = *typeOfPair;
4567 auto* typeOf = typeOfName->input()->toTypeOf();
4569 auto* input = typeOf->input();
4570 MOZ_ASSERT(input->type() == MIRType::Value ||
4571 input->type() == MIRType::Object);
4573 // Constant typeof folding handles the other cases.
4574 MOZ_ASSERT_IF(input->type() == MIRType::Object, type == JSTYPE_UNDEFINED ||
4575 type == JSTYPE_OBJECT ||
4576 type == JSTYPE_FUNCTION);
4578 MOZ_ASSERT(type != JSTYPE_LIMIT, "unknown typeof strings folded earlier");
4580 // If there's only a single use, assume this |typeof| is used in a simple
4581 // comparison context.
4583 // if (typeof thing === "number") { ... }
4585 // It'll be compiled into something similar to:
4587 // if (IsNumber(thing)) { ... }
4589 // This heuristic can go wrong when repeated |typeof| are used in consecutive
4590 // if-statements.
4592 // if (typeof thing === "number") { ... }
4593 // else if (typeof thing === "string") { ... }
4594 // ... repeated for all possible types
4596 // In that case it'd more efficient to emit MTypeOf compared to MTypeOfIs. We
4597 // don't yet handle that case, because it'd require a separate optimization
4598 // pass to correctly detect it.
4599 if (typeOfName->hasOneUse()) {
4600 return MTypeOfIs::New(alloc, input, jsop(), type);
4603 MConstant* cst = MConstant::New(alloc, Int32Value(type));
4604 block()->insertBefore(this, cst);
4606 return MCompare::New(alloc, typeOf, cst, jsop(), MCompare::Compare_Int32);
4609 MDefinition* MCompare::tryFoldCharCompare(TempAllocator& alloc) {
4610 if (compareType() != Compare_String) {
4611 return this;
4614 MDefinition* left = lhs();
4615 MOZ_ASSERT(left->type() == MIRType::String);
4617 MDefinition* right = rhs();
4618 MOZ_ASSERT(right->type() == MIRType::String);
4620 // |str[i]| is compiled as |MFromCharCode(MCharCodeAt(str, i))|.
4621 // Out-of-bounds access is compiled as
4622 // |FromCharCodeEmptyIfNegative(CharCodeAtOrNegative(str, i))|.
4623 auto isCharAccess = [](MDefinition* ins) {
4624 if (ins->isFromCharCode()) {
4625 return ins->toFromCharCode()->code()->isCharCodeAt();
4627 if (ins->isFromCharCodeEmptyIfNegative()) {
4628 auto* fromCharCode = ins->toFromCharCodeEmptyIfNegative();
4629 return fromCharCode->code()->isCharCodeAtOrNegative();
4631 return false;
4634 auto charAccessCode = [](MDefinition* ins) {
4635 if (ins->isFromCharCode()) {
4636 return ins->toFromCharCode()->code();
4638 return ins->toFromCharCodeEmptyIfNegative()->code();
4641 if (left->isConstant() || right->isConstant()) {
4642 // Try to optimize |MConstant(string) <compare> (MFromCharCode MCharCodeAt)|
4643 // as |MConstant(charcode) <compare> MCharCodeAt|.
4644 MConstant* constant;
4645 MDefinition* operand;
4646 if (left->isConstant()) {
4647 constant = left->toConstant();
4648 operand = right;
4649 } else {
4650 constant = right->toConstant();
4651 operand = left;
4654 if (constant->toString()->length() != 1 || !isCharAccess(operand)) {
4655 return this;
4658 char16_t charCode = constant->toString()->asLinear().latin1OrTwoByteChar(0);
4659 MConstant* charCodeConst = MConstant::New(alloc, Int32Value(charCode));
4660 block()->insertBefore(this, charCodeConst);
4662 MDefinition* charCodeAt = charAccessCode(operand);
4664 if (left->isConstant()) {
4665 left = charCodeConst;
4666 right = charCodeAt;
4667 } else {
4668 left = charCodeAt;
4669 right = charCodeConst;
4671 } else if (isCharAccess(left) && isCharAccess(right)) {
4672 // Try to optimize |(MFromCharCode MCharCodeAt) <compare> (MFromCharCode
4673 // MCharCodeAt)| as |MCharCodeAt <compare> MCharCodeAt|.
4675 left = charAccessCode(left);
4676 right = charAccessCode(right);
4677 } else {
4678 return this;
4681 return MCompare::New(alloc, left, right, jsop(), MCompare::Compare_Int32);
4684 MDefinition* MCompare::tryFoldStringCompare(TempAllocator& alloc) {
4685 if (compareType() != Compare_String) {
4686 return this;
4689 MDefinition* left = lhs();
4690 MOZ_ASSERT(left->type() == MIRType::String);
4692 MDefinition* right = rhs();
4693 MOZ_ASSERT(right->type() == MIRType::String);
4695 if (!left->isConstant() && !right->isConstant()) {
4696 return this;
4699 // Try to optimize |string <compare> MConstant("")| as |MStringLength(string)
4700 // <compare> MConstant(0)|.
4702 MConstant* constant =
4703 left->isConstant() ? left->toConstant() : right->toConstant();
4704 if (!constant->toString()->empty()) {
4705 return this;
4708 MDefinition* operand = left->isConstant() ? right : left;
4710 auto* strLength = MStringLength::New(alloc, operand);
4711 block()->insertBefore(this, strLength);
4713 auto* zero = MConstant::New(alloc, Int32Value(0));
4714 block()->insertBefore(this, zero);
4716 if (left->isConstant()) {
4717 left = zero;
4718 right = strLength;
4719 } else {
4720 left = strLength;
4721 right = zero;
4724 return MCompare::New(alloc, left, right, jsop(), MCompare::Compare_Int32);
4727 MDefinition* MCompare::tryFoldStringSubstring(TempAllocator& alloc) {
4728 if (compareType() != Compare_String) {
4729 return this;
4731 if (!IsEqualityOp(jsop())) {
4732 return this;
4735 auto* left = lhs();
4736 MOZ_ASSERT(left->type() == MIRType::String);
4738 auto* right = rhs();
4739 MOZ_ASSERT(right->type() == MIRType::String);
4741 // One operand must be a constant string.
4742 if (!left->isConstant() && !right->isConstant()) {
4743 return this;
4746 // The constant string must be non-empty.
4747 auto* constant =
4748 left->isConstant() ? left->toConstant() : right->toConstant();
4749 if (constant->toString()->empty()) {
4750 return this;
4753 // The other operand must be a substring operation.
4754 auto* operand = left->isConstant() ? right : left;
4755 if (!operand->isSubstr()) {
4756 return this;
4758 auto* substr = operand->toSubstr();
4760 static_assert(JSString::MAX_LENGTH < INT32_MAX,
4761 "string length can be casted to int32_t");
4763 if (!IsSubstrTo(substr, int32_t(constant->toString()->length()))) {
4764 return this;
4767 // Now fold code like |str.substring(0, 2) == "aa"| to |str.startsWith("aa")|.
4769 auto* startsWith = MStringStartsWith::New(alloc, substr->string(), constant);
4770 if (jsop() == JSOp::Eq || jsop() == JSOp::StrictEq) {
4771 return startsWith;
4774 // Invert for inequality.
4775 MOZ_ASSERT(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe);
4777 block()->insertBefore(this, startsWith);
4778 return MNot::New(alloc, startsWith);
4781 MDefinition* MCompare::tryFoldStringIndexOf(TempAllocator& alloc) {
4782 if (compareType() != Compare_Int32) {
4783 return this;
4785 if (!IsEqualityOp(jsop())) {
4786 return this;
4789 auto* left = lhs();
4790 MOZ_ASSERT(left->type() == MIRType::Int32);
4792 auto* right = rhs();
4793 MOZ_ASSERT(right->type() == MIRType::Int32);
4795 // One operand must be a constant integer.
4796 if (!left->isConstant() && !right->isConstant()) {
4797 return this;
4800 // The constant must be zero.
4801 auto* constant =
4802 left->isConstant() ? left->toConstant() : right->toConstant();
4803 if (!constant->isInt32(0)) {
4804 return this;
4807 // The other operand must be an indexOf operation.
4808 auto* operand = left->isConstant() ? right : left;
4809 if (!operand->isStringIndexOf()) {
4810 return this;
4813 // Fold |str.indexOf(searchStr) == 0| to |str.startsWith(searchStr)|.
4815 auto* indexOf = operand->toStringIndexOf();
4816 auto* startsWith =
4817 MStringStartsWith::New(alloc, indexOf->string(), indexOf->searchString());
4818 if (jsop() == JSOp::Eq || jsop() == JSOp::StrictEq) {
4819 return startsWith;
4822 // Invert for inequality.
4823 MOZ_ASSERT(jsop() == JSOp::Ne || jsop() == JSOp::StrictNe);
4825 block()->insertBefore(this, startsWith);
4826 return MNot::New(alloc, startsWith);
4829 MDefinition* MCompare::foldsTo(TempAllocator& alloc) {
4830 bool result;
4832 if (tryFold(&result) || evaluateConstantOperands(alloc, &result)) {
4833 if (type() == MIRType::Int32) {
4834 return MConstant::New(alloc, Int32Value(result));
4837 MOZ_ASSERT(type() == MIRType::Boolean);
4838 return MConstant::New(alloc, BooleanValue(result));
4841 if (MDefinition* folded = tryFoldTypeOf(alloc); folded != this) {
4842 return folded;
4845 if (MDefinition* folded = tryFoldCharCompare(alloc); folded != this) {
4846 return folded;
4849 if (MDefinition* folded = tryFoldStringCompare(alloc); folded != this) {
4850 return folded;
4853 if (MDefinition* folded = tryFoldStringSubstring(alloc); folded != this) {
4854 return folded;
4857 if (MDefinition* folded = tryFoldStringIndexOf(alloc); folded != this) {
4858 return folded;
4861 return this;
4864 void MCompare::trySpecializeFloat32(TempAllocator& alloc) {
4865 if (AllOperandsCanProduceFloat32(this) && compareType_ == Compare_Double) {
4866 compareType_ = Compare_Float32;
4867 } else {
4868 ConvertOperandsToDouble(this, alloc);
4872 MDefinition* MNot::foldsTo(TempAllocator& alloc) {
4873 // Fold if the input is constant
4874 if (MConstant* inputConst = input()->maybeConstantValue()) {
4875 bool b;
4876 if (inputConst->valueToBoolean(&b)) {
4877 if (type() == MIRType::Int32 || type() == MIRType::Int64) {
4878 return MConstant::New(alloc, Int32Value(!b));
4880 return MConstant::New(alloc, BooleanValue(!b));
4884 // If the operand of the Not is itself a Not, they cancel out. But we can't
4885 // always convert Not(Not(x)) to x because that may loose the conversion to
4886 // boolean. We can simplify Not(Not(Not(x))) to Not(x) though.
4887 MDefinition* op = getOperand(0);
4888 if (op->isNot()) {
4889 MDefinition* opop = op->getOperand(0);
4890 if (opop->isNot()) {
4891 return opop;
4895 // Not of an undefined or null value is always true
4896 if (input()->type() == MIRType::Undefined ||
4897 input()->type() == MIRType::Null) {
4898 return MConstant::New(alloc, BooleanValue(true));
4901 // Not of a symbol is always false.
4902 if (input()->type() == MIRType::Symbol) {
4903 return MConstant::New(alloc, BooleanValue(false));
4906 return this;
4909 void MNot::trySpecializeFloat32(TempAllocator& alloc) {
4910 (void)EnsureFloatInputOrConvert(this, alloc);
4913 #ifdef JS_JITSPEW
4914 void MBeta::printOpcode(GenericPrinter& out) const {
4915 MDefinition::printOpcode(out);
4917 out.printf(" ");
4918 comparison_->dump(out);
4920 #endif
4922 AliasSet MCreateThis::getAliasSet() const {
4923 return AliasSet::Load(AliasSet::Any);
4926 bool MGetArgumentsObjectArg::congruentTo(const MDefinition* ins) const {
4927 if (!ins->isGetArgumentsObjectArg()) {
4928 return false;
4930 if (ins->toGetArgumentsObjectArg()->argno() != argno()) {
4931 return false;
4933 return congruentIfOperandsEqual(ins);
4936 AliasSet MGetArgumentsObjectArg::getAliasSet() const {
4937 return AliasSet::Load(AliasSet::Any);
4940 AliasSet MSetArgumentsObjectArg::getAliasSet() const {
4941 return AliasSet::Store(AliasSet::Any);
4944 MObjectState::MObjectState(MObjectState* state)
4945 : MVariadicInstruction(classOpcode),
4946 numSlots_(state->numSlots_),
4947 numFixedSlots_(state->numFixedSlots_) {
4948 // This instruction is only used as a summary for bailout paths.
4949 setResultType(MIRType::Object);
4950 setRecoveredOnBailout();
4953 MObjectState::MObjectState(JSObject* templateObject)
4954 : MObjectState(templateObject->as<NativeObject>().shape()) {}
4956 MObjectState::MObjectState(const Shape* shape)
4957 : MVariadicInstruction(classOpcode) {
4958 // This instruction is only used as a summary for bailout paths.
4959 setResultType(MIRType::Object);
4960 setRecoveredOnBailout();
4962 numSlots_ = shape->asShared().slotSpan();
4963 numFixedSlots_ = shape->asShared().numFixedSlots();
4966 /* static */
4967 JSObject* MObjectState::templateObjectOf(MDefinition* obj) {
4968 // MNewPlainObject uses a shape constant, not an object.
4969 MOZ_ASSERT(!obj->isNewPlainObject());
4971 if (obj->isNewObject()) {
4972 return obj->toNewObject()->templateObject();
4973 } else if (obj->isNewCallObject()) {
4974 return obj->toNewCallObject()->templateObject();
4975 } else if (obj->isNewIterator()) {
4976 return obj->toNewIterator()->templateObject();
4979 MOZ_CRASH("unreachable");
4982 bool MObjectState::init(TempAllocator& alloc, MDefinition* obj) {
4983 if (!MVariadicInstruction::init(alloc, numSlots() + 1)) {
4984 return false;
4986 // +1, for the Object.
4987 initOperand(0, obj);
4988 return true;
4991 void MObjectState::initFromTemplateObject(TempAllocator& alloc,
4992 MDefinition* undefinedVal) {
4993 if (object()->isNewPlainObject()) {
4994 MOZ_ASSERT(object()->toNewPlainObject()->shape()->asShared().slotSpan() ==
4995 numSlots());
4996 for (size_t i = 0; i < numSlots(); i++) {
4997 initSlot(i, undefinedVal);
4999 return;
5002 JSObject* templateObject = templateObjectOf(object());
5004 // Initialize all the slots of the object state with the value contained in
5005 // the template object. This is needed to account values which are baked in
5006 // the template objects and not visible in IonMonkey, such as the
5007 // uninitialized-lexical magic value of call objects.
5009 MOZ_ASSERT(templateObject->is<NativeObject>());
5010 NativeObject& nativeObject = templateObject->as<NativeObject>();
5011 MOZ_ASSERT(nativeObject.slotSpan() == numSlots());
5013 for (size_t i = 0; i < numSlots(); i++) {
5014 Value val = nativeObject.getSlot(i);
5015 MDefinition* def = undefinedVal;
5016 if (!val.isUndefined()) {
5017 MConstant* ins = MConstant::New(alloc, val);
5018 block()->insertBefore(this, ins);
5019 def = ins;
5021 initSlot(i, def);
5025 MObjectState* MObjectState::New(TempAllocator& alloc, MDefinition* obj) {
5026 MObjectState* res;
5027 if (obj->isNewPlainObject()) {
5028 const Shape* shape = obj->toNewPlainObject()->shape();
5029 res = new (alloc) MObjectState(shape);
5030 } else {
5031 JSObject* templateObject = templateObjectOf(obj);
5032 MOZ_ASSERT(templateObject, "Unexpected object creation.");
5033 res = new (alloc) MObjectState(templateObject);
5036 if (!res || !res->init(alloc, obj)) {
5037 return nullptr;
5039 return res;
5042 MObjectState* MObjectState::Copy(TempAllocator& alloc, MObjectState* state) {
5043 MObjectState* res = new (alloc) MObjectState(state);
5044 if (!res || !res->init(alloc, state->object())) {
5045 return nullptr;
5047 for (size_t i = 0; i < res->numSlots(); i++) {
5048 res->initSlot(i, state->getSlot(i));
5050 return res;
5053 MArrayState::MArrayState(MDefinition* arr) : MVariadicInstruction(classOpcode) {
5054 // This instruction is only used as a summary for bailout paths.
5055 setResultType(MIRType::Object);
5056 setRecoveredOnBailout();
5057 if (arr->isNewArrayObject()) {
5058 numElements_ = arr->toNewArrayObject()->length();
5059 } else {
5060 numElements_ = arr->toNewArray()->length();
5064 bool MArrayState::init(TempAllocator& alloc, MDefinition* obj,
5065 MDefinition* len) {
5066 if (!MVariadicInstruction::init(alloc, numElements() + 2)) {
5067 return false;
5069 // +1, for the Array object.
5070 initOperand(0, obj);
5071 // +1, for the length value of the array.
5072 initOperand(1, len);
5073 return true;
5076 void MArrayState::initFromTemplateObject(TempAllocator& alloc,
5077 MDefinition* undefinedVal) {
5078 for (size_t i = 0; i < numElements(); i++) {
5079 initElement(i, undefinedVal);
5083 MArrayState* MArrayState::New(TempAllocator& alloc, MDefinition* arr,
5084 MDefinition* initLength) {
5085 MArrayState* res = new (alloc) MArrayState(arr);
5086 if (!res || !res->init(alloc, arr, initLength)) {
5087 return nullptr;
5089 return res;
5092 MArrayState* MArrayState::Copy(TempAllocator& alloc, MArrayState* state) {
5093 MDefinition* arr = state->array();
5094 MDefinition* len = state->initializedLength();
5095 MArrayState* res = new (alloc) MArrayState(arr);
5096 if (!res || !res->init(alloc, arr, len)) {
5097 return nullptr;
5099 for (size_t i = 0; i < res->numElements(); i++) {
5100 res->initElement(i, state->getElement(i));
5102 return res;
5105 MNewArray::MNewArray(uint32_t length, MConstant* templateConst,
5106 gc::Heap initialHeap, bool vmCall)
5107 : MUnaryInstruction(classOpcode, templateConst),
5108 length_(length),
5109 initialHeap_(initialHeap),
5110 vmCall_(vmCall) {
5111 setResultType(MIRType::Object);
5114 MDefinition::AliasType MLoadFixedSlot::mightAlias(
5115 const MDefinition* def) const {
5116 if (def->isStoreFixedSlot()) {
5117 const MStoreFixedSlot* store = def->toStoreFixedSlot();
5118 if (store->slot() != slot()) {
5119 return AliasType::NoAlias;
5121 if (store->object() != object()) {
5122 return AliasType::MayAlias;
5124 return AliasType::MustAlias;
5126 return AliasType::MayAlias;
5129 MDefinition* MLoadFixedSlot::foldsTo(TempAllocator& alloc) {
5130 if (MDefinition* def = foldsToStore(alloc)) {
5131 return def;
5134 return this;
5137 MDefinition::AliasType MLoadFixedSlotAndUnbox::mightAlias(
5138 const MDefinition* def) const {
5139 if (def->isStoreFixedSlot()) {
5140 const MStoreFixedSlot* store = def->toStoreFixedSlot();
5141 if (store->slot() != slot()) {
5142 return AliasType::NoAlias;
5144 if (store->object() != object()) {
5145 return AliasType::MayAlias;
5147 return AliasType::MustAlias;
5149 return AliasType::MayAlias;
5152 MDefinition* MLoadFixedSlotAndUnbox::foldsTo(TempAllocator& alloc) {
5153 if (MDefinition* def = foldsToStore(alloc)) {
5154 return def;
5157 return this;
5160 MDefinition* MWasmExtendU32Index::foldsTo(TempAllocator& alloc) {
5161 MDefinition* input = this->input();
5162 if (input->isConstant()) {
5163 return MConstant::NewInt64(
5164 alloc, int64_t(uint32_t(input->toConstant()->toInt32())));
5167 return this;
5170 MDefinition* MWasmWrapU32Index::foldsTo(TempAllocator& alloc) {
5171 MDefinition* input = this->input();
5172 if (input->isConstant()) {
5173 return MConstant::New(
5174 alloc, Int32Value(int32_t(uint32_t(input->toConstant()->toInt64()))));
5177 return this;
5180 // Some helpers for folding wasm and/or/xor on int32/64 values. Rather than
5181 // duplicating these for 32 and 64-bit values, all folding is done on 64-bit
5182 // values and masked for the 32-bit case.
5184 const uint64_t Low32Mask = uint64_t(0xFFFFFFFFULL);
5186 // Routines to check and disassemble values.
5188 static bool IsIntegralConstant(const MDefinition* def) {
5189 return def->isConstant() &&
5190 (def->type() == MIRType::Int32 || def->type() == MIRType::Int64);
5193 static uint64_t GetIntegralConstant(const MDefinition* def) {
5194 if (def->type() == MIRType::Int32) {
5195 return uint64_t(def->toConstant()->toInt32()) & Low32Mask;
5197 return uint64_t(def->toConstant()->toInt64());
5200 static bool IsIntegralConstantZero(const MDefinition* def) {
5201 return IsIntegralConstant(def) && GetIntegralConstant(def) == 0;
5204 static bool IsIntegralConstantOnes(const MDefinition* def) {
5205 uint64_t ones = def->type() == MIRType::Int32 ? Low32Mask : ~uint64_t(0);
5206 return IsIntegralConstant(def) && GetIntegralConstant(def) == ones;
5209 // Routines to create values.
5210 static MDefinition* ToIntegralConstant(TempAllocator& alloc, MIRType ty,
5211 uint64_t val) {
5212 switch (ty) {
5213 case MIRType::Int32:
5214 return MConstant::New(alloc,
5215 Int32Value(int32_t(uint32_t(val & Low32Mask))));
5216 case MIRType::Int64:
5217 return MConstant::NewInt64(alloc, int64_t(val));
5218 default:
5219 MOZ_CRASH();
5223 static MDefinition* ZeroOfType(TempAllocator& alloc, MIRType ty) {
5224 return ToIntegralConstant(alloc, ty, 0);
5227 static MDefinition* OnesOfType(TempAllocator& alloc, MIRType ty) {
5228 return ToIntegralConstant(alloc, ty, ~uint64_t(0));
5231 MDefinition* MWasmBinaryBitwise::foldsTo(TempAllocator& alloc) {
5232 MOZ_ASSERT(op() == Opcode::WasmBinaryBitwise);
5233 MOZ_ASSERT(type() == MIRType::Int32 || type() == MIRType::Int64);
5235 MDefinition* argL = getOperand(0);
5236 MDefinition* argR = getOperand(1);
5237 MOZ_ASSERT(argL->type() == type() && argR->type() == type());
5239 // The args are the same (SSA name)
5240 if (argL == argR) {
5241 switch (subOpcode()) {
5242 case SubOpcode::And:
5243 case SubOpcode::Or:
5244 return argL;
5245 case SubOpcode::Xor:
5246 return ZeroOfType(alloc, type());
5247 default:
5248 MOZ_CRASH();
5252 // Both args constant
5253 if (IsIntegralConstant(argL) && IsIntegralConstant(argR)) {
5254 uint64_t valL = GetIntegralConstant(argL);
5255 uint64_t valR = GetIntegralConstant(argR);
5256 uint64_t val = valL;
5257 switch (subOpcode()) {
5258 case SubOpcode::And:
5259 val &= valR;
5260 break;
5261 case SubOpcode::Or:
5262 val |= valR;
5263 break;
5264 case SubOpcode::Xor:
5265 val ^= valR;
5266 break;
5267 default:
5268 MOZ_CRASH();
5270 return ToIntegralConstant(alloc, type(), val);
5273 // Left arg is zero
5274 if (IsIntegralConstantZero(argL)) {
5275 switch (subOpcode()) {
5276 case SubOpcode::And:
5277 return ZeroOfType(alloc, type());
5278 case SubOpcode::Or:
5279 case SubOpcode::Xor:
5280 return argR;
5281 default:
5282 MOZ_CRASH();
5286 // Right arg is zero
5287 if (IsIntegralConstantZero(argR)) {
5288 switch (subOpcode()) {
5289 case SubOpcode::And:
5290 return ZeroOfType(alloc, type());
5291 case SubOpcode::Or:
5292 case SubOpcode::Xor:
5293 return argL;
5294 default:
5295 MOZ_CRASH();
5299 // Left arg is ones
5300 if (IsIntegralConstantOnes(argL)) {
5301 switch (subOpcode()) {
5302 case SubOpcode::And:
5303 return argR;
5304 case SubOpcode::Or:
5305 return OnesOfType(alloc, type());
5306 case SubOpcode::Xor:
5307 return MBitNot::New(alloc, argR);
5308 default:
5309 MOZ_CRASH();
5313 // Right arg is ones
5314 if (IsIntegralConstantOnes(argR)) {
5315 switch (subOpcode()) {
5316 case SubOpcode::And:
5317 return argL;
5318 case SubOpcode::Or:
5319 return OnesOfType(alloc, type());
5320 case SubOpcode::Xor:
5321 return MBitNot::New(alloc, argL);
5322 default:
5323 MOZ_CRASH();
5327 return this;
5330 MDefinition* MWasmAddOffset::foldsTo(TempAllocator& alloc) {
5331 MDefinition* baseArg = base();
5332 if (!baseArg->isConstant()) {
5333 return this;
5336 if (baseArg->type() == MIRType::Int32) {
5337 CheckedInt<uint32_t> ptr = baseArg->toConstant()->toInt32();
5338 ptr += offset();
5339 if (!ptr.isValid()) {
5340 return this;
5342 return MConstant::New(alloc, Int32Value(ptr.value()));
5345 MOZ_ASSERT(baseArg->type() == MIRType::Int64);
5346 CheckedInt<uint64_t> ptr = baseArg->toConstant()->toInt64();
5347 ptr += offset();
5348 if (!ptr.isValid()) {
5349 return this;
5351 return MConstant::NewInt64(alloc, ptr.value());
5354 bool MWasmAlignmentCheck::congruentTo(const MDefinition* ins) const {
5355 if (!ins->isWasmAlignmentCheck()) {
5356 return false;
5358 const MWasmAlignmentCheck* check = ins->toWasmAlignmentCheck();
5359 return byteSize_ == check->byteSize() && congruentIfOperandsEqual(check);
5362 MDefinition::AliasType MAsmJSLoadHeap::mightAlias(
5363 const MDefinition* def) const {
5364 if (def->isAsmJSStoreHeap()) {
5365 const MAsmJSStoreHeap* store = def->toAsmJSStoreHeap();
5366 if (store->accessType() != accessType()) {
5367 return AliasType::MayAlias;
5369 if (!base()->isConstant() || !store->base()->isConstant()) {
5370 return AliasType::MayAlias;
5372 const MConstant* otherBase = store->base()->toConstant();
5373 if (base()->toConstant()->equals(otherBase)) {
5374 return AliasType::MayAlias;
5376 return AliasType::NoAlias;
5378 return AliasType::MayAlias;
5381 bool MAsmJSLoadHeap::congruentTo(const MDefinition* ins) const {
5382 if (!ins->isAsmJSLoadHeap()) {
5383 return false;
5385 const MAsmJSLoadHeap* load = ins->toAsmJSLoadHeap();
5386 return load->accessType() == accessType() && congruentIfOperandsEqual(load);
5389 MDefinition::AliasType MWasmLoadInstanceDataField::mightAlias(
5390 const MDefinition* def) const {
5391 if (def->isWasmStoreInstanceDataField()) {
5392 const MWasmStoreInstanceDataField* store =
5393 def->toWasmStoreInstanceDataField();
5394 return store->instanceDataOffset() == instanceDataOffset_
5395 ? AliasType::MayAlias
5396 : AliasType::NoAlias;
5399 return AliasType::MayAlias;
5402 MDefinition::AliasType MWasmLoadGlobalCell::mightAlias(
5403 const MDefinition* def) const {
5404 if (def->isWasmStoreGlobalCell()) {
5405 // No globals of different type can alias. See bug 1467415 comment 3.
5406 if (type() != def->toWasmStoreGlobalCell()->value()->type()) {
5407 return AliasType::NoAlias;
5410 // We could do better here. We're dealing with two indirect globals.
5411 // If at at least one of them is created in this module, then they
5412 // can't alias -- in other words they can only alias if they are both
5413 // imported. That would require having a flag on globals to indicate
5414 // which are imported. See bug 1467415 comment 3, 4th rule.
5417 return AliasType::MayAlias;
5420 HashNumber MWasmLoadInstanceDataField::valueHash() const {
5421 // Same comment as in MWasmLoadInstanceDataField::congruentTo() applies here.
5422 HashNumber hash = MDefinition::valueHash();
5423 hash = addU32ToHash(hash, instanceDataOffset_);
5424 return hash;
5427 bool MWasmLoadInstanceDataField::congruentTo(const MDefinition* ins) const {
5428 if (!ins->isWasmLoadInstanceDataField()) {
5429 return false;
5432 const MWasmLoadInstanceDataField* other = ins->toWasmLoadInstanceDataField();
5434 // We don't need to consider the isConstant_ markings here, because
5435 // equivalence of offsets implies equivalence of constness.
5436 bool sameOffsets = instanceDataOffset_ == other->instanceDataOffset_;
5437 MOZ_ASSERT_IF(sameOffsets, isConstant_ == other->isConstant_);
5439 // We omit checking congruence of the operands. There is only one
5440 // operand, the instance pointer, and it only ever has one value within the
5441 // domain of optimization. If that should ever change then operand
5442 // congruence checking should be reinstated.
5443 return sameOffsets /* && congruentIfOperandsEqual(other) */;
5446 MDefinition* MWasmLoadInstanceDataField::foldsTo(TempAllocator& alloc) {
5447 if (!dependency() || !dependency()->isWasmStoreInstanceDataField()) {
5448 return this;
5451 MWasmStoreInstanceDataField* store =
5452 dependency()->toWasmStoreInstanceDataField();
5453 if (!store->block()->dominates(block())) {
5454 return this;
5457 if (store->instanceDataOffset() != instanceDataOffset()) {
5458 return this;
5461 if (store->value()->type() != type()) {
5462 return this;
5465 return store->value();
5468 bool MWasmLoadGlobalCell::congruentTo(const MDefinition* ins) const {
5469 if (!ins->isWasmLoadGlobalCell()) {
5470 return false;
5472 const MWasmLoadGlobalCell* other = ins->toWasmLoadGlobalCell();
5473 return congruentIfOperandsEqual(other);
5476 #ifdef ENABLE_WASM_SIMD
5477 MDefinition* MWasmTernarySimd128::foldsTo(TempAllocator& alloc) {
5478 if (simdOp() == wasm::SimdOp::V128Bitselect) {
5479 if (v2()->op() == MDefinition::Opcode::WasmFloatConstant) {
5480 int8_t shuffle[16];
5481 if (specializeBitselectConstantMaskAsShuffle(shuffle)) {
5482 return BuildWasmShuffleSimd128(alloc, shuffle, v0(), v1());
5484 } else if (canRelaxBitselect()) {
5485 return MWasmTernarySimd128::New(alloc, v0(), v1(), v2(),
5486 wasm::SimdOp::I8x16RelaxedLaneSelect);
5489 return this;
5492 inline static bool MatchSpecificShift(MDefinition* instr,
5493 wasm::SimdOp simdShiftOp,
5494 int shiftValue) {
5495 return instr->isWasmShiftSimd128() &&
5496 instr->toWasmShiftSimd128()->simdOp() == simdShiftOp &&
5497 instr->toWasmShiftSimd128()->rhs()->isConstant() &&
5498 instr->toWasmShiftSimd128()->rhs()->toConstant()->toInt32() ==
5499 shiftValue;
5502 // Matches MIR subtree that represents PMADDUBSW instruction generated by
5503 // emscripten. The a and b parameters return subtrees that correspond
5504 // operands of the instruction, if match is found.
5505 static bool MatchPmaddubswSequence(MWasmBinarySimd128* lhs,
5506 MWasmBinarySimd128* rhs, MDefinition** a,
5507 MDefinition** b) {
5508 MOZ_ASSERT(lhs->simdOp() == wasm::SimdOp::I16x8Mul &&
5509 rhs->simdOp() == wasm::SimdOp::I16x8Mul);
5510 // The emscripten/LLVM produced the following sequence for _mm_maddubs_epi16:
5512 // return _mm_adds_epi16(
5513 // _mm_mullo_epi16(
5514 // _mm_and_si128(__a, _mm_set1_epi16(0x00FF)),
5515 // _mm_srai_epi16(_mm_slli_epi16(__b, 8), 8)),
5516 // _mm_mullo_epi16(_mm_srli_epi16(__a, 8), _mm_srai_epi16(__b, 8)));
5518 // This will roughly correspond the following MIR:
5519 // MWasmBinarySimd128[I16x8AddSatS]
5520 // |-- lhs: MWasmBinarySimd128[I16x8Mul] (lhs)
5521 // | |-- lhs: MWasmBinarySimd128WithConstant[V128And] (op0)
5522 // | | |-- lhs: a
5523 // | | -- rhs: SimdConstant::SplatX8(0x00FF)
5524 // | -- rhs: MWasmShiftSimd128[I16x8ShrS] (op1)
5525 // | |-- lhs: MWasmShiftSimd128[I16x8Shl]
5526 // | | |-- lhs: b
5527 // | | -- rhs: MConstant[8]
5528 // | -- rhs: MConstant[8]
5529 // -- rhs: MWasmBinarySimd128[I16x8Mul] (rhs)
5530 // |-- lhs: MWasmShiftSimd128[I16x8ShrU] (op2)
5531 // | |-- lhs: a
5532 // | |-- rhs: MConstant[8]
5533 // -- rhs: MWasmShiftSimd128[I16x8ShrS] (op3)
5534 // |-- lhs: b
5535 // -- rhs: MConstant[8]
5537 // The I16x8AddSatS and I16x8Mul are commutative, so their operands
5538 // may be swapped. Rearrange op0, op1, op2, op3 to be in the order
5539 // noted above.
5540 MDefinition *op0 = lhs->lhs(), *op1 = lhs->rhs(), *op2 = rhs->lhs(),
5541 *op3 = rhs->rhs();
5542 if (op1->isWasmBinarySimd128WithConstant()) {
5543 // Move MWasmBinarySimd128WithConstant[V128And] as first operand in lhs.
5544 std::swap(op0, op1);
5545 } else if (op3->isWasmBinarySimd128WithConstant()) {
5546 // Move MWasmBinarySimd128WithConstant[V128And] as first operand in rhs.
5547 std::swap(op2, op3);
5549 if (op2->isWasmBinarySimd128WithConstant()) {
5550 // The lhs and rhs are swapped.
5551 // Make MWasmBinarySimd128WithConstant[V128And] to be op0.
5552 std::swap(op0, op2);
5553 std::swap(op1, op3);
5555 if (op2->isWasmShiftSimd128() &&
5556 op2->toWasmShiftSimd128()->simdOp() == wasm::SimdOp::I16x8ShrS) {
5557 // The op2 and op3 appears to be in wrong order, swap.
5558 std::swap(op2, op3);
5561 // Check all instructions SIMD code and constant values for assigned
5562 // names op0, op1, op2, op3 (see diagram above).
5563 const uint16_t const00FF[8] = {255, 255, 255, 255, 255, 255, 255, 255};
5564 if (!op0->isWasmBinarySimd128WithConstant() ||
5565 op0->toWasmBinarySimd128WithConstant()->simdOp() !=
5566 wasm::SimdOp::V128And ||
5567 memcmp(op0->toWasmBinarySimd128WithConstant()->rhs().bytes(), const00FF,
5568 16) != 0 ||
5569 !MatchSpecificShift(op1, wasm::SimdOp::I16x8ShrS, 8) ||
5570 !MatchSpecificShift(op2, wasm::SimdOp::I16x8ShrU, 8) ||
5571 !MatchSpecificShift(op3, wasm::SimdOp::I16x8ShrS, 8) ||
5572 !MatchSpecificShift(op1->toWasmShiftSimd128()->lhs(),
5573 wasm::SimdOp::I16x8Shl, 8)) {
5574 return false;
5577 // Check if the instructions arguments that are subtrees match the
5578 // a and b assignments. May depend on GVN behavior.
5579 MDefinition* maybeA = op0->toWasmBinarySimd128WithConstant()->lhs();
5580 MDefinition* maybeB = op3->toWasmShiftSimd128()->lhs();
5581 if (maybeA != op2->toWasmShiftSimd128()->lhs() ||
5582 maybeB != op1->toWasmShiftSimd128()->lhs()->toWasmShiftSimd128()->lhs()) {
5583 return false;
5586 *a = maybeA;
5587 *b = maybeB;
5588 return true;
5591 MDefinition* MWasmBinarySimd128::foldsTo(TempAllocator& alloc) {
5592 if (simdOp() == wasm::SimdOp::I8x16Swizzle && rhs()->isWasmFloatConstant()) {
5593 // Specialize swizzle(v, constant) as shuffle(mask, v, zero) to trigger all
5594 // our shuffle optimizations. We don't report this rewriting as the report
5595 // will be overwritten by the subsequent shuffle analysis.
5596 int8_t shuffleMask[16];
5597 memcpy(shuffleMask, rhs()->toWasmFloatConstant()->toSimd128().bytes(), 16);
5598 for (int i = 0; i < 16; i++) {
5599 // Out-of-bounds lanes reference the zero vector; in many cases, the zero
5600 // vector is removed by subsequent optimizations.
5601 if (shuffleMask[i] < 0 || shuffleMask[i] > 15) {
5602 shuffleMask[i] = 16;
5605 MWasmFloatConstant* zero =
5606 MWasmFloatConstant::NewSimd128(alloc, SimdConstant::SplatX4(0));
5607 if (!zero) {
5608 return nullptr;
5610 block()->insertBefore(this, zero);
5611 return BuildWasmShuffleSimd128(alloc, shuffleMask, lhs(), zero);
5614 // Specialize var OP const / const OP var when possible.
5616 // As the LIR layer can't directly handle v128 constants as part of its normal
5617 // machinery we specialize some nodes here if they have single-use v128
5618 // constant arguments. The purpose is to generate code that inlines the
5619 // constant in the instruction stream, using either a rip-relative load+op or
5620 // quickly-synthesized constant in a scratch on x64. There is a general
5621 // assumption here that that is better than generating the constant into an
5622 // allocatable register, since that register value could not be reused. (This
5623 // ignores the possibility that the constant load could be hoisted).
5625 if (lhs()->isWasmFloatConstant() != rhs()->isWasmFloatConstant() &&
5626 specializeForConstantRhs()) {
5627 if (isCommutative() && lhs()->isWasmFloatConstant() && lhs()->hasOneUse()) {
5628 return MWasmBinarySimd128WithConstant::New(
5629 alloc, rhs(), lhs()->toWasmFloatConstant()->toSimd128(), simdOp());
5632 if (rhs()->isWasmFloatConstant() && rhs()->hasOneUse()) {
5633 return MWasmBinarySimd128WithConstant::New(
5634 alloc, lhs(), rhs()->toWasmFloatConstant()->toSimd128(), simdOp());
5638 // Check special encoding for PMADDUBSW.
5639 if (canPmaddubsw() && simdOp() == wasm::SimdOp::I16x8AddSatS &&
5640 lhs()->isWasmBinarySimd128() && rhs()->isWasmBinarySimd128() &&
5641 lhs()->toWasmBinarySimd128()->simdOp() == wasm::SimdOp::I16x8Mul &&
5642 rhs()->toWasmBinarySimd128()->simdOp() == wasm::SimdOp::I16x8Mul) {
5643 MDefinition *a, *b;
5644 if (MatchPmaddubswSequence(lhs()->toWasmBinarySimd128(),
5645 rhs()->toWasmBinarySimd128(), &a, &b)) {
5646 return MWasmBinarySimd128::New(alloc, a, b, /* commutative = */ false,
5647 wasm::SimdOp::MozPMADDUBSW);
5651 return this;
5654 MDefinition* MWasmScalarToSimd128::foldsTo(TempAllocator& alloc) {
5655 # ifdef DEBUG
5656 auto logging = mozilla::MakeScopeExit([&] {
5657 js::wasm::ReportSimdAnalysis("scalar-to-simd128 -> constant folded");
5659 # endif
5660 if (input()->isConstant()) {
5661 MConstant* c = input()->toConstant();
5662 switch (simdOp()) {
5663 case wasm::SimdOp::I8x16Splat:
5664 return MWasmFloatConstant::NewSimd128(
5665 alloc, SimdConstant::SplatX16(c->toInt32()));
5666 case wasm::SimdOp::I16x8Splat:
5667 return MWasmFloatConstant::NewSimd128(
5668 alloc, SimdConstant::SplatX8(c->toInt32()));
5669 case wasm::SimdOp::I32x4Splat:
5670 return MWasmFloatConstant::NewSimd128(
5671 alloc, SimdConstant::SplatX4(c->toInt32()));
5672 case wasm::SimdOp::I64x2Splat:
5673 return MWasmFloatConstant::NewSimd128(
5674 alloc, SimdConstant::SplatX2(c->toInt64()));
5675 default:
5676 # ifdef DEBUG
5677 logging.release();
5678 # endif
5679 return this;
5682 if (input()->isWasmFloatConstant()) {
5683 MWasmFloatConstant* c = input()->toWasmFloatConstant();
5684 switch (simdOp()) {
5685 case wasm::SimdOp::F32x4Splat:
5686 return MWasmFloatConstant::NewSimd128(
5687 alloc, SimdConstant::SplatX4(c->toFloat32()));
5688 case wasm::SimdOp::F64x2Splat:
5689 return MWasmFloatConstant::NewSimd128(
5690 alloc, SimdConstant::SplatX2(c->toDouble()));
5691 default:
5692 # ifdef DEBUG
5693 logging.release();
5694 # endif
5695 return this;
5698 # ifdef DEBUG
5699 logging.release();
5700 # endif
5701 return this;
5704 template <typename T>
5705 static bool AllTrue(const T& v) {
5706 constexpr size_t count = sizeof(T) / sizeof(*v);
5707 static_assert(count == 16 || count == 8 || count == 4 || count == 2);
5708 bool result = true;
5709 for (unsigned i = 0; i < count; i++) {
5710 result = result && v[i] != 0;
5712 return result;
5715 template <typename T>
5716 static int32_t Bitmask(const T& v) {
5717 constexpr size_t count = sizeof(T) / sizeof(*v);
5718 constexpr size_t shift = 8 * sizeof(*v) - 1;
5719 static_assert(shift == 7 || shift == 15 || shift == 31 || shift == 63);
5720 int32_t result = 0;
5721 for (unsigned i = 0; i < count; i++) {
5722 result = result | int32_t(((v[i] >> shift) & 1) << i);
5724 return result;
5727 MDefinition* MWasmReduceSimd128::foldsTo(TempAllocator& alloc) {
5728 # ifdef DEBUG
5729 auto logging = mozilla::MakeScopeExit([&] {
5730 js::wasm::ReportSimdAnalysis("simd128-to-scalar -> constant folded");
5732 # endif
5733 if (input()->isWasmFloatConstant()) {
5734 SimdConstant c = input()->toWasmFloatConstant()->toSimd128();
5735 int32_t i32Result = 0;
5736 switch (simdOp()) {
5737 case wasm::SimdOp::V128AnyTrue:
5738 i32Result = !c.isZeroBits();
5739 break;
5740 case wasm::SimdOp::I8x16AllTrue:
5741 i32Result = AllTrue(
5742 SimdConstant::CreateSimd128((int8_t*)c.bytes()).asInt8x16());
5743 break;
5744 case wasm::SimdOp::I8x16Bitmask:
5745 i32Result = Bitmask(
5746 SimdConstant::CreateSimd128((int8_t*)c.bytes()).asInt8x16());
5747 break;
5748 case wasm::SimdOp::I16x8AllTrue:
5749 i32Result = AllTrue(
5750 SimdConstant::CreateSimd128((int16_t*)c.bytes()).asInt16x8());
5751 break;
5752 case wasm::SimdOp::I16x8Bitmask:
5753 i32Result = Bitmask(
5754 SimdConstant::CreateSimd128((int16_t*)c.bytes()).asInt16x8());
5755 break;
5756 case wasm::SimdOp::I32x4AllTrue:
5757 i32Result = AllTrue(
5758 SimdConstant::CreateSimd128((int32_t*)c.bytes()).asInt32x4());
5759 break;
5760 case wasm::SimdOp::I32x4Bitmask:
5761 i32Result = Bitmask(
5762 SimdConstant::CreateSimd128((int32_t*)c.bytes()).asInt32x4());
5763 break;
5764 case wasm::SimdOp::I64x2AllTrue:
5765 i32Result = AllTrue(
5766 SimdConstant::CreateSimd128((int64_t*)c.bytes()).asInt64x2());
5767 break;
5768 case wasm::SimdOp::I64x2Bitmask:
5769 i32Result = Bitmask(
5770 SimdConstant::CreateSimd128((int64_t*)c.bytes()).asInt64x2());
5771 break;
5772 case wasm::SimdOp::I8x16ExtractLaneS:
5773 i32Result =
5774 SimdConstant::CreateSimd128((int8_t*)c.bytes()).asInt8x16()[imm()];
5775 break;
5776 case wasm::SimdOp::I8x16ExtractLaneU:
5777 i32Result = int32_t(SimdConstant::CreateSimd128((int8_t*)c.bytes())
5778 .asInt8x16()[imm()]) &
5779 0xFF;
5780 break;
5781 case wasm::SimdOp::I16x8ExtractLaneS:
5782 i32Result =
5783 SimdConstant::CreateSimd128((int16_t*)c.bytes()).asInt16x8()[imm()];
5784 break;
5785 case wasm::SimdOp::I16x8ExtractLaneU:
5786 i32Result = int32_t(SimdConstant::CreateSimd128((int16_t*)c.bytes())
5787 .asInt16x8()[imm()]) &
5788 0xFFFF;
5789 break;
5790 case wasm::SimdOp::I32x4ExtractLane:
5791 i32Result =
5792 SimdConstant::CreateSimd128((int32_t*)c.bytes()).asInt32x4()[imm()];
5793 break;
5794 case wasm::SimdOp::I64x2ExtractLane:
5795 return MConstant::NewInt64(
5796 alloc, SimdConstant::CreateSimd128((int64_t*)c.bytes())
5797 .asInt64x2()[imm()]);
5798 case wasm::SimdOp::F32x4ExtractLane:
5799 return MWasmFloatConstant::NewFloat32(
5800 alloc, SimdConstant::CreateSimd128((float*)c.bytes())
5801 .asFloat32x4()[imm()]);
5802 case wasm::SimdOp::F64x2ExtractLane:
5803 return MWasmFloatConstant::NewDouble(
5804 alloc, SimdConstant::CreateSimd128((double*)c.bytes())
5805 .asFloat64x2()[imm()]);
5806 default:
5807 # ifdef DEBUG
5808 logging.release();
5809 # endif
5810 return this;
5812 return MConstant::New(alloc, Int32Value(i32Result), MIRType::Int32);
5814 # ifdef DEBUG
5815 logging.release();
5816 # endif
5817 return this;
5819 #endif // ENABLE_WASM_SIMD
5821 MDefinition::AliasType MLoadDynamicSlot::mightAlias(
5822 const MDefinition* def) const {
5823 if (def->isStoreDynamicSlot()) {
5824 const MStoreDynamicSlot* store = def->toStoreDynamicSlot();
5825 if (store->slot() != slot()) {
5826 return AliasType::NoAlias;
5829 if (store->slots() != slots()) {
5830 return AliasType::MayAlias;
5833 return AliasType::MustAlias;
5835 return AliasType::MayAlias;
5838 HashNumber MLoadDynamicSlot::valueHash() const {
5839 HashNumber hash = MDefinition::valueHash();
5840 hash = addU32ToHash(hash, slot_);
5841 return hash;
5844 MDefinition* MLoadDynamicSlot::foldsTo(TempAllocator& alloc) {
5845 if (MDefinition* def = foldsToStore(alloc)) {
5846 return def;
5849 return this;
5852 #ifdef JS_JITSPEW
5853 void MLoadDynamicSlot::printOpcode(GenericPrinter& out) const {
5854 MDefinition::printOpcode(out);
5855 out.printf(" (slot %u)", slot());
5858 void MLoadDynamicSlotAndUnbox::printOpcode(GenericPrinter& out) const {
5859 MDefinition::printOpcode(out);
5860 out.printf(" (slot %zu)", slot());
5863 void MStoreDynamicSlot::printOpcode(GenericPrinter& out) const {
5864 MDefinition::printOpcode(out);
5865 out.printf(" (slot %u)", slot());
5868 void MLoadFixedSlot::printOpcode(GenericPrinter& out) const {
5869 MDefinition::printOpcode(out);
5870 out.printf(" (slot %zu)", slot());
5873 void MLoadFixedSlotAndUnbox::printOpcode(GenericPrinter& out) const {
5874 MDefinition::printOpcode(out);
5875 out.printf(" (slot %zu)", slot());
5878 void MStoreFixedSlot::printOpcode(GenericPrinter& out) const {
5879 MDefinition::printOpcode(out);
5880 out.printf(" (slot %zu)", slot());
5882 #endif
5884 MDefinition* MGuardFunctionScript::foldsTo(TempAllocator& alloc) {
5885 MDefinition* in = input();
5886 if (in->isLambda() &&
5887 in->toLambda()->templateFunction()->baseScript() == expected()) {
5888 return in;
5890 return this;
5893 MDefinition* MFunctionEnvironment::foldsTo(TempAllocator& alloc) {
5894 if (input()->isLambda()) {
5895 return input()->toLambda()->environmentChain();
5897 if (input()->isFunctionWithProto()) {
5898 return input()->toFunctionWithProto()->environmentChain();
5900 return this;
5903 static bool AddIsANonZeroAdditionOf(MAdd* add, MDefinition* ins) {
5904 if (add->lhs() != ins && add->rhs() != ins) {
5905 return false;
5907 MDefinition* other = (add->lhs() == ins) ? add->rhs() : add->lhs();
5908 if (!IsNumberType(other->type())) {
5909 return false;
5911 if (!other->isConstant()) {
5912 return false;
5914 if (other->toConstant()->numberToDouble() == 0) {
5915 return false;
5917 return true;
5920 // Skip over instructions that usually appear between the actual index
5921 // value being used and the MLoadElement.
5922 // They don't modify the index value in a meaningful way.
5923 static MDefinition* SkipUninterestingInstructions(MDefinition* ins) {
5924 // Drop the MToNumberInt32 added by the TypePolicy for double and float
5925 // values.
5926 if (ins->isToNumberInt32()) {
5927 return SkipUninterestingInstructions(ins->toToNumberInt32()->input());
5930 // Ignore the bounds check, which don't modify the index.
5931 if (ins->isBoundsCheck()) {
5932 return SkipUninterestingInstructions(ins->toBoundsCheck()->index());
5935 // Masking the index for Spectre-mitigation is not observable.
5936 if (ins->isSpectreMaskIndex()) {
5937 return SkipUninterestingInstructions(ins->toSpectreMaskIndex()->index());
5940 return ins;
5943 static bool DefinitelyDifferentValue(MDefinition* ins1, MDefinition* ins2) {
5944 ins1 = SkipUninterestingInstructions(ins1);
5945 ins2 = SkipUninterestingInstructions(ins2);
5947 if (ins1 == ins2) {
5948 return false;
5951 // For constants check they are not equal.
5952 if (ins1->isConstant() && ins2->isConstant()) {
5953 MConstant* cst1 = ins1->toConstant();
5954 MConstant* cst2 = ins2->toConstant();
5956 if (!cst1->isTypeRepresentableAsDouble() ||
5957 !cst2->isTypeRepresentableAsDouble()) {
5958 return false;
5961 // Be conservative and only allow values that fit into int32.
5962 int32_t n1, n2;
5963 if (!mozilla::NumberIsInt32(cst1->numberToDouble(), &n1) ||
5964 !mozilla::NumberIsInt32(cst2->numberToDouble(), &n2)) {
5965 return false;
5968 return n1 != n2;
5971 // Check if "ins1 = ins2 + cte", which would make both instructions
5972 // have different values.
5973 if (ins1->isAdd()) {
5974 if (AddIsANonZeroAdditionOf(ins1->toAdd(), ins2)) {
5975 return true;
5978 if (ins2->isAdd()) {
5979 if (AddIsANonZeroAdditionOf(ins2->toAdd(), ins1)) {
5980 return true;
5984 return false;
5987 MDefinition::AliasType MLoadElement::mightAlias(const MDefinition* def) const {
5988 if (def->isStoreElement()) {
5989 const MStoreElement* store = def->toStoreElement();
5990 if (store->index() != index()) {
5991 if (DefinitelyDifferentValue(store->index(), index())) {
5992 return AliasType::NoAlias;
5994 return AliasType::MayAlias;
5997 if (store->elements() != elements()) {
5998 return AliasType::MayAlias;
6001 return AliasType::MustAlias;
6003 return AliasType::MayAlias;
6006 MDefinition* MLoadElement::foldsTo(TempAllocator& alloc) {
6007 if (MDefinition* def = foldsToStore(alloc)) {
6008 return def;
6011 return this;
6014 MDefinition* MWasmUnsignedToDouble::foldsTo(TempAllocator& alloc) {
6015 if (input()->isConstant()) {
6016 return MConstant::New(
6017 alloc, DoubleValue(uint32_t(input()->toConstant()->toInt32())));
6020 return this;
6023 MDefinition* MWasmUnsignedToFloat32::foldsTo(TempAllocator& alloc) {
6024 if (input()->isConstant()) {
6025 double dval = double(uint32_t(input()->toConstant()->toInt32()));
6026 if (IsFloat32Representable(dval)) {
6027 return MConstant::NewFloat32(alloc, float(dval));
6031 return this;
6034 MWasmCallCatchable* MWasmCallCatchable::New(TempAllocator& alloc,
6035 const wasm::CallSiteDesc& desc,
6036 const wasm::CalleeDesc& callee,
6037 const Args& args,
6038 uint32_t stackArgAreaSizeUnaligned,
6039 const MWasmCallTryDesc& tryDesc,
6040 MDefinition* tableIndexOrRef) {
6041 MOZ_ASSERT(tryDesc.inTry);
6043 MWasmCallCatchable* call = new (alloc) MWasmCallCatchable(
6044 desc, callee, stackArgAreaSizeUnaligned, tryDesc.tryNoteIndex);
6046 call->setSuccessor(FallthroughBranchIndex, tryDesc.fallthroughBlock);
6047 call->setSuccessor(PrePadBranchIndex, tryDesc.prePadBlock);
6049 MOZ_ASSERT_IF(callee.isTable() || callee.isFuncRef(), tableIndexOrRef);
6050 if (!call->initWithArgs(alloc, call, args, tableIndexOrRef)) {
6051 return nullptr;
6054 return call;
6057 MWasmCallUncatchable* MWasmCallUncatchable::New(
6058 TempAllocator& alloc, const wasm::CallSiteDesc& desc,
6059 const wasm::CalleeDesc& callee, const Args& args,
6060 uint32_t stackArgAreaSizeUnaligned, MDefinition* tableIndexOrRef) {
6061 MWasmCallUncatchable* call =
6062 new (alloc) MWasmCallUncatchable(desc, callee, stackArgAreaSizeUnaligned);
6064 MOZ_ASSERT_IF(callee.isTable() || callee.isFuncRef(), tableIndexOrRef);
6065 if (!call->initWithArgs(alloc, call, args, tableIndexOrRef)) {
6066 return nullptr;
6069 return call;
6072 MWasmCallUncatchable* MWasmCallUncatchable::NewBuiltinInstanceMethodCall(
6073 TempAllocator& alloc, const wasm::CallSiteDesc& desc,
6074 const wasm::SymbolicAddress builtin, wasm::FailureMode failureMode,
6075 const ABIArg& instanceArg, const Args& args,
6076 uint32_t stackArgAreaSizeUnaligned) {
6077 auto callee = wasm::CalleeDesc::builtinInstanceMethod(builtin);
6078 MWasmCallUncatchable* call = MWasmCallUncatchable::New(
6079 alloc, desc, callee, args, stackArgAreaSizeUnaligned, nullptr);
6080 if (!call) {
6081 return nullptr;
6084 MOZ_ASSERT(instanceArg != ABIArg());
6085 call->instanceArg_ = instanceArg;
6086 call->builtinMethodFailureMode_ = failureMode;
6087 return call;
6090 MWasmReturnCall* MWasmReturnCall::New(TempAllocator& alloc,
6091 const wasm::CallSiteDesc& desc,
6092 const wasm::CalleeDesc& callee,
6093 const Args& args,
6094 uint32_t stackArgAreaSizeUnaligned,
6095 MDefinition* tableIndexOrRef) {
6096 MWasmReturnCall* call =
6097 new (alloc) MWasmReturnCall(desc, callee, stackArgAreaSizeUnaligned);
6099 MOZ_ASSERT_IF(callee.isTable() || callee.isFuncRef(), tableIndexOrRef);
6100 if (!call->initWithArgs(alloc, call, args, tableIndexOrRef)) {
6101 return nullptr;
6104 return call;
6107 void MSqrt::trySpecializeFloat32(TempAllocator& alloc) {
6108 if (EnsureFloatConsumersAndInputOrConvert(this, alloc)) {
6109 setResultType(MIRType::Float32);
6110 specialization_ = MIRType::Float32;
6114 MDefinition* MClz::foldsTo(TempAllocator& alloc) {
6115 if (num()->isConstant()) {
6116 MConstant* c = num()->toConstant();
6117 if (type() == MIRType::Int32) {
6118 int32_t n = c->toInt32();
6119 if (n == 0) {
6120 return MConstant::New(alloc, Int32Value(32));
6122 return MConstant::New(alloc,
6123 Int32Value(mozilla::CountLeadingZeroes32(n)));
6125 int64_t n = c->toInt64();
6126 if (n == 0) {
6127 return MConstant::NewInt64(alloc, int64_t(64));
6129 return MConstant::NewInt64(alloc,
6130 int64_t(mozilla::CountLeadingZeroes64(n)));
6133 return this;
6136 MDefinition* MCtz::foldsTo(TempAllocator& alloc) {
6137 if (num()->isConstant()) {
6138 MConstant* c = num()->toConstant();
6139 if (type() == MIRType::Int32) {
6140 int32_t n = num()->toConstant()->toInt32();
6141 if (n == 0) {
6142 return MConstant::New(alloc, Int32Value(32));
6144 return MConstant::New(alloc,
6145 Int32Value(mozilla::CountTrailingZeroes32(n)));
6147 int64_t n = c->toInt64();
6148 if (n == 0) {
6149 return MConstant::NewInt64(alloc, int64_t(64));
6151 return MConstant::NewInt64(alloc,
6152 int64_t(mozilla::CountTrailingZeroes64(n)));
6155 return this;
6158 MDefinition* MPopcnt::foldsTo(TempAllocator& alloc) {
6159 if (num()->isConstant()) {
6160 MConstant* c = num()->toConstant();
6161 if (type() == MIRType::Int32) {
6162 int32_t n = num()->toConstant()->toInt32();
6163 return MConstant::New(alloc, Int32Value(mozilla::CountPopulation32(n)));
6165 int64_t n = c->toInt64();
6166 return MConstant::NewInt64(alloc, int64_t(mozilla::CountPopulation64(n)));
6169 return this;
6172 MDefinition* MBoundsCheck::foldsTo(TempAllocator& alloc) {
6173 if (type() == MIRType::Int32 && index()->isConstant() &&
6174 length()->isConstant()) {
6175 uint32_t len = length()->toConstant()->toInt32();
6176 uint32_t idx = index()->toConstant()->toInt32();
6177 if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len) {
6178 return index();
6182 return this;
6185 MDefinition* MTableSwitch::foldsTo(TempAllocator& alloc) {
6186 MDefinition* op = getOperand(0);
6188 // If we only have one successor, convert to a plain goto to the only
6189 // successor. TableSwitch indices are numeric; other types will always go to
6190 // the only successor.
6191 if (numSuccessors() == 1 ||
6192 (op->type() != MIRType::Value && !IsNumberType(op->type()))) {
6193 return MGoto::New(alloc, getDefault());
6196 if (MConstant* opConst = op->maybeConstantValue()) {
6197 if (op->type() == MIRType::Int32) {
6198 int32_t i = opConst->toInt32() - low_;
6199 MBasicBlock* target;
6200 if (size_t(i) < numCases()) {
6201 target = getCase(size_t(i));
6202 } else {
6203 target = getDefault();
6205 MOZ_ASSERT(target);
6206 return MGoto::New(alloc, target);
6210 return this;
6213 MDefinition* MArrayJoin::foldsTo(TempAllocator& alloc) {
6214 MDefinition* arr = array();
6216 if (!arr->isStringSplit()) {
6217 return this;
6220 setRecoveredOnBailout();
6221 if (arr->hasLiveDefUses()) {
6222 setNotRecoveredOnBailout();
6223 return this;
6226 // The MStringSplit won't generate any code.
6227 arr->setRecoveredOnBailout();
6229 // We're replacing foo.split(bar).join(baz) by
6230 // foo.replace(bar, baz). MStringSplit could be recovered by
6231 // a bailout. As we are removing its last use, and its result
6232 // could be captured by a resume point, this MStringSplit will
6233 // be executed on the bailout path.
6234 MDefinition* string = arr->toStringSplit()->string();
6235 MDefinition* pattern = arr->toStringSplit()->separator();
6236 MDefinition* replacement = sep();
6238 MStringReplace* substr =
6239 MStringReplace::New(alloc, string, pattern, replacement);
6240 substr->setFlatReplacement();
6241 return substr;
6244 MDefinition* MGetFirstDollarIndex::foldsTo(TempAllocator& alloc) {
6245 MDefinition* strArg = str();
6246 if (!strArg->isConstant()) {
6247 return this;
6250 JSLinearString* str = &strArg->toConstant()->toString()->asLinear();
6251 int32_t index = GetFirstDollarIndexRawFlat(str);
6252 return MConstant::New(alloc, Int32Value(index));
6255 AliasSet MThrowRuntimeLexicalError::getAliasSet() const {
6256 return AliasSet::Store(AliasSet::ExceptionState);
6259 AliasSet MSlots::getAliasSet() const {
6260 return AliasSet::Load(AliasSet::ObjectFields);
6263 MDefinition::AliasType MSlots::mightAlias(const MDefinition* store) const {
6264 // ArrayPush only modifies object elements, but not object slots.
6265 if (store->isArrayPush()) {
6266 return AliasType::NoAlias;
6268 return MInstruction::mightAlias(store);
6271 AliasSet MElements::getAliasSet() const {
6272 return AliasSet::Load(AliasSet::ObjectFields);
6275 AliasSet MInitializedLength::getAliasSet() const {
6276 return AliasSet::Load(AliasSet::ObjectFields);
6279 AliasSet MSetInitializedLength::getAliasSet() const {
6280 return AliasSet::Store(AliasSet::ObjectFields);
6283 AliasSet MObjectKeysLength::getAliasSet() const {
6284 return AliasSet::Load(AliasSet::ObjectFields);
6287 AliasSet MArrayLength::getAliasSet() const {
6288 return AliasSet::Load(AliasSet::ObjectFields);
6291 AliasSet MSetArrayLength::getAliasSet() const {
6292 return AliasSet::Store(AliasSet::ObjectFields);
6295 AliasSet MFunctionLength::getAliasSet() const {
6296 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6297 AliasSet::DynamicSlot);
6300 AliasSet MFunctionName::getAliasSet() const {
6301 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6302 AliasSet::DynamicSlot);
6305 AliasSet MArrayBufferByteLength::getAliasSet() const {
6306 return AliasSet::Load(AliasSet::FixedSlot);
6309 AliasSet MArrayBufferViewLength::getAliasSet() const {
6310 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset);
6313 AliasSet MArrayBufferViewByteOffset::getAliasSet() const {
6314 return AliasSet::Load(AliasSet::ArrayBufferViewLengthOrOffset);
6317 AliasSet MArrayBufferViewElements::getAliasSet() const {
6318 return AliasSet::Load(AliasSet::ObjectFields);
6321 AliasSet MGuardHasAttachedArrayBuffer::getAliasSet() const {
6322 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot);
6325 AliasSet MArrayPush::getAliasSet() const {
6326 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element);
6329 MDefinition* MGuardNumberToIntPtrIndex::foldsTo(TempAllocator& alloc) {
6330 MDefinition* input = this->input();
6332 if (input->isToDouble() && input->getOperand(0)->type() == MIRType::Int32) {
6333 return MInt32ToIntPtr::New(alloc, input->getOperand(0));
6336 if (!input->isConstant()) {
6337 return this;
6340 // Fold constant double representable as intptr to intptr.
6341 int64_t ival;
6342 if (!mozilla::NumberEqualsInt64(input->toConstant()->toDouble(), &ival)) {
6343 // If not representable as an int64, this access is equal to an OOB access.
6344 // So replace it with a known int64/intptr value which also produces an OOB
6345 // access. If we don't support OOB accesses we have to bail out.
6346 if (!supportOOB()) {
6347 return this;
6349 ival = -1;
6352 if (ival < INTPTR_MIN || ival > INTPTR_MAX) {
6353 return this;
6356 return MConstant::NewIntPtr(alloc, intptr_t(ival));
6359 MDefinition* MIsObject::foldsTo(TempAllocator& alloc) {
6360 if (!object()->isBox()) {
6361 return this;
6364 MDefinition* unboxed = object()->getOperand(0);
6365 if (unboxed->type() == MIRType::Object) {
6366 return MConstant::New(alloc, BooleanValue(true));
6369 return this;
6372 MDefinition* MIsNullOrUndefined::foldsTo(TempAllocator& alloc) {
6373 MDefinition* input = value();
6374 if (input->isBox()) {
6375 input = input->toBox()->input();
6378 if (input->definitelyType({MIRType::Null, MIRType::Undefined})) {
6379 return MConstant::New(alloc, BooleanValue(true));
6382 if (!input->mightBeType(MIRType::Null) &&
6383 !input->mightBeType(MIRType::Undefined)) {
6384 return MConstant::New(alloc, BooleanValue(false));
6387 return this;
6390 AliasSet MHomeObjectSuperBase::getAliasSet() const {
6391 return AliasSet::Load(AliasSet::ObjectFields);
6394 MDefinition* MGuardValue::foldsTo(TempAllocator& alloc) {
6395 if (MConstant* cst = value()->maybeConstantValue()) {
6396 if (cst->toJSValue() == expected()) {
6397 return value();
6401 return this;
6404 MDefinition* MGuardNullOrUndefined::foldsTo(TempAllocator& alloc) {
6405 MDefinition* input = value();
6406 if (input->isBox()) {
6407 input = input->toBox()->input();
6410 if (input->definitelyType({MIRType::Null, MIRType::Undefined})) {
6411 return value();
6414 return this;
6417 MDefinition* MGuardIsNotObject::foldsTo(TempAllocator& alloc) {
6418 MDefinition* input = value();
6419 if (input->isBox()) {
6420 input = input->toBox()->input();
6423 if (!input->mightBeType(MIRType::Object)) {
6424 return value();
6427 return this;
6430 MDefinition* MGuardObjectIdentity::foldsTo(TempAllocator& alloc) {
6431 if (object()->isConstant() && expected()->isConstant()) {
6432 JSObject* obj = &object()->toConstant()->toObject();
6433 JSObject* other = &expected()->toConstant()->toObject();
6434 if (!bailOnEquality()) {
6435 if (obj == other) {
6436 return object();
6438 } else {
6439 if (obj != other) {
6440 return object();
6445 if (!bailOnEquality() && object()->isNurseryObject() &&
6446 expected()->isNurseryObject()) {
6447 uint32_t objIndex = object()->toNurseryObject()->nurseryIndex();
6448 uint32_t otherIndex = expected()->toNurseryObject()->nurseryIndex();
6449 if (objIndex == otherIndex) {
6450 return object();
6454 return this;
6457 MDefinition* MGuardSpecificFunction::foldsTo(TempAllocator& alloc) {
6458 if (function()->isConstant() && expected()->isConstant()) {
6459 JSObject* fun = &function()->toConstant()->toObject();
6460 JSObject* other = &expected()->toConstant()->toObject();
6461 if (fun == other) {
6462 return function();
6466 if (function()->isNurseryObject() && expected()->isNurseryObject()) {
6467 uint32_t funIndex = function()->toNurseryObject()->nurseryIndex();
6468 uint32_t otherIndex = expected()->toNurseryObject()->nurseryIndex();
6469 if (funIndex == otherIndex) {
6470 return function();
6474 return this;
6477 MDefinition* MGuardSpecificAtom::foldsTo(TempAllocator& alloc) {
6478 if (str()->isConstant()) {
6479 JSString* s = str()->toConstant()->toString();
6480 if (s->isAtom()) {
6481 JSAtom* cstAtom = &s->asAtom();
6482 if (cstAtom == atom()) {
6483 return str();
6488 return this;
6491 MDefinition* MGuardSpecificSymbol::foldsTo(TempAllocator& alloc) {
6492 if (symbol()->isConstant()) {
6493 if (symbol()->toConstant()->toSymbol() == expected()) {
6494 return symbol();
6498 return this;
6501 MDefinition* MGuardSpecificInt32::foldsTo(TempAllocator& alloc) {
6502 if (num()->isConstant() && num()->toConstant()->isInt32(expected())) {
6503 return num();
6505 return this;
6508 bool MCallBindVar::congruentTo(const MDefinition* ins) const {
6509 if (!ins->isCallBindVar()) {
6510 return false;
6512 return congruentIfOperandsEqual(ins);
6515 bool MGuardShape::congruentTo(const MDefinition* ins) const {
6516 if (!ins->isGuardShape()) {
6517 return false;
6519 if (shape() != ins->toGuardShape()->shape()) {
6520 return false;
6522 return congruentIfOperandsEqual(ins);
6525 AliasSet MGuardShape::getAliasSet() const {
6526 return AliasSet::Load(AliasSet::ObjectFields);
6529 MDefinition::AliasType MGuardShape::mightAlias(const MDefinition* store) const {
6530 // These instructions only modify object elements, but not the shape.
6531 if (store->isStoreElementHole() || store->isArrayPush()) {
6532 return AliasType::NoAlias;
6534 if (object()->isConstantProto()) {
6535 const MDefinition* receiverObject =
6536 object()->toConstantProto()->getReceiverObject();
6537 switch (store->op()) {
6538 case MDefinition::Opcode::StoreFixedSlot:
6539 if (store->toStoreFixedSlot()->object()->skipObjectGuards() ==
6540 receiverObject) {
6541 return AliasType::NoAlias;
6543 break;
6544 case MDefinition::Opcode::StoreDynamicSlot:
6545 if (store->toStoreDynamicSlot()
6546 ->slots()
6547 ->toSlots()
6548 ->object()
6549 ->skipObjectGuards() == receiverObject) {
6550 return AliasType::NoAlias;
6552 break;
6553 case MDefinition::Opcode::AddAndStoreSlot:
6554 if (store->toAddAndStoreSlot()->object()->skipObjectGuards() ==
6555 receiverObject) {
6556 return AliasType::NoAlias;
6558 break;
6559 case MDefinition::Opcode::AllocateAndStoreSlot:
6560 if (store->toAllocateAndStoreSlot()->object()->skipObjectGuards() ==
6561 receiverObject) {
6562 return AliasType::NoAlias;
6564 break;
6565 default:
6566 break;
6569 return MInstruction::mightAlias(store);
6572 bool MGuardFuse::congruentTo(const MDefinition* ins) const {
6573 if (!ins->isGuardFuse()) {
6574 return false;
6576 if (fuseIndex() != ins->toGuardFuse()->fuseIndex()) {
6577 return false;
6579 return congruentIfOperandsEqual(ins);
6582 AliasSet MGuardFuse::getAliasSet() const {
6583 // The alias set below reflects the set of operations which could cause a fuse
6584 // to be popped, and therefore MGuardFuse aliases with.
6585 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::DynamicSlot |
6586 AliasSet::FixedSlot |
6587 AliasSet::GlobalGenerationCounter);
6590 AliasSet MGuardMultipleShapes::getAliasSet() const {
6591 // Note: This instruction loads the elements of the ListObject used to
6592 // store the list of shapes, but that object is internal and not exposed
6593 // to script, so it doesn't have to be in the alias set.
6594 return AliasSet::Load(AliasSet::ObjectFields);
6597 AliasSet MGuardGlobalGeneration::getAliasSet() const {
6598 return AliasSet::Load(AliasSet::GlobalGenerationCounter);
6601 bool MGuardGlobalGeneration::congruentTo(const MDefinition* ins) const {
6602 return ins->isGuardGlobalGeneration() &&
6603 ins->toGuardGlobalGeneration()->expected() == expected() &&
6604 ins->toGuardGlobalGeneration()->generationAddr() == generationAddr();
6607 MDefinition* MGuardIsNotProxy::foldsTo(TempAllocator& alloc) {
6608 KnownClass known = GetObjectKnownClass(object());
6609 if (known == KnownClass::None) {
6610 return this;
6613 MOZ_ASSERT(!GetObjectKnownJSClass(object())->isProxyObject());
6614 AssertKnownClass(alloc, this, object());
6615 return object();
6618 AliasSet MMegamorphicLoadSlotByValue::getAliasSet() const {
6619 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6620 AliasSet::DynamicSlot);
6623 MDefinition* MMegamorphicLoadSlotByValue::foldsTo(TempAllocator& alloc) {
6624 MDefinition* input = idVal();
6625 if (input->isBox()) {
6626 input = input->toBox()->input();
6629 MDefinition* result = this;
6631 if (input->isConstant()) {
6632 MConstant* constant = input->toConstant();
6633 if (constant->type() == MIRType::Symbol) {
6634 PropertyKey id = PropertyKey::Symbol(constant->toSymbol());
6635 result = MMegamorphicLoadSlot::New(alloc, object(), id);
6638 if (constant->type() == MIRType::String) {
6639 JSString* str = constant->toString();
6640 if (str->isAtom() && !str->asAtom().isIndex()) {
6641 PropertyKey id = PropertyKey::NonIntAtom(str);
6642 result = MMegamorphicLoadSlot::New(alloc, object(), id);
6647 if (result != this) {
6648 result->setDependency(dependency());
6651 return result;
6654 bool MMegamorphicLoadSlot::congruentTo(const MDefinition* ins) const {
6655 if (!ins->isMegamorphicLoadSlot()) {
6656 return false;
6658 if (ins->toMegamorphicLoadSlot()->name() != name()) {
6659 return false;
6661 return congruentIfOperandsEqual(ins);
6664 AliasSet MMegamorphicLoadSlot::getAliasSet() const {
6665 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6666 AliasSet::DynamicSlot);
6669 bool MMegamorphicHasProp::congruentTo(const MDefinition* ins) const {
6670 if (!ins->isMegamorphicHasProp()) {
6671 return false;
6673 if (ins->toMegamorphicHasProp()->hasOwn() != hasOwn()) {
6674 return false;
6676 return congruentIfOperandsEqual(ins);
6679 AliasSet MMegamorphicHasProp::getAliasSet() const {
6680 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6681 AliasSet::DynamicSlot);
6684 bool MNurseryObject::congruentTo(const MDefinition* ins) const {
6685 if (!ins->isNurseryObject()) {
6686 return false;
6688 return nurseryIndex() == ins->toNurseryObject()->nurseryIndex();
6691 AliasSet MGuardFunctionIsNonBuiltinCtor::getAliasSet() const {
6692 return AliasSet::Load(AliasSet::ObjectFields);
6695 bool MGuardFunctionKind::congruentTo(const MDefinition* ins) const {
6696 if (!ins->isGuardFunctionKind()) {
6697 return false;
6699 if (expected() != ins->toGuardFunctionKind()->expected()) {
6700 return false;
6702 if (bailOnEquality() != ins->toGuardFunctionKind()->bailOnEquality()) {
6703 return false;
6705 return congruentIfOperandsEqual(ins);
6708 AliasSet MGuardFunctionKind::getAliasSet() const {
6709 return AliasSet::Load(AliasSet::ObjectFields);
6712 bool MGuardFunctionScript::congruentTo(const MDefinition* ins) const {
6713 if (!ins->isGuardFunctionScript()) {
6714 return false;
6716 if (expected() != ins->toGuardFunctionScript()->expected()) {
6717 return false;
6719 return congruentIfOperandsEqual(ins);
6722 AliasSet MGuardFunctionScript::getAliasSet() const {
6723 // A JSFunction's BaseScript pointer is immutable. Relazification of
6724 // top-level/named self-hosted functions is an exception to this, but we don't
6725 // use this guard for those self-hosted functions.
6726 // See IRGenerator::emitCalleeGuard.
6727 MOZ_ASSERT_IF(flags_.isSelfHostedOrIntrinsic(), flags_.isLambda());
6728 return AliasSet::None();
6731 bool MGuardSpecificAtom::congruentTo(const MDefinition* ins) const {
6732 if (!ins->isGuardSpecificAtom()) {
6733 return false;
6735 if (atom() != ins->toGuardSpecificAtom()->atom()) {
6736 return false;
6738 return congruentIfOperandsEqual(ins);
6741 MDefinition* MGuardStringToIndex::foldsTo(TempAllocator& alloc) {
6742 if (!string()->isConstant()) {
6743 return this;
6746 JSString* str = string()->toConstant()->toString();
6748 int32_t index = GetIndexFromString(str);
6749 if (index < 0) {
6750 return this;
6753 return MConstant::New(alloc, Int32Value(index));
6756 MDefinition* MGuardStringToInt32::foldsTo(TempAllocator& alloc) {
6757 if (!string()->isConstant()) {
6758 return this;
6761 JSLinearString* str = &string()->toConstant()->toString()->asLinear();
6762 double number = LinearStringToNumber(str);
6764 int32_t n;
6765 if (!mozilla::NumberIsInt32(number, &n)) {
6766 return this;
6769 return MConstant::New(alloc, Int32Value(n));
6772 MDefinition* MGuardStringToDouble::foldsTo(TempAllocator& alloc) {
6773 if (!string()->isConstant()) {
6774 return this;
6777 JSLinearString* str = &string()->toConstant()->toString()->asLinear();
6778 double number = LinearStringToNumber(str);
6779 return MConstant::New(alloc, DoubleValue(number));
6782 AliasSet MGuardNoDenseElements::getAliasSet() const {
6783 return AliasSet::Load(AliasSet::ObjectFields);
6786 AliasSet MIteratorHasIndices::getAliasSet() const {
6787 return AliasSet::Load(AliasSet::ObjectFields);
6790 AliasSet MAllocateAndStoreSlot::getAliasSet() const {
6791 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::DynamicSlot);
6794 AliasSet MLoadDOMExpandoValue::getAliasSet() const {
6795 return AliasSet::Load(AliasSet::DOMProxyExpando);
6798 AliasSet MLoadDOMExpandoValueIgnoreGeneration::getAliasSet() const {
6799 return AliasSet::Load(AliasSet::DOMProxyExpando);
6802 bool MGuardDOMExpandoMissingOrGuardShape::congruentTo(
6803 const MDefinition* ins) const {
6804 if (!ins->isGuardDOMExpandoMissingOrGuardShape()) {
6805 return false;
6807 if (shape() != ins->toGuardDOMExpandoMissingOrGuardShape()->shape()) {
6808 return false;
6810 return congruentIfOperandsEqual(ins);
6813 AliasSet MGuardDOMExpandoMissingOrGuardShape::getAliasSet() const {
6814 return AliasSet::Load(AliasSet::ObjectFields);
6817 MDefinition* MGuardToClass::foldsTo(TempAllocator& alloc) {
6818 const JSClass* clasp = GetObjectKnownJSClass(object());
6819 if (!clasp || getClass() != clasp) {
6820 return this;
6823 AssertKnownClass(alloc, this, object());
6824 return object();
6827 MDefinition* MGuardToFunction::foldsTo(TempAllocator& alloc) {
6828 if (GetObjectKnownClass(object()) != KnownClass::Function) {
6829 return this;
6832 AssertKnownClass(alloc, this, object());
6833 return object();
6836 MDefinition* MHasClass::foldsTo(TempAllocator& alloc) {
6837 const JSClass* clasp = GetObjectKnownJSClass(object());
6838 if (!clasp) {
6839 return this;
6842 AssertKnownClass(alloc, this, object());
6843 return MConstant::New(alloc, BooleanValue(getClass() == clasp));
6846 MDefinition* MIsCallable::foldsTo(TempAllocator& alloc) {
6847 if (input()->type() != MIRType::Object) {
6848 return this;
6851 KnownClass known = GetObjectKnownClass(input());
6852 if (known == KnownClass::None) {
6853 return this;
6856 AssertKnownClass(alloc, this, input());
6857 return MConstant::New(alloc, BooleanValue(known == KnownClass::Function));
6860 MDefinition* MIsArray::foldsTo(TempAllocator& alloc) {
6861 if (input()->type() != MIRType::Object) {
6862 return this;
6865 KnownClass known = GetObjectKnownClass(input());
6866 if (known == KnownClass::None) {
6867 return this;
6870 AssertKnownClass(alloc, this, input());
6871 return MConstant::New(alloc, BooleanValue(known == KnownClass::Array));
6874 AliasSet MObjectClassToString::getAliasSet() const {
6875 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
6876 AliasSet::DynamicSlot);
6879 MDefinition* MGuardIsNotArrayBufferMaybeShared::foldsTo(TempAllocator& alloc) {
6880 switch (GetObjectKnownClass(object())) {
6881 case KnownClass::PlainObject:
6882 case KnownClass::Array:
6883 case KnownClass::Function:
6884 case KnownClass::RegExp:
6885 case KnownClass::ArrayIterator:
6886 case KnownClass::StringIterator:
6887 case KnownClass::RegExpStringIterator: {
6888 AssertKnownClass(alloc, this, object());
6889 return object();
6891 case KnownClass::None:
6892 break;
6895 return this;
6898 MDefinition* MCheckIsObj::foldsTo(TempAllocator& alloc) {
6899 if (!input()->isBox()) {
6900 return this;
6903 MDefinition* unboxed = input()->getOperand(0);
6904 if (unboxed->type() == MIRType::Object) {
6905 return unboxed;
6908 return this;
6911 AliasSet MCheckIsObj::getAliasSet() const {
6912 return AliasSet::Store(AliasSet::ExceptionState);
6915 #ifdef JS_PUNBOX64
6916 AliasSet MCheckScriptedProxyGetResult::getAliasSet() const {
6917 return AliasSet::Store(AliasSet::ExceptionState);
6919 #endif
6921 static bool IsBoxedObject(MDefinition* def) {
6922 MOZ_ASSERT(def->type() == MIRType::Value);
6924 if (def->isBox()) {
6925 return def->toBox()->input()->type() == MIRType::Object;
6928 // Construct calls are always returning a boxed object.
6930 // TODO: We should consider encoding this directly in the graph instead of
6931 // having to special case it here.
6932 if (def->isCall()) {
6933 return def->toCall()->isConstructing();
6935 if (def->isConstructArray()) {
6936 return true;
6938 if (def->isConstructArgs()) {
6939 return true;
6942 return false;
6945 MDefinition* MCheckReturn::foldsTo(TempAllocator& alloc) {
6946 auto* returnVal = returnValue();
6947 if (!returnVal->isBox()) {
6948 return this;
6951 auto* unboxedReturnVal = returnVal->toBox()->input();
6952 if (unboxedReturnVal->type() == MIRType::Object) {
6953 return returnVal;
6956 if (unboxedReturnVal->type() != MIRType::Undefined) {
6957 return this;
6960 auto* thisVal = thisValue();
6961 if (IsBoxedObject(thisVal)) {
6962 return thisVal;
6965 return this;
6968 MDefinition* MCheckThis::foldsTo(TempAllocator& alloc) {
6969 MDefinition* input = thisValue();
6970 if (!input->isBox()) {
6971 return this;
6974 MDefinition* unboxed = input->getOperand(0);
6975 if (unboxed->mightBeMagicType()) {
6976 return this;
6979 return input;
6982 MDefinition* MCheckThisReinit::foldsTo(TempAllocator& alloc) {
6983 MDefinition* input = thisValue();
6984 if (!input->isBox()) {
6985 return this;
6988 MDefinition* unboxed = input->getOperand(0);
6989 if (unboxed->type() != MIRType::MagicUninitializedLexical) {
6990 return this;
6993 return input;
6996 MDefinition* MCheckObjCoercible::foldsTo(TempAllocator& alloc) {
6997 MDefinition* input = checkValue();
6998 if (!input->isBox()) {
6999 return this;
7002 MDefinition* unboxed = input->getOperand(0);
7003 if (unboxed->mightBeType(MIRType::Null) ||
7004 unboxed->mightBeType(MIRType::Undefined)) {
7005 return this;
7008 return input;
7011 AliasSet MCheckObjCoercible::getAliasSet() const {
7012 return AliasSet::Store(AliasSet::ExceptionState);
7015 AliasSet MCheckReturn::getAliasSet() const {
7016 return AliasSet::Store(AliasSet::ExceptionState);
7019 AliasSet MCheckThis::getAliasSet() const {
7020 return AliasSet::Store(AliasSet::ExceptionState);
7023 AliasSet MCheckThisReinit::getAliasSet() const {
7024 return AliasSet::Store(AliasSet::ExceptionState);
7027 AliasSet MIsPackedArray::getAliasSet() const {
7028 return AliasSet::Load(AliasSet::ObjectFields);
7031 AliasSet MGuardArrayIsPacked::getAliasSet() const {
7032 return AliasSet::Load(AliasSet::ObjectFields);
7035 AliasSet MSuperFunction::getAliasSet() const {
7036 return AliasSet::Load(AliasSet::ObjectFields);
7039 AliasSet MInitHomeObject::getAliasSet() const {
7040 return AliasSet::Store(AliasSet::ObjectFields);
7043 AliasSet MLoadWrapperTarget::getAliasSet() const {
7044 return AliasSet::Load(AliasSet::Any);
7047 AliasSet MGuardHasGetterSetter::getAliasSet() const {
7048 return AliasSet::Load(AliasSet::ObjectFields);
7051 bool MGuardHasGetterSetter::congruentTo(const MDefinition* ins) const {
7052 if (!ins->isGuardHasGetterSetter()) {
7053 return false;
7055 if (ins->toGuardHasGetterSetter()->propId() != propId()) {
7056 return false;
7058 if (ins->toGuardHasGetterSetter()->getterSetter() != getterSetter()) {
7059 return false;
7061 return congruentIfOperandsEqual(ins);
7064 AliasSet MGuardIsExtensible::getAliasSet() const {
7065 return AliasSet::Load(AliasSet::ObjectFields);
7068 AliasSet MGuardIndexIsNotDenseElement::getAliasSet() const {
7069 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::Element);
7072 AliasSet MGuardIndexIsValidUpdateOrAdd::getAliasSet() const {
7073 return AliasSet::Load(AliasSet::ObjectFields);
7076 AliasSet MCallObjectHasSparseElement::getAliasSet() const {
7077 return AliasSet::Load(AliasSet::Element | AliasSet::ObjectFields |
7078 AliasSet::FixedSlot | AliasSet::DynamicSlot);
7081 AliasSet MLoadSlotByIteratorIndex::getAliasSet() const {
7082 return AliasSet::Load(AliasSet::ObjectFields | AliasSet::FixedSlot |
7083 AliasSet::DynamicSlot | AliasSet::Element);
7086 AliasSet MStoreSlotByIteratorIndex::getAliasSet() const {
7087 return AliasSet::Store(AliasSet::ObjectFields | AliasSet::FixedSlot |
7088 AliasSet::DynamicSlot | AliasSet::Element);
7091 MDefinition* MGuardInt32IsNonNegative::foldsTo(TempAllocator& alloc) {
7092 MOZ_ASSERT(index()->type() == MIRType::Int32);
7094 MDefinition* input = index();
7095 if (!input->isConstant() || input->toConstant()->toInt32() < 0) {
7096 return this;
7098 return input;
7101 MDefinition* MGuardInt32Range::foldsTo(TempAllocator& alloc) {
7102 MOZ_ASSERT(input()->type() == MIRType::Int32);
7103 MOZ_ASSERT(minimum() <= maximum());
7105 MDefinition* in = input();
7106 if (!in->isConstant()) {
7107 return this;
7109 int32_t cst = in->toConstant()->toInt32();
7110 if (cst < minimum() || cst > maximum()) {
7111 return this;
7113 return in;
7116 MDefinition* MGuardNonGCThing::foldsTo(TempAllocator& alloc) {
7117 if (!input()->isBox()) {
7118 return this;
7121 MDefinition* unboxed = input()->getOperand(0);
7122 if (!IsNonGCThing(unboxed->type())) {
7123 return this;
7125 return input();
7128 AliasSet MSetObjectHasNonBigInt::getAliasSet() const {
7129 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7132 AliasSet MSetObjectHasBigInt::getAliasSet() const {
7133 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7136 AliasSet MSetObjectHasValue::getAliasSet() const {
7137 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7140 AliasSet MSetObjectHasValueVMCall::getAliasSet() const {
7141 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7144 AliasSet MSetObjectSize::getAliasSet() const {
7145 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7148 AliasSet MMapObjectHasNonBigInt::getAliasSet() const {
7149 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7152 AliasSet MMapObjectHasBigInt::getAliasSet() const {
7153 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7156 AliasSet MMapObjectHasValue::getAliasSet() const {
7157 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7160 AliasSet MMapObjectHasValueVMCall::getAliasSet() const {
7161 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7164 AliasSet MMapObjectGetNonBigInt::getAliasSet() const {
7165 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7168 AliasSet MMapObjectGetBigInt::getAliasSet() const {
7169 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7172 AliasSet MMapObjectGetValue::getAliasSet() const {
7173 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7176 AliasSet MMapObjectGetValueVMCall::getAliasSet() const {
7177 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7180 AliasSet MMapObjectSize::getAliasSet() const {
7181 return AliasSet::Load(AliasSet::MapOrSetHashTable);
7184 MIonToWasmCall* MIonToWasmCall::New(TempAllocator& alloc,
7185 WasmInstanceObject* instanceObj,
7186 const wasm::FuncExport& funcExport) {
7187 const wasm::FuncType& funcType =
7188 instanceObj->instance().metadata().getFuncExportType(funcExport);
7189 const wasm::ValTypeVector& results = funcType.results();
7190 MIRType resultType = MIRType::Value;
7191 // At the JS boundary some wasm types must be represented as a Value, and in
7192 // addition a void return requires an Undefined value.
7193 if (results.length() > 0 && !results[0].isEncodedAsJSValueOnEscape()) {
7194 MOZ_ASSERT(results.length() == 1,
7195 "multiple returns not implemented for inlined Wasm calls");
7196 resultType = results[0].toMIRType();
7199 auto* ins = new (alloc) MIonToWasmCall(instanceObj, resultType, funcExport);
7200 if (!ins->init(alloc, funcType.args().length())) {
7201 return nullptr;
7203 return ins;
7206 MBindFunction* MBindFunction::New(TempAllocator& alloc, MDefinition* target,
7207 uint32_t argc, JSObject* templateObj) {
7208 auto* ins = new (alloc) MBindFunction(templateObj);
7209 if (!ins->init(alloc, NumNonArgumentOperands + argc)) {
7210 return nullptr;
7212 ins->initOperand(0, target);
7213 return ins;
7216 #ifdef DEBUG
7217 bool MIonToWasmCall::isConsistentFloat32Use(MUse* use) const {
7218 const wasm::FuncType& funcType =
7219 instance()->metadata().getFuncExportType(funcExport_);
7220 return funcType.args()[use->index()].kind() == wasm::ValType::F32;
7222 #endif
7224 MCreateInlinedArgumentsObject* MCreateInlinedArgumentsObject::New(
7225 TempAllocator& alloc, MDefinition* callObj, MDefinition* callee,
7226 MDefinitionVector& args, ArgumentsObject* templateObj) {
7227 MCreateInlinedArgumentsObject* ins =
7228 new (alloc) MCreateInlinedArgumentsObject(templateObj);
7230 uint32_t argc = args.length();
7231 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs);
7233 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7234 return nullptr;
7237 ins->initOperand(0, callObj);
7238 ins->initOperand(1, callee);
7239 for (uint32_t i = 0; i < argc; i++) {
7240 ins->initOperand(i + NumNonArgumentOperands, args[i]);
7243 return ins;
7246 MGetInlinedArgument* MGetInlinedArgument::New(
7247 TempAllocator& alloc, MDefinition* index,
7248 MCreateInlinedArgumentsObject* args) {
7249 MGetInlinedArgument* ins = new (alloc) MGetInlinedArgument();
7251 uint32_t argc = args->numActuals();
7252 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs);
7254 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7255 return nullptr;
7258 ins->initOperand(0, index);
7259 for (uint32_t i = 0; i < argc; i++) {
7260 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7263 return ins;
7266 MGetInlinedArgument* MGetInlinedArgument::New(TempAllocator& alloc,
7267 MDefinition* index,
7268 const CallInfo& callInfo) {
7269 MGetInlinedArgument* ins = new (alloc) MGetInlinedArgument();
7271 uint32_t argc = callInfo.argc();
7272 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs);
7274 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7275 return nullptr;
7278 ins->initOperand(0, index);
7279 for (uint32_t i = 0; i < argc; i++) {
7280 ins->initOperand(i + NumNonArgumentOperands, callInfo.getArg(i));
7283 return ins;
7286 MDefinition* MGetInlinedArgument::foldsTo(TempAllocator& alloc) {
7287 MDefinition* indexDef = SkipUninterestingInstructions(index());
7288 if (!indexDef->isConstant() || indexDef->type() != MIRType::Int32) {
7289 return this;
7292 int32_t indexConst = indexDef->toConstant()->toInt32();
7293 if (indexConst < 0 || uint32_t(indexConst) >= numActuals()) {
7294 return this;
7297 MDefinition* arg = getArg(indexConst);
7298 if (arg->type() != MIRType::Value) {
7299 arg = MBox::New(alloc, arg);
7302 return arg;
7305 MGetInlinedArgumentHole* MGetInlinedArgumentHole::New(
7306 TempAllocator& alloc, MDefinition* index,
7307 MCreateInlinedArgumentsObject* args) {
7308 auto* ins = new (alloc) MGetInlinedArgumentHole();
7310 uint32_t argc = args->numActuals();
7311 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs);
7313 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7314 return nullptr;
7317 ins->initOperand(0, index);
7318 for (uint32_t i = 0; i < argc; i++) {
7319 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7322 return ins;
7325 MDefinition* MGetInlinedArgumentHole::foldsTo(TempAllocator& alloc) {
7326 MDefinition* indexDef = SkipUninterestingInstructions(index());
7327 if (!indexDef->isConstant() || indexDef->type() != MIRType::Int32) {
7328 return this;
7331 int32_t indexConst = indexDef->toConstant()->toInt32();
7332 if (indexConst < 0) {
7333 return this;
7336 MDefinition* arg;
7337 if (uint32_t(indexConst) < numActuals()) {
7338 arg = getArg(indexConst);
7340 if (arg->type() != MIRType::Value) {
7341 arg = MBox::New(alloc, arg);
7343 } else {
7344 auto* undefined = MConstant::New(alloc, UndefinedValue());
7345 block()->insertBefore(this, undefined);
7347 arg = MBox::New(alloc, undefined);
7350 return arg;
7353 MInlineArgumentsSlice* MInlineArgumentsSlice::New(
7354 TempAllocator& alloc, MDefinition* begin, MDefinition* count,
7355 MCreateInlinedArgumentsObject* args, JSObject* templateObj,
7356 gc::Heap initialHeap) {
7357 auto* ins = new (alloc) MInlineArgumentsSlice(templateObj, initialHeap);
7359 uint32_t argc = args->numActuals();
7360 MOZ_ASSERT(argc <= ArgumentsObject::MaxInlinedArgs);
7362 if (!ins->init(alloc, argc + NumNonArgumentOperands)) {
7363 return nullptr;
7366 ins->initOperand(0, begin);
7367 ins->initOperand(1, count);
7368 for (uint32_t i = 0; i < argc; i++) {
7369 ins->initOperand(i + NumNonArgumentOperands, args->getArg(i));
7372 return ins;
7375 MDefinition* MArrayLength::foldsTo(TempAllocator& alloc) {
7376 // Object.keys() is potentially effectful, in case of Proxies. Otherwise, when
7377 // it is only computed for its length property, there is no need to
7378 // materialize the Array which results from it and it can be marked as
7379 // recovered on bailout as long as no properties are added to / removed from
7380 // the object.
7381 MDefinition* elems = elements();
7382 if (!elems->isElements()) {
7383 return this;
7386 MDefinition* guardshape = elems->toElements()->object();
7387 if (!guardshape->isGuardShape()) {
7388 return this;
7391 // The Guard shape is guarding the shape of the object returned by
7392 // Object.keys, this guard can be removed as knowing the function is good
7393 // enough to infer that we are returning an array.
7394 MDefinition* keys = guardshape->toGuardShape()->object();
7395 if (!keys->isObjectKeys()) {
7396 return this;
7399 // Object.keys() inline cache guards against proxies when creating the IC. We
7400 // rely on this here as we are looking to elide `Object.keys(...)` call, which
7401 // is only possible if we know for sure that no side-effect might have
7402 // happened.
7403 MDefinition* noproxy = keys->toObjectKeys()->object();
7404 if (!noproxy->isGuardIsNotProxy()) {
7405 // The guard might have been replaced by an assertion, in case the class is
7406 // known at compile time. IF the guard has been removed check whether check
7407 // has been removed.
7408 MOZ_RELEASE_ASSERT(GetObjectKnownClass(noproxy) != KnownClass::None);
7409 MOZ_RELEASE_ASSERT(!GetObjectKnownJSClass(noproxy)->isProxyObject());
7412 // Check if both the elements and the Object.keys() have a single use. We only
7413 // check for live uses, and are ok if a branch which was previously using the
7414 // keys array has been removed since.
7415 if (!elems->hasOneLiveDefUse() || !guardshape->hasOneLiveDefUse() ||
7416 !keys->hasOneLiveDefUse()) {
7417 return this;
7420 // Check that the latest active resume point is the one from Object.keys(), in
7421 // order to steal it. If this is not the latest active resume point then some
7422 // side-effect might happen which updates the content of the object, making
7423 // any recovery of the keys exhibit a different behavior than expected.
7424 if (keys->toObjectKeys()->resumePoint() != block()->activeResumePoint(this)) {
7425 return this;
7428 // Verify whether any resume point captures the keys array after any aliasing
7429 // mutations. If this were to be the case the recovery of ObjectKeys on
7430 // bailout might compute a version which might not match with the elided
7431 // result.
7433 // Iterate over the resume point uses of ObjectKeys, and check whether the
7434 // instructions they are attached to are aliasing Object fields. If so, skip
7435 // this optimization.
7436 AliasSet enumKeysAliasSet = AliasSet::Load(AliasSet::Flag::ObjectFields);
7437 for (auto* use : UsesIterator(keys)) {
7438 if (!use->consumer()->isResumePoint()) {
7439 // There is only a single use, and this is the length computation as
7440 // asserted with `hasOneLiveDefUse`.
7441 continue;
7444 MResumePoint* rp = use->consumer()->toResumePoint();
7445 if (!rp->instruction()) {
7446 // If there is no instruction, this is a resume point which is attached to
7447 // the entry of a block. Thus no risk of mutating the object on which the
7448 // keys are queried.
7449 continue;
7452 MInstruction* ins = rp->instruction();
7453 if (ins == keys) {
7454 continue;
7457 // Check whether the instruction can potentially alias the object fields of
7458 // the object from which we are querying the keys.
7459 AliasSet mightAlias = ins->getAliasSet() & enumKeysAliasSet;
7460 if (!mightAlias.isNone()) {
7461 return this;
7465 // Flag every instructions since Object.keys(..) as recovered on bailout, and
7466 // make Object.keys(..) be the recovered value in-place of the shape guard.
7467 setRecoveredOnBailout();
7468 elems->setRecoveredOnBailout();
7469 guardshape->replaceAllUsesWith(keys);
7470 guardshape->block()->discard(guardshape->toGuardShape());
7471 keys->setRecoveredOnBailout();
7473 // Steal the resume point from Object.keys, which is ok as we confirmed that
7474 // there is no other resume point in-between.
7475 MObjectKeysLength* keysLength = MObjectKeysLength::New(alloc, noproxy);
7476 keysLength->stealResumePoint(keys->toObjectKeys());
7478 return keysLength;
7481 MDefinition* MNormalizeSliceTerm::foldsTo(TempAllocator& alloc) {
7482 auto* length = this->length();
7483 if (!length->isConstant() && !length->isArgumentsLength()) {
7484 return this;
7487 if (length->isConstant()) {
7488 int32_t lengthConst = length->toConstant()->toInt32();
7489 MOZ_ASSERT(lengthConst >= 0);
7491 // Result is always zero when |length| is zero.
7492 if (lengthConst == 0) {
7493 return length;
7496 auto* value = this->value();
7497 if (value->isConstant()) {
7498 int32_t valueConst = value->toConstant()->toInt32();
7500 int32_t normalized;
7501 if (valueConst < 0) {
7502 normalized = std::max(valueConst + lengthConst, 0);
7503 } else {
7504 normalized = std::min(valueConst, lengthConst);
7507 if (normalized == valueConst) {
7508 return value;
7510 if (normalized == lengthConst) {
7511 return length;
7513 return MConstant::New(alloc, Int32Value(normalized));
7516 return this;
7519 auto* value = this->value();
7520 if (value->isConstant()) {
7521 int32_t valueConst = value->toConstant()->toInt32();
7523 // Minimum of |value| and |length|.
7524 if (valueConst > 0) {
7525 bool isMax = false;
7526 return MMinMax::New(alloc, value, length, MIRType::Int32, isMax);
7529 // Maximum of |value + length| and zero.
7530 if (valueConst < 0) {
7531 // Safe to truncate because |length| is never negative.
7532 auto* add = MAdd::New(alloc, value, length, TruncateKind::Truncate);
7533 block()->insertBefore(this, add);
7535 auto* zero = MConstant::New(alloc, Int32Value(0));
7536 block()->insertBefore(this, zero);
7538 bool isMax = true;
7539 return MMinMax::New(alloc, add, zero, MIRType::Int32, isMax);
7542 // Directly return the value when it's zero.
7543 return value;
7546 // Normalizing MArgumentsLength is a no-op.
7547 if (value->isArgumentsLength()) {
7548 return value;
7551 return this;
7554 bool MInt32ToStringWithBase::congruentTo(const MDefinition* ins) const {
7555 if (!ins->isInt32ToStringWithBase()) {
7556 return false;
7558 if (ins->toInt32ToStringWithBase()->lowerCase() != lowerCase()) {
7559 return false;
7561 return congruentIfOperandsEqual(ins);
7564 bool MWasmShiftSimd128::congruentTo(const MDefinition* ins) const {
7565 if (!ins->isWasmShiftSimd128()) {
7566 return false;
7568 return ins->toWasmShiftSimd128()->simdOp() == simdOp_ &&
7569 congruentIfOperandsEqual(ins);
7572 bool MWasmShuffleSimd128::congruentTo(const MDefinition* ins) const {
7573 if (!ins->isWasmShuffleSimd128()) {
7574 return false;
7576 return ins->toWasmShuffleSimd128()->shuffle().equals(&shuffle_) &&
7577 congruentIfOperandsEqual(ins);
7580 bool MWasmUnarySimd128::congruentTo(const MDefinition* ins) const {
7581 if (!ins->isWasmUnarySimd128()) {
7582 return false;
7584 return ins->toWasmUnarySimd128()->simdOp() == simdOp_ &&
7585 congruentIfOperandsEqual(ins);
7588 #ifdef ENABLE_WASM_SIMD
7589 MWasmShuffleSimd128* jit::BuildWasmShuffleSimd128(TempAllocator& alloc,
7590 const int8_t* control,
7591 MDefinition* lhs,
7592 MDefinition* rhs) {
7593 SimdShuffle s =
7594 AnalyzeSimdShuffle(SimdConstant::CreateX16(control), lhs, rhs);
7595 switch (s.opd) {
7596 case SimdShuffle::Operand::LEFT:
7597 // When SimdShuffle::Operand is LEFT the right operand is not used,
7598 // lose reference to rhs.
7599 rhs = lhs;
7600 break;
7601 case SimdShuffle::Operand::RIGHT:
7602 // When SimdShuffle::Operand is RIGHT the left operand is not used,
7603 // lose reference to lhs.
7604 lhs = rhs;
7605 break;
7606 default:
7607 break;
7609 return MWasmShuffleSimd128::New(alloc, lhs, rhs, s);
7611 #endif // ENABLE_WASM_SIMD
7613 static MDefinition* FoldTrivialWasmCasts(TempAllocator& alloc,
7614 wasm::RefType sourceType,
7615 wasm::RefType destType) {
7616 // Upcasts are trivially valid.
7617 if (wasm::RefType::isSubTypeOf(sourceType, destType)) {
7618 return MConstant::New(alloc, Int32Value(1), MIRType::Int32);
7621 // If two types are completely disjoint, then all casts between them are
7622 // impossible.
7623 if (!wasm::RefType::castPossible(destType, sourceType)) {
7624 return MConstant::New(alloc, Int32Value(0), MIRType::Int32);
7627 return nullptr;
7630 MDefinition* MWasmRefIsSubtypeOfAbstract::foldsTo(TempAllocator& alloc) {
7631 MDefinition* folded = FoldTrivialWasmCasts(alloc, sourceType(), destType());
7632 if (folded) {
7633 return folded;
7635 return this;
7638 MDefinition* MWasmRefIsSubtypeOfConcrete::foldsTo(TempAllocator& alloc) {
7639 MDefinition* folded = FoldTrivialWasmCasts(alloc, sourceType(), destType());
7640 if (folded) {
7641 return folded;
7643 return this;