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