1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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/. */
6 
7 #include "jit/MIR.h"
8 
9 #include "mozilla/FloatingPoint.h"
10 #include "mozilla/IntegerPrintfMacros.h"
11 #include "mozilla/MathAlgorithms.h"
12 #include "mozilla/SizePrintfMacros.h"
13 
14 #include <ctype.h>
15 
16 #include "jslibmath.h"
17 #include "jsstr.h"
18 
19 #include "jit/AtomicOperations.h"
20 #include "jit/BaselineInspector.h"
21 #include "jit/IonBuilder.h"
22 #include "jit/JitSpewer.h"
23 #include "jit/MIRGraph.h"
24 #include "jit/RangeAnalysis.h"
25 #include "js/Conversions.h"
26 
27 #include "jsatominlines.h"
28 #include "jsobjinlines.h"
29 #include "jsscriptinlines.h"
30 
31 using namespace js;
32 using namespace js::jit;
33 
34 using JS::ToInt32;
35 
36 using mozilla::NumbersAreIdentical;
37 using mozilla::IsFloat32Representable;
38 using mozilla::IsNaN;
39 using mozilla::Maybe;
40 using mozilla::DebugOnly;
41 
42 #ifdef DEBUG
index() const43 size_t MUse::index() const
44 {
45     return consumer()->indexOf(this);
46 }
47 #endif
48 
49 template<size_t Op> static void
ConvertDefinitionToDouble(TempAllocator & alloc,MDefinition * def,MInstruction * consumer)50 ConvertDefinitionToDouble(TempAllocator& alloc, MDefinition* def, MInstruction* consumer)
51 {
52     MInstruction* replace = MToDouble::New(alloc, def);
53     consumer->replaceOperand(Op, replace);
54     consumer->block()->insertBefore(consumer, replace);
55 }
56 
57 static bool
CheckUsesAreFloat32Consumers(const MInstruction * ins)58 CheckUsesAreFloat32Consumers(const MInstruction* ins)
59 {
60     bool allConsumerUses = true;
61     for (MUseDefIterator use(ins); allConsumerUses && use; use++)
62         allConsumerUses &= use.def()->canConsumeFloat32(use.use());
63     return allConsumerUses;
64 }
65 
66 void
PrintOpcodeName(GenericPrinter & out,MDefinition::Opcode op)67 MDefinition::PrintOpcodeName(GenericPrinter& out, MDefinition::Opcode op)
68 {
69     static const char * const names[] =
70     {
71 #define NAME(x) #x,
72         MIR_OPCODE_LIST(NAME)
73 #undef NAME
74     };
75     const char* name = names[op];
76     size_t len = strlen(name);
77     for (size_t i = 0; i < len; i++)
78         out.printf("%c", tolower(name[i]));
79 }
80 
81 const Value&
constantValue()82 MDefinition::constantValue()
83 {
84     MOZ_ASSERT(isConstantValue());
85 
86     if (isBox())
87         return getOperand(0)->constantValue();
88     return toConstant()->value();
89 }
90 
91 const Value*
constantVp()92 MDefinition::constantVp()
93 {
94     MOZ_ASSERT(isConstantValue());
95     if (isBox())
96         return getOperand(0)->constantVp();
97     return toConstant()->vp();
98 }
99 
100 bool
constantToBoolean()101 MDefinition::constantToBoolean()
102 {
103     MOZ_ASSERT(isConstantValue());
104     if (isBox())
105         return getOperand(0)->constantToBoolean();
106     return toConstant()->valueToBoolean();
107 }
108 
109 static MConstant*
EvaluateConstantOperands(TempAllocator & alloc,MBinaryInstruction * ins,bool * ptypeChange=nullptr)110 EvaluateConstantOperands(TempAllocator& alloc, MBinaryInstruction* ins, bool* ptypeChange = nullptr)
111 {
112     MDefinition* left = ins->getOperand(0);
113     MDefinition* right = ins->getOperand(1);
114 
115     MOZ_ASSERT(IsNumberType(left->type()) && IsNumberType(right->type()));
116 
117     if (!left->isConstantValue() || !right->isConstantValue())
118         return nullptr;
119 
120     Value lhs = left->constantValue();
121     Value rhs = right->constantValue();
122     Value ret = UndefinedValue();
123 
124     switch (ins->op()) {
125       case MDefinition::Op_BitAnd:
126         ret = Int32Value(lhs.toInt32() & rhs.toInt32());
127         break;
128       case MDefinition::Op_BitOr:
129         ret = Int32Value(lhs.toInt32() | rhs.toInt32());
130         break;
131       case MDefinition::Op_BitXor:
132         ret = Int32Value(lhs.toInt32() ^ rhs.toInt32());
133         break;
134       case MDefinition::Op_Lsh:
135         ret = Int32Value(uint32_t(lhs.toInt32()) << (rhs.toInt32() & 0x1F));
136         break;
137       case MDefinition::Op_Rsh:
138         ret = Int32Value(lhs.toInt32() >> (rhs.toInt32() & 0x1F));
139         break;
140       case MDefinition::Op_Ursh:
141         ret.setNumber(uint32_t(lhs.toInt32()) >> (rhs.toInt32() & 0x1F));
142         break;
143       case MDefinition::Op_Add:
144         ret.setNumber(lhs.toNumber() + rhs.toNumber());
145         break;
146       case MDefinition::Op_Sub:
147         ret.setNumber(lhs.toNumber() - rhs.toNumber());
148         break;
149       case MDefinition::Op_Mul:
150         ret.setNumber(lhs.toNumber() * rhs.toNumber());
151         break;
152       case MDefinition::Op_Div:
153         if (ins->toDiv()->isUnsigned())
154             ret.setInt32(rhs.isInt32(0) ? 0 : uint32_t(lhs.toInt32()) / uint32_t(rhs.toInt32()));
155         else
156             ret.setNumber(NumberDiv(lhs.toNumber(), rhs.toNumber()));
157         break;
158       case MDefinition::Op_Mod:
159         if (ins->toMod()->isUnsigned())
160             ret.setInt32(rhs.isInt32(0) ? 0 : uint32_t(lhs.toInt32()) % uint32_t(rhs.toInt32()));
161         else
162             ret.setNumber(NumberMod(lhs.toNumber(), rhs.toNumber()));
163         break;
164       default:
165         MOZ_CRASH("NYI");
166     }
167 
168     // setNumber eagerly transforms a number to int32.
169     // Transform back to double, if the output type is double.
170     if (ins->type() == MIRType_Double && ret.isInt32())
171         ret.setDouble(ret.toNumber());
172 
173     if (ins->type() != MIRTypeFromValue(ret)) {
174         if (ptypeChange)
175             *ptypeChange = true;
176         return nullptr;
177     }
178 
179     return MConstant::New(alloc, ret);
180 }
181 
182 static MMul*
EvaluateExactReciprocal(TempAllocator & alloc,MDiv * ins)183 EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins)
184 {
185     // we should fold only when it is a floating point operation
186     if (!IsFloatingPointType(ins->type()))
187         return nullptr;
188 
189     MDefinition* left = ins->getOperand(0);
190     MDefinition* right = ins->getOperand(1);
191 
192     if (!right->isConstantValue())
193         return nullptr;
194 
195     Value rhs = right->constantValue();
196 
197     int32_t num;
198     if (!mozilla::NumberIsInt32(rhs.toNumber(), &num))
199         return nullptr;
200 
201     // check if rhs is a power of two
202     if (mozilla::Abs(num) & (mozilla::Abs(num) - 1))
203         return nullptr;
204 
205     Value ret;
206     ret.setDouble(1.0 / (double) num);
207     MConstant* foldedRhs = MConstant::New(alloc, ret);
208     foldedRhs->setResultType(ins->type());
209     ins->block()->insertBefore(ins, foldedRhs);
210 
211     MMul* mul = MMul::New(alloc, left, foldedRhs, ins->type());
212     mul->setCommutative();
213     return mul;
214 }
215 
216 void
printName(GenericPrinter & out) const217 MDefinition::printName(GenericPrinter& out) const
218 {
219     PrintOpcodeName(out, op());
220     out.printf("%u", id());
221 }
222 
223 HashNumber
addU32ToHash(HashNumber hash,uint32_t data)224 MDefinition::addU32ToHash(HashNumber hash, uint32_t data)
225 {
226     return data + (hash << 6) + (hash << 16) - hash;
227 }
228 
229 HashNumber
valueHash() const230 MDefinition::valueHash() const
231 {
232     HashNumber out = op();
233     for (size_t i = 0, e = numOperands(); i < e; i++)
234         out = addU32ToHash(out, getOperand(i)->id());
235     if (MInstruction* dep = dependency())
236         out = addU32ToHash(out, dep->id());
237     return out;
238 }
239 
240 bool
congruentIfOperandsEqual(const MDefinition * ins) const241 MDefinition::congruentIfOperandsEqual(const MDefinition* ins) const
242 {
243     if (op() != ins->op())
244         return false;
245 
246     if (type() != ins->type())
247         return false;
248 
249     if (isEffectful() || ins->isEffectful())
250         return false;
251 
252     if (numOperands() != ins->numOperands())
253         return false;
254 
255     for (size_t i = 0, e = numOperands(); i < e; i++) {
256         if (getOperand(i) != ins->getOperand(i))
257             return false;
258     }
259 
260     return true;
261 }
262 
263 MDefinition*
foldsTo(TempAllocator & alloc)264 MDefinition::foldsTo(TempAllocator& alloc)
265 {
266     // In the default case, there are no constants to fold.
267     return this;
268 }
269 
270 bool
mightBeMagicType() const271 MDefinition::mightBeMagicType() const
272 {
273     if (IsMagicType(type()))
274         return true;
275 
276     if (MIRType_Value != type())
277         return false;
278 
279     return !resultTypeSet() || resultTypeSet()->hasType(TypeSet::MagicArgType());
280 }
281 
282 MDefinition*
foldsToStoredValue(TempAllocator & alloc,MDefinition * loaded)283 MInstruction::foldsToStoredValue(TempAllocator& alloc, MDefinition* loaded)
284 {
285     // If the type are matching then we return the value which is used as
286     // argument of the store.
287     if (loaded->type() != type()) {
288         // If we expect to read a type which is more generic than the type seen
289         // by the store, then we box the value used by the store.
290         if (type() != MIRType_Value)
291             return this;
292 
293         MOZ_ASSERT(loaded->type() < MIRType_Value);
294         MBox* box = MBox::New(alloc, loaded);
295         loaded = box;
296     }
297 
298     return loaded;
299 }
300 
301 void
analyzeEdgeCasesForward()302 MDefinition::analyzeEdgeCasesForward()
303 {
304 }
305 
306 void
analyzeEdgeCasesBackward()307 MDefinition::analyzeEdgeCasesBackward()
308 {
309 }
310 
311 void
setResumePoint(MResumePoint * resumePoint)312 MInstruction::setResumePoint(MResumePoint* resumePoint)
313 {
314     MOZ_ASSERT(!resumePoint_);
315     resumePoint_ = resumePoint;
316     resumePoint_->setInstruction(this);
317 }
318 
319 void
stealResumePoint(MInstruction * ins)320 MInstruction::stealResumePoint(MInstruction* ins)
321 {
322     MOZ_ASSERT(ins->resumePoint_->instruction() == ins);
323     resumePoint_ = ins->resumePoint_;
324     ins->resumePoint_ = nullptr;
325     resumePoint_->replaceInstruction(this);
326 }
327 
328 void
moveResumePointAsEntry()329 MInstruction::moveResumePointAsEntry()
330 {
331     MOZ_ASSERT(isNop());
332     block()->clearEntryResumePoint();
333     block()->setEntryResumePoint(resumePoint_);
334     resumePoint_->resetInstruction();
335     resumePoint_ = nullptr;
336 }
337 
338 void
clearResumePoint()339 MInstruction::clearResumePoint()
340 {
341     resumePoint_->resetInstruction();
342     block()->discardPreAllocatedResumePoint(resumePoint_);
343     resumePoint_ = nullptr;
344 }
345 
346 bool
maybeEmulatesUndefined(CompilerConstraintList * constraints)347 MDefinition::maybeEmulatesUndefined(CompilerConstraintList* constraints)
348 {
349     if (!mightBeType(MIRType_Object))
350         return false;
351 
352     TemporaryTypeSet* types = resultTypeSet();
353     if (!types)
354         return true;
355 
356     return types->maybeEmulatesUndefined(constraints);
357 }
358 
359 static bool
MaybeCallable(CompilerConstraintList * constraints,MDefinition * op)360 MaybeCallable(CompilerConstraintList* constraints, MDefinition* op)
361 {
362     if (!op->mightBeType(MIRType_Object))
363         return false;
364 
365     TemporaryTypeSet* types = op->resultTypeSet();
366     if (!types)
367         return true;
368 
369     return types->maybeCallable(constraints);
370 }
371 
372 MTest*
New(TempAllocator & alloc,MDefinition * ins,MBasicBlock * ifTrue,MBasicBlock * ifFalse)373 MTest::New(TempAllocator& alloc, MDefinition* ins, MBasicBlock* ifTrue, MBasicBlock* ifFalse)
374 {
375     return new(alloc) MTest(ins, ifTrue, ifFalse);
376 }
377 
378 void
cacheOperandMightEmulateUndefined(CompilerConstraintList * constraints)379 MTest::cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints)
380 {
381     MOZ_ASSERT(operandMightEmulateUndefined());
382 
383     if (!getOperand(0)->maybeEmulatesUndefined(constraints))
384         markNoOperandEmulatesUndefined();
385 }
386 
387 MDefinition*
foldsTo(TempAllocator & alloc)388 MTest::foldsTo(TempAllocator& alloc)
389 {
390     MDefinition* op = getOperand(0);
391 
392     if (op->isNot()) {
393         // If the operand of the Not is itself a Not, they cancel out.
394         MDefinition* opop = op->getOperand(0);
395         if (opop->isNot())
396             return MTest::New(alloc, opop->toNot()->input(), ifTrue(), ifFalse());
397         return MTest::New(alloc, op->toNot()->input(), ifFalse(), ifTrue());
398     }
399 
400     if (op->isConstantValue() && !op->constantValue().isMagic())
401         return MGoto::New(alloc, op->constantToBoolean() ? ifTrue() : ifFalse());
402 
403     switch (op->type()) {
404       case MIRType_Undefined:
405       case MIRType_Null:
406         return MGoto::New(alloc, ifFalse());
407       case MIRType_Symbol:
408         return MGoto::New(alloc, ifTrue());
409       case MIRType_Object:
410         if (!operandMightEmulateUndefined())
411             return MGoto::New(alloc, ifTrue());
412         break;
413       default:
414         break;
415     }
416 
417     return this;
418 }
419 
420 void
filtersUndefinedOrNull(bool trueBranch,MDefinition ** subject,bool * filtersUndefined,bool * filtersNull)421 MTest::filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
422                               bool* filtersNull)
423 {
424     MDefinition* ins = getOperand(0);
425     if (ins->isCompare()) {
426         ins->toCompare()->filtersUndefinedOrNull(trueBranch, subject, filtersUndefined, filtersNull);
427         return;
428     }
429 
430     if (!trueBranch && ins->isNot()) {
431         *subject = ins->getOperand(0);
432         *filtersUndefined = *filtersNull = true;
433         return;
434     }
435 
436     if (trueBranch) {
437         *subject = ins;
438         *filtersUndefined = *filtersNull = true;
439         return;
440     }
441 
442     *filtersUndefined = *filtersNull = false;
443     *subject = nullptr;
444 }
445 
446 void
printOpcode(GenericPrinter & out) const447 MDefinition::printOpcode(GenericPrinter& out) const
448 {
449     PrintOpcodeName(out, op());
450     for (size_t j = 0, e = numOperands(); j < e; j++) {
451         out.printf(" ");
452         if (getUseFor(j)->hasProducer())
453             getOperand(j)->printName(out);
454         else
455             out.printf("(null)");
456     }
457 }
458 
459 void
dump(GenericPrinter & out) const460 MDefinition::dump(GenericPrinter& out) const
461 {
462     printName(out);
463     out.printf(" = ");
464     printOpcode(out);
465     out.printf("\n");
466 
467     if (isInstruction()) {
468         if (MResumePoint* resume = toInstruction()->resumePoint())
469             resume->dump(out);
470     }
471 }
472 
473 void
dump() const474 MDefinition::dump() const
475 {
476     Fprinter out(stderr);
477     dump(out);
478     out.finish();
479 }
480 
481 void
dumpLocation(GenericPrinter & out) const482 MDefinition::dumpLocation(GenericPrinter& out) const
483 {
484     MResumePoint* rp = nullptr;
485     const char* linkWord = nullptr;
486     if (isInstruction() && toInstruction()->resumePoint()) {
487         rp = toInstruction()->resumePoint();
488         linkWord = "at";
489     } else {
490         rp = block()->entryResumePoint();
491         linkWord = "after";
492     }
493 
494     while (rp) {
495         JSScript* script = rp->block()->info().script();
496         uint32_t lineno = PCToLineNumber(rp->block()->info().script(), rp->pc());
497         out.printf("  %s %s:%d\n", linkWord, script->filename(), lineno);
498         rp = rp->caller();
499         linkWord = "in";
500     }
501 }
502 
503 void
dumpLocation() const504 MDefinition::dumpLocation() const
505 {
506     Fprinter out(stderr);
507     dumpLocation(out);
508     out.finish();
509 }
510 
511 #if defined(DEBUG) || defined(JS_JITSPEW)
512 size_t
useCount() const513 MDefinition::useCount() const
514 {
515     size_t count = 0;
516     for (MUseIterator i(uses_.begin()); i != uses_.end(); i++)
517         count++;
518     return count;
519 }
520 
521 size_t
defUseCount() const522 MDefinition::defUseCount() const
523 {
524     size_t count = 0;
525     for (MUseIterator i(uses_.begin()); i != uses_.end(); i++)
526         if ((*i)->consumer()->isDefinition())
527             count++;
528     return count;
529 }
530 #endif
531 
532 bool
hasOneUse() const533 MDefinition::hasOneUse() const
534 {
535     MUseIterator i(uses_.begin());
536     if (i == uses_.end())
537         return false;
538     i++;
539     return i == uses_.end();
540 }
541 
542 bool
hasOneDefUse() const543 MDefinition::hasOneDefUse() const
544 {
545     bool hasOneDefUse = false;
546     for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
547         if (!(*i)->consumer()->isDefinition())
548             continue;
549 
550         // We already have a definition use. So 1+
551         if (hasOneDefUse)
552             return false;
553 
554         // We saw one definition. Loop to test if there is another.
555         hasOneDefUse = true;
556     }
557 
558     return hasOneDefUse;
559 }
560 
561 bool
hasDefUses() const562 MDefinition::hasDefUses() const
563 {
564     for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
565         if ((*i)->consumer()->isDefinition())
566             return true;
567     }
568 
569     return false;
570 }
571 
572 bool
hasLiveDefUses() const573 MDefinition::hasLiveDefUses() const
574 {
575     for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
576         MNode* ins = (*i)->consumer();
577         if (ins->isDefinition()) {
578             if (!ins->toDefinition()->isRecoveredOnBailout())
579                 return true;
580         } else {
581             MOZ_ASSERT(ins->isResumePoint());
582             if (!ins->toResumePoint()->isRecoverableOperand(*i))
583                 return true;
584         }
585     }
586 
587     return false;
588 }
589 
590 void
replaceAllUsesWith(MDefinition * dom)591 MDefinition::replaceAllUsesWith(MDefinition* dom)
592 {
593     for (size_t i = 0, e = numOperands(); i < e; ++i)
594         getOperand(i)->setUseRemovedUnchecked();
595 
596     justReplaceAllUsesWith(dom);
597 }
598 
599 void
justReplaceAllUsesWith(MDefinition * dom)600 MDefinition::justReplaceAllUsesWith(MDefinition* dom)
601 {
602     MOZ_ASSERT(dom != nullptr);
603     MOZ_ASSERT(dom != this);
604 
605     // Carry over the fact the value has uses which are no longer inspectable
606     // with the graph.
607     if (isUseRemoved())
608         dom->setUseRemovedUnchecked();
609 
610     for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i)
611         i->setProducerUnchecked(dom);
612     dom->uses_.takeElements(uses_);
613 }
614 
615 void
justReplaceAllUsesWithExcept(MDefinition * dom)616 MDefinition::justReplaceAllUsesWithExcept(MDefinition* dom)
617 {
618     MOZ_ASSERT(dom != nullptr);
619     MOZ_ASSERT(dom != this);
620 
621     // Carry over the fact the value has uses which are no longer inspectable
622     // with the graph.
623     if (isUseRemoved())
624         dom->setUseRemovedUnchecked();
625 
626     // Move all uses to new dom. Save the use of the dominating instruction.
627     MUse *exceptUse = nullptr;
628     for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i) {
629         if (i->consumer() != dom) {
630             i->setProducerUnchecked(dom);
631         } else {
632             MOZ_ASSERT(!exceptUse);
633             exceptUse = *i;
634         }
635     }
636     dom->uses_.takeElements(uses_);
637 
638     // Restore the use to the original definition.
639     dom->uses_.remove(exceptUse);
640     exceptUse->setProducerUnchecked(this);
641     uses_.pushFront(exceptUse);
642 }
643 
644 void
optimizeOutAllUses(TempAllocator & alloc)645 MDefinition::optimizeOutAllUses(TempAllocator& alloc)
646 {
647     for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) {
648         MUse* use = *i++;
649         MConstant* constant = use->consumer()->block()->optimizedOutConstant(alloc);
650 
651         // Update the resume point operand to use the optimized-out constant.
652         use->setProducerUnchecked(constant);
653         constant->addUseUnchecked(use);
654     }
655 
656     // Remove dangling pointers.
657     this->uses_.clear();
658 }
659 
660 void
replaceAllLiveUsesWith(MDefinition * dom)661 MDefinition::replaceAllLiveUsesWith(MDefinition* dom)
662 {
663     for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ) {
664         MUse* use = *i++;
665         MNode* consumer = use->consumer();
666         if (consumer->isResumePoint())
667             continue;
668         if (consumer->isDefinition() && consumer->toDefinition()->isRecoveredOnBailout())
669             continue;
670 
671         // Update the operand to use the dominating definition.
672         use->replaceProducer(dom);
673     }
674 }
675 
676 bool
emptyResultTypeSet() const677 MDefinition::emptyResultTypeSet() const
678 {
679     return resultTypeSet() && resultTypeSet()->empty();
680 }
681 
682 MConstant*
New(TempAllocator & alloc,const Value & v,CompilerConstraintList * constraints)683 MConstant::New(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints)
684 {
685     return new(alloc) MConstant(v, constraints);
686 }
687 
688 MConstant*
NewTypedValue(TempAllocator & alloc,const Value & v,MIRType type,CompilerConstraintList * constraints)689 MConstant::NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type,
690                          CompilerConstraintList* constraints)
691 {
692     MOZ_ASSERT(!IsSimdType(type));
693     MOZ_ASSERT_IF(type == MIRType_Float32,
694                   IsNaN(v.toDouble()) || v.toDouble() == double(float(v.toDouble())));
695     MConstant* constant = new(alloc) MConstant(v, constraints);
696     constant->setResultType(type);
697     return constant;
698 }
699 
700 MConstant*
NewAsmJS(TempAllocator & alloc,const Value & v,MIRType type)701 MConstant::NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type)
702 {
703     if (type == MIRType_Float32)
704         return NewTypedValue(alloc, Float32Value(v.toNumber()), type);
705     return NewTypedValue(alloc, v, type);
706 }
707 
708 MConstant*
NewConstraintlessObject(TempAllocator & alloc,JSObject * v)709 MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v)
710 {
711     return new(alloc) MConstant(v);
712 }
713 
714 static TemporaryTypeSet*
MakeSingletonTypeSetFromKey(CompilerConstraintList * constraints,TypeSet::ObjectKey * key)715 MakeSingletonTypeSetFromKey(CompilerConstraintList* constraints, TypeSet::ObjectKey* key)
716 {
717     // Invalidate when this object's ObjectGroup gets unknown properties. This
718     // happens for instance when we mutate an object's __proto__, in this case
719     // we want to invalidate and mark this TypeSet as containing AnyObject
720     // (because mutating __proto__ will change an object's ObjectGroup).
721     MOZ_ASSERT(constraints);
722     key->hasStableClassAndProto(constraints);
723 
724     LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
725     return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::ObjectType(key));
726 }
727 
728 TemporaryTypeSet*
MakeSingletonTypeSet(CompilerConstraintList * constraints,JSObject * obj)729 jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj)
730 {
731     return MakeSingletonTypeSetFromKey(constraints, TypeSet::ObjectKey::get(obj));
732 }
733 
734 TemporaryTypeSet*
MakeSingletonTypeSet(CompilerConstraintList * constraints,ObjectGroup * obj)735 jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, ObjectGroup* obj)
736 {
737     return MakeSingletonTypeSetFromKey(constraints, TypeSet::ObjectKey::get(obj));
738 }
739 
740 static TemporaryTypeSet*
MakeUnknownTypeSet()741 MakeUnknownTypeSet()
742 {
743     LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
744     return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::UnknownType());
745 }
746 
747 #ifdef DEBUG
748 
749 bool
IonCompilationCanUseNurseryPointers()750 jit::IonCompilationCanUseNurseryPointers()
751 {
752     // If we are doing backend compilation, which could occur on a helper
753     // thread but might actually be on the main thread, check the flag set on
754     // the PerThreadData by AutoEnterIonCompilation.
755     if (CurrentThreadIsIonCompiling())
756         return !CurrentThreadIsIonCompilingSafeForMinorGC();
757 
758     // Otherwise, we must be on the main thread during MIR construction. The
759     // store buffer must have been notified that minor GCs must cancel pending
760     // or in progress Ion compilations.
761     JSRuntime* rt = TlsPerThreadData.get()->runtimeFromMainThread();
762     return rt->gc.storeBuffer.cancelIonCompilations();
763 }
764 
765 #endif // DEBUG
766 
MConstant(const js::Value & vp,CompilerConstraintList * constraints)767 MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints)
768   : value_(vp)
769 {
770     setResultType(MIRTypeFromValue(vp));
771     if (vp.isObject()) {
772         // Create a singleton type set for the object. This isn't necessary for
773         // other types as the result type encodes all needed information.
774         MOZ_ASSERT_IF(IsInsideNursery(&vp.toObject()), IonCompilationCanUseNurseryPointers());
775         setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject()));
776     }
777     if (vp.isMagic() && vp.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
778         // JS_UNINITIALIZED_LEXICAL does not escape to script and is not
779         // observed in type sets. However, it may flow around freely during
780         // Ion compilation. Give it an unknown typeset to poison any type sets
781         // it merges with.
782         //
783         // TODO We could track uninitialized lexicals more precisely by tracking
784         // them in type sets.
785         setResultTypeSet(MakeUnknownTypeSet());
786     }
787 
788     MOZ_ASSERT_IF(vp.isString(), vp.toString()->isAtom());
789 
790     setMovable();
791 }
792 
MConstant(JSObject * obj)793 MConstant::MConstant(JSObject* obj)
794   : value_(ObjectValue(*obj))
795 {
796     MOZ_ASSERT_IF(IsInsideNursery(obj), IonCompilationCanUseNurseryPointers());
797     setResultType(MIRType_Object);
798     setMovable();
799 }
800 
801 HashNumber
valueHash() const802 MConstant::valueHash() const
803 {
804     // Fold all 64 bits into the 32-bit result. It's tempting to just discard
805     // half of the bits, as this is just a hash, however there are many common
806     // patterns of values where only the low or the high bits vary, so
807     // discarding either side would lead to excessive hash collisions.
808     uint64_t bits = JSVAL_TO_IMPL(value_).asBits;
809     return (HashNumber)bits ^ (HashNumber)(bits >> 32);
810 }
811 
812 bool
congruentTo(const MDefinition * ins) const813 MConstant::congruentTo(const MDefinition* ins) const
814 {
815     if (!ins->isConstant())
816         return false;
817     return ins->toConstant()->value() == value();
818 }
819 
820 void
printOpcode(GenericPrinter & out) const821 MConstant::printOpcode(GenericPrinter& out) const
822 {
823     PrintOpcodeName(out, op());
824     out.printf(" ");
825     switch (type()) {
826       case MIRType_Undefined:
827         out.printf("undefined");
828         break;
829       case MIRType_Null:
830         out.printf("null");
831         break;
832       case MIRType_Boolean:
833         out.printf(value().toBoolean() ? "true" : "false");
834         break;
835       case MIRType_Int32:
836         out.printf("0x%x", value().toInt32());
837         break;
838       case MIRType_Double:
839         out.printf("%.16g", value().toDouble());
840         break;
841       case MIRType_Float32:
842       {
843         float val = value().toDouble();
844         out.printf("%.16g", val);
845         break;
846       }
847       case MIRType_Object:
848         if (value().toObject().is<JSFunction>()) {
849             JSFunction* fun = &value().toObject().as<JSFunction>();
850             if (fun->displayAtom()) {
851                 out.put("function ");
852                 EscapedStringPrinter(out, fun->displayAtom(), 0);
853             } else {
854                 out.put("unnamed function");
855             }
856             if (fun->hasScript()) {
857                 JSScript* script = fun->nonLazyScript();
858                 out.printf(" (%s:%" PRIuSIZE ")",
859                         script->filename() ? script->filename() : "", script->lineno());
860             }
861             out.printf(" at %p", (void*) fun);
862             break;
863         }
864         out.printf("object %p (%s)", (void*)&value().toObject(),
865                 value().toObject().getClass()->name);
866         break;
867       case MIRType_Symbol:
868         out.printf("symbol at %p", (void*)value().toSymbol());
869         break;
870       case MIRType_String:
871         out.printf("string %p", (void*)value().toString());
872         break;
873       case MIRType_MagicOptimizedArguments:
874         out.printf("magic lazyargs");
875         break;
876       case MIRType_MagicHole:
877         out.printf("magic hole");
878         break;
879       case MIRType_MagicIsConstructing:
880         out.printf("magic is-constructing");
881         break;
882       case MIRType_MagicOptimizedOut:
883         out.printf("magic optimized-out");
884         break;
885       case MIRType_MagicUninitializedLexical:
886         out.printf("magic uninitialized-lexical");
887         break;
888       default:
889         MOZ_CRASH("unexpected type");
890     }
891 }
892 
893 bool
canProduceFloat32() const894 MConstant::canProduceFloat32() const
895 {
896     if (!IsNumberType(type()))
897         return false;
898 
899     if (type() == MIRType_Int32)
900         return IsFloat32Representable(static_cast<double>(value_.toInt32()));
901     if (type() == MIRType_Double)
902         return IsFloat32Representable(value_.toDouble());
903     return true;
904 }
905 
906 MDefinition*
foldsTo(TempAllocator & alloc)907 MSimdValueX4::foldsTo(TempAllocator& alloc)
908 {
909     DebugOnly<MIRType> laneType = SimdTypeToLaneType(type());
910     bool allConstants = true;
911     bool allSame = true;
912 
913     for (size_t i = 0; i < 4; ++i) {
914         MDefinition* op = getOperand(i);
915         MOZ_ASSERT(op->type() == laneType);
916         if (!op->isConstantValue())
917             allConstants = false;
918         if (i > 0 && op != getOperand(i - 1))
919             allSame = false;
920     }
921 
922     if (!allConstants && !allSame)
923         return this;
924 
925     if (allConstants) {
926         SimdConstant cst;
927         switch (type()) {
928           case MIRType_Int32x4: {
929             int32_t a[4];
930             for (size_t i = 0; i < 4; ++i)
931                 a[i] = getOperand(i)->constantValue().toInt32();
932             cst = SimdConstant::CreateX4(a);
933             break;
934           }
935           case MIRType_Float32x4: {
936             float a[4];
937             for (size_t i = 0; i < 4; ++i)
938                 a[i] = getOperand(i)->constantValue().toNumber();
939             cst = SimdConstant::CreateX4(a);
940             break;
941           }
942           default: MOZ_CRASH("unexpected type in MSimdValueX4::foldsTo");
943         }
944 
945         return MSimdConstant::New(alloc, cst, type());
946     }
947 
948     MOZ_ASSERT(allSame);
949     return MSimdSplatX4::New(alloc, getOperand(0), type());
950 }
951 
952 MDefinition*
foldsTo(TempAllocator & alloc)953 MSimdSplatX4::foldsTo(TempAllocator& alloc)
954 {
955     DebugOnly<MIRType> laneType = SimdTypeToLaneType(type());
956     MDefinition* op = getOperand(0);
957     if (!op->isConstantValue())
958         return this;
959     MOZ_ASSERT(op->type() == laneType);
960 
961     SimdConstant cst;
962     switch (type()) {
963       case MIRType_Int32x4: {
964         int32_t a[4];
965         int32_t v = getOperand(0)->constantValue().toInt32();
966         for (size_t i = 0; i < 4; ++i)
967             a[i] = v;
968         cst = SimdConstant::CreateX4(a);
969         break;
970       }
971       case MIRType_Float32x4: {
972         float a[4];
973         float v = getOperand(0)->constantValue().toNumber();
974         for (size_t i = 0; i < 4; ++i)
975             a[i] = v;
976         cst = SimdConstant::CreateX4(a);
977         break;
978       }
979       default: MOZ_CRASH("unexpected type in MSimdSplatX4::foldsTo");
980     }
981 
982     return MSimdConstant::New(alloc, cst, type());
983 }
984 
985 MDefinition*
foldsTo(TempAllocator & alloc)986 MSimdUnbox::foldsTo(TempAllocator& alloc)
987 {
988     MDefinition* in = input();
989 
990     if (in->isSimdBox()) {
991         // If the operand is a MSimdBox, then we just reuse the operand of the
992         // MSimdBox as long as the type corresponds to what we are supposed to
993         // unbox.
994         in = in->toSimdBox()->input();
995         if (in->type() != type())
996             return this;
997         return in;
998     }
999 
1000     return this;
1001 }
1002 
1003 MDefinition*
foldsTo(TempAllocator & alloc)1004 MSimdSwizzle::foldsTo(TempAllocator& alloc)
1005 {
1006     if (lanesMatch(0, 1, 2, 3))
1007         return input();
1008     return this;
1009 }
1010 
1011 MDefinition*
foldsTo(TempAllocator & alloc)1012 MSimdGeneralShuffle::foldsTo(TempAllocator& alloc)
1013 {
1014     FixedList<uint32_t> lanes;
1015     if (!lanes.init(alloc, numLanes()))
1016         return this;
1017 
1018     for (size_t i = 0; i < numLanes(); i++) {
1019         if (!lane(i)->isConstant() || lane(i)->type() != MIRType_Int32)
1020             return this;
1021         int32_t temp = lane(i)->toConstant()->value().toInt32();
1022         if (temp < 0 || uint32_t(temp) >= numLanes() * numVectors())
1023             return this;
1024         lanes[i] = uint32_t(temp);
1025     }
1026 
1027     if (numVectors() == 1)
1028         return MSimdSwizzle::New(alloc, vector(0), type(), lanes[0], lanes[1], lanes[2], lanes[3]);
1029 
1030     MOZ_ASSERT(numVectors() == 2);
1031     return MSimdShuffle::New(alloc, vector(0), vector(1), type(), lanes[0], lanes[1], lanes[2], lanes[3]);
1032 }
1033 
1034 template <typename T>
1035 static void
PrintOpcodeOperation(T * mir,GenericPrinter & out)1036 PrintOpcodeOperation(T* mir, GenericPrinter& out)
1037 {
1038     mir->MDefinition::printOpcode(out);
1039     out.printf(" (%s)", T::OperationName(mir->operation()));
1040 }
1041 
1042 void
printOpcode(GenericPrinter & out) const1043 MSimdBinaryArith::printOpcode(GenericPrinter& out) const
1044 {
1045     PrintOpcodeOperation(this, out);
1046 }
1047 void
printOpcode(GenericPrinter & out) const1048 MSimdBinaryBitwise::printOpcode(GenericPrinter& out) const
1049 {
1050     PrintOpcodeOperation(this, out);
1051 }
1052 void
printOpcode(GenericPrinter & out) const1053 MSimdUnaryArith::printOpcode(GenericPrinter& out) const
1054 {
1055     PrintOpcodeOperation(this, out);
1056 }
1057 void
printOpcode(GenericPrinter & out) const1058 MSimdBinaryComp::printOpcode(GenericPrinter& out) const
1059 {
1060     PrintOpcodeOperation(this, out);
1061 }
1062 void
printOpcode(GenericPrinter & out) const1063 MSimdShift::printOpcode(GenericPrinter& out) const
1064 {
1065     PrintOpcodeOperation(this, out);
1066 }
1067 
1068 void
printOpcode(GenericPrinter & out) const1069 MSimdInsertElement::printOpcode(GenericPrinter& out) const
1070 {
1071     MDefinition::printOpcode(out);
1072     out.printf(" (%s)", MSimdInsertElement::LaneName(lane()));
1073 }
1074 
1075 MCloneLiteral*
New(TempAllocator & alloc,MDefinition * obj)1076 MCloneLiteral::New(TempAllocator& alloc, MDefinition* obj)
1077 {
1078     return new(alloc) MCloneLiteral(obj);
1079 }
1080 
1081 void
printOpcode(GenericPrinter & out) const1082 MControlInstruction::printOpcode(GenericPrinter& out) const
1083 {
1084     MDefinition::printOpcode(out);
1085     for (size_t j = 0; j < numSuccessors(); j++)
1086         out.printf(" block%u", getSuccessor(j)->id());
1087 }
1088 
1089 void
printOpcode(GenericPrinter & out) const1090 MCompare::printOpcode(GenericPrinter& out) const
1091 {
1092     MDefinition::printOpcode(out);
1093     out.printf(" %s", CodeName[jsop()]);
1094 }
1095 
1096 void
printOpcode(GenericPrinter & out) const1097 MConstantElements::printOpcode(GenericPrinter& out) const
1098 {
1099     PrintOpcodeName(out, op());
1100     out.printf(" 0x%" PRIxPTR, value().asValue());
1101 }
1102 
1103 void
printOpcode(GenericPrinter & out) const1104 MLoadUnboxedScalar::printOpcode(GenericPrinter& out) const
1105 {
1106     MDefinition::printOpcode(out);
1107     out.printf(" %s", ScalarTypeDescr::typeName(storageType()));
1108 }
1109 
1110 void
printOpcode(GenericPrinter & out) const1111 MAssertRange::printOpcode(GenericPrinter& out) const
1112 {
1113     MDefinition::printOpcode(out);
1114     out.put(" ");
1115     assertedRange()->dump(out);
1116 }
1117 
1118 const char*
FunctionName(Function function)1119 MMathFunction::FunctionName(Function function)
1120 {
1121     switch (function) {
1122       case Log:    return "Log";
1123       case Sin:    return "Sin";
1124       case Cos:    return "Cos";
1125       case Exp:    return "Exp";
1126       case Tan:    return "Tan";
1127       case ACos:   return "ACos";
1128       case ASin:   return "ASin";
1129       case ATan:   return "ATan";
1130       case Log10:  return "Log10";
1131       case Log2:   return "Log2";
1132       case Log1P:  return "Log1P";
1133       case ExpM1:  return "ExpM1";
1134       case CosH:   return "CosH";
1135       case SinH:   return "SinH";
1136       case TanH:   return "TanH";
1137       case ACosH:  return "ACosH";
1138       case ASinH:  return "ASinH";
1139       case ATanH:  return "ATanH";
1140       case Sign:   return "Sign";
1141       case Trunc:  return "Trunc";
1142       case Cbrt:   return "Cbrt";
1143       case Floor:  return "Floor";
1144       case Ceil:   return "Ceil";
1145       case Round:  return "Round";
1146       default:
1147         MOZ_CRASH("Unknown math function");
1148     }
1149 }
1150 
1151 void
printOpcode(GenericPrinter & out) const1152 MMathFunction::printOpcode(GenericPrinter& out) const
1153 {
1154     MDefinition::printOpcode(out);
1155     out.printf(" %s", FunctionName(function()));
1156 }
1157 
1158 MDefinition*
foldsTo(TempAllocator & alloc)1159 MMathFunction::foldsTo(TempAllocator& alloc)
1160 {
1161     MDefinition* input = getOperand(0);
1162     if (!input->isConstant())
1163         return this;
1164 
1165     Value val = input->toConstant()->value();
1166     if (!val.isNumber())
1167         return this;
1168 
1169     double in = val.toNumber();
1170     double out;
1171     switch (function_) {
1172       case Log:
1173         out = js::math_log_uncached(in);
1174         break;
1175       case Sin:
1176         out = js::math_sin_uncached(in);
1177         break;
1178       case Cos:
1179         out = js::math_cos_uncached(in);
1180         break;
1181       case Exp:
1182         out = js::math_exp_uncached(in);
1183         break;
1184       case Tan:
1185         out = js::math_tan_uncached(in);
1186         break;
1187       case ACos:
1188         out = js::math_acos_uncached(in);
1189         break;
1190       case ASin:
1191         out = js::math_asin_uncached(in);
1192         break;
1193       case ATan:
1194         out = js::math_atan_uncached(in);
1195         break;
1196       case Log10:
1197         out = js::math_log10_uncached(in);
1198         break;
1199       case Log2:
1200         out = js::math_log2_uncached(in);
1201         break;
1202       case Log1P:
1203         out = js::math_log1p_uncached(in);
1204         break;
1205       case ExpM1:
1206         out = js::math_expm1_uncached(in);
1207         break;
1208       case CosH:
1209         out = js::math_cosh_uncached(in);
1210         break;
1211       case SinH:
1212         out = js::math_sinh_uncached(in);
1213         break;
1214       case TanH:
1215         out = js::math_tanh_uncached(in);
1216         break;
1217       case ACosH:
1218         out = js::math_acosh_uncached(in);
1219         break;
1220       case ASinH:
1221         out = js::math_asinh_uncached(in);
1222         break;
1223       case ATanH:
1224         out = js::math_atanh_uncached(in);
1225         break;
1226       case Sign:
1227         out = js::math_sign_uncached(in);
1228         break;
1229       case Trunc:
1230         out = js::math_trunc_uncached(in);
1231         break;
1232       case Cbrt:
1233         out = js::math_cbrt_uncached(in);
1234         break;
1235       case Floor:
1236         out = js::math_floor_impl(in);
1237         break;
1238       case Ceil:
1239         out = js::math_ceil_impl(in);
1240         break;
1241       case Round:
1242         out = js::math_round_impl(in);
1243         break;
1244       default:
1245         return this;
1246     }
1247 
1248     if (input->type() == MIRType_Float32)
1249         return MConstant::NewTypedValue(alloc, DoubleValue(out), MIRType_Float32);
1250     return MConstant::New(alloc, DoubleValue(out));
1251 }
1252 
1253 MDefinition*
foldsTo(TempAllocator & alloc)1254 MAtomicIsLockFree::foldsTo(TempAllocator& alloc)
1255 {
1256     MDefinition* input = getOperand(0);
1257     if (!input->isConstantValue())
1258         return this;
1259 
1260     Value val = input->constantValue();
1261     if (!val.isInt32())
1262         return this;
1263 
1264     return MConstant::New(alloc, BooleanValue(AtomicOperations::isLockfree(val.toInt32())));
1265 }
1266 
1267 MParameter*
New(TempAllocator & alloc,int32_t index,TemporaryTypeSet * types)1268 MParameter::New(TempAllocator& alloc, int32_t index, TemporaryTypeSet* types)
1269 {
1270     return new(alloc) MParameter(index, types);
1271 }
1272 
1273 void
printOpcode(GenericPrinter & out) const1274 MParameter::printOpcode(GenericPrinter& out) const
1275 {
1276     PrintOpcodeName(out, op());
1277     if (index() == THIS_SLOT)
1278         out.printf(" THIS_SLOT");
1279     else
1280         out.printf(" %d", index());
1281 }
1282 
1283 HashNumber
valueHash() const1284 MParameter::valueHash() const
1285 {
1286     HashNumber hash = MDefinition::valueHash();
1287     hash = addU32ToHash(hash, index_);
1288     return hash;
1289 }
1290 
1291 bool
congruentTo(const MDefinition * ins) const1292 MParameter::congruentTo(const MDefinition* ins) const
1293 {
1294     if (!ins->isParameter())
1295         return false;
1296 
1297     return ins->toParameter()->index() == index_;
1298 }
1299 
1300 MCall*
New(TempAllocator & alloc,JSFunction * target,size_t maxArgc,size_t numActualArgs,bool construct,bool isDOMCall)1301 MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
1302            bool construct, bool isDOMCall)
1303 {
1304     MOZ_ASSERT(maxArgc >= numActualArgs);
1305     MCall* ins;
1306     if (isDOMCall) {
1307         MOZ_ASSERT(!construct);
1308         ins = new(alloc) MCallDOMNative(target, numActualArgs);
1309     } else {
1310         ins = new(alloc) MCall(target, numActualArgs, construct);
1311     }
1312     if (!ins->init(alloc, maxArgc + NumNonArgumentOperands))
1313         return nullptr;
1314     return ins;
1315 }
1316 
1317 AliasSet
getAliasSet() const1318 MCallDOMNative::getAliasSet() const
1319 {
1320     const JSJitInfo* jitInfo = getJitInfo();
1321 
1322     // If we don't know anything about the types of our arguments, we have to
1323     // assume that type-coercions can have side-effects, so we need to alias
1324     // everything.
1325     if (jitInfo->aliasSet() == JSJitInfo::AliasEverything || !jitInfo->isTypedMethodJitInfo())
1326         return AliasSet::Store(AliasSet::Any);
1327 
1328     uint32_t argIndex = 0;
1329     const JSTypedMethodJitInfo* methodInfo =
1330         reinterpret_cast<const JSTypedMethodJitInfo*>(jitInfo);
1331     for (const JSJitInfo::ArgType* argType = methodInfo->argTypes;
1332          *argType != JSJitInfo::ArgTypeListEnd;
1333          ++argType, ++argIndex)
1334     {
1335         if (argIndex >= numActualArgs()) {
1336             // Passing through undefined can't have side-effects
1337             continue;
1338         }
1339         // getArg(0) is "this", so skip it
1340         MDefinition* arg = getArg(argIndex+1);
1341         MIRType actualType = arg->type();
1342         // The only way to reliably avoid side-effects given the information we
1343         // have here is if we're passing in a known primitive value to an
1344         // argument that expects a primitive value.
1345         //
1346         // XXXbz maybe we need to communicate better information.  For example,
1347         // a sequence argument will sort of unavoidably have side effects, while
1348         // a typed array argument won't have any, but both are claimed to be
1349         // JSJitInfo::Object.  But if we do that, we need to watch out for our
1350         // movability/DCE-ability bits: if we have an arg type that can reliably
1351         // throw an exception on conversion, that might not affect our alias set
1352         // per se, but it should prevent us being moved or DCE-ed, unless we
1353         // know the incoming things match that arg type and won't throw.
1354         //
1355         if ((actualType == MIRType_Value || actualType == MIRType_Object) ||
1356             (*argType & JSJitInfo::Object))
1357          {
1358              return AliasSet::Store(AliasSet::Any);
1359          }
1360     }
1361 
1362     // We checked all the args, and they check out.  So we only alias DOM
1363     // mutations or alias nothing, depending on the alias set in the jitinfo.
1364     if (jitInfo->aliasSet() == JSJitInfo::AliasNone)
1365         return AliasSet::None();
1366 
1367     MOZ_ASSERT(jitInfo->aliasSet() == JSJitInfo::AliasDOMSets);
1368     return AliasSet::Load(AliasSet::DOMProperty);
1369 }
1370 
1371 void
computeMovable()1372 MCallDOMNative::computeMovable()
1373 {
1374     // We are movable if the jitinfo says we can be and if we're also not
1375     // effectful.  The jitinfo can't check for the latter, since it depends on
1376     // the types of our arguments.
1377     const JSJitInfo* jitInfo = getJitInfo();
1378 
1379     MOZ_ASSERT_IF(jitInfo->isMovable,
1380                   jitInfo->aliasSet() != JSJitInfo::AliasEverything);
1381 
1382     if (jitInfo->isMovable && !isEffectful())
1383         setMovable();
1384 }
1385 
1386 bool
congruentTo(const MDefinition * ins) const1387 MCallDOMNative::congruentTo(const MDefinition* ins) const
1388 {
1389     if (!isMovable())
1390         return false;
1391 
1392     if (!ins->isCall())
1393         return false;
1394 
1395     const MCall* call = ins->toCall();
1396 
1397     if (!call->isCallDOMNative())
1398         return false;
1399 
1400     if (getSingleTarget() != call->getSingleTarget())
1401         return false;
1402 
1403     if (isConstructing() != call->isConstructing())
1404         return false;
1405 
1406     if (numActualArgs() != call->numActualArgs())
1407         return false;
1408 
1409     if (needsArgCheck() != call->needsArgCheck())
1410         return false;
1411 
1412     if (!congruentIfOperandsEqual(call))
1413         return false;
1414 
1415     // The other call had better be movable at this point!
1416     MOZ_ASSERT(call->isMovable());
1417 
1418     return true;
1419 }
1420 
1421 const JSJitInfo*
getJitInfo() const1422 MCallDOMNative::getJitInfo() const
1423 {
1424     MOZ_ASSERT(getSingleTarget() && getSingleTarget()->isNative());
1425 
1426     const JSJitInfo* jitInfo = getSingleTarget()->jitInfo();
1427     MOZ_ASSERT(jitInfo);
1428 
1429     return jitInfo;
1430 }
1431 
1432 MApplyArgs*
New(TempAllocator & alloc,JSFunction * target,MDefinition * fun,MDefinition * argc,MDefinition * self)1433 MApplyArgs::New(TempAllocator& alloc, JSFunction* target, MDefinition* fun, MDefinition* argc,
1434                 MDefinition* self)
1435 {
1436     return new(alloc) MApplyArgs(target, fun, argc, self);
1437 }
1438 
1439 MApplyArray*
New(TempAllocator & alloc,JSFunction * target,MDefinition * fun,MDefinition * elements,MDefinition * self)1440 MApplyArray::New(TempAllocator& alloc, JSFunction* target, MDefinition* fun, MDefinition* elements,
1441                  MDefinition* self)
1442 {
1443     return new(alloc) MApplyArray(target, fun, elements, self);
1444 }
1445 
1446 MDefinition*
foldsTo(TempAllocator & alloc)1447 MStringLength::foldsTo(TempAllocator& alloc)
1448 {
1449     if ((type() == MIRType_Int32) && (string()->isConstantValue())) {
1450         Value value = string()->constantValue();
1451         JSAtom* atom = &value.toString()->asAtom();
1452         return MConstant::New(alloc, Int32Value(atom->length()));
1453     }
1454 
1455     return this;
1456 }
1457 
1458 MDefinition*
foldsTo(TempAllocator & alloc)1459 MConcat::foldsTo(TempAllocator& alloc)
1460 {
1461     if (lhs()->isConstantValue() && lhs()->constantValue().toString()->empty())
1462         return rhs();
1463 
1464     if (rhs()->isConstantValue() && rhs()->constantValue().toString()->empty())
1465         return lhs();
1466 
1467     return this;
1468 }
1469 
1470 static bool
EnsureFloatInputOrConvert(MUnaryInstruction * owner,TempAllocator & alloc)1471 EnsureFloatInputOrConvert(MUnaryInstruction* owner, TempAllocator& alloc)
1472 {
1473     MDefinition* input = owner->input();
1474     if (!input->canProduceFloat32()) {
1475         if (input->type() == MIRType_Float32)
1476             ConvertDefinitionToDouble<0>(alloc, input, owner);
1477         return false;
1478     }
1479     return true;
1480 }
1481 
1482 void
trySpecializeFloat32(TempAllocator & alloc)1483 MFloor::trySpecializeFloat32(TempAllocator& alloc)
1484 {
1485     MOZ_ASSERT(type() == MIRType_Int32);
1486     if (EnsureFloatInputOrConvert(this, alloc))
1487         specialization_ = MIRType_Float32;
1488 }
1489 
1490 void
trySpecializeFloat32(TempAllocator & alloc)1491 MCeil::trySpecializeFloat32(TempAllocator& alloc)
1492 {
1493     MOZ_ASSERT(type() == MIRType_Int32);
1494     if (EnsureFloatInputOrConvert(this, alloc))
1495         specialization_ = MIRType_Float32;
1496 }
1497 
1498 void
trySpecializeFloat32(TempAllocator & alloc)1499 MRound::trySpecializeFloat32(TempAllocator& alloc)
1500 {
1501     MOZ_ASSERT(type() == MIRType_Int32);
1502     if (EnsureFloatInputOrConvert(this, alloc))
1503         specialization_ = MIRType_Float32;
1504 }
1505 
1506 MCompare*
New(TempAllocator & alloc,MDefinition * left,MDefinition * right,JSOp op)1507 MCompare::New(TempAllocator& alloc, MDefinition* left, MDefinition* right, JSOp op)
1508 {
1509     return new(alloc) MCompare(left, right, op);
1510 }
1511 
1512 MCompare*
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right,JSOp op,CompareType compareType)1513 MCompare::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right, JSOp op,
1514                    CompareType compareType)
1515 {
1516     MOZ_ASSERT(compareType == Compare_Int32 || compareType == Compare_UInt32 ||
1517                compareType == Compare_Double || compareType == Compare_Float32);
1518     MCompare* comp = new(alloc) MCompare(left, right, op);
1519     comp->compareType_ = compareType;
1520     comp->operandMightEmulateUndefined_ = false;
1521     comp->setResultType(MIRType_Int32);
1522     return comp;
1523 }
1524 
1525 MTableSwitch*
New(TempAllocator & alloc,MDefinition * ins,int32_t low,int32_t high)1526 MTableSwitch::New(TempAllocator& alloc, MDefinition* ins, int32_t low, int32_t high)
1527 {
1528     return new(alloc) MTableSwitch(alloc, ins, low, high);
1529 }
1530 
1531 MGoto*
New(TempAllocator & alloc,MBasicBlock * target)1532 MGoto::New(TempAllocator& alloc, MBasicBlock* target)
1533 {
1534     MOZ_ASSERT(target);
1535     return new(alloc) MGoto(target);
1536 }
1537 
1538 void
printOpcode(GenericPrinter & out) const1539 MUnbox::printOpcode(GenericPrinter& out) const
1540 {
1541     PrintOpcodeName(out, op());
1542     out.printf(" ");
1543     getOperand(0)->printName(out);
1544     out.printf(" ");
1545 
1546     switch (type()) {
1547       case MIRType_Int32: out.printf("to Int32"); break;
1548       case MIRType_Double: out.printf("to Double"); break;
1549       case MIRType_Boolean: out.printf("to Boolean"); break;
1550       case MIRType_String: out.printf("to String"); break;
1551       case MIRType_Symbol: out.printf("to Symbol"); break;
1552       case MIRType_Object: out.printf("to Object"); break;
1553       default: break;
1554     }
1555 
1556     switch (mode()) {
1557       case Fallible: out.printf(" (fallible)"); break;
1558       case Infallible: out.printf(" (infallible)"); break;
1559       case TypeBarrier: out.printf(" (typebarrier)"); break;
1560       default: break;
1561     }
1562 }
1563 
1564 MDefinition*
foldsTo(TempAllocator & alloc)1565 MUnbox::foldsTo(TempAllocator &alloc)
1566 {
1567     if (!input()->isLoadFixedSlot())
1568         return this;
1569     MLoadFixedSlot* load = input()->toLoadFixedSlot();
1570     if (load->type() != MIRType_Value)
1571         return this;
1572     if (type() != MIRType_Boolean && !IsNumberType(type()))
1573         return this;
1574     // Only optimize if the load comes immediately before the unbox, so it's
1575     // safe to copy the load's dependency field.
1576     MInstructionIterator iter(load->block()->begin(load));
1577     ++iter;
1578     if (*iter != this)
1579         return this;
1580 
1581     MLoadFixedSlotAndUnbox* ins = MLoadFixedSlotAndUnbox::New(alloc, load->object(), load->slot(),
1582                                                               mode(), type(), bailoutKind());
1583     // As GVN runs after the Alias Analysis, we have to set the dependency by hand
1584     ins->setDependency(load->dependency());
1585     return ins;
1586 }
1587 
1588 void
printOpcode(GenericPrinter & out) const1589 MTypeBarrier::printOpcode(GenericPrinter& out) const
1590 {
1591     PrintOpcodeName(out, op());
1592     out.printf(" ");
1593     getOperand(0)->printName(out);
1594 }
1595 
1596 bool
congruentTo(const MDefinition * def) const1597 MTypeBarrier::congruentTo(const MDefinition* def) const
1598 {
1599     if (!def->isTypeBarrier())
1600         return false;
1601     const MTypeBarrier* other = def->toTypeBarrier();
1602     if (barrierKind() != other->barrierKind() || isGuard() != other->isGuard())
1603         return false;
1604     if (!resultTypeSet()->equals(other->resultTypeSet()))
1605         return false;
1606     return congruentIfOperandsEqual(other);
1607 }
1608 
1609 #ifdef DEBUG
1610 void
assertLoopPhi() const1611 MPhi::assertLoopPhi() const
1612 {
1613     // getLoopPredecessorOperand and getLoopBackedgeOperand rely on these
1614     // predecessors being at indices 0 and 1.
1615     MBasicBlock* pred = block()->getPredecessor(0);
1616     MBasicBlock* back = block()->getPredecessor(1);
1617     MOZ_ASSERT(pred == block()->loopPredecessor());
1618     MOZ_ASSERT(pred->successorWithPhis() == block());
1619     MOZ_ASSERT(pred->positionInPhiSuccessor() == 0);
1620     MOZ_ASSERT(back == block()->backedge());
1621     MOZ_ASSERT(back->successorWithPhis() == block());
1622     MOZ_ASSERT(back->positionInPhiSuccessor() == 1);
1623 }
1624 #endif
1625 
1626 void
removeOperand(size_t index)1627 MPhi::removeOperand(size_t index)
1628 {
1629     MOZ_ASSERT(index < numOperands());
1630     MOZ_ASSERT(getUseFor(index)->index() == index);
1631     MOZ_ASSERT(getUseFor(index)->consumer() == this);
1632 
1633     // If we have phi(..., a, b, c, d, ..., z) and we plan
1634     // on removing a, then first shift downward so that we have
1635     // phi(..., b, c, d, ..., z, z):
1636     MUse* p = inputs_.begin() + index;
1637     MUse* e = inputs_.end();
1638     p->producer()->removeUse(p);
1639     for (; p < e - 1; ++p) {
1640         MDefinition* producer = (p + 1)->producer();
1641         p->setProducerUnchecked(producer);
1642         producer->replaceUse(p + 1, p);
1643     }
1644 
1645     // truncate the inputs_ list:
1646     inputs_.popBack();
1647 }
1648 
1649 void
removeAllOperands()1650 MPhi::removeAllOperands()
1651 {
1652     for (MUse& p : inputs_)
1653         p.producer()->removeUse(&p);
1654     inputs_.clear();
1655 }
1656 
1657 MDefinition*
foldsTernary()1658 MPhi::foldsTernary()
1659 {
1660     /* Look if this MPhi is a ternary construct.
1661      * This is a very loose term as it actually only checks for
1662      *
1663      *      MTest X
1664      *       /  \
1665      *    ...    ...
1666      *       \  /
1667      *     MPhi X Y
1668      *
1669      * Which we will simply call:
1670      * x ? x : y or x ? y : x
1671      */
1672 
1673     if (numOperands() != 2)
1674         return nullptr;
1675 
1676     MOZ_ASSERT(block()->numPredecessors() == 2);
1677 
1678     MBasicBlock* pred = block()->immediateDominator();
1679     if (!pred || !pred->lastIns()->isTest())
1680         return nullptr;
1681 
1682     MTest* test = pred->lastIns()->toTest();
1683 
1684     // True branch may only dominate one edge of MPhi.
1685     if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
1686         test->ifTrue()->dominates(block()->getPredecessor(1)))
1687     {
1688         return nullptr;
1689     }
1690 
1691     // False branch may only dominate one edge of MPhi.
1692     if (test->ifFalse()->dominates(block()->getPredecessor(0)) ==
1693         test->ifFalse()->dominates(block()->getPredecessor(1)))
1694     {
1695         return nullptr;
1696     }
1697 
1698     // True and false branch must dominate different edges of MPhi.
1699     if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
1700         test->ifFalse()->dominates(block()->getPredecessor(0)))
1701     {
1702         return nullptr;
1703     }
1704 
1705     // We found a ternary construct.
1706     bool firstIsTrueBranch = test->ifTrue()->dominates(block()->getPredecessor(0));
1707     MDefinition* trueDef = firstIsTrueBranch ? getOperand(0) : getOperand(1);
1708     MDefinition* falseDef = firstIsTrueBranch ? getOperand(1) : getOperand(0);
1709 
1710     // Accept either
1711     // testArg ? testArg : constant or
1712     // testArg ? constant : testArg
1713     if (!trueDef->isConstant() && !falseDef->isConstant())
1714         return nullptr;
1715 
1716     MConstant* c = trueDef->isConstant() ? trueDef->toConstant() : falseDef->toConstant();
1717     MDefinition* testArg = (trueDef == c) ? falseDef : trueDef;
1718     if (testArg != test->input())
1719         return nullptr;
1720 
1721     // This check should be a tautology, except that the constant might be the
1722     // result of the removal of a branch.  In such case the domination scope of
1723     // the block which is holding the constant might be incomplete. This
1724     // condition is used to prevent doing this optimization based on incomplete
1725     // information.
1726     //
1727     // As GVN removed a branch, it will update the dominations rules before
1728     // trying to fold this MPhi again. Thus, this condition does not inhibit
1729     // this optimization.
1730     MBasicBlock* truePred = block()->getPredecessor(firstIsTrueBranch ? 0 : 1);
1731     MBasicBlock* falsePred = block()->getPredecessor(firstIsTrueBranch ? 1 : 0);
1732     if (!trueDef->block()->dominates(truePred) ||
1733         !falseDef->block()->dominates(falsePred))
1734     {
1735         return nullptr;
1736     }
1737 
1738     // If testArg is an int32 type we can:
1739     // - fold testArg ? testArg : 0 to testArg
1740     // - fold testArg ? 0 : testArg to 0
1741     if (testArg->type() == MIRType_Int32 && c->vp()->toNumber() == 0) {
1742         // When folding to the constant we need to hoist it.
1743         if (trueDef == c && !c->block()->dominates(block()))
1744             c->block()->moveBefore(pred->lastIns(), c);
1745         return trueDef;
1746     }
1747 
1748     // If testArg is a string type we can:
1749     // - fold testArg ? testArg : "" to testArg
1750     // - fold testArg ? "" : testArg to ""
1751     if (testArg->type() == MIRType_String &&
1752         c->vp()->toString() == GetJitContext()->runtime->emptyString())
1753     {
1754         // When folding to the constant we need to hoist it.
1755         if (trueDef == c && !c->block()->dominates(block()))
1756             c->block()->moveBefore(pred->lastIns(), c);
1757         return trueDef;
1758     }
1759 
1760     return nullptr;
1761 }
1762 
1763 MDefinition*
operandIfRedundant()1764 MPhi::operandIfRedundant()
1765 {
1766     if (inputs_.length() == 0)
1767         return nullptr;
1768 
1769     // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
1770     // returns the operand that it will always be equal to (a, in
1771     // those two cases).
1772     MDefinition* first = getOperand(0);
1773     for (size_t i = 1, e = numOperands(); i < e; i++) {
1774         MDefinition* op = getOperand(i);
1775         if (op != first && op != this)
1776             return nullptr;
1777     }
1778     return first;
1779 }
1780 
1781 MDefinition*
foldsFilterTypeSet()1782 MPhi::foldsFilterTypeSet()
1783 {
1784     // Fold phi with as operands a combination of 'subject' and
1785     // MFilterTypeSet(subject) to 'subject'.
1786 
1787     if (inputs_.length() == 0)
1788         return nullptr;
1789 
1790     MDefinition* subject = getOperand(0);
1791     if (subject->isFilterTypeSet())
1792         subject = subject->toFilterTypeSet()->input();
1793 
1794     // Not same type, don't fold.
1795     if (subject->type() != type())
1796         return nullptr;
1797 
1798     // Phi is better typed (has typeset). Don't fold.
1799     if (resultTypeSet() && !subject->resultTypeSet())
1800         return nullptr;
1801 
1802     // Phi is better typed (according to typeset). Don't fold.
1803     if (subject->resultTypeSet() && resultTypeSet()) {
1804         if (!subject->resultTypeSet()->isSubset(resultTypeSet()))
1805             return nullptr;
1806     }
1807 
1808     for (size_t i = 1, e = numOperands(); i < e; i++) {
1809         MDefinition* op = getOperand(i);
1810         if (op == subject)
1811             continue;
1812         if (op->isFilterTypeSet() && op->toFilterTypeSet()->input() == subject)
1813             continue;
1814 
1815         return nullptr;
1816     }
1817 
1818     return subject;
1819 }
1820 
1821 MDefinition*
foldsTo(TempAllocator & alloc)1822 MPhi::foldsTo(TempAllocator& alloc)
1823 {
1824     if (MDefinition* def = operandIfRedundant())
1825         return def;
1826 
1827     if (MDefinition* def = foldsTernary())
1828         return def;
1829 
1830     if (MDefinition* def = foldsFilterTypeSet())
1831         return def;
1832 
1833     return this;
1834 }
1835 
1836 bool
congruentTo(const MDefinition * ins) const1837 MPhi::congruentTo(const MDefinition* ins) const
1838 {
1839     if (!ins->isPhi())
1840         return false;
1841 
1842     // Phis in different blocks may have different control conditions.
1843     // For example, these phis:
1844     //
1845     //   if (p)
1846     //     goto a
1847     //   a:
1848     //     t = phi(x, y)
1849     //
1850     //   if (q)
1851     //     goto b
1852     //   b:
1853     //     s = phi(x, y)
1854     //
1855     // have identical operands, but they are not equvalent because t is
1856     // effectively p?x:y and s is effectively q?x:y.
1857     //
1858     // For now, consider phis in different blocks incongruent.
1859     if (ins->block() != block())
1860         return false;
1861 
1862     return congruentIfOperandsEqual(ins);
1863 }
1864 
1865 static inline TemporaryTypeSet*
MakeMIRTypeSet(MIRType type)1866 MakeMIRTypeSet(MIRType type)
1867 {
1868     MOZ_ASSERT(type != MIRType_Value);
1869     TypeSet::Type ntype = type == MIRType_Object
1870                           ? TypeSet::AnyObjectType()
1871                           : TypeSet::PrimitiveType(ValueTypeFromMIRType(type));
1872     LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
1873     return alloc->new_<TemporaryTypeSet>(alloc, ntype);
1874 }
1875 
1876 bool
MergeTypes(MIRType * ptype,TemporaryTypeSet ** ptypeSet,MIRType newType,TemporaryTypeSet * newTypeSet)1877 jit::MergeTypes(MIRType* ptype, TemporaryTypeSet** ptypeSet,
1878                 MIRType newType, TemporaryTypeSet* newTypeSet)
1879 {
1880     if (newTypeSet && newTypeSet->empty())
1881         return true;
1882     if (newType != *ptype) {
1883         if (IsNumberType(newType) && IsNumberType(*ptype)) {
1884             *ptype = MIRType_Double;
1885         } else if (*ptype != MIRType_Value) {
1886             if (!*ptypeSet) {
1887                 *ptypeSet = MakeMIRTypeSet(*ptype);
1888                 if (!*ptypeSet)
1889                     return false;
1890             }
1891             *ptype = MIRType_Value;
1892         } else if (*ptypeSet && (*ptypeSet)->empty()) {
1893             *ptype = newType;
1894         }
1895     }
1896     if (*ptypeSet) {
1897         LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
1898         if (!newTypeSet && newType != MIRType_Value) {
1899             newTypeSet = MakeMIRTypeSet(newType);
1900             if (!newTypeSet)
1901                 return false;
1902         }
1903         if (newTypeSet) {
1904             if (!newTypeSet->isSubset(*ptypeSet)) {
1905                 *ptypeSet = TypeSet::unionSets(*ptypeSet, newTypeSet, alloc);
1906                 if (!*ptypeSet)
1907                     return false;
1908             }
1909         } else {
1910             *ptypeSet = nullptr;
1911         }
1912     }
1913     return true;
1914 }
1915 
1916 // Tests whether 'types' includes all possible values represented by
1917 // input/inputTypes.
1918 bool
TypeSetIncludes(TypeSet * types,MIRType input,TypeSet * inputTypes)1919 jit::TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes)
1920 {
1921     if (!types)
1922         return inputTypes && inputTypes->empty();
1923 
1924     switch (input) {
1925       case MIRType_Undefined:
1926       case MIRType_Null:
1927       case MIRType_Boolean:
1928       case MIRType_Int32:
1929       case MIRType_Double:
1930       case MIRType_Float32:
1931       case MIRType_String:
1932       case MIRType_Symbol:
1933       case MIRType_MagicOptimizedArguments:
1934         return types->hasType(TypeSet::PrimitiveType(ValueTypeFromMIRType(input)));
1935 
1936       case MIRType_Object:
1937         return types->unknownObject() || (inputTypes && inputTypes->isSubset(types));
1938 
1939       case MIRType_Value:
1940         return types->unknown() || (inputTypes && inputTypes->isSubset(types));
1941 
1942       default:
1943         MOZ_CRASH("Bad input type");
1944     }
1945 }
1946 
1947 // Tests if two type combos (type/typeset) are equal.
1948 bool
EqualTypes(MIRType type1,TemporaryTypeSet * typeset1,MIRType type2,TemporaryTypeSet * typeset2)1949 jit::EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
1950                 MIRType type2, TemporaryTypeSet* typeset2)
1951 {
1952     // Types should equal.
1953     if (type1 != type2)
1954         return false;
1955 
1956     // Both have equal type and no typeset.
1957     if (!typeset1 && !typeset2)
1958         return true;
1959 
1960     // If only one instructions has a typeset.
1961     // Test if the typset contains the same information as the MIRType.
1962     if (typeset1 && !typeset2)
1963         return TypeSetIncludes(typeset1, type2, nullptr);
1964     if (!typeset1 && typeset2)
1965         return TypeSetIncludes(typeset2, type1, nullptr);
1966 
1967     // Typesets should equal.
1968     return typeset1->equals(typeset2);
1969 }
1970 
1971 // Tests whether input/inputTypes can always be stored to an unboxed
1972 // object/array property with the given unboxed type.
1973 bool
CanStoreUnboxedType(TempAllocator & alloc,JSValueType unboxedType,MIRType input,TypeSet * inputTypes)1974 jit::CanStoreUnboxedType(TempAllocator& alloc,
1975                          JSValueType unboxedType, MIRType input, TypeSet* inputTypes)
1976 {
1977     TemporaryTypeSet types;
1978 
1979     switch (unboxedType) {
1980       case JSVAL_TYPE_BOOLEAN:
1981       case JSVAL_TYPE_INT32:
1982       case JSVAL_TYPE_DOUBLE:
1983       case JSVAL_TYPE_STRING:
1984         types.addType(TypeSet::PrimitiveType(unboxedType), alloc.lifoAlloc());
1985         break;
1986 
1987       case JSVAL_TYPE_OBJECT:
1988         types.addType(TypeSet::AnyObjectType(), alloc.lifoAlloc());
1989         types.addType(TypeSet::NullType(), alloc.lifoAlloc());
1990         break;
1991 
1992       default:
1993         MOZ_CRASH("Bad unboxed type");
1994     }
1995 
1996     return TypeSetIncludes(&types, input, inputTypes);
1997 }
1998 
1999 static bool
CanStoreUnboxedType(TempAllocator & alloc,JSValueType unboxedType,MDefinition * value)2000 CanStoreUnboxedType(TempAllocator& alloc, JSValueType unboxedType, MDefinition* value)
2001 {
2002     return CanStoreUnboxedType(alloc, unboxedType, value->type(), value->resultTypeSet());
2003 }
2004 
2005 bool
specializeType()2006 MPhi::specializeType()
2007 {
2008 #ifdef DEBUG
2009     MOZ_ASSERT(!specialized_);
2010     specialized_ = true;
2011 #endif
2012 
2013     MOZ_ASSERT(!inputs_.empty());
2014 
2015     size_t start;
2016     if (hasBackedgeType_) {
2017         // The type of this phi has already been populated with potential types
2018         // that could come in via loop backedges.
2019         start = 0;
2020     } else {
2021         setResultType(getOperand(0)->type());
2022         setResultTypeSet(getOperand(0)->resultTypeSet());
2023         start = 1;
2024     }
2025 
2026     MIRType resultType = this->type();
2027     TemporaryTypeSet* resultTypeSet = this->resultTypeSet();
2028 
2029     for (size_t i = start; i < inputs_.length(); i++) {
2030         MDefinition* def = getOperand(i);
2031         if (!MergeTypes(&resultType, &resultTypeSet, def->type(), def->resultTypeSet()))
2032             return false;
2033     }
2034 
2035     setResultType(resultType);
2036     setResultTypeSet(resultTypeSet);
2037     return true;
2038 }
2039 
2040 bool
addBackedgeType(MIRType type,TemporaryTypeSet * typeSet)2041 MPhi::addBackedgeType(MIRType type, TemporaryTypeSet* typeSet)
2042 {
2043     MOZ_ASSERT(!specialized_);
2044 
2045     if (hasBackedgeType_) {
2046         MIRType resultType = this->type();
2047         TemporaryTypeSet* resultTypeSet = this->resultTypeSet();
2048 
2049         if (!MergeTypes(&resultType, &resultTypeSet, type, typeSet))
2050             return false;
2051 
2052         setResultType(resultType);
2053         setResultTypeSet(resultTypeSet);
2054     } else {
2055         setResultType(type);
2056         setResultTypeSet(typeSet);
2057         hasBackedgeType_ = true;
2058     }
2059     return true;
2060 }
2061 
2062 bool
typeIncludes(MDefinition * def)2063 MPhi::typeIncludes(MDefinition* def)
2064 {
2065     if (def->type() == MIRType_Int32 && this->type() == MIRType_Double)
2066         return true;
2067 
2068     if (TemporaryTypeSet* types = def->resultTypeSet()) {
2069         if (this->resultTypeSet())
2070             return types->isSubset(this->resultTypeSet());
2071         if (this->type() == MIRType_Value || types->empty())
2072             return true;
2073         return this->type() == types->getKnownMIRType();
2074     }
2075 
2076     if (def->type() == MIRType_Value) {
2077         // This phi must be able to be any value.
2078         return this->type() == MIRType_Value
2079             && (!this->resultTypeSet() || this->resultTypeSet()->unknown());
2080     }
2081 
2082     return this->mightBeType(def->type());
2083 }
2084 
2085 bool
checkForTypeChange(MDefinition * ins,bool * ptypeChange)2086 MPhi::checkForTypeChange(MDefinition* ins, bool* ptypeChange)
2087 {
2088     MIRType resultType = this->type();
2089     TemporaryTypeSet* resultTypeSet = this->resultTypeSet();
2090 
2091     if (!MergeTypes(&resultType, &resultTypeSet, ins->type(), ins->resultTypeSet()))
2092         return false;
2093 
2094     if (resultType != this->type() || resultTypeSet != this->resultTypeSet()) {
2095         *ptypeChange = true;
2096         setResultType(resultType);
2097         setResultTypeSet(resultTypeSet);
2098     }
2099     return true;
2100 }
2101 
2102 void
addArg(size_t argnum,MDefinition * arg)2103 MCall::addArg(size_t argnum, MDefinition* arg)
2104 {
2105     // The operand vector is initialized in reverse order by the IonBuilder.
2106     // It cannot be checked for consistency until all arguments are added.
2107     // FixedList doesn't initialize its elements, so do an unchecked init.
2108     initOperand(argnum + NumNonArgumentOperands, arg);
2109 }
2110 
2111 static inline bool
IsConstant(MDefinition * def,double v)2112 IsConstant(MDefinition* def, double v)
2113 {
2114     if (!def->isConstant())
2115         return false;
2116 
2117     return NumbersAreIdentical(def->toConstant()->value().toNumber(), v);
2118 }
2119 
2120 MDefinition*
foldsTo(TempAllocator & alloc)2121 MBinaryBitwiseInstruction::foldsTo(TempAllocator& alloc)
2122 {
2123     if (specialization_ != MIRType_Int32)
2124         return this;
2125 
2126     if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
2127         return folded;
2128 
2129     return this;
2130 }
2131 
2132 MDefinition*
foldUnnecessaryBitop()2133 MBinaryBitwiseInstruction::foldUnnecessaryBitop()
2134 {
2135     if (specialization_ != MIRType_Int32)
2136         return this;
2137 
2138     // Eliminate bitwise operations that are no-ops when used on integer
2139     // inputs, such as (x | 0).
2140 
2141     MDefinition* lhs = getOperand(0);
2142     MDefinition* rhs = getOperand(1);
2143 
2144     if (IsConstant(lhs, 0))
2145         return foldIfZero(0);
2146 
2147     if (IsConstant(rhs, 0))
2148         return foldIfZero(1);
2149 
2150     if (IsConstant(lhs, -1))
2151         return foldIfNegOne(0);
2152 
2153     if (IsConstant(rhs, -1))
2154         return foldIfNegOne(1);
2155 
2156     if (lhs == rhs)
2157         return foldIfEqual();
2158 
2159     return this;
2160 }
2161 
2162 void
infer(BaselineInspector *,jsbytecode *)2163 MBinaryBitwiseInstruction::infer(BaselineInspector*, jsbytecode*)
2164 {
2165     if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(0)->mightBeType(MIRType_Symbol) ||
2166         getOperand(1)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Symbol))
2167     {
2168         specialization_ = MIRType_None;
2169     } else {
2170         specializeAsInt32();
2171     }
2172 }
2173 
2174 void
specializeAsInt32()2175 MBinaryBitwiseInstruction::specializeAsInt32()
2176 {
2177     specialization_ = MIRType_Int32;
2178     MOZ_ASSERT(type() == MIRType_Int32);
2179 
2180     if (isBitOr() || isBitAnd() || isBitXor())
2181         setCommutative();
2182 }
2183 
2184 void
infer(BaselineInspector *,jsbytecode *)2185 MShiftInstruction::infer(BaselineInspector*, jsbytecode*)
2186 {
2187     if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object) ||
2188         getOperand(0)->mightBeType(MIRType_Symbol) || getOperand(1)->mightBeType(MIRType_Symbol))
2189         specialization_ = MIRType_None;
2190     else
2191         specialization_ = MIRType_Int32;
2192 }
2193 
2194 void
infer(BaselineInspector * inspector,jsbytecode * pc)2195 MUrsh::infer(BaselineInspector* inspector, jsbytecode* pc)
2196 {
2197     if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object) ||
2198         getOperand(0)->mightBeType(MIRType_Symbol) || getOperand(1)->mightBeType(MIRType_Symbol))
2199     {
2200         specialization_ = MIRType_None;
2201         setResultType(MIRType_Value);
2202         return;
2203     }
2204 
2205     if (inspector->hasSeenDoubleResult(pc)) {
2206         specialization_ = MIRType_Double;
2207         setResultType(MIRType_Double);
2208         return;
2209     }
2210 
2211     specialization_ = MIRType_Int32;
2212     setResultType(MIRType_Int32);
2213 }
2214 
2215 static inline bool
CanProduceNegativeZero(MDefinition * def)2216 CanProduceNegativeZero(MDefinition* def) {
2217     // Test if this instruction can produce negative zero even when bailing out
2218     // and changing types.
2219     switch (def->op()) {
2220         case MDefinition::Op_Constant:
2221             if (def->type() == MIRType_Double && def->constantValue().toDouble() == -0.0)
2222                 return true;
2223         case MDefinition::Op_BitAnd:
2224         case MDefinition::Op_BitOr:
2225         case MDefinition::Op_BitXor:
2226         case MDefinition::Op_BitNot:
2227         case MDefinition::Op_Lsh:
2228         case MDefinition::Op_Rsh:
2229             return false;
2230         default:
2231             return true;
2232     }
2233 }
2234 
2235 static inline bool
NeedNegativeZeroCheck(MDefinition * def)2236 NeedNegativeZeroCheck(MDefinition* def)
2237 {
2238     // Test if all uses have the same semantics for -0 and 0
2239     for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) {
2240         if (use->consumer()->isResumePoint())
2241             continue;
2242 
2243         MDefinition* use_def = use->consumer()->toDefinition();
2244         switch (use_def->op()) {
2245           case MDefinition::Op_Add: {
2246             // If add is truncating -0 and 0 are observed as the same.
2247             if (use_def->toAdd()->isTruncated())
2248                 break;
2249 
2250             // x + y gives -0, when both x and y are -0
2251 
2252             // Figure out the order in which the addition's operands will
2253             // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR
2254             // definitions for us so that this just requires comparing ids.
2255             MDefinition* first = use_def->toAdd()->lhs();
2256             MDefinition* second = use_def->toAdd()->rhs();
2257             if (first->id() > second->id()) {
2258                 MDefinition* temp = first;
2259                 first = second;
2260                 second = temp;
2261             }
2262             // Negative zero checks can be removed on the first executed
2263             // operand only if it is guaranteed the second executed operand
2264             // will produce a value other than -0. While the second is
2265             // typed as an int32, a bailout taken between execution of the
2266             // operands may change that type and cause a -0 to flow to the
2267             // second.
2268             //
2269             // There is no way to test whether there are any bailouts
2270             // between execution of the operands, so remove negative
2271             // zero checks from the first only if the second's type is
2272             // independent from type changes that may occur after bailing.
2273             if (def == first && CanProduceNegativeZero(second))
2274                 return true;
2275 
2276             // The negative zero check can always be removed on the second
2277             // executed operand; by the time this executes the first will have
2278             // been evaluated as int32 and the addition's result cannot be -0.
2279             break;
2280           }
2281           case MDefinition::Op_Sub: {
2282             // If sub is truncating -0 and 0 are observed as the same
2283             if (use_def->toSub()->isTruncated())
2284                 break;
2285 
2286             // x + y gives -0, when x is -0 and y is 0
2287 
2288             // We can remove the negative zero check on the rhs, only if we
2289             // are sure the lhs isn't negative zero.
2290 
2291             // The lhs is typed as integer (i.e. not -0.0), but it can bailout
2292             // and change type. This should be fine if the lhs is executed
2293             // first. However if the rhs is executed first, the lhs can bail,
2294             // change type and become -0.0 while the rhs has already been
2295             // optimized to not make a difference between zero and negative zero.
2296             MDefinition* lhs = use_def->toSub()->lhs();
2297             MDefinition* rhs = use_def->toSub()->rhs();
2298             if (rhs->id() < lhs->id() && CanProduceNegativeZero(lhs))
2299                 return true;
2300 
2301             /* Fall through...  */
2302           }
2303           case MDefinition::Op_StoreElement:
2304           case MDefinition::Op_StoreElementHole:
2305           case MDefinition::Op_LoadElement:
2306           case MDefinition::Op_LoadElementHole:
2307           case MDefinition::Op_LoadUnboxedScalar:
2308           case MDefinition::Op_LoadTypedArrayElementHole:
2309           case MDefinition::Op_CharCodeAt:
2310           case MDefinition::Op_Mod:
2311             // Only allowed to remove check when definition is the second operand
2312             if (use_def->getOperand(0) == def)
2313                 return true;
2314             for (size_t i = 2, e = use_def->numOperands(); i < e; i++) {
2315                 if (use_def->getOperand(i) == def)
2316                     return true;
2317             }
2318             break;
2319           case MDefinition::Op_BoundsCheck:
2320             // Only allowed to remove check when definition is the first operand
2321             if (use_def->toBoundsCheck()->getOperand(1) == def)
2322                 return true;
2323             break;
2324           case MDefinition::Op_ToString:
2325           case MDefinition::Op_FromCharCode:
2326           case MDefinition::Op_TableSwitch:
2327           case MDefinition::Op_Compare:
2328           case MDefinition::Op_BitAnd:
2329           case MDefinition::Op_BitOr:
2330           case MDefinition::Op_BitXor:
2331           case MDefinition::Op_Abs:
2332           case MDefinition::Op_TruncateToInt32:
2333             // Always allowed to remove check. No matter which operand.
2334             break;
2335           default:
2336             return true;
2337         }
2338     }
2339     return false;
2340 }
2341 
2342 MBinaryArithInstruction*
New(TempAllocator & alloc,Opcode op,MDefinition * left,MDefinition * right)2343 MBinaryArithInstruction::New(TempAllocator& alloc, Opcode op,
2344                              MDefinition* left, MDefinition* right)
2345 {
2346     switch (op) {
2347       case Op_Add:
2348         return MAdd::New(alloc, left, right);
2349       case Op_Sub:
2350         return MSub::New(alloc, left, right);
2351       case Op_Mul:
2352         return MMul::New(alloc, left, right);
2353       case Op_Div:
2354         return MDiv::New(alloc, left, right);
2355       case Op_Mod:
2356         return MMod::New(alloc, left, right);
2357       default:
2358         MOZ_CRASH("unexpected binary opcode");
2359     }
2360 }
2361 
2362 void
setNumberSpecialization(TempAllocator & alloc,BaselineInspector * inspector,jsbytecode * pc)2363 MBinaryArithInstruction::setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector,
2364                                                  jsbytecode* pc)
2365 {
2366     setSpecialization(MIRType_Double);
2367 
2368     // Try to specialize as int32.
2369     if (getOperand(0)->type() == MIRType_Int32 && getOperand(1)->type() == MIRType_Int32) {
2370         bool seenDouble = inspector->hasSeenDoubleResult(pc);
2371 
2372         // Use int32 specialization if the operation doesn't overflow on its
2373         // constant operands and if the operation has never overflowed.
2374         if (!seenDouble && !constantDoubleResult(alloc))
2375             setInt32Specialization();
2376     }
2377 }
2378 
2379 bool
constantDoubleResult(TempAllocator & alloc)2380 MBinaryArithInstruction::constantDoubleResult(TempAllocator& alloc)
2381 {
2382     bool typeChange = false;
2383     EvaluateConstantOperands(alloc, this, &typeChange);
2384     return typeChange;
2385 }
2386 
2387 MDefinition*
foldsTo(TempAllocator & alloc)2388 MBinaryArithInstruction::foldsTo(TempAllocator& alloc)
2389 {
2390     if (specialization_ == MIRType_None)
2391         return this;
2392 
2393     MDefinition* lhs = getOperand(0);
2394     MDefinition* rhs = getOperand(1);
2395     if (MConstant* folded = EvaluateConstantOperands(alloc, this)) {
2396         if (isTruncated()) {
2397             if (!folded->block())
2398                 block()->insertBefore(this, folded);
2399             return MTruncateToInt32::New(alloc, folded);
2400         }
2401         return folded;
2402     }
2403 
2404     // 0 + -0 = 0. So we can't remove addition
2405     if (isAdd() && specialization_ != MIRType_Int32)
2406         return this;
2407 
2408     if (IsConstant(rhs, getIdentity())) {
2409         if (isTruncated())
2410             return MTruncateToInt32::New(alloc, lhs);
2411         return lhs;
2412     }
2413 
2414     // subtraction isn't commutative. So we can't remove subtraction when lhs equals 0
2415     if (isSub())
2416         return this;
2417 
2418     if (IsConstant(lhs, getIdentity())) {
2419         if (isTruncated())
2420             return MTruncateToInt32::New(alloc, rhs);
2421         return rhs; // x op id => x
2422     }
2423 
2424     return this;
2425 }
2426 
2427 void
trySpecializeFloat32(TempAllocator & alloc)2428 MFilterTypeSet::trySpecializeFloat32(TempAllocator& alloc)
2429 {
2430     MDefinition* in = input();
2431     if (in->type() != MIRType_Float32)
2432         return;
2433 
2434     setResultType(MIRType_Float32);
2435 }
2436 
2437 bool
canProduceFloat32() const2438 MFilterTypeSet::canProduceFloat32() const
2439 {
2440     // A FilterTypeSet should be a producer if the input is a producer too.
2441     // Also, be overly conservative by marking as not float32 producer when the
2442     // input is a phi, as phis can be cyclic (phiA -> FilterTypeSet -> phiB ->
2443     // phiA) and FilterTypeSet doesn't belong in the Float32 phi analysis.
2444     return !input()->isPhi() && input()->canProduceFloat32();
2445 }
2446 
2447 bool
canConsumeFloat32(MUse * operand) const2448 MFilterTypeSet::canConsumeFloat32(MUse* operand) const
2449 {
2450     MOZ_ASSERT(getUseFor(0) == operand);
2451     // A FilterTypeSet should be a consumer if all uses are consumer. See also
2452     // comment below MFilterTypeSet::canProduceFloat32.
2453     bool allConsumerUses = true;
2454     for (MUseDefIterator use(this); allConsumerUses && use; use++)
2455         allConsumerUses &= !use.def()->isPhi() && use.def()->canConsumeFloat32(use.use());
2456     return allConsumerUses;
2457 }
2458 
2459 void
trySpecializeFloat32(TempAllocator & alloc)2460 MBinaryArithInstruction::trySpecializeFloat32(TempAllocator& alloc)
2461 {
2462     // Do not use Float32 if we can use int32.
2463     if (specialization_ == MIRType_Int32)
2464         return;
2465     if (specialization_ == MIRType_None)
2466         return;
2467 
2468     MDefinition* left = lhs();
2469     MDefinition* right = rhs();
2470 
2471     if (!left->canProduceFloat32() || !right->canProduceFloat32() ||
2472         !CheckUsesAreFloat32Consumers(this))
2473     {
2474         if (left->type() == MIRType_Float32)
2475             ConvertDefinitionToDouble<0>(alloc, left, this);
2476         if (right->type() == MIRType_Float32)
2477             ConvertDefinitionToDouble<1>(alloc, right, this);
2478         return;
2479     }
2480 
2481     specialization_ = MIRType_Float32;
2482     setResultType(MIRType_Float32);
2483 }
2484 
2485 void
trySpecializeFloat32(TempAllocator & alloc)2486 MMinMax::trySpecializeFloat32(TempAllocator& alloc)
2487 {
2488     if (specialization_ == MIRType_Int32)
2489         return;
2490 
2491     MDefinition* left = lhs();
2492     MDefinition* right = rhs();
2493 
2494     if (!(left->canProduceFloat32() || (left->isMinMax() && left->type() == MIRType_Float32)) ||
2495         !(right->canProduceFloat32() || (right->isMinMax() && right->type() == MIRType_Float32)))
2496     {
2497         if (left->type() == MIRType_Float32)
2498             ConvertDefinitionToDouble<0>(alloc, left, this);
2499         if (right->type() == MIRType_Float32)
2500             ConvertDefinitionToDouble<1>(alloc, right, this);
2501         return;
2502     }
2503 
2504     specialization_ = MIRType_Float32;
2505     setResultType(MIRType_Float32);
2506 }
2507 
2508 MDefinition*
foldsTo(TempAllocator & alloc)2509 MMinMax::foldsTo(TempAllocator& alloc)
2510 {
2511     if (!lhs()->isConstant() && !rhs()->isConstant())
2512         return this;
2513 
2514     // Directly apply math utility to compare the rhs() and lhs() when
2515     // they are both constants.
2516     if (lhs()->isConstant() && rhs()->isConstant()) {
2517         Value lval = lhs()->toConstant()->value();
2518         Value rval = rhs()->toConstant()->value();
2519         if (!lval.isNumber() || !rval.isNumber())
2520             return this;
2521 
2522         double lnum = lval.toNumber();
2523         double rnum = rval.toNumber();
2524         double result;
2525         if (isMax())
2526             result = js::math_max_impl(lnum, rnum);
2527         else
2528             result = js::math_min_impl(lnum, rnum);
2529 
2530         // The folded MConstant should maintain the same MIRType with
2531         // the original MMinMax.
2532         if (type() == MIRType_Int32) {
2533             int32_t cast;
2534             if (mozilla::NumberEqualsInt32(result, &cast))
2535                 return MConstant::New(alloc, Int32Value(cast));
2536         } else {
2537             MOZ_ASSERT(IsFloatingPointType(type()));
2538             MConstant* constant = MConstant::New(alloc, DoubleValue(result));
2539             if (type() == MIRType_Float32)
2540                 constant->setResultType(MIRType_Float32);
2541             return constant;
2542         }
2543     }
2544 
2545     MDefinition* operand = lhs()->isConstantValue() ? rhs() : lhs();
2546     const js::Value& val = lhs()->isConstantValue() ? lhs()->constantValue() : rhs()->constantValue();
2547 
2548     if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType_Int32) {
2549         // min(int32, cte >= INT32_MAX) = int32
2550         if (val.isDouble() && val.toDouble() >= INT32_MAX && !isMax()) {
2551             MLimitedTruncate* limit =
2552                 MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate);
2553             block()->insertBefore(this, limit);
2554             MToDouble* toDouble = MToDouble::New(alloc, limit);
2555             return toDouble;
2556         }
2557 
2558         // max(int32, cte <= INT32_MIN) = int32
2559         if (val.isDouble() && val.toDouble() <= INT32_MIN && isMax()) {
2560             MLimitedTruncate* limit =
2561                 MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate);
2562             block()->insertBefore(this, limit);
2563             MToDouble* toDouble = MToDouble::New(alloc, limit);
2564             return toDouble;
2565         }
2566     }
2567     return this;
2568 }
2569 
2570 bool
fallible() const2571 MAbs::fallible() const
2572 {
2573     return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds());
2574 }
2575 
2576 void
trySpecializeFloat32(TempAllocator & alloc)2577 MAbs::trySpecializeFloat32(TempAllocator& alloc)
2578 {
2579     // Do not use Float32 if we can use int32.
2580     if (input()->type() == MIRType_Int32)
2581         return;
2582 
2583     if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
2584         if (input()->type() == MIRType_Float32)
2585             ConvertDefinitionToDouble<0>(alloc, input(), this);
2586         return;
2587     }
2588 
2589     setResultType(MIRType_Float32);
2590     specialization_ = MIRType_Float32;
2591 }
2592 
2593 MDefinition*
foldsTo(TempAllocator & alloc)2594 MDiv::foldsTo(TempAllocator& alloc)
2595 {
2596     if (specialization_ == MIRType_None)
2597         return this;
2598 
2599     if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
2600         return folded;
2601 
2602     if (MDefinition* folded = EvaluateExactReciprocal(alloc, this))
2603         return folded;
2604 
2605     return this;
2606 }
2607 
2608 void
analyzeEdgeCasesForward()2609 MDiv::analyzeEdgeCasesForward()
2610 {
2611     // This is only meaningful when doing integer division.
2612     if (specialization_ != MIRType_Int32)
2613         return;
2614 
2615     // Try removing divide by zero check
2616     if (rhs()->isConstantValue() && !rhs()->constantValue().isInt32(0))
2617         canBeDivideByZero_ = false;
2618 
2619     // If lhs is a constant int != INT32_MIN, then
2620     // negative overflow check can be skipped.
2621     if (lhs()->isConstantValue() && !lhs()->constantValue().isInt32(INT32_MIN))
2622         canBeNegativeOverflow_ = false;
2623 
2624     // If rhs is a constant int != -1, likewise.
2625     if (rhs()->isConstantValue() && !rhs()->constantValue().isInt32(-1))
2626         canBeNegativeOverflow_ = false;
2627 
2628     // If lhs is != 0, then negative zero check can be skipped.
2629     if (lhs()->isConstantValue() && !lhs()->constantValue().isInt32(0))
2630         setCanBeNegativeZero(false);
2631 
2632     // If rhs is >= 0, likewise.
2633     if (rhs()->isConstantValue()) {
2634         const js::Value& val = rhs()->constantValue();
2635         if (val.isInt32() && val.toInt32() >= 0)
2636             setCanBeNegativeZero(false);
2637     }
2638 }
2639 
2640 void
analyzeEdgeCasesBackward()2641 MDiv::analyzeEdgeCasesBackward()
2642 {
2643     if (canBeNegativeZero() && !NeedNegativeZeroCheck(this))
2644         setCanBeNegativeZero(false);
2645 }
2646 
2647 bool
fallible() const2648 MDiv::fallible() const
2649 {
2650     return !isTruncated();
2651 }
2652 
2653 MDefinition*
foldsTo(TempAllocator & alloc)2654 MMod::foldsTo(TempAllocator& alloc)
2655 {
2656     if (specialization_ == MIRType_None)
2657         return this;
2658 
2659     if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
2660         return folded;
2661 
2662     return this;
2663 }
2664 
2665 void
analyzeEdgeCasesForward()2666 MMod::analyzeEdgeCasesForward()
2667 {
2668     // These optimizations make sense only for integer division
2669     if (specialization_ != MIRType_Int32)
2670         return;
2671 
2672     if (rhs()->isConstantValue() && !rhs()->constantValue().isInt32(0))
2673         canBeDivideByZero_ = false;
2674 
2675     if (rhs()->isConstantValue()) {
2676         int32_t n = rhs()->constantValue().toInt32();
2677         if (n > 0 && !IsPowerOfTwo(n))
2678             canBePowerOfTwoDivisor_ = false;
2679     }
2680 }
2681 
2682 bool
fallible() const2683 MMod::fallible() const
2684 {
2685     return !isTruncated() &&
2686            (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend());
2687 }
2688 
2689 void
trySpecializeFloat32(TempAllocator & alloc)2690 MMathFunction::trySpecializeFloat32(TempAllocator& alloc)
2691 {
2692     if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
2693         if (input()->type() == MIRType_Float32)
2694             ConvertDefinitionToDouble<0>(alloc, input(), this);
2695         return;
2696     }
2697 
2698     setResultType(MIRType_Float32);
2699     specialization_ = MIRType_Float32;
2700 }
2701 
New(TempAllocator & alloc,const MDefinitionVector & vector)2702 MHypot* MHypot::New(TempAllocator& alloc, const MDefinitionVector & vector)
2703 {
2704     uint32_t length = vector.length();
2705     MHypot * hypot = new(alloc) MHypot;
2706     if (!hypot->init(alloc, length))
2707         return nullptr;
2708 
2709     for (uint32_t i = 0; i < length; ++i)
2710         hypot->initOperand(i, vector[i]);
2711     return hypot;
2712 }
2713 
2714 bool
fallible() const2715 MAdd::fallible() const
2716 {
2717     // the add is fallible if range analysis does not say that it is finite, AND
2718     // either the truncation analysis shows that there are non-truncated uses.
2719     if (truncateKind() >= IndirectTruncate)
2720         return false;
2721     if (range() && range()->hasInt32Bounds())
2722         return false;
2723     return true;
2724 }
2725 
2726 bool
fallible() const2727 MSub::fallible() const
2728 {
2729     // see comment in MAdd::fallible()
2730     if (truncateKind() >= IndirectTruncate)
2731         return false;
2732     if (range() && range()->hasInt32Bounds())
2733         return false;
2734     return true;
2735 }
2736 
2737 MDefinition*
foldsTo(TempAllocator & alloc)2738 MMul::foldsTo(TempAllocator& alloc)
2739 {
2740     MDefinition* out = MBinaryArithInstruction::foldsTo(alloc);
2741     if (out != this)
2742         return out;
2743 
2744     if (specialization() != MIRType_Int32)
2745         return this;
2746 
2747     if (lhs() == rhs())
2748         setCanBeNegativeZero(false);
2749 
2750     return this;
2751 }
2752 
2753 void
analyzeEdgeCasesForward()2754 MMul::analyzeEdgeCasesForward()
2755 {
2756     // Try to remove the check for negative zero
2757     // This only makes sense when using the integer multiplication
2758     if (specialization() != MIRType_Int32)
2759         return;
2760 
2761     // If lhs is > 0, no need for negative zero check.
2762     if (lhs()->isConstantValue()) {
2763         const js::Value& val = lhs()->constantValue();
2764         if (val.isInt32() && val.toInt32() > 0)
2765             setCanBeNegativeZero(false);
2766     }
2767 
2768     // If rhs is > 0, likewise.
2769     if (rhs()->isConstantValue()) {
2770         const js::Value& val = rhs()->constantValue();
2771         if (val.isInt32() && val.toInt32() > 0)
2772             setCanBeNegativeZero(false);
2773     }
2774 }
2775 
2776 void
analyzeEdgeCasesBackward()2777 MMul::analyzeEdgeCasesBackward()
2778 {
2779     if (canBeNegativeZero() && !NeedNegativeZeroCheck(this))
2780         setCanBeNegativeZero(false);
2781 }
2782 
2783 bool
updateForReplacement(MDefinition * ins_)2784 MMul::updateForReplacement(MDefinition* ins_)
2785 {
2786     MMul* ins = ins_->toMul();
2787     bool negativeZero = canBeNegativeZero() || ins->canBeNegativeZero();
2788     setCanBeNegativeZero(negativeZero);
2789     // Remove the imul annotation when merging imul and normal multiplication.
2790     if (mode_ == Integer && ins->mode() != Integer)
2791         mode_ = Normal;
2792     return true;
2793 }
2794 
2795 bool
canOverflow() const2796 MMul::canOverflow() const
2797 {
2798     if (isTruncated())
2799         return false;
2800     return !range() || !range()->hasInt32Bounds();
2801 }
2802 
2803 bool
fallible() const2804 MUrsh::fallible() const
2805 {
2806     if (bailoutsDisabled())
2807         return false;
2808     return !range() || !range()->hasInt32Bounds();
2809 }
2810 
2811 static inline bool
SimpleArithOperand(MDefinition * op)2812 SimpleArithOperand(MDefinition* op)
2813 {
2814     return !op->mightBeType(MIRType_Object)
2815         && !op->mightBeType(MIRType_String)
2816         && !op->mightBeType(MIRType_Symbol)
2817         && !op->mightBeType(MIRType_MagicOptimizedArguments)
2818         && !op->mightBeType(MIRType_MagicHole)
2819         && !op->mightBeType(MIRType_MagicIsConstructing);
2820 }
2821 
2822 static bool
SafelyCoercesToDouble(MDefinition * op)2823 SafelyCoercesToDouble(MDefinition* op)
2824 {
2825     // Strings and symbols are unhandled -- visitToDouble() doesn't support them yet.
2826     // Null is unhandled -- ToDouble(null) == 0, but (0 == null) is false.
2827     return SimpleArithOperand(op) && !op->mightBeType(MIRType_Null);
2828 }
2829 
2830 MIRType
inputType()2831 MCompare::inputType()
2832 {
2833     switch(compareType_) {
2834       case Compare_Undefined:
2835         return MIRType_Undefined;
2836       case Compare_Null:
2837         return MIRType_Null;
2838       case Compare_Boolean:
2839         return MIRType_Boolean;
2840       case Compare_UInt32:
2841       case Compare_Int32:
2842       case Compare_Int32MaybeCoerceBoth:
2843       case Compare_Int32MaybeCoerceLHS:
2844       case Compare_Int32MaybeCoerceRHS:
2845         return MIRType_Int32;
2846       case Compare_Double:
2847       case Compare_DoubleMaybeCoerceLHS:
2848       case Compare_DoubleMaybeCoerceRHS:
2849         return MIRType_Double;
2850       case Compare_Float32:
2851         return MIRType_Float32;
2852       case Compare_String:
2853       case Compare_StrictString:
2854         return MIRType_String;
2855       case Compare_Object:
2856         return MIRType_Object;
2857       case Compare_Unknown:
2858       case Compare_Bitwise:
2859         return MIRType_Value;
2860       default:
2861         MOZ_CRASH("No known conversion");
2862     }
2863 }
2864 
2865 static inline bool
MustBeUInt32(MDefinition * def,MDefinition ** pwrapped)2866 MustBeUInt32(MDefinition* def, MDefinition** pwrapped)
2867 {
2868     if (def->isUrsh()) {
2869         *pwrapped = def->toUrsh()->getOperand(0);
2870         MDefinition* rhs = def->toUrsh()->getOperand(1);
2871         return def->toUrsh()->bailoutsDisabled()
2872             && rhs->isConstantValue()
2873             && rhs->constantValue().isInt32()
2874             && rhs->constantValue().toInt32() == 0;
2875     }
2876 
2877     if (def->isConstantValue()) {
2878         if (def->isBox())
2879             def = def->toBox()->getOperand(0);
2880         *pwrapped = def;
2881         return def->constantValue().isInt32()
2882             && def->constantValue().toInt32() >= 0;
2883     }
2884 
2885     return false;
2886 }
2887 
2888 /* static */ bool
unsignedOperands(MDefinition * left,MDefinition * right)2889 MBinaryInstruction::unsignedOperands(MDefinition* left, MDefinition* right)
2890 {
2891     MDefinition* replace;
2892     if (!MustBeUInt32(left, &replace))
2893         return false;
2894     if (replace->type() != MIRType_Int32)
2895         return false;
2896     if (!MustBeUInt32(right, &replace))
2897         return false;
2898     if (replace->type() != MIRType_Int32)
2899         return false;
2900     return true;
2901 }
2902 
2903 bool
unsignedOperands()2904 MBinaryInstruction::unsignedOperands()
2905 {
2906     return unsignedOperands(getOperand(0), getOperand(1));
2907 }
2908 
2909 void
replaceWithUnsignedOperands()2910 MBinaryInstruction::replaceWithUnsignedOperands()
2911 {
2912     MOZ_ASSERT(unsignedOperands());
2913 
2914     for (size_t i = 0; i < numOperands(); i++) {
2915         MDefinition* replace;
2916         MustBeUInt32(getOperand(i), &replace);
2917         if (replace == getOperand(i))
2918             continue;
2919 
2920         getOperand(i)->setImplicitlyUsedUnchecked();
2921         replaceOperand(i, replace);
2922     }
2923 }
2924 
2925 MCompare::CompareType
determineCompareType(JSOp op,MDefinition * left,MDefinition * right)2926 MCompare::determineCompareType(JSOp op, MDefinition* left, MDefinition* right)
2927 {
2928     MIRType lhs = left->type();
2929     MIRType rhs = right->type();
2930 
2931     bool looseEq = op == JSOP_EQ || op == JSOP_NE;
2932     bool strictEq = op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
2933     bool relationalEq = !(looseEq || strictEq);
2934 
2935     // Comparisons on unsigned integers may be treated as UInt32.
2936     if (unsignedOperands(left, right))
2937         return Compare_UInt32;
2938 
2939     // Integer to integer or boolean to boolean comparisons may be treated as Int32.
2940     if ((lhs == MIRType_Int32 && rhs == MIRType_Int32) ||
2941         (lhs == MIRType_Boolean && rhs == MIRType_Boolean))
2942     {
2943         return Compare_Int32MaybeCoerceBoth;
2944     }
2945 
2946     // Loose/relational cross-integer/boolean comparisons may be treated as Int32.
2947     if (!strictEq &&
2948         (lhs == MIRType_Int32 || lhs == MIRType_Boolean) &&
2949         (rhs == MIRType_Int32 || rhs == MIRType_Boolean))
2950     {
2951         return Compare_Int32MaybeCoerceBoth;
2952     }
2953 
2954     // Numeric comparisons against a double coerce to double.
2955     if (IsNumberType(lhs) && IsNumberType(rhs))
2956         return Compare_Double;
2957 
2958     // Any comparison is allowed except strict eq.
2959     if (!strictEq && IsFloatingPointType(rhs) && SafelyCoercesToDouble(left))
2960         return Compare_DoubleMaybeCoerceLHS;
2961     if (!strictEq && IsFloatingPointType(lhs) && SafelyCoercesToDouble(right))
2962         return Compare_DoubleMaybeCoerceRHS;
2963 
2964     // Handle object comparison.
2965     if (!relationalEq && lhs == MIRType_Object && rhs == MIRType_Object)
2966         return Compare_Object;
2967 
2968     // Handle string comparisons. (Relational string compares are still unsupported).
2969     if (!relationalEq && lhs == MIRType_String && rhs == MIRType_String)
2970         return Compare_String;
2971 
2972     // Handle strict string compare.
2973     if (strictEq && lhs == MIRType_String)
2974         return Compare_StrictString;
2975     if (strictEq && rhs == MIRType_String)
2976         return Compare_StrictString;
2977 
2978     // Handle compare with lhs or rhs being Undefined or Null.
2979     if (!relationalEq && IsNullOrUndefined(lhs))
2980         return (lhs == MIRType_Null) ? Compare_Null : Compare_Undefined;
2981     if (!relationalEq && IsNullOrUndefined(rhs))
2982         return (rhs == MIRType_Null) ? Compare_Null : Compare_Undefined;
2983 
2984     // Handle strict comparison with lhs/rhs being typed Boolean.
2985     if (strictEq && (lhs == MIRType_Boolean || rhs == MIRType_Boolean)) {
2986         // bool/bool case got an int32 specialization earlier.
2987         MOZ_ASSERT(!(lhs == MIRType_Boolean && rhs == MIRType_Boolean));
2988         return Compare_Boolean;
2989     }
2990 
2991     return Compare_Unknown;
2992 }
2993 
2994 void
cacheOperandMightEmulateUndefined(CompilerConstraintList * constraints)2995 MCompare::cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints)
2996 {
2997     MOZ_ASSERT(operandMightEmulateUndefined());
2998 
2999     if (getOperand(0)->maybeEmulatesUndefined(constraints))
3000         return;
3001     if (getOperand(1)->maybeEmulatesUndefined(constraints))
3002         return;
3003 
3004     markNoOperandEmulatesUndefined();
3005 }
3006 
3007 MBitNot*
New(TempAllocator & alloc,MDefinition * input)3008 MBitNot::New(TempAllocator& alloc, MDefinition* input)
3009 {
3010     return new(alloc) MBitNot(input);
3011 }
3012 
3013 MBitNot*
NewAsmJS(TempAllocator & alloc,MDefinition * input)3014 MBitNot::NewAsmJS(TempAllocator& alloc, MDefinition* input)
3015 {
3016     MBitNot* ins = new(alloc) MBitNot(input);
3017     ins->specialization_ = MIRType_Int32;
3018     MOZ_ASSERT(ins->type() == MIRType_Int32);
3019     return ins;
3020 }
3021 
3022 MDefinition*
foldsTo(TempAllocator & alloc)3023 MBitNot::foldsTo(TempAllocator& alloc)
3024 {
3025     if (specialization_ != MIRType_Int32)
3026         return this;
3027 
3028     MDefinition* input = getOperand(0);
3029 
3030     if (input->isConstant()) {
3031         js::Value v = Int32Value(~(input->constantValue().toInt32()));
3032         return MConstant::New(alloc, v);
3033     }
3034 
3035     if (input->isBitNot() && input->toBitNot()->specialization_ == MIRType_Int32) {
3036         MOZ_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType_Int32);
3037         return MTruncateToInt32::New(alloc, input->toBitNot()->input()); // ~~x => x | 0
3038     }
3039 
3040     return this;
3041 }
3042 
3043 MDefinition*
foldsTo(TempAllocator & alloc)3044 MTypeOf::foldsTo(TempAllocator& alloc)
3045 {
3046     // Note: we can't use input->type() here, type analysis has
3047     // boxed the input.
3048     MOZ_ASSERT(input()->type() == MIRType_Value);
3049 
3050     JSType type;
3051 
3052     switch (inputType()) {
3053       case MIRType_Double:
3054       case MIRType_Float32:
3055       case MIRType_Int32:
3056         type = JSTYPE_NUMBER;
3057         break;
3058       case MIRType_String:
3059         type = JSTYPE_STRING;
3060         break;
3061       case MIRType_Symbol:
3062         type = JSTYPE_SYMBOL;
3063         break;
3064       case MIRType_Null:
3065         type = JSTYPE_OBJECT;
3066         break;
3067       case MIRType_Undefined:
3068         type = JSTYPE_VOID;
3069         break;
3070       case MIRType_Boolean:
3071         type = JSTYPE_BOOLEAN;
3072         break;
3073       case MIRType_Object:
3074         if (!inputMaybeCallableOrEmulatesUndefined()) {
3075             // Object is not callable and does not emulate undefined, so it's
3076             // safe to fold to "object".
3077             type = JSTYPE_OBJECT;
3078             break;
3079         }
3080         // FALL THROUGH
3081       default:
3082         return this;
3083     }
3084 
3085     return MConstant::New(alloc, StringValue(TypeName(type, GetJitContext()->runtime->names())));
3086 }
3087 
3088 void
cacheInputMaybeCallableOrEmulatesUndefined(CompilerConstraintList * constraints)3089 MTypeOf::cacheInputMaybeCallableOrEmulatesUndefined(CompilerConstraintList* constraints)
3090 {
3091     MOZ_ASSERT(inputMaybeCallableOrEmulatesUndefined());
3092 
3093     if (!input()->maybeEmulatesUndefined(constraints) && !MaybeCallable(constraints, input()))
3094         markInputNotCallableOrEmulatesUndefined();
3095 }
3096 
3097 MBitAnd*
New(TempAllocator & alloc,MDefinition * left,MDefinition * right)3098 MBitAnd::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3099 {
3100     return new(alloc) MBitAnd(left, right);
3101 }
3102 
3103 MBitAnd*
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right)3104 MBitAnd::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3105 {
3106     MBitAnd* ins = new(alloc) MBitAnd(left, right);
3107     ins->specializeAsInt32();
3108     return ins;
3109 }
3110 
3111 MBitOr*
New(TempAllocator & alloc,MDefinition * left,MDefinition * right)3112 MBitOr::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3113 {
3114     return new(alloc) MBitOr(left, right);
3115 }
3116 
3117 MBitOr*
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right)3118 MBitOr::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3119 {
3120     MBitOr* ins = new(alloc) MBitOr(left, right);
3121     ins->specializeAsInt32();
3122     return ins;
3123 }
3124 
3125 MBitXor*
New(TempAllocator & alloc,MDefinition * left,MDefinition * right)3126 MBitXor::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3127 {
3128     return new(alloc) MBitXor(left, right);
3129 }
3130 
3131 MBitXor*
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right)3132 MBitXor::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3133 {
3134     MBitXor* ins = new(alloc) MBitXor(left, right);
3135     ins->specializeAsInt32();
3136     return ins;
3137 }
3138 
3139 MLsh*
New(TempAllocator & alloc,MDefinition * left,MDefinition * right)3140 MLsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3141 {
3142     return new(alloc) MLsh(left, right);
3143 }
3144 
3145 MLsh*
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right)3146 MLsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3147 {
3148     MLsh* ins = new(alloc) MLsh(left, right);
3149     ins->specializeAsInt32();
3150     return ins;
3151 }
3152 
3153 MRsh*
New(TempAllocator & alloc,MDefinition * left,MDefinition * right)3154 MRsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3155 {
3156     return new(alloc) MRsh(left, right);
3157 }
3158 
3159 MRsh*
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right)3160 MRsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3161 {
3162     MRsh* ins = new(alloc) MRsh(left, right);
3163     ins->specializeAsInt32();
3164     return ins;
3165 }
3166 
3167 MUrsh*
New(TempAllocator & alloc,MDefinition * left,MDefinition * right)3168 MUrsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3169 {
3170     return new(alloc) MUrsh(left, right);
3171 }
3172 
3173 MUrsh*
NewAsmJS(TempAllocator & alloc,MDefinition * left,MDefinition * right)3174 MUrsh::NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3175 {
3176     MUrsh* ins = new(alloc) MUrsh(left, right);
3177     ins->specializeAsInt32();
3178 
3179     // Since Ion has no UInt32 type, we use Int32 and we have a special
3180     // exception to the type rules: we can return values in
3181     // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
3182     // without bailing out. This is necessary because Ion has no UInt32
3183     // type and we can't have bailouts in asm.js code.
3184     ins->bailoutsDisabled_ = true;
3185 
3186     return ins;
3187 }
3188 
3189 MResumePoint*
New(TempAllocator & alloc,MBasicBlock * block,jsbytecode * pc,Mode mode)3190 MResumePoint::New(TempAllocator& alloc, MBasicBlock* block, jsbytecode* pc,
3191                   Mode mode)
3192 {
3193     MResumePoint* resume = new(alloc) MResumePoint(block, pc, mode);
3194     if (!resume->init(alloc))
3195         return nullptr;
3196     resume->inherit(block);
3197     return resume;
3198 }
3199 
3200 MResumePoint*
New(TempAllocator & alloc,MBasicBlock * block,MResumePoint * model,const MDefinitionVector & operands)3201 MResumePoint::New(TempAllocator& alloc, MBasicBlock* block, MResumePoint* model,
3202                   const MDefinitionVector& operands)
3203 {
3204     MResumePoint* resume = new(alloc) MResumePoint(block, model->pc(), model->mode());
3205 
3206     // Allocate the same number of operands as the original resume point, and
3207     // copy operands from the operands vector and not the not from the current
3208     // block stack.
3209     if (!resume->operands_.init(alloc, model->numAllocatedOperands()))
3210         return nullptr;
3211 
3212     // Copy the operands.
3213     for (size_t i = 0; i < operands.length(); i++)
3214         resume->initOperand(i, operands[i]);
3215 
3216     return resume;
3217 }
3218 
3219 MResumePoint*
Copy(TempAllocator & alloc,MResumePoint * src)3220 MResumePoint::Copy(TempAllocator& alloc, MResumePoint* src)
3221 {
3222     MResumePoint* resume = new(alloc) MResumePoint(src->block(), src->pc(),
3223                                                    src->mode());
3224     // Copy the operands from the original resume point, and not from the
3225     // current block stack.
3226     if (!resume->operands_.init(alloc, src->numAllocatedOperands()))
3227         return nullptr;
3228 
3229     // Copy the operands.
3230     for (size_t i = 0; i < resume->numOperands(); i++)
3231         resume->initOperand(i, src->getOperand(i));
3232     return resume;
3233 }
3234 
MResumePoint(MBasicBlock * block,jsbytecode * pc,Mode mode)3235 MResumePoint::MResumePoint(MBasicBlock* block, jsbytecode* pc, Mode mode)
3236   : MNode(block),
3237     pc_(pc),
3238     instruction_(nullptr),
3239     mode_(mode)
3240 {
3241     block->addResumePoint(this);
3242 }
3243 
3244 bool
init(TempAllocator & alloc)3245 MResumePoint::init(TempAllocator& alloc)
3246 {
3247     return operands_.init(alloc, block()->stackDepth());
3248 }
3249 
3250 MResumePoint*
caller() const3251 MResumePoint::caller() const
3252 {
3253     return block_->callerResumePoint();
3254 }
3255 
3256 void
inherit(MBasicBlock * block)3257 MResumePoint::inherit(MBasicBlock* block)
3258 {
3259     // FixedList doesn't initialize its elements, so do unchecked inits.
3260     for (size_t i = 0; i < stackDepth(); i++)
3261         initOperand(i, block->getSlot(i));
3262 }
3263 
3264 void
addStore(TempAllocator & alloc,MDefinition * store,const MResumePoint * cache)3265 MResumePoint::addStore(TempAllocator& alloc, MDefinition* store, const MResumePoint* cache)
3266 {
3267     MOZ_ASSERT(block()->outerResumePoint() != this);
3268     MOZ_ASSERT_IF(cache, !cache->stores_.empty());
3269 
3270     if (cache && cache->stores_.begin()->operand == store) {
3271         // If the last resume point had the same side-effect stack, then we can
3272         // reuse the current side effect without cloning it. This is a simple
3273         // way to share common context by making a spaghetti stack.
3274         if (++cache->stores_.begin() == stores_.begin()) {
3275             stores_.copy(cache->stores_);
3276             return;
3277         }
3278     }
3279 
3280     // Ensure that the store would not be deleted by DCE.
3281     MOZ_ASSERT(store->isEffectful());
3282 
3283     MStoreToRecover* top = new(alloc) MStoreToRecover(store);
3284     stores_.push(top);
3285 }
3286 
3287 void
dump(GenericPrinter & out) const3288 MResumePoint::dump(GenericPrinter& out) const
3289 {
3290     out.printf("resumepoint mode=");
3291 
3292     switch (mode()) {
3293       case MResumePoint::ResumeAt:
3294         out.printf("At");
3295         break;
3296       case MResumePoint::ResumeAfter:
3297         out.printf("After");
3298         break;
3299       case MResumePoint::Outer:
3300         out.printf("Outer");
3301         break;
3302     }
3303 
3304     if (MResumePoint* c = caller())
3305         out.printf(" (caller in block%u)", c->block()->id());
3306 
3307     for (size_t i = 0; i < numOperands(); i++) {
3308         out.printf(" ");
3309         if (operands_[i].hasProducer())
3310             getOperand(i)->printName(out);
3311         else
3312             out.printf("(null)");
3313     }
3314     out.printf("\n");
3315 }
3316 
3317 void
dump() const3318 MResumePoint::dump() const
3319 {
3320     Fprinter out(stderr);
3321     dump(out);
3322     out.finish();
3323 }
3324 
3325 bool
isObservableOperand(MUse * u) const3326 MResumePoint::isObservableOperand(MUse* u) const
3327 {
3328     return isObservableOperand(indexOf(u));
3329 }
3330 
3331 bool
isObservableOperand(size_t index) const3332 MResumePoint::isObservableOperand(size_t index) const
3333 {
3334     return block()->info().isObservableSlot(index);
3335 }
3336 
3337 bool
isRecoverableOperand(MUse * u) const3338 MResumePoint::isRecoverableOperand(MUse* u) const
3339 {
3340     return block()->info().isRecoverableOperand(indexOf(u));
3341 }
3342 
3343 MDefinition*
foldsTo(TempAllocator & alloc)3344 MToInt32::foldsTo(TempAllocator& alloc)
3345 {
3346     MDefinition* input = getOperand(0);
3347 
3348     // Fold this operation if the input operand is constant.
3349     if (input->isConstant()) {
3350         Value val = input->toConstant()->value();
3351         DebugOnly<MacroAssembler::IntConversionInputKind> convert = conversion();
3352         switch (input->type()) {
3353           case MIRType_Null:
3354             MOZ_ASSERT(convert == MacroAssembler::IntConversion_Any);
3355             return MConstant::New(alloc, Int32Value(0));
3356           case MIRType_Boolean:
3357             MOZ_ASSERT(convert == MacroAssembler::IntConversion_Any ||
3358                        convert == MacroAssembler::IntConversion_NumbersOrBoolsOnly);
3359             return MConstant::New(alloc, Int32Value(val.toBoolean()));
3360           case MIRType_Int32:
3361             return MConstant::New(alloc, Int32Value(val.toInt32()));
3362           case MIRType_Float32:
3363           case MIRType_Double:
3364             int32_t ival;
3365             // Only the value within the range of Int32 can be substitued as constant.
3366             if (mozilla::NumberEqualsInt32(val.toNumber(), &ival))
3367                 return MConstant::New(alloc, Int32Value(ival));
3368           default:
3369             break;
3370         }
3371     }
3372 
3373     if (input->type() == MIRType_Int32)
3374         return input;
3375     return this;
3376 }
3377 
3378 void
analyzeEdgeCasesBackward()3379 MToInt32::analyzeEdgeCasesBackward()
3380 {
3381     if (!NeedNegativeZeroCheck(this))
3382         setCanBeNegativeZero(false);
3383 }
3384 
3385 MDefinition*
foldsTo(TempAllocator & alloc)3386 MTruncateToInt32::foldsTo(TempAllocator& alloc)
3387 {
3388     MDefinition* input = getOperand(0);
3389     if (input->isBox())
3390         input = input->getOperand(0);
3391 
3392     if (input->type() == MIRType_Int32)
3393         return input;
3394 
3395     if (input->type() == MIRType_Double && input->isConstant()) {
3396         const Value& v = input->constantValue();
3397         int32_t ret = ToInt32(v.toDouble());
3398         return MConstant::New(alloc, Int32Value(ret));
3399     }
3400 
3401     return this;
3402 }
3403 
3404 MDefinition*
foldsTo(TempAllocator & alloc)3405 MToDouble::foldsTo(TempAllocator& alloc)
3406 {
3407     MDefinition* input = getOperand(0);
3408     if (input->isBox())
3409         input = input->getOperand(0);
3410 
3411     if (input->type() == MIRType_Double)
3412         return input;
3413 
3414     if (input->isConstant()) {
3415         const Value& v = input->toConstant()->value();
3416         if (v.isNumber()) {
3417             double out = v.toNumber();
3418             return MConstant::New(alloc, DoubleValue(out));
3419         }
3420     }
3421 
3422     return this;
3423 }
3424 
3425 MDefinition*
foldsTo(TempAllocator & alloc)3426 MToFloat32::foldsTo(TempAllocator& alloc)
3427 {
3428     MDefinition* input = getOperand(0);
3429     if (input->isBox())
3430         input = input->getOperand(0);
3431 
3432     if (input->type() == MIRType_Float32)
3433         return input;
3434 
3435     // If x is a Float32, Float32(Double(x)) == x
3436     if (input->isToDouble() && input->toToDouble()->input()->type() == MIRType_Float32)
3437         return input->toToDouble()->input();
3438 
3439     if (input->isConstant()) {
3440         const Value& v = input->toConstant()->value();
3441         if (v.isNumber()) {
3442             float out = v.toNumber();
3443             MConstant* c = MConstant::New(alloc, DoubleValue(out));
3444             c->setResultType(MIRType_Float32);
3445             return c;
3446         }
3447     }
3448     return this;
3449 }
3450 
3451 MDefinition*
foldsTo(TempAllocator & alloc)3452 MToString::foldsTo(TempAllocator& alloc)
3453 {
3454     MDefinition* in = input();
3455     if (in->isBox())
3456         in = in->getOperand(0);
3457 
3458     if (in->type() == MIRType_String)
3459         return in;
3460     return this;
3461 }
3462 
3463 MDefinition*
foldsTo(TempAllocator & alloc)3464 MClampToUint8::foldsTo(TempAllocator& alloc)
3465 {
3466     if (input()->isConstantValue()) {
3467         const Value& v = input()->constantValue();
3468         if (v.isDouble()) {
3469             int32_t clamped = ClampDoubleToUint8(v.toDouble());
3470             return MConstant::New(alloc, Int32Value(clamped));
3471         }
3472         if (v.isInt32()) {
3473             int32_t clamped = ClampIntForUint8Array(v.toInt32());
3474             return MConstant::New(alloc, Int32Value(clamped));
3475         }
3476     }
3477     return this;
3478 }
3479 
3480 bool
tryFoldEqualOperands(bool * result)3481 MCompare::tryFoldEqualOperands(bool* result)
3482 {
3483     if (lhs() != rhs())
3484         return false;
3485 
3486     // Intuitively somebody would think that if lhs == rhs,
3487     // then we can just return true. (Or false for !==)
3488     // However NaN !== NaN is true! So we spend some time trying
3489     // to eliminate this case.
3490 
3491     if (jsop() != JSOP_STRICTEQ && jsop() != JSOP_STRICTNE)
3492         return false;
3493 
3494     if (compareType_ == Compare_Unknown)
3495         return false;
3496 
3497     MOZ_ASSERT(compareType_ == Compare_Undefined || compareType_ == Compare_Null ||
3498                compareType_ == Compare_Boolean || compareType_ == Compare_Int32 ||
3499                compareType_ == Compare_Int32MaybeCoerceBoth ||
3500                compareType_ == Compare_Int32MaybeCoerceLHS ||
3501                compareType_ == Compare_Int32MaybeCoerceRHS || compareType_ == Compare_UInt32 ||
3502                compareType_ == Compare_Double || compareType_ == Compare_DoubleMaybeCoerceLHS ||
3503                compareType_ == Compare_DoubleMaybeCoerceRHS || compareType_ == Compare_Float32 ||
3504                compareType_ == Compare_String || compareType_ == Compare_StrictString ||
3505                compareType_ == Compare_Object || compareType_ == Compare_Bitwise);
3506 
3507     if (isDoubleComparison() || isFloat32Comparison()) {
3508         if (!operandsAreNeverNaN())
3509             return false;
3510     }
3511 
3512     if (DeadIfUnused(lhs()))
3513         lhs()->setGuardRangeBailouts();
3514 
3515     *result = (jsop() == JSOP_STRICTEQ);
3516     return true;
3517 }
3518 
3519 bool
tryFoldTypeOf(bool * result)3520 MCompare::tryFoldTypeOf(bool* result)
3521 {
3522     if (!lhs()->isTypeOf() && !rhs()->isTypeOf())
3523         return false;
3524     if (!lhs()->isConstantValue() && !rhs()->isConstantValue())
3525         return false;
3526 
3527     MTypeOf* typeOf = lhs()->isTypeOf() ? lhs()->toTypeOf() : rhs()->toTypeOf();
3528     const Value* constant = lhs()->isConstantValue() ? lhs()->constantVp() : rhs()->constantVp();
3529 
3530     if (!constant->isString())
3531         return false;
3532 
3533     if (jsop() != JSOP_STRICTEQ && jsop() != JSOP_STRICTNE &&
3534         jsop() != JSOP_EQ && jsop() != JSOP_NE)
3535     {
3536         return false;
3537     }
3538 
3539     const JSAtomState& names = GetJitContext()->runtime->names();
3540     if (constant->toString() == TypeName(JSTYPE_VOID, names)) {
3541         if (!typeOf->input()->mightBeType(MIRType_Undefined) &&
3542             !typeOf->inputMaybeCallableOrEmulatesUndefined())
3543         {
3544             *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
3545             return true;
3546         }
3547     } else if (constant->toString() == TypeName(JSTYPE_BOOLEAN, names)) {
3548         if (!typeOf->input()->mightBeType(MIRType_Boolean)) {
3549             *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
3550             return true;
3551         }
3552     } else if (constant->toString() == TypeName(JSTYPE_NUMBER, names)) {
3553         if (!typeOf->input()->mightBeType(MIRType_Int32) &&
3554             !typeOf->input()->mightBeType(MIRType_Float32) &&
3555             !typeOf->input()->mightBeType(MIRType_Double))
3556         {
3557             *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
3558             return true;
3559         }
3560     } else if (constant->toString() == TypeName(JSTYPE_STRING, names)) {
3561         if (!typeOf->input()->mightBeType(MIRType_String)) {
3562             *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
3563             return true;
3564         }
3565     } else if (constant->toString() == TypeName(JSTYPE_SYMBOL, names)) {
3566         if (!typeOf->input()->mightBeType(MIRType_Symbol)) {
3567             *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
3568             return true;
3569         }
3570     } else if (constant->toString() == TypeName(JSTYPE_OBJECT, names)) {
3571         if (!typeOf->input()->mightBeType(MIRType_Object) &&
3572             !typeOf->input()->mightBeType(MIRType_Null))
3573         {
3574             *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
3575             return true;
3576         }
3577     } else if (constant->toString() == TypeName(JSTYPE_FUNCTION, names)) {
3578         if (!typeOf->inputMaybeCallableOrEmulatesUndefined()) {
3579             *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
3580             return true;
3581         }
3582     }
3583 
3584     return false;
3585 }
3586 
3587 bool
tryFold(bool * result)3588 MCompare::tryFold(bool* result)
3589 {
3590     JSOp op = jsop();
3591 
3592     if (tryFoldEqualOperands(result))
3593         return true;
3594 
3595     if (tryFoldTypeOf(result))
3596         return true;
3597 
3598     if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) {
3599         // The LHS is the value we want to test against null or undefined.
3600         if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) {
3601             if (lhs()->type() == inputType()) {
3602                 *result = (op == JSOP_STRICTEQ);
3603                 return true;
3604             }
3605             if (!lhs()->mightBeType(inputType())) {
3606                 *result = (op == JSOP_STRICTNE);
3607                 return true;
3608             }
3609         } else {
3610             MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE);
3611             if (IsNullOrUndefined(lhs()->type())) {
3612                 *result = (op == JSOP_EQ);
3613                 return true;
3614             }
3615             if (!lhs()->mightBeType(MIRType_Null) &&
3616                 !lhs()->mightBeType(MIRType_Undefined) &&
3617                 !(lhs()->mightBeType(MIRType_Object) && operandMightEmulateUndefined()))
3618             {
3619                 *result = (op == JSOP_NE);
3620                 return true;
3621             }
3622         }
3623         return false;
3624     }
3625 
3626     if (compareType_ == Compare_Boolean) {
3627         MOZ_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
3628         MOZ_ASSERT(rhs()->type() == MIRType_Boolean);
3629         MOZ_ASSERT(lhs()->type() != MIRType_Boolean, "Should use Int32 comparison");
3630 
3631         if (!lhs()->mightBeType(MIRType_Boolean)) {
3632             *result = (op == JSOP_STRICTNE);
3633             return true;
3634         }
3635         return false;
3636     }
3637 
3638     if (compareType_ == Compare_StrictString) {
3639         MOZ_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
3640         MOZ_ASSERT(rhs()->type() == MIRType_String);
3641         MOZ_ASSERT(lhs()->type() != MIRType_String, "Should use String comparison");
3642 
3643         if (!lhs()->mightBeType(MIRType_String)) {
3644             *result = (op == JSOP_STRICTNE);
3645             return true;
3646         }
3647         return false;
3648     }
3649 
3650     return false;
3651 }
3652 
3653 bool
evaluateConstantOperands(TempAllocator & alloc,bool * result)3654 MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result)
3655 {
3656     if (type() != MIRType_Boolean && type() != MIRType_Int32)
3657         return false;
3658 
3659     MDefinition* left = getOperand(0);
3660     MDefinition* right = getOperand(1);
3661 
3662     if (compareType() == Compare_Double) {
3663         // Optimize "MCompare MConstant (MToDouble SomethingInInt32Range).
3664         // In most cases the MToDouble was added, because the constant is
3665         // a double.
3666         // e.g. v < 9007199254740991, where v is an int32 is always true.
3667         if (!lhs()->isConstant() && !rhs()->isConstant())
3668             return false;
3669 
3670         MDefinition* operand = left->isConstant() ? right : left;
3671         MConstant* constant = left->isConstant() ? left->toConstant() : right->toConstant();
3672         MOZ_ASSERT(constant->value().isDouble());
3673         double cte = constant->value().toDouble();
3674 
3675         if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType_Int32) {
3676             bool replaced = false;
3677             switch (jsop_) {
3678               case JSOP_LT:
3679                 if (cte > INT32_MAX || cte < INT32_MIN) {
3680                     *result = !((constant == lhs()) ^ (cte < INT32_MIN));
3681                     replaced = true;
3682                 }
3683                 break;
3684               case JSOP_LE:
3685                 if (constant == lhs()) {
3686                     if (cte > INT32_MAX || cte <= INT32_MIN) {
3687                         *result = (cte <= INT32_MIN);
3688                         replaced = true;
3689                     }
3690                 } else {
3691                     if (cte >= INT32_MAX || cte < INT32_MIN) {
3692                         *result = (cte >= INT32_MIN);
3693                         replaced = true;
3694                     }
3695                 }
3696                 break;
3697               case JSOP_GT:
3698                 if (cte > INT32_MAX || cte < INT32_MIN) {
3699                     *result = !((constant == rhs()) ^ (cte < INT32_MIN));
3700                     replaced = true;
3701                 }
3702                 break;
3703               case JSOP_GE:
3704                 if (constant == lhs()) {
3705                     if (cte >= INT32_MAX || cte < INT32_MIN) {
3706                         *result = (cte >= INT32_MAX);
3707                         replaced = true;
3708                     }
3709                 } else {
3710                     if (cte > INT32_MAX || cte <= INT32_MIN) {
3711                         *result = (cte <= INT32_MIN);
3712                         replaced = true;
3713                     }
3714                 }
3715                 break;
3716               case JSOP_STRICTEQ: // Fall through.
3717               case JSOP_EQ:
3718                 if (cte > INT32_MAX || cte < INT32_MIN) {
3719                     *result = false;
3720                     replaced = true;
3721                 }
3722                 break;
3723               case JSOP_STRICTNE: // Fall through.
3724               case JSOP_NE:
3725                 if (cte > INT32_MAX || cte < INT32_MIN) {
3726                     *result = true;
3727                     replaced = true;
3728                 }
3729                 break;
3730               default:
3731                 MOZ_CRASH("Unexpected op.");
3732             }
3733             if (replaced) {
3734                 MLimitedTruncate* limit =
3735                     MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate);
3736                 limit->setGuardUnchecked();
3737                 block()->insertBefore(this, limit);
3738                 return true;
3739             }
3740         }
3741     }
3742 
3743     if (!left->isConstant() || !right->isConstant())
3744         return false;
3745 
3746     Value lhs = left->toConstant()->value();
3747     Value rhs = right->toConstant()->value();
3748 
3749     // Fold away some String equality comparisons.
3750     if (lhs.isString() && rhs.isString()) {
3751         int32_t comp = 0; // Default to equal.
3752         if (left != right)
3753             comp = CompareAtoms(&lhs.toString()->asAtom(), &rhs.toString()->asAtom());
3754 
3755         switch (jsop_) {
3756           case JSOP_LT:
3757             *result = (comp < 0);
3758             break;
3759           case JSOP_LE:
3760             *result = (comp <= 0);
3761             break;
3762           case JSOP_GT:
3763             *result = (comp > 0);
3764             break;
3765           case JSOP_GE:
3766             *result = (comp >= 0);
3767             break;
3768           case JSOP_STRICTEQ: // Fall through.
3769           case JSOP_EQ:
3770             *result = (comp == 0);
3771             break;
3772           case JSOP_STRICTNE: // Fall through.
3773           case JSOP_NE:
3774             *result = (comp != 0);
3775             break;
3776           default:
3777             MOZ_CRASH("Unexpected op.");
3778         }
3779 
3780         return true;
3781     }
3782 
3783     if (compareType_ == Compare_UInt32) {
3784         uint32_t lhsUint = uint32_t(lhs.toInt32());
3785         uint32_t rhsUint = uint32_t(rhs.toInt32());
3786 
3787         switch (jsop_) {
3788           case JSOP_LT:
3789             *result = (lhsUint < rhsUint);
3790             break;
3791           case JSOP_LE:
3792             *result = (lhsUint <= rhsUint);
3793             break;
3794           case JSOP_GT:
3795             *result = (lhsUint > rhsUint);
3796             break;
3797           case JSOP_GE:
3798             *result = (lhsUint >= rhsUint);
3799             break;
3800           case JSOP_STRICTEQ: // Fall through.
3801           case JSOP_EQ:
3802             *result = (lhsUint == rhsUint);
3803             break;
3804           case JSOP_STRICTNE: // Fall through.
3805           case JSOP_NE:
3806             *result = (lhsUint != rhsUint);
3807             break;
3808           default:
3809             MOZ_CRASH("Unexpected op.");
3810         }
3811 
3812         return true;
3813     }
3814 
3815     if (!lhs.isNumber() || !rhs.isNumber())
3816         return false;
3817 
3818     switch (jsop_) {
3819       case JSOP_LT:
3820         *result = (lhs.toNumber() < rhs.toNumber());
3821         break;
3822       case JSOP_LE:
3823         *result = (lhs.toNumber() <= rhs.toNumber());
3824         break;
3825       case JSOP_GT:
3826         *result = (lhs.toNumber() > rhs.toNumber());
3827         break;
3828       case JSOP_GE:
3829         *result = (lhs.toNumber() >= rhs.toNumber());
3830         break;
3831       case JSOP_STRICTEQ: // Fall through.
3832       case JSOP_EQ:
3833         *result = (lhs.toNumber() == rhs.toNumber());
3834         break;
3835       case JSOP_STRICTNE: // Fall through.
3836       case JSOP_NE:
3837         *result = (lhs.toNumber() != rhs.toNumber());
3838         break;
3839       default:
3840         return false;
3841     }
3842 
3843     return true;
3844 }
3845 
3846 MDefinition*
foldsTo(TempAllocator & alloc)3847 MCompare::foldsTo(TempAllocator& alloc)
3848 {
3849     bool result;
3850 
3851     if (tryFold(&result) || evaluateConstantOperands(alloc, &result)) {
3852         if (type() == MIRType_Int32)
3853             return MConstant::New(alloc, Int32Value(result));
3854 
3855         MOZ_ASSERT(type() == MIRType_Boolean);
3856         return MConstant::New(alloc, BooleanValue(result));
3857     }
3858 
3859     return this;
3860 }
3861 
3862 void
trySpecializeFloat32(TempAllocator & alloc)3863 MCompare::trySpecializeFloat32(TempAllocator& alloc)
3864 {
3865     MDefinition* lhs = getOperand(0);
3866     MDefinition* rhs = getOperand(1);
3867 
3868     if (lhs->canProduceFloat32() && rhs->canProduceFloat32() && compareType_ == Compare_Double) {
3869         compareType_ = Compare_Float32;
3870     } else {
3871         if (lhs->type() == MIRType_Float32)
3872             ConvertDefinitionToDouble<0>(alloc, lhs, this);
3873         if (rhs->type() == MIRType_Float32)
3874             ConvertDefinitionToDouble<1>(alloc, rhs, this);
3875     }
3876 }
3877 
3878 void
filtersUndefinedOrNull(bool trueBranch,MDefinition ** subject,bool * filtersUndefined,bool * filtersNull)3879 MCompare::filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
3880                                  bool* filtersNull)
3881 {
3882     *filtersNull = *filtersUndefined = false;
3883     *subject = nullptr;
3884 
3885     if (compareType() != Compare_Undefined && compareType() != Compare_Null)
3886         return;
3887 
3888     MOZ_ASSERT(jsop() == JSOP_STRICTNE || jsop() == JSOP_NE ||
3889                jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ);
3890 
3891     // JSOP_*NE only removes undefined/null from if/true branch
3892     if (!trueBranch && (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE))
3893         return;
3894 
3895     // JSOP_*EQ only removes undefined/null from else/false branch
3896     if (trueBranch && (jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ))
3897         return;
3898 
3899     if (jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE) {
3900         *filtersUndefined = compareType() == Compare_Undefined;
3901         *filtersNull = compareType() == Compare_Null;
3902     } else {
3903         *filtersUndefined = *filtersNull = true;
3904     }
3905 
3906     *subject = lhs();
3907 }
3908 
3909 void
cacheOperandMightEmulateUndefined(CompilerConstraintList * constraints)3910 MNot::cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints)
3911 {
3912     MOZ_ASSERT(operandMightEmulateUndefined());
3913 
3914     if (!getOperand(0)->maybeEmulatesUndefined(constraints))
3915         markNoOperandEmulatesUndefined();
3916 }
3917 
3918 MDefinition*
foldsTo(TempAllocator & alloc)3919 MNot::foldsTo(TempAllocator& alloc)
3920 {
3921     // Fold if the input is constant
3922     if (input()->isConstantValue() && !input()->constantValue().isMagic()) {
3923         bool result = input()->constantToBoolean();
3924         if (type() == MIRType_Int32)
3925             return MConstant::New(alloc, Int32Value(!result));
3926 
3927         // ToBoolean can't cause side effects, so this is safe.
3928         return MConstant::New(alloc, BooleanValue(!result));
3929     }
3930 
3931     // If the operand of the Not is itself a Not, they cancel out. But we can't
3932     // always convert Not(Not(x)) to x because that may loose the conversion to
3933     // boolean. We can simplify Not(Not(Not(x))) to Not(x) though.
3934     MDefinition* op = getOperand(0);
3935     if (op->isNot()) {
3936         MDefinition* opop = op->getOperand(0);
3937         if (opop->isNot())
3938             return opop;
3939     }
3940 
3941     // NOT of an undefined or null value is always true
3942     if (input()->type() == MIRType_Undefined || input()->type() == MIRType_Null)
3943         return MConstant::New(alloc, BooleanValue(true));
3944 
3945     // NOT of a symbol is always false.
3946     if (input()->type() == MIRType_Symbol)
3947         return MConstant::New(alloc, BooleanValue(false));
3948 
3949     // NOT of an object that can't emulate undefined is always false.
3950     if (input()->type() == MIRType_Object && !operandMightEmulateUndefined())
3951         return MConstant::New(alloc, BooleanValue(false));
3952 
3953     return this;
3954 }
3955 
3956 void
trySpecializeFloat32(TempAllocator & alloc)3957 MNot::trySpecializeFloat32(TempAllocator& alloc)
3958 {
3959     MDefinition* in = input();
3960     if (!in->canProduceFloat32() && in->type() == MIRType_Float32)
3961         ConvertDefinitionToDouble<0>(alloc, in, this);
3962 }
3963 
3964 void
printOpcode(GenericPrinter & out) const3965 MBeta::printOpcode(GenericPrinter& out) const
3966 {
3967     MDefinition::printOpcode(out);
3968 
3969     out.printf(" ");
3970     comparison_->dump(out);
3971 }
3972 
3973 bool
shouldUseVM() const3974 MNewObject::shouldUseVM() const
3975 {
3976     if (JSObject* obj = templateObject())
3977         return obj->is<PlainObject>() && obj->as<PlainObject>().hasDynamicSlots();
3978     return true;
3979 }
3980 
3981 bool
canRecoverOnBailout() const3982 MCreateThisWithTemplate::canRecoverOnBailout() const
3983 {
3984     MOZ_ASSERT(templateObject()->is<PlainObject>() || templateObject()->is<UnboxedPlainObject>());
3985     MOZ_ASSERT_IF(templateObject()->is<PlainObject>(),
3986                   !templateObject()->as<PlainObject>().denseElementsAreCopyOnWrite());
3987     return true;
3988 }
3989 
3990 bool
init(TempAllocator & alloc,JSObject * templateObject)3991 OperandIndexMap::init(TempAllocator& alloc, JSObject* templateObject)
3992 {
3993     const UnboxedLayout& layout =
3994         templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration();
3995 
3996     // 0 is used as an error code.
3997     const UnboxedLayout::PropertyVector& properties = layout.properties();
3998     MOZ_ASSERT(properties.length() < 255);
3999 
4000     // Allocate an array of indexes, where the top of each field correspond to
4001     // the index of the operand in the MObjectState instance.
4002     if (!map.init(alloc, layout.size()))
4003         return false;
4004 
4005     // Reset all indexes to 0, which is an error code.
4006     for (size_t i = 0; i < map.length(); i++)
4007         map[i] = 0;
4008 
4009     // Map the property offsets to the indexes of MObjectState operands.
4010     uint8_t index = 1;
4011     for (size_t i = 0; i < properties.length(); i++, index++)
4012         map[properties[i].offset] = index;
4013 
4014     return true;
4015 }
4016 
MObjectState(MObjectState * state)4017 MObjectState::MObjectState(MObjectState* state)
4018   : numSlots_(state->numSlots_),
4019     numFixedSlots_(state->numFixedSlots_),
4020     operandIndex_(state->operandIndex_)
4021 {
4022     // This instruction is only used as a summary for bailout paths.
4023     setResultType(MIRType_Object);
4024     setRecoveredOnBailout();
4025 }
4026 
MObjectState(JSObject * templateObject,OperandIndexMap * operandIndex)4027 MObjectState::MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex)
4028 {
4029     // This instruction is only used as a summary for bailout paths.
4030     setResultType(MIRType_Object);
4031     setRecoveredOnBailout();
4032 
4033     if (templateObject->is<NativeObject>()) {
4034         NativeObject* nativeObject = &templateObject->as<NativeObject>();
4035         numSlots_ = nativeObject->slotSpan();
4036         numFixedSlots_ = nativeObject->numFixedSlots();
4037     } else {
4038         const UnboxedLayout& layout =
4039             templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration();
4040         // Same as UnboxedLayout::makeNativeGroup
4041         numSlots_ = layout.properties().length();
4042         numFixedSlots_ = gc::GetGCKindSlots(layout.getAllocKind());
4043     }
4044 
4045     operandIndex_ = operandIndex;
4046 }
4047 
4048 JSObject*
templateObjectOf(MDefinition * obj)4049 MObjectState::templateObjectOf(MDefinition* obj)
4050 {
4051     if (obj->isNewObject())
4052         return obj->toNewObject()->templateObject();
4053     else if (obj->isCreateThisWithTemplate())
4054         return obj->toCreateThisWithTemplate()->templateObject();
4055     else
4056         return obj->toNewCallObject()->templateObject();
4057 
4058     return nullptr;
4059 }
4060 
4061 bool
init(TempAllocator & alloc,MDefinition * obj)4062 MObjectState::init(TempAllocator& alloc, MDefinition* obj)
4063 {
4064     if (!MVariadicInstruction::init(alloc, numSlots() + 1))
4065         return false;
4066     // +1, for the Object.
4067     initOperand(0, obj);
4068     return true;
4069 }
4070 
4071 MObjectState*
New(TempAllocator & alloc,MDefinition * obj,MDefinition * undefinedVal)4072 MObjectState::New(TempAllocator& alloc, MDefinition* obj, MDefinition* undefinedVal)
4073 {
4074     JSObject* templateObject = templateObjectOf(obj);
4075     MOZ_ASSERT(templateObject, "Unexpected object creation.");
4076 
4077     OperandIndexMap* operandIndex = nullptr;
4078     if (templateObject->is<UnboxedPlainObject>()) {
4079         operandIndex = new(alloc) OperandIndexMap;
4080         if (!operandIndex || !operandIndex->init(alloc, templateObject))
4081             return nullptr;
4082     }
4083 
4084     MObjectState* res = new(alloc) MObjectState(templateObject, operandIndex);
4085     if (!res || !res->init(alloc, obj))
4086         return nullptr;
4087     for (size_t i = 0; i < res->numSlots(); i++)
4088         res->initSlot(i, undefinedVal);
4089     return res;
4090 }
4091 
4092 MObjectState*
Copy(TempAllocator & alloc,MObjectState * state)4093 MObjectState::Copy(TempAllocator& alloc, MObjectState* state)
4094 {
4095     MObjectState* res = new(alloc) MObjectState(state);
4096     if (!res || !res->init(alloc, state->object()))
4097         return nullptr;
4098     for (size_t i = 0; i < res->numSlots(); i++)
4099         res->initSlot(i, state->getSlot(i));
4100     return res;
4101 }
4102 
MArrayState(MDefinition * arr)4103 MArrayState::MArrayState(MDefinition* arr)
4104 {
4105     // This instruction is only used as a summary for bailout paths.
4106     setResultType(MIRType_Object);
4107     setRecoveredOnBailout();
4108     numElements_ = arr->toNewArray()->length();
4109 }
4110 
4111 bool
init(TempAllocator & alloc,MDefinition * obj,MDefinition * len)4112 MArrayState::init(TempAllocator& alloc, MDefinition* obj, MDefinition* len)
4113 {
4114     if (!MVariadicInstruction::init(alloc, numElements() + 2))
4115         return false;
4116     // +1, for the Array object.
4117     initOperand(0, obj);
4118     // +1, for the length value of the array.
4119     initOperand(1, len);
4120     return true;
4121 }
4122 
4123 MArrayState*
New(TempAllocator & alloc,MDefinition * arr,MDefinition * undefinedVal,MDefinition * initLength)4124 MArrayState::New(TempAllocator& alloc, MDefinition* arr, MDefinition* undefinedVal,
4125                  MDefinition* initLength)
4126 {
4127     MArrayState* res = new(alloc) MArrayState(arr);
4128     if (!res || !res->init(alloc, arr, initLength))
4129         return nullptr;
4130     for (size_t i = 0; i < res->numElements(); i++)
4131         res->initElement(i, undefinedVal);
4132     return res;
4133 }
4134 
4135 MArrayState*
Copy(TempAllocator & alloc,MArrayState * state)4136 MArrayState::Copy(TempAllocator& alloc, MArrayState* state)
4137 {
4138     MDefinition* arr = state->array();
4139     MDefinition* len = state->initializedLength();
4140     MArrayState* res = new(alloc) MArrayState(arr);
4141     if (!res || !res->init(alloc, arr, len))
4142         return nullptr;
4143     for (size_t i = 0; i < res->numElements(); i++)
4144         res->initElement(i, state->getElement(i));
4145     return res;
4146 }
4147 
MNewArray(CompilerConstraintList * constraints,uint32_t length,MConstant * templateConst,gc::InitialHeap initialHeap,jsbytecode * pc)4148 MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t length, MConstant* templateConst,
4149                      gc::InitialHeap initialHeap, jsbytecode* pc)
4150   : MUnaryInstruction(templateConst),
4151     length_(length),
4152     initialHeap_(initialHeap),
4153     convertDoubleElements_(false),
4154     pc_(pc)
4155 {
4156     setResultType(MIRType_Object);
4157     if (templateObject()) {
4158         if (TemporaryTypeSet* types = MakeSingletonTypeSet(constraints, templateObject())) {
4159             setResultTypeSet(types);
4160             if (types->convertDoubleElements(constraints) == TemporaryTypeSet::AlwaysConvertToDoubles)
4161                 convertDoubleElements_ = true;
4162         }
4163     }
4164 }
4165 
4166 bool
shouldUseVM() const4167 MNewArray::shouldUseVM() const
4168 {
4169     if (!templateObject())
4170         return true;
4171 
4172     if (templateObject()->is<UnboxedArrayObject>()) {
4173         MOZ_ASSERT(templateObject()->as<UnboxedArrayObject>().capacity() >= length());
4174         return !templateObject()->as<UnboxedArrayObject>().hasInlineElements();
4175     }
4176 
4177     MOZ_ASSERT(length() <= NativeObject::MAX_DENSE_ELEMENTS_COUNT);
4178 
4179     size_t arraySlots =
4180         gc::GetGCKindSlots(templateObject()->asTenured().getAllocKind()) - ObjectElements::VALUES_PER_HEADER;
4181 
4182     return length() > arraySlots;
4183 }
4184 
4185 bool
mightAlias(const MDefinition * store) const4186 MLoadFixedSlot::mightAlias(const MDefinition* store) const
4187 {
4188     if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot())
4189         return false;
4190     return true;
4191 }
4192 
4193 bool
mightAlias(const MDefinition * store) const4194 MLoadFixedSlotAndUnbox::mightAlias(const MDefinition* store) const
4195 {
4196     if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot())
4197         return false;
4198     return true;
4199 }
4200 
4201 MDefinition*
foldsTo(TempAllocator & alloc)4202 MLoadFixedSlot::foldsTo(TempAllocator& alloc)
4203 {
4204     if (!dependency() || !dependency()->isStoreFixedSlot())
4205         return this;
4206 
4207     MStoreFixedSlot* store = dependency()->toStoreFixedSlot();
4208     if (!store->block()->dominates(block()))
4209         return this;
4210 
4211     if (store->object() != object())
4212         return this;
4213 
4214     if (store->slot() != slot())
4215         return this;
4216 
4217     return foldsToStoredValue(alloc, store->value());
4218 }
4219 
4220 bool
mightAlias(const MDefinition * def) const4221 MAsmJSLoadHeap::mightAlias(const MDefinition* def) const
4222 {
4223     if (def->isAsmJSStoreHeap()) {
4224         const MAsmJSStoreHeap* store = def->toAsmJSStoreHeap();
4225         if (store->accessType() != accessType())
4226             return true;
4227         if (!ptr()->isConstant() || !store->ptr()->isConstant())
4228             return true;
4229         const MConstant* otherPtr = store->ptr()->toConstant();
4230         return ptr()->toConstant()->value() == otherPtr->value();
4231     }
4232     return true;
4233 }
4234 
4235 bool
congruentTo(const MDefinition * ins) const4236 MAsmJSLoadHeap::congruentTo(const MDefinition* ins) const
4237 {
4238     if (!ins->isAsmJSLoadHeap())
4239         return false;
4240     const MAsmJSLoadHeap* load = ins->toAsmJSLoadHeap();
4241     return load->accessType() == accessType() && congruentIfOperandsEqual(load);
4242 }
4243 
4244 bool
mightAlias(const MDefinition * def) const4245 MAsmJSLoadGlobalVar::mightAlias(const MDefinition* def) const
4246 {
4247     if (def->isAsmJSStoreGlobalVar()) {
4248         const MAsmJSStoreGlobalVar* store = def->toAsmJSStoreGlobalVar();
4249         return store->globalDataOffset() == globalDataOffset_;
4250     }
4251     return true;
4252 }
4253 
4254 HashNumber
valueHash() const4255 MAsmJSLoadGlobalVar::valueHash() const
4256 {
4257     HashNumber hash = MDefinition::valueHash();
4258     hash = addU32ToHash(hash, globalDataOffset_);
4259     return hash;
4260 }
4261 
4262 bool
congruentTo(const MDefinition * ins) const4263 MAsmJSLoadGlobalVar::congruentTo(const MDefinition* ins) const
4264 {
4265     if (ins->isAsmJSLoadGlobalVar()) {
4266         const MAsmJSLoadGlobalVar* load = ins->toAsmJSLoadGlobalVar();
4267         return globalDataOffset_ == load->globalDataOffset_;
4268     }
4269     return false;
4270 }
4271 
4272 MDefinition*
foldsTo(TempAllocator & alloc)4273 MAsmJSLoadGlobalVar::foldsTo(TempAllocator& alloc)
4274 {
4275     if (!dependency() || !dependency()->isAsmJSStoreGlobalVar())
4276         return this;
4277 
4278     MAsmJSStoreGlobalVar* store = dependency()->toAsmJSStoreGlobalVar();
4279     if (!store->block()->dominates(block()))
4280         return this;
4281 
4282     if (store->globalDataOffset() != globalDataOffset())
4283         return this;
4284 
4285     if (store->value()->type() != type())
4286         return this;
4287 
4288     return store->value();
4289 }
4290 
4291 HashNumber
valueHash() const4292 MAsmJSLoadFuncPtr::valueHash() const
4293 {
4294     HashNumber hash = MDefinition::valueHash();
4295     hash = addU32ToHash(hash, globalDataOffset_);
4296     return hash;
4297 }
4298 
4299 bool
congruentTo(const MDefinition * ins) const4300 MAsmJSLoadFuncPtr::congruentTo(const MDefinition* ins) const
4301 {
4302     if (ins->isAsmJSLoadFuncPtr()) {
4303         const MAsmJSLoadFuncPtr* load = ins->toAsmJSLoadFuncPtr();
4304         return globalDataOffset_ == load->globalDataOffset_;
4305     }
4306     return false;
4307 }
4308 
4309 HashNumber
valueHash() const4310 MAsmJSLoadFFIFunc::valueHash() const
4311 {
4312     HashNumber hash = MDefinition::valueHash();
4313     hash = addU32ToHash(hash, globalDataOffset_);
4314     return hash;
4315 }
4316 
4317 bool
congruentTo(const MDefinition * ins) const4318 MAsmJSLoadFFIFunc::congruentTo(const MDefinition* ins) const
4319 {
4320     if (ins->isAsmJSLoadFFIFunc()) {
4321         const MAsmJSLoadFFIFunc* load = ins->toAsmJSLoadFFIFunc();
4322         return globalDataOffset_ == load->globalDataOffset_;
4323     }
4324     return false;
4325 }
4326 
4327 bool
mightAlias(const MDefinition * store) const4328 MLoadSlot::mightAlias(const MDefinition* store) const
4329 {
4330     if (store->isStoreSlot() && store->toStoreSlot()->slot() != slot())
4331         return false;
4332     return true;
4333 }
4334 
4335 HashNumber
valueHash() const4336 MLoadSlot::valueHash() const
4337 {
4338     HashNumber hash = MDefinition::valueHash();
4339     hash = addU32ToHash(hash, slot_);
4340     return hash;
4341 }
4342 
4343 MDefinition*
foldsTo(TempAllocator & alloc)4344 MLoadSlot::foldsTo(TempAllocator& alloc)
4345 {
4346     if (!dependency() || !dependency()->isStoreSlot())
4347         return this;
4348 
4349     MStoreSlot* store = dependency()->toStoreSlot();
4350     if (!store->block()->dominates(block()))
4351         return this;
4352 
4353     if (store->slots() != slots())
4354         return this;
4355 
4356     return foldsToStoredValue(alloc, store->value());
4357 }
4358 
4359 MDefinition*
foldsTo(TempAllocator & alloc)4360 MFunctionEnvironment::foldsTo(TempAllocator& alloc)
4361 {
4362     if (!input()->isLambda())
4363         return this;
4364 
4365     return input()->toLambda()->scopeChain();
4366 }
4367 
4368 MDefinition*
foldsTo(TempAllocator & alloc)4369 MLoadElement::foldsTo(TempAllocator& alloc)
4370 {
4371     if (!dependency() || !dependency()->isStoreElement())
4372         return this;
4373 
4374     MStoreElement* store = dependency()->toStoreElement();
4375     if (!store->block()->dominates(block()))
4376         return this;
4377 
4378     if (store->elements() != elements())
4379         return this;
4380 
4381     if (store->index() != index())
4382         return this;
4383 
4384     return foldsToStoredValue(alloc, store->value());
4385 }
4386 
4387 // Gets the MDefinition* representing the source/target object's storage.
4388 // Usually this is just an MElements*, but sometimes there are layers
4389 // of indirection or inlining, which are handled elsewhere.
4390 static inline const MElements*
MaybeUnwrapElements(const MDefinition * elementsOrObj)4391 MaybeUnwrapElements(const MDefinition* elementsOrObj)
4392 {
4393     // Sometimes there is a level of indirection for conversion.
4394     if (elementsOrObj->isConvertElementsToDoubles())
4395         return MaybeUnwrapElements(elementsOrObj->toConvertElementsToDoubles()->elements());
4396 
4397     // For inline elements, the object may be passed directly, for example as MUnbox.
4398     if (elementsOrObj->type() == MIRType_Object)
4399         return nullptr;
4400 
4401     // MTypedArrayElements and MTypedObjectElements aren't handled.
4402     if (!elementsOrObj->isElements())
4403         return nullptr;
4404 
4405     return elementsOrObj->toElements();
4406 }
4407 
4408 static inline const MDefinition*
GetElementsObject(const MDefinition * elementsOrObj)4409 GetElementsObject(const MDefinition* elementsOrObj)
4410 {
4411     if (elementsOrObj->type() == MIRType_Object)
4412         return elementsOrObj;
4413 
4414     const MDefinition* elements = MaybeUnwrapElements(elementsOrObj);
4415     if (elements)
4416         return elements->toElements()->input();
4417 
4418     return nullptr;
4419 }
4420 
4421 // Gets the MDefinition of the target Object for the given store operation.
4422 static inline const MDefinition*
GetStoreObject(const MDefinition * store)4423 GetStoreObject(const MDefinition* store)
4424 {
4425     switch (store->op()) {
4426       case MDefinition::Op_StoreElement:
4427         return GetElementsObject(store->toStoreElement()->elements());
4428 
4429       case MDefinition::Op_StoreElementHole:
4430         return store->toStoreElementHole()->object();
4431 
4432       case MDefinition::Op_StoreUnboxedObjectOrNull:
4433         return GetElementsObject(store->toStoreUnboxedObjectOrNull()->elements());
4434 
4435       case MDefinition::Op_StoreUnboxedString:
4436         return GetElementsObject(store->toStoreUnboxedString()->elements());
4437 
4438       case MDefinition::Op_StoreUnboxedScalar:
4439         return GetElementsObject(store->toStoreUnboxedScalar()->elements());
4440 
4441       default:
4442         return nullptr;
4443     }
4444 }
4445 
4446 // Implements mightAlias() logic common to all load operations.
4447 static bool
GenericLoadMightAlias(const MDefinition * elementsOrObj,const MDefinition * store)4448 GenericLoadMightAlias(const MDefinition* elementsOrObj, const MDefinition* store)
4449 {
4450     const MElements* elements = MaybeUnwrapElements(elementsOrObj);
4451     if (elements)
4452         return elements->mightAlias(store);
4453 
4454     // Unhandled Elements kind.
4455     if (elementsOrObj->type() != MIRType_Object)
4456         return true;
4457 
4458     // Inline storage for objects.
4459     // Refer to IsValidElementsType().
4460     const MDefinition* object = elementsOrObj;
4461     MOZ_ASSERT(object->type() == MIRType_Object);
4462     if (!object->resultTypeSet())
4463         return true;
4464 
4465     const MDefinition* storeObject = GetStoreObject(store);
4466     if (!storeObject)
4467         return true;
4468     if (!storeObject->resultTypeSet())
4469         return true;
4470 
4471     return object->resultTypeSet()->objectsIntersect(storeObject->resultTypeSet());
4472 }
4473 
4474 bool
mightAlias(const MDefinition * store) const4475 MElements::mightAlias(const MDefinition* store) const
4476 {
4477     if (!input()->resultTypeSet())
4478         return true;
4479 
4480     const MDefinition* storeObj = GetStoreObject(store);
4481     if (!storeObj)
4482         return true;
4483     if (!storeObj->resultTypeSet())
4484         return true;
4485 
4486     return input()->resultTypeSet()->objectsIntersect(storeObj->resultTypeSet());
4487 }
4488 
4489 bool
mightAlias(const MDefinition * store) const4490 MLoadElement::mightAlias(const MDefinition* store) const
4491 {
4492     return GenericLoadMightAlias(elements(), store);
4493 }
4494 
4495 bool
mightAlias(const MDefinition * store) const4496 MInitializedLength::mightAlias(const MDefinition* store) const
4497 {
4498     return GenericLoadMightAlias(elements(), store);
4499 }
4500 
4501 bool
mightAlias(const MDefinition * store) const4502 MLoadUnboxedObjectOrNull::mightAlias(const MDefinition* store) const
4503 {
4504     return GenericLoadMightAlias(elements(), store);
4505 }
4506 
4507 bool
mightAlias(const MDefinition * store) const4508 MLoadUnboxedString::mightAlias(const MDefinition* store) const
4509 {
4510     return GenericLoadMightAlias(elements(), store);
4511 }
4512 
4513 bool
mightAlias(const MDefinition * store) const4514 MLoadUnboxedScalar::mightAlias(const MDefinition* store) const
4515 {
4516     return GenericLoadMightAlias(elements(), store);
4517 }
4518 
4519 bool
mightAlias(const MDefinition * store) const4520 MUnboxedArrayInitializedLength::mightAlias(const MDefinition* store) const
4521 {
4522     return GenericLoadMightAlias(object(), store);
4523 }
4524 
4525 bool
congruentTo(const MDefinition * ins) const4526 MGuardReceiverPolymorphic::congruentTo(const MDefinition* ins) const
4527 {
4528     if (!ins->isGuardReceiverPolymorphic())
4529         return false;
4530 
4531     const MGuardReceiverPolymorphic* other = ins->toGuardReceiverPolymorphic();
4532 
4533     if (numReceivers() != other->numReceivers())
4534         return false;
4535     for (size_t i = 0; i < numReceivers(); i++) {
4536         if (receiver(i) != other->receiver(i))
4537             return false;
4538     }
4539 
4540     return congruentIfOperandsEqual(ins);
4541 }
4542 
4543 void
trimTo(const ObjectVector & targets,const BoolVector & choiceSet)4544 InlinePropertyTable::trimTo(const ObjectVector& targets, const BoolVector& choiceSet)
4545 {
4546     for (size_t i = 0; i < targets.length(); i++) {
4547         // If the target was inlined, don't erase the entry.
4548         if (choiceSet[i])
4549             continue;
4550 
4551         JSFunction* target = &targets[i]->as<JSFunction>();
4552 
4553         // Eliminate all entries containing the vetoed function from the map.
4554         size_t j = 0;
4555         while (j < numEntries()) {
4556             if (entries_[j]->func == target)
4557                 entries_.erase(&entries_[j]);
4558             else
4559                 j++;
4560         }
4561     }
4562 }
4563 
4564 void
trimToTargets(const ObjectVector & targets)4565 InlinePropertyTable::trimToTargets(const ObjectVector& targets)
4566 {
4567     JitSpew(JitSpew_Inlining, "Got inlineable property cache with %d cases",
4568             (int)numEntries());
4569 
4570     size_t i = 0;
4571     while (i < numEntries()) {
4572         bool foundFunc = false;
4573         for (size_t j = 0; j < targets.length(); j++) {
4574             if (entries_[i]->func == targets[j]) {
4575                 foundFunc = true;
4576                 break;
4577             }
4578         }
4579         if (!foundFunc)
4580             entries_.erase(&(entries_[i]));
4581         else
4582             i++;
4583     }
4584 
4585     JitSpew(JitSpew_Inlining, "%d inlineable cases left after trimming to %d targets",
4586             (int)numEntries(), (int)targets.length());
4587 }
4588 
4589 bool
hasFunction(JSFunction * func) const4590 InlinePropertyTable::hasFunction(JSFunction* func) const
4591 {
4592     for (size_t i = 0; i < numEntries(); i++) {
4593         if (entries_[i]->func == func)
4594             return true;
4595     }
4596     return false;
4597 }
4598 
4599 bool
hasObjectGroup(ObjectGroup * group) const4600 InlinePropertyTable::hasObjectGroup(ObjectGroup* group) const
4601 {
4602     for (size_t i = 0; i < numEntries(); i++) {
4603         if (entries_[i]->group == group)
4604             return true;
4605     }
4606     return false;
4607 }
4608 
4609 TemporaryTypeSet*
buildTypeSetForFunction(JSFunction * func) const4610 InlinePropertyTable::buildTypeSetForFunction(JSFunction* func) const
4611 {
4612     LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
4613     TemporaryTypeSet* types = alloc->new_<TemporaryTypeSet>();
4614     if (!types)
4615         return nullptr;
4616     for (size_t i = 0; i < numEntries(); i++) {
4617         if (entries_[i]->func == func)
4618             types->addType(TypeSet::ObjectType(entries_[i]->group), alloc);
4619     }
4620     return types;
4621 }
4622 
4623 SharedMem<void*>
base() const4624 MLoadTypedArrayElementStatic::base() const
4625 {
4626     return AnyTypedArrayViewData(someTypedArray_);
4627 }
4628 
4629 size_t
length() const4630 MLoadTypedArrayElementStatic::length() const
4631 {
4632     return AnyTypedArrayByteLength(someTypedArray_);
4633 }
4634 
4635 bool
congruentTo(const MDefinition * ins) const4636 MLoadTypedArrayElementStatic::congruentTo(const MDefinition* ins) const
4637 {
4638     if (!ins->isLoadTypedArrayElementStatic())
4639         return false;
4640     const MLoadTypedArrayElementStatic* other = ins->toLoadTypedArrayElementStatic();
4641     if (offset() != other->offset())
4642         return false;
4643     if (needsBoundsCheck() != other->needsBoundsCheck())
4644         return false;
4645     if (accessType() != other->accessType())
4646         return false;
4647     if (base() != other->base())
4648         return false;
4649     return congruentIfOperandsEqual(other);
4650 }
4651 
4652 SharedMem<void*>
base() const4653 MStoreTypedArrayElementStatic::base() const
4654 {
4655     return AnyTypedArrayViewData(someTypedArray_);
4656 }
4657 
4658 bool
allowDoubleResult() const4659 MGetPropertyCache::allowDoubleResult() const
4660 {
4661     if (!resultTypeSet())
4662         return true;
4663 
4664     return resultTypeSet()->hasType(TypeSet::DoubleType());
4665 }
4666 
4667 size_t
length() const4668 MStoreTypedArrayElementStatic::length() const
4669 {
4670     return AnyTypedArrayByteLength(someTypedArray_);
4671 }
4672 
4673 bool
mightAlias(const MDefinition * store) const4674 MGetPropertyPolymorphic::mightAlias(const MDefinition* store) const
4675 {
4676     // Allow hoisting this instruction if the store does not write to a
4677     // slot read by this instruction.
4678 
4679     if (!store->isStoreFixedSlot() && !store->isStoreSlot())
4680         return true;
4681 
4682     for (size_t i = 0; i < numReceivers(); i++) {
4683         const Shape* shape = this->shape(i);
4684         if (!shape)
4685             continue;
4686         if (shape->slot() < shape->numFixedSlots()) {
4687             // Fixed slot.
4688             uint32_t slot = shape->slot();
4689             if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot)
4690                 continue;
4691             if (store->isStoreSlot())
4692                 continue;
4693         } else {
4694             // Dynamic slot.
4695             uint32_t slot = shape->slot() - shape->numFixedSlots();
4696             if (store->isStoreSlot() && store->toStoreSlot()->slot() != slot)
4697                 continue;
4698             if (store->isStoreFixedSlot())
4699                 continue;
4700         }
4701 
4702         return true;
4703     }
4704 
4705     return false;
4706 }
4707 
4708 void
setBlock(MBasicBlock * block)4709 MGetPropertyCache::setBlock(MBasicBlock* block)
4710 {
4711     MDefinition::setBlock(block);
4712     // Track where we started.
4713     if (!location_.pc) {
4714         location_.pc = block->trackedPc();
4715         location_.script = block->info().script();
4716     }
4717 }
4718 
4719 bool
updateForReplacement(MDefinition * ins)4720 MGetPropertyCache::updateForReplacement(MDefinition* ins)
4721 {
4722     MGetPropertyCache* other = ins->toGetPropertyCache();
4723     location_.append(&other->location_);
4724     return true;
4725 }
4726 
4727 MDefinition*
foldsTo(TempAllocator & alloc)4728 MAsmJSUnsignedToDouble::foldsTo(TempAllocator& alloc)
4729 {
4730     if (input()->isConstantValue()) {
4731         const Value& v = input()->constantValue();
4732         if (v.isInt32())
4733             return MConstant::New(alloc, DoubleValue(uint32_t(v.toInt32())));
4734     }
4735 
4736     return this;
4737 }
4738 
4739 MDefinition*
foldsTo(TempAllocator & alloc)4740 MAsmJSUnsignedToFloat32::foldsTo(TempAllocator& alloc)
4741 {
4742     if (input()->isConstantValue()) {
4743         const Value& v = input()->constantValue();
4744         if (v.isInt32()) {
4745             double dval = double(uint32_t(v.toInt32()));
4746             if (IsFloat32Representable(dval))
4747                 return MConstant::NewAsmJS(alloc, JS::Float32Value(float(dval)), MIRType_Float32);
4748         }
4749     }
4750 
4751     return this;
4752 }
4753 
4754 MAsmJSCall*
New(TempAllocator & alloc,const wasm::CallSiteDesc & desc,Callee callee,const Args & args,MIRType resultType,size_t spIncrement)4755 MAsmJSCall::New(TempAllocator& alloc, const wasm::CallSiteDesc& desc, Callee callee,
4756                 const Args& args, MIRType resultType, size_t spIncrement)
4757 {
4758     MAsmJSCall* call = new(alloc) MAsmJSCall(desc, callee, spIncrement);
4759     call->setResultType(resultType);
4760 
4761     if (!call->argRegs_.init(alloc, args.length()))
4762         return nullptr;
4763     for (size_t i = 0; i < call->argRegs_.length(); i++)
4764         call->argRegs_[i] = args[i].reg;
4765 
4766     if (!call->init(alloc, call->argRegs_.length() + (callee.which() == Callee::Dynamic ? 1 : 0)))
4767         return nullptr;
4768     // FixedList doesn't initialize its elements, so do an unchecked init.
4769     for (size_t i = 0; i < call->argRegs_.length(); i++)
4770         call->initOperand(i, args[i].def);
4771     if (callee.which() == Callee::Dynamic)
4772         call->initOperand(call->argRegs_.length(), callee.dynamic());
4773 
4774     return call;
4775 }
4776 
4777 void
trySpecializeFloat32(TempAllocator & alloc)4778 MSqrt::trySpecializeFloat32(TempAllocator& alloc) {
4779     if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
4780         if (input()->type() == MIRType_Float32)
4781             ConvertDefinitionToDouble<0>(alloc, input(), this);
4782         return;
4783     }
4784 
4785     setResultType(MIRType_Float32);
4786     specialization_ = MIRType_Float32;
4787 }
4788 
4789 MDefinition*
foldsTo(TempAllocator & alloc)4790 MClz::foldsTo(TempAllocator& alloc)
4791 {
4792     if (num()->isConstantValue()) {
4793         int32_t n = num()->constantValue().toInt32();
4794         if (n == 0)
4795             return MConstant::New(alloc, Int32Value(32));
4796         return MConstant::New(alloc, Int32Value(mozilla::CountLeadingZeroes32(n)));
4797     }
4798 
4799     return this;
4800 }
4801 
4802 MDefinition*
foldsTo(TempAllocator & alloc)4803 MBoundsCheck::foldsTo(TempAllocator& alloc)
4804 {
4805     if (index()->isConstantValue() && length()->isConstantValue()) {
4806        uint32_t len = length()->constantValue().toInt32();
4807        uint32_t idx = index()->constantValue().toInt32();
4808        if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len)
4809            return index();
4810     }
4811 
4812     return this;
4813 }
4814 
4815 MDefinition*
foldsTo(TempAllocator & alloc)4816 MTableSwitch::foldsTo(TempAllocator& alloc)
4817 {
4818     MDefinition* op = getOperand(0);
4819 
4820     // If we only have one successor, convert to a plain goto to the only
4821     // successor. TableSwitch indices are numeric; other types will always go to
4822     // the only successor.
4823     if (numSuccessors() == 1 || (op->type() != MIRType_Value && !IsNumberType(op->type())))
4824         return MGoto::New(alloc, getDefault());
4825 
4826     return this;
4827 }
4828 
4829 MDefinition*
foldsTo(TempAllocator & alloc)4830 MArrayJoin::foldsTo(TempAllocator& alloc)
4831 {
4832     // :TODO: Enable this optimization after fixing Bug 977966 test cases.
4833     return this;
4834 
4835     MDefinition* arr = array();
4836 
4837     if (!arr->isStringSplit())
4838         return this;
4839 
4840     this->setRecoveredOnBailout();
4841     if (arr->hasLiveDefUses()) {
4842         this->setNotRecoveredOnBailout();
4843         return this;
4844     }
4845 
4846     // We're replacing foo.split(bar).join(baz) by
4847     // foo.replace(bar, baz).  MStringSplit could be recovered by
4848     // a bailout.  As we are removing its last use, and its result
4849     // could be captured by a resume point, this MStringSplit will
4850     // be executed on the bailout path.
4851     MDefinition* string = arr->toStringSplit()->string();
4852     MDefinition* pattern = arr->toStringSplit()->separator();
4853     MDefinition* replacement = sep();
4854 
4855     setNotRecoveredOnBailout();
4856     return MStringReplace::New(alloc, string, pattern, replacement);
4857 }
4858 
4859 MConvertUnboxedObjectToNative*
New(TempAllocator & alloc,MDefinition * obj,ObjectGroup * group)4860 MConvertUnboxedObjectToNative::New(TempAllocator& alloc, MDefinition* obj, ObjectGroup* group)
4861 {
4862     MConvertUnboxedObjectToNative* res = new(alloc) MConvertUnboxedObjectToNative(obj, group);
4863 
4864     ObjectGroup* nativeGroup = group->unboxedLayout().nativeGroup();
4865 
4866     // Make a new type set for the result of this instruction which replaces
4867     // the input group with the native group we will convert it to.
4868     TemporaryTypeSet* types = obj->resultTypeSet();
4869     if (types && !types->unknownObject()) {
4870         TemporaryTypeSet* newTypes = types->cloneWithoutObjects(alloc.lifoAlloc());
4871         if (newTypes) {
4872             for (size_t i = 0; i < types->getObjectCount(); i++) {
4873                 TypeSet::ObjectKey* key = types->getObject(i);
4874                 if (!key)
4875                     continue;
4876                 if (key->unknownProperties() || !key->isGroup() || key->group() != group)
4877                     newTypes->addType(TypeSet::ObjectType(key), alloc.lifoAlloc());
4878                 else
4879                     newTypes->addType(TypeSet::ObjectType(nativeGroup), alloc.lifoAlloc());
4880             }
4881             res->setResultTypeSet(newTypes);
4882         }
4883     }
4884 
4885     return res;
4886 }
4887 
4888 bool
ElementAccessIsDenseNative(CompilerConstraintList * constraints,MDefinition * obj,MDefinition * id)4889 jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints,
4890                                 MDefinition* obj, MDefinition* id)
4891 {
4892     if (obj->mightBeType(MIRType_String))
4893         return false;
4894 
4895     if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
4896         return false;
4897 
4898     TemporaryTypeSet* types = obj->resultTypeSet();
4899     if (!types)
4900         return false;
4901 
4902     // Typed arrays are native classes but do not have dense elements.
4903     const Class* clasp = types->getKnownClass(constraints);
4904     return clasp && clasp->isNative() && !IsAnyTypedArrayClass(clasp);
4905 }
4906 
4907 JSValueType
UnboxedArrayElementType(CompilerConstraintList * constraints,MDefinition * obj,MDefinition * id)4908 jit::UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj,
4909                              MDefinition* id)
4910 {
4911     if (obj->mightBeType(MIRType_String))
4912         return JSVAL_TYPE_MAGIC;
4913 
4914     if (id && id->type() != MIRType_Int32 && id->type() != MIRType_Double)
4915         return JSVAL_TYPE_MAGIC;
4916 
4917     TemporaryTypeSet* types = obj->resultTypeSet();
4918     if (!types || types->unknownObject())
4919         return JSVAL_TYPE_MAGIC;
4920 
4921     JSValueType elementType = JSVAL_TYPE_MAGIC;
4922     for (unsigned i = 0; i < types->getObjectCount(); i++) {
4923         TypeSet::ObjectKey* key = types->getObject(i);
4924         if (!key)
4925             continue;
4926 
4927         if (key->unknownProperties() || !key->isGroup())
4928             return JSVAL_TYPE_MAGIC;
4929 
4930         if (key->clasp() != &UnboxedArrayObject::class_)
4931             return JSVAL_TYPE_MAGIC;
4932 
4933         const UnboxedLayout &layout = key->group()->unboxedLayout();
4934 
4935         if (layout.nativeGroup())
4936             return JSVAL_TYPE_MAGIC;
4937 
4938         if (elementType == layout.elementType() || elementType == JSVAL_TYPE_MAGIC)
4939             elementType = layout.elementType();
4940         else
4941             return JSVAL_TYPE_MAGIC;
4942 
4943         key->watchStateChangeForUnboxedConvertedToNative(constraints);
4944     }
4945 
4946     return elementType;
4947 }
4948 
4949 bool
ElementAccessIsAnyTypedArray(CompilerConstraintList * constraints,MDefinition * obj,MDefinition * id,Scalar::Type * arrayType)4950 jit::ElementAccessIsAnyTypedArray(CompilerConstraintList* constraints,
4951                                   MDefinition* obj, MDefinition* id,
4952                                   Scalar::Type* arrayType)
4953 {
4954     if (obj->mightBeType(MIRType_String))
4955         return false;
4956 
4957     if (id->type() != MIRType_Int32 && id->type() != MIRType_Double)
4958         return false;
4959 
4960     TemporaryTypeSet* types = obj->resultTypeSet();
4961     if (!types)
4962         return false;
4963 
4964     *arrayType = types->getTypedArrayType(constraints);
4965     return *arrayType != Scalar::MaxTypedArrayViewType;
4966 }
4967 
4968 bool
ElementAccessIsPacked(CompilerConstraintList * constraints,MDefinition * obj)4969 jit::ElementAccessIsPacked(CompilerConstraintList* constraints, MDefinition* obj)
4970 {
4971     TemporaryTypeSet* types = obj->resultTypeSet();
4972     return types && !types->hasObjectFlags(constraints, OBJECT_FLAG_NON_PACKED);
4973 }
4974 
4975 bool
ElementAccessMightBeCopyOnWrite(CompilerConstraintList * constraints,MDefinition * obj)4976 jit::ElementAccessMightBeCopyOnWrite(CompilerConstraintList* constraints, MDefinition* obj)
4977 {
4978     TemporaryTypeSet* types = obj->resultTypeSet();
4979     return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_COPY_ON_WRITE);
4980 }
4981 
4982 bool
ElementAccessHasExtraIndexedProperty(IonBuilder * builder,MDefinition * obj)4983 jit::ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj)
4984 {
4985     TemporaryTypeSet* types = obj->resultTypeSet();
4986 
4987     if (!types || types->hasObjectFlags(builder->constraints(), OBJECT_FLAG_LENGTH_OVERFLOW))
4988         return true;
4989 
4990     return TypeCanHaveExtraIndexedProperties(builder, types);
4991 }
4992 
4993 MIRType
DenseNativeElementType(CompilerConstraintList * constraints,MDefinition * obj)4994 jit::DenseNativeElementType(CompilerConstraintList* constraints, MDefinition* obj)
4995 {
4996     TemporaryTypeSet* types = obj->resultTypeSet();
4997     MIRType elementType = MIRType_None;
4998     unsigned count = types->getObjectCount();
4999 
5000     for (unsigned i = 0; i < count; i++) {
5001         TypeSet::ObjectKey* key = types->getObject(i);
5002         if (!key)
5003             continue;
5004 
5005         if (key->unknownProperties())
5006             return MIRType_None;
5007 
5008         HeapTypeSetKey elementTypes = key->property(JSID_VOID);
5009 
5010         MIRType type = elementTypes.knownMIRType(constraints);
5011         if (type == MIRType_None)
5012             return MIRType_None;
5013 
5014         if (elementType == MIRType_None)
5015             elementType = type;
5016         else if (elementType != type)
5017             return MIRType_None;
5018     }
5019 
5020     return elementType;
5021 }
5022 
5023 static BarrierKind
PropertyReadNeedsTypeBarrier(CompilerConstraintList * constraints,TypeSet::ObjectKey * key,PropertyName * name,TypeSet * observed)5024 PropertyReadNeedsTypeBarrier(CompilerConstraintList* constraints,
5025                              TypeSet::ObjectKey* key, PropertyName* name,
5026                              TypeSet* observed)
5027 {
5028     // If the object being read from has types for the property which haven't
5029     // been observed at this access site, the read could produce a new type and
5030     // a barrier is needed. Note that this only covers reads from properties
5031     // which are accounted for by type information, i.e. native data properties
5032     // and elements.
5033     //
5034     // We also need a barrier if the object is a proxy, because then all bets
5035     // are off, just as if it has unknown properties.
5036     if (key->unknownProperties() || observed->empty() ||
5037         key->clasp()->isProxy())
5038     {
5039         return BarrierKind::TypeSet;
5040     }
5041 
5042     jsid id = name ? NameToId(name) : JSID_VOID;
5043     HeapTypeSetKey property = key->property(id);
5044     if (property.maybeTypes()) {
5045         if (!TypeSetIncludes(observed, MIRType_Value, property.maybeTypes())) {
5046             // If all possible objects have been observed, we don't have to
5047             // guard on the specific object types.
5048             if (property.maybeTypes()->objectsAreSubset(observed)) {
5049                 property.freeze(constraints);
5050                 return BarrierKind::TypeTagOnly;
5051             }
5052             return BarrierKind::TypeSet;
5053         }
5054     }
5055 
5056     // Type information for global objects is not required to reflect the
5057     // initial 'undefined' value for properties, in particular global
5058     // variables declared with 'var'. Until the property is assigned a value
5059     // other than undefined, a barrier is required.
5060     if (key->isSingleton()) {
5061         JSObject* obj = key->singleton();
5062         if (name && CanHaveEmptyPropertyTypesForOwnProperty(obj) &&
5063             (!property.maybeTypes() || property.maybeTypes()->empty()))
5064         {
5065             return BarrierKind::TypeSet;
5066         }
5067     }
5068 
5069     property.freeze(constraints);
5070     return BarrierKind::NoBarrier;
5071 }
5072 
5073 static bool
ObjectSubsumes(TypeSet::ObjectKey * first,TypeSet::ObjectKey * second)5074 ObjectSubsumes(TypeSet::ObjectKey* first, TypeSet::ObjectKey* second)
5075 {
5076     if (first->isSingleton() ||
5077         second->isSingleton() ||
5078         first->clasp() != second->clasp() ||
5079         first->unknownProperties() ||
5080         second->unknownProperties())
5081     {
5082         return false;
5083     }
5084 
5085     if (first->clasp() == &ArrayObject::class_) {
5086         HeapTypeSetKey firstElements = first->property(JSID_VOID);
5087         HeapTypeSetKey secondElements = second->property(JSID_VOID);
5088 
5089         return firstElements.maybeTypes() && secondElements.maybeTypes() &&
5090                firstElements.maybeTypes()->equals(secondElements.maybeTypes());
5091     }
5092 
5093     if (first->clasp() == &UnboxedArrayObject::class_) {
5094         return first->group()->unboxedLayout().elementType() ==
5095                second->group()->unboxedLayout().elementType();
5096     }
5097 
5098     return false;
5099 }
5100 
5101 BarrierKind
PropertyReadNeedsTypeBarrier(JSContext * propertycx,CompilerConstraintList * constraints,TypeSet::ObjectKey * key,PropertyName * name,TemporaryTypeSet * observed,bool updateObserved)5102 jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx,
5103                                   CompilerConstraintList* constraints,
5104                                   TypeSet::ObjectKey* key, PropertyName* name,
5105                                   TemporaryTypeSet* observed, bool updateObserved)
5106 {
5107     if (!updateObserved)
5108         return PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
5109 
5110     // If this access has never executed, try to add types to the observed set
5111     // according to any property which exists on the object or its prototype.
5112     if (observed->empty() && name) {
5113         JSObject* obj;
5114         if (key->isSingleton())
5115             obj = key->singleton();
5116         else
5117             obj = key->proto().isLazy() ? nullptr : key->proto().toObjectOrNull();
5118 
5119         while (obj) {
5120             if (!obj->getClass()->isNative())
5121                 break;
5122 
5123             TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(obj);
5124             if (propertycx)
5125                 key->ensureTrackedProperty(propertycx, NameToId(name));
5126 
5127             if (!key->unknownProperties()) {
5128                 HeapTypeSetKey property = key->property(NameToId(name));
5129                 if (property.maybeTypes()) {
5130                     TypeSet::TypeList types;
5131                     if (!property.maybeTypes()->enumerateTypes(&types))
5132                         break;
5133                     if (types.length()) {
5134                         // Note: the return value here is ignored.
5135                         observed->addType(types[0], GetJitContext()->temp->lifoAlloc());
5136                         break;
5137                     }
5138                 }
5139             }
5140 
5141             obj = obj->getProto();
5142         }
5143     }
5144 
5145     // If any objects which could be observed are similar to ones that have
5146     // already been observed, add them to the observed type set.
5147     if (!key->unknownProperties()) {
5148         HeapTypeSetKey property = key->property(name ? NameToId(name) : JSID_VOID);
5149 
5150         if (property.maybeTypes() && !property.maybeTypes()->unknownObject()) {
5151             for (size_t i = 0; i < property.maybeTypes()->getObjectCount(); i++) {
5152                 TypeSet::ObjectKey* key = property.maybeTypes()->getObject(i);
5153                 if (!key || observed->unknownObject())
5154                     continue;
5155 
5156                 for (size_t j = 0; j < observed->getObjectCount(); j++) {
5157                     TypeSet::ObjectKey* observedKey = observed->getObject(j);
5158                     if (observedKey && ObjectSubsumes(observedKey, key)) {
5159                         // Note: the return value here is ignored.
5160                         observed->addType(TypeSet::ObjectType(key),
5161                                           GetJitContext()->temp->lifoAlloc());
5162                         break;
5163                     }
5164                 }
5165             }
5166         }
5167     }
5168 
5169     return PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
5170 }
5171 
5172 BarrierKind
PropertyReadNeedsTypeBarrier(JSContext * propertycx,CompilerConstraintList * constraints,MDefinition * obj,PropertyName * name,TemporaryTypeSet * observed)5173 jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx,
5174                                   CompilerConstraintList* constraints,
5175                                   MDefinition* obj, PropertyName* name,
5176                                   TemporaryTypeSet* observed)
5177 {
5178     if (observed->unknown())
5179         return BarrierKind::NoBarrier;
5180 
5181     TypeSet* types = obj->resultTypeSet();
5182     if (!types || types->unknownObject())
5183         return BarrierKind::TypeSet;
5184 
5185     BarrierKind res = BarrierKind::NoBarrier;
5186 
5187     bool updateObserved = types->getObjectCount() == 1;
5188     for (size_t i = 0; i < types->getObjectCount(); i++) {
5189         if (TypeSet::ObjectKey* key = types->getObject(i)) {
5190             BarrierKind kind = PropertyReadNeedsTypeBarrier(propertycx, constraints, key, name,
5191                                                             observed, updateObserved);
5192             if (kind == BarrierKind::TypeSet)
5193                 return BarrierKind::TypeSet;
5194 
5195             if (kind == BarrierKind::TypeTagOnly) {
5196                 MOZ_ASSERT(res == BarrierKind::NoBarrier || res == BarrierKind::TypeTagOnly);
5197                 res = BarrierKind::TypeTagOnly;
5198             } else {
5199                 MOZ_ASSERT(kind == BarrierKind::NoBarrier);
5200             }
5201         }
5202     }
5203 
5204     return res;
5205 }
5206 
5207 BarrierKind
PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder * builder,MDefinition * obj,PropertyName * name,TemporaryTypeSet * observed)5208 jit::PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder* builder,
5209                                              MDefinition* obj, PropertyName* name,
5210                                              TemporaryTypeSet* observed)
5211 {
5212     if (observed->unknown())
5213         return BarrierKind::NoBarrier;
5214 
5215     TypeSet* types = obj->resultTypeSet();
5216     if (!types || types->unknownObject())
5217         return BarrierKind::TypeSet;
5218 
5219     BarrierKind res = BarrierKind::NoBarrier;
5220 
5221     for (size_t i = 0; i < types->getObjectCount(); i++) {
5222         TypeSet::ObjectKey* key = types->getObject(i);
5223         if (!key)
5224             continue;
5225         while (true) {
5226             if (!key->hasStableClassAndProto(builder->constraints()))
5227                 return BarrierKind::TypeSet;
5228             if (!key->proto().isObject())
5229                 break;
5230             JSObject* proto = builder->checkNurseryObject(key->proto().toObject());
5231             key = TypeSet::ObjectKey::get(proto);
5232             BarrierKind kind = PropertyReadNeedsTypeBarrier(builder->constraints(),
5233                                                             key, name, observed);
5234             if (kind == BarrierKind::TypeSet)
5235                 return BarrierKind::TypeSet;
5236 
5237             if (kind == BarrierKind::TypeTagOnly) {
5238                 MOZ_ASSERT(res == BarrierKind::NoBarrier || res == BarrierKind::TypeTagOnly);
5239                 res = BarrierKind::TypeTagOnly;
5240             } else {
5241                 MOZ_ASSERT(kind == BarrierKind::NoBarrier);
5242             }
5243         }
5244     }
5245 
5246     return res;
5247 }
5248 
5249 bool
PropertyReadIsIdempotent(CompilerConstraintList * constraints,MDefinition * obj,PropertyName * name)5250 jit::PropertyReadIsIdempotent(CompilerConstraintList* constraints,
5251                               MDefinition* obj, PropertyName* name)
5252 {
5253     // Determine if reading a property from obj is likely to be idempotent.
5254 
5255     TypeSet* types = obj->resultTypeSet();
5256     if (!types || types->unknownObject())
5257         return false;
5258 
5259     for (size_t i = 0; i < types->getObjectCount(); i++) {
5260         if (TypeSet::ObjectKey* key = types->getObject(i)) {
5261             if (key->unknownProperties())
5262                 return false;
5263 
5264             // Check if the property has been reconfigured or is a getter.
5265             HeapTypeSetKey property = key->property(NameToId(name));
5266             if (property.nonData(constraints))
5267                 return false;
5268         }
5269     }
5270 
5271     return true;
5272 }
5273 
5274 void
AddObjectsForPropertyRead(MDefinition * obj,PropertyName * name,TemporaryTypeSet * observed)5275 jit::AddObjectsForPropertyRead(MDefinition* obj, PropertyName* name,
5276                                TemporaryTypeSet* observed)
5277 {
5278     // Add objects to observed which *could* be observed by reading name from obj,
5279     // to hopefully avoid unnecessary type barriers and code invalidations.
5280 
5281     LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
5282 
5283     TemporaryTypeSet* types = obj->resultTypeSet();
5284     if (!types || types->unknownObject()) {
5285         observed->addType(TypeSet::AnyObjectType(), alloc);
5286         return;
5287     }
5288 
5289     for (size_t i = 0; i < types->getObjectCount(); i++) {
5290         TypeSet::ObjectKey* key = types->getObject(i);
5291         if (!key)
5292             continue;
5293 
5294         if (key->unknownProperties()) {
5295             observed->addType(TypeSet::AnyObjectType(), alloc);
5296             return;
5297         }
5298 
5299         jsid id = name ? NameToId(name) : JSID_VOID;
5300         HeapTypeSetKey property = key->property(id);
5301         HeapTypeSet* types = property.maybeTypes();
5302         if (!types)
5303             continue;
5304 
5305         if (types->unknownObject()) {
5306             observed->addType(TypeSet::AnyObjectType(), alloc);
5307             return;
5308         }
5309 
5310         for (size_t i = 0; i < types->getObjectCount(); i++) {
5311             if (TypeSet::ObjectKey* key = types->getObject(i))
5312                 observed->addType(TypeSet::ObjectType(key), alloc);
5313         }
5314     }
5315 }
5316 
5317 static bool
PrototypeHasIndexedProperty(IonBuilder * builder,JSObject * obj)5318 PrototypeHasIndexedProperty(IonBuilder* builder, JSObject* obj)
5319 {
5320     do {
5321         TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(builder->checkNurseryObject(obj));
5322         if (ClassCanHaveExtraProperties(key->clasp()))
5323             return true;
5324         if (key->unknownProperties())
5325             return true;
5326         HeapTypeSetKey index = key->property(JSID_VOID);
5327         if (index.nonData(builder->constraints()) || index.isOwnProperty(builder->constraints()))
5328             return true;
5329         obj = obj->getProto();
5330     } while (obj);
5331 
5332     return false;
5333 }
5334 
5335 // Whether Array.prototype, or an object on its proto chain, has an indexed property.
5336 bool
ArrayPrototypeHasIndexedProperty(IonBuilder * builder,JSScript * script)5337 jit::ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script)
5338 {
5339     if (JSObject* proto = script->global().maybeGetArrayPrototype())
5340         return PrototypeHasIndexedProperty(builder, proto);
5341     return true;
5342 }
5343 
5344 // Whether obj or any of its prototypes have an indexed property.
5345 bool
TypeCanHaveExtraIndexedProperties(IonBuilder * builder,TemporaryTypeSet * types)5346 jit::TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types)
5347 {
5348     const Class* clasp = types->getKnownClass(builder->constraints());
5349 
5350     // Note: typed arrays have indexed properties not accounted for by type
5351     // information, though these are all in bounds and will be accounted for
5352     // by JIT paths.
5353     if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsAnyTypedArrayClass(clasp)))
5354         return true;
5355 
5356     if (types->hasObjectFlags(builder->constraints(), OBJECT_FLAG_SPARSE_INDEXES))
5357         return true;
5358 
5359     JSObject* proto;
5360     if (!types->getCommonPrototype(builder->constraints(), &proto))
5361         return true;
5362 
5363     if (!proto)
5364         return false;
5365 
5366     return PrototypeHasIndexedProperty(builder, proto);
5367 }
5368 
5369 static bool
PropertyTypeIncludes(TempAllocator & alloc,HeapTypeSetKey property,MDefinition * value,MIRType implicitType)5370 PropertyTypeIncludes(TempAllocator& alloc, HeapTypeSetKey property,
5371                      MDefinition* value, MIRType implicitType)
5372 {
5373     // If implicitType is not MIRType_None, it is an additional type which the
5374     // property implicitly includes. In this case, make a new type set which
5375     // explicitly contains the type.
5376     TypeSet* types = property.maybeTypes();
5377     if (implicitType != MIRType_None) {
5378         TypeSet::Type newType = TypeSet::PrimitiveType(ValueTypeFromMIRType(implicitType));
5379         if (types)
5380             types = types->clone(alloc.lifoAlloc());
5381         else
5382             types = alloc.lifoAlloc()->new_<TemporaryTypeSet>();
5383         types->addType(newType, alloc.lifoAlloc());
5384     }
5385 
5386     return TypeSetIncludes(types, value->type(), value->resultTypeSet());
5387 }
5388 
5389 static bool
TryAddTypeBarrierForWrite(TempAllocator & alloc,CompilerConstraintList * constraints,MBasicBlock * current,TemporaryTypeSet * objTypes,PropertyName * name,MDefinition ** pvalue,MIRType implicitType)5390 TryAddTypeBarrierForWrite(TempAllocator& alloc, CompilerConstraintList* constraints,
5391                           MBasicBlock* current, TemporaryTypeSet* objTypes,
5392                           PropertyName* name, MDefinition** pvalue, MIRType implicitType)
5393 {
5394     // Return whether pvalue was modified to include a type barrier ensuring
5395     // that writing the value to objTypes/id will not require changing type
5396     // information.
5397 
5398     // All objects in the set must have the same types for name. Otherwise, we
5399     // could bail out without subsequently triggering a type change that
5400     // invalidates the compiled code.
5401     Maybe<HeapTypeSetKey> aggregateProperty;
5402 
5403     for (size_t i = 0; i < objTypes->getObjectCount(); i++) {
5404         TypeSet::ObjectKey* key = objTypes->getObject(i);
5405         if (!key)
5406             continue;
5407 
5408         if (key->unknownProperties())
5409             return false;
5410 
5411         jsid id = name ? NameToId(name) : JSID_VOID;
5412         HeapTypeSetKey property = key->property(id);
5413         if (!property.maybeTypes() || property.couldBeConstant(constraints))
5414             return false;
5415 
5416         if (PropertyTypeIncludes(alloc, property, *pvalue, implicitType))
5417             return false;
5418 
5419         // This freeze is not required for correctness, but ensures that we
5420         // will recompile if the property types change and the barrier can
5421         // potentially be removed.
5422         property.freeze(constraints);
5423 
5424         if (!aggregateProperty) {
5425             aggregateProperty.emplace(property);
5426         } else {
5427             if (!aggregateProperty->maybeTypes()->equals(property.maybeTypes()))
5428                 return false;
5429         }
5430     }
5431 
5432     MOZ_ASSERT(aggregateProperty);
5433 
5434     MIRType propertyType = aggregateProperty->knownMIRType(constraints);
5435     switch (propertyType) {
5436       case MIRType_Boolean:
5437       case MIRType_Int32:
5438       case MIRType_Double:
5439       case MIRType_String:
5440       case MIRType_Symbol: {
5441         // The property is a particular primitive type, guard by unboxing the
5442         // value before the write.
5443         if (!(*pvalue)->mightBeType(propertyType)) {
5444             // The value's type does not match the property type. Just do a VM
5445             // call as it will always trigger invalidation of the compiled code.
5446             MOZ_ASSERT_IF((*pvalue)->type() != MIRType_Value, (*pvalue)->type() != propertyType);
5447             return false;
5448         }
5449         MInstruction* ins = MUnbox::New(alloc, *pvalue, propertyType, MUnbox::Fallible);
5450         current->add(ins);
5451         *pvalue = ins;
5452         return true;
5453       }
5454       default:;
5455     }
5456 
5457     if ((*pvalue)->type() != MIRType_Value)
5458         return false;
5459 
5460     TemporaryTypeSet* types = aggregateProperty->maybeTypes()->clone(alloc.lifoAlloc());
5461     if (!types)
5462         return false;
5463 
5464     // If all possible objects can be stored without a barrier, we don't have to
5465     // guard on the specific object types.
5466     BarrierKind kind = BarrierKind::TypeSet;
5467     if ((*pvalue)->resultTypeSet() && (*pvalue)->resultTypeSet()->objectsAreSubset(types))
5468         kind = BarrierKind::TypeTagOnly;
5469 
5470     MInstruction* ins = MMonitorTypes::New(alloc, *pvalue, types, kind);
5471     current->add(ins);
5472     return true;
5473 }
5474 
5475 static MInstruction*
AddGroupGuard(TempAllocator & alloc,MBasicBlock * current,MDefinition * obj,TypeSet::ObjectKey * key,bool bailOnEquality)5476 AddGroupGuard(TempAllocator& alloc, MBasicBlock* current, MDefinition* obj,
5477               TypeSet::ObjectKey* key, bool bailOnEquality)
5478 {
5479     MInstruction* guard;
5480 
5481     if (key->isGroup()) {
5482         guard = MGuardObjectGroup::New(alloc, obj, key->group(), bailOnEquality,
5483                                        Bailout_ObjectIdentityOrTypeGuard);
5484     } else {
5485         MConstant* singletonConst = MConstant::NewConstraintlessObject(alloc, key->singleton());
5486         current->add(singletonConst);
5487         guard = MGuardObjectIdentity::New(alloc, obj, singletonConst, bailOnEquality);
5488     }
5489 
5490     current->add(guard);
5491 
5492     // For now, never move object group / identity guards.
5493     guard->setNotMovable();
5494 
5495     return guard;
5496 }
5497 
5498 // Whether value can be written to property without changing type information.
5499 bool
CanWriteProperty(TempAllocator & alloc,CompilerConstraintList * constraints,HeapTypeSetKey property,MDefinition * value,MIRType implicitType)5500 jit::CanWriteProperty(TempAllocator& alloc, CompilerConstraintList* constraints,
5501                       HeapTypeSetKey property, MDefinition* value,
5502                       MIRType implicitType /* = MIRType_None */)
5503 {
5504     if (property.couldBeConstant(constraints))
5505         return false;
5506     return PropertyTypeIncludes(alloc, property, value, implicitType);
5507 }
5508 
5509 bool
PropertyWriteNeedsTypeBarrier(TempAllocator & alloc,CompilerConstraintList * constraints,MBasicBlock * current,MDefinition ** pobj,PropertyName * name,MDefinition ** pvalue,bool canModify,MIRType implicitType)5510 jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* constraints,
5511                                    MBasicBlock* current, MDefinition** pobj,
5512                                    PropertyName* name, MDefinition** pvalue,
5513                                    bool canModify, MIRType implicitType)
5514 {
5515     // If any value being written is not reflected in the type information for
5516     // objects which obj could represent, a type barrier is needed when writing
5517     // the value. As for propertyReadNeedsTypeBarrier, this only applies for
5518     // properties that are accounted for by type information, i.e. normal data
5519     // properties and elements.
5520 
5521     TemporaryTypeSet* types = (*pobj)->resultTypeSet();
5522     if (!types || types->unknownObject())
5523         return true;
5524 
5525     // If all of the objects being written to have property types which already
5526     // reflect the value, no barrier at all is needed. Additionally, if all
5527     // objects being written to have the same types for the property, and those
5528     // types do *not* reflect the value, add a type barrier for the value.
5529 
5530     bool success = true;
5531     for (size_t i = 0; i < types->getObjectCount(); i++) {
5532         TypeSet::ObjectKey* key = types->getObject(i);
5533         if (!key || key->unknownProperties())
5534             continue;
5535 
5536         // TI doesn't track TypedArray indexes and should never insert a type
5537         // barrier for them.
5538         if (!name && IsAnyTypedArrayClass(key->clasp()))
5539             continue;
5540 
5541         jsid id = name ? NameToId(name) : JSID_VOID;
5542         HeapTypeSetKey property = key->property(id);
5543         if (!CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) {
5544             // Either pobj or pvalue needs to be modified to filter out the
5545             // types which the value could have but are not in the property,
5546             // or a VM call is required. A VM call is always required if pobj
5547             // and pvalue cannot be modified.
5548             if (!canModify)
5549                 return true;
5550             success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue,
5551                                                 implicitType);
5552             break;
5553         }
5554     }
5555 
5556     // Perform additional filtering to make sure that any unboxed property
5557     // being written can accommodate the value.
5558     for (size_t i = 0; i < types->getObjectCount(); i++) {
5559         TypeSet::ObjectKey* key = types->getObject(i);
5560         if (key && key->isGroup() && key->group()->maybeUnboxedLayout()) {
5561             const UnboxedLayout& layout = key->group()->unboxedLayout();
5562             if (name) {
5563                 const UnboxedLayout::Property* property = layout.lookup(name);
5564                 if (property && !CanStoreUnboxedType(alloc, property->type, *pvalue))
5565                     return true;
5566             } else {
5567                 if (layout.isArray() && !CanStoreUnboxedType(alloc, layout.elementType(), *pvalue))
5568                     return true;
5569             }
5570         }
5571     }
5572 
5573     if (success)
5574         return false;
5575 
5576     // If all of the objects except one have property types which reflect the
5577     // value, and the remaining object has no types at all for the property,
5578     // add a guard that the object does not have that remaining object's type.
5579 
5580     if (types->getObjectCount() <= 1)
5581         return true;
5582 
5583     TypeSet::ObjectKey* excluded = nullptr;
5584     for (size_t i = 0; i < types->getObjectCount(); i++) {
5585         TypeSet::ObjectKey* key = types->getObject(i);
5586         if (!key || key->unknownProperties())
5587             continue;
5588         if (!name && IsAnyTypedArrayClass(key->clasp()))
5589             continue;
5590 
5591         jsid id = name ? NameToId(name) : JSID_VOID;
5592         HeapTypeSetKey property = key->property(id);
5593         if (CanWriteProperty(alloc, constraints, property, *pvalue, implicitType))
5594             continue;
5595 
5596         if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded)
5597             return true;
5598         excluded = key;
5599     }
5600 
5601     MOZ_ASSERT(excluded);
5602 
5603     // If the excluded object is a group with an unboxed layout, make sure it
5604     // does not have a corresponding native group. Objects with the native
5605     // group might appear even though they are not in the type set.
5606     if (excluded->isGroup()) {
5607         if (UnboxedLayout* layout = excluded->group()->maybeUnboxedLayout()) {
5608             if (layout->nativeGroup())
5609                 return true;
5610             excluded->watchStateChangeForUnboxedConvertedToNative(constraints);
5611         }
5612     }
5613 
5614     *pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true);
5615     return false;
5616 }
5617