Line data Source code
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/CheckedInt.h"
10 : #include "mozilla/FloatingPoint.h"
11 : #include "mozilla/IntegerPrintfMacros.h"
12 : #include "mozilla/MathAlgorithms.h"
13 : #include "mozilla/SizePrintfMacros.h"
14 :
15 : #include <ctype.h>
16 :
17 : #include "jslibmath.h"
18 : #include "jsstr.h"
19 :
20 : #include "builtin/RegExp.h"
21 : #include "jit/AtomicOperations.h"
22 : #include "jit/BaselineInspector.h"
23 : #include "jit/IonBuilder.h"
24 : #include "jit/JitSpewer.h"
25 : #include "jit/MIRGraph.h"
26 : #include "jit/RangeAnalysis.h"
27 : #include "js/Conversions.h"
28 :
29 : #include "jsatominlines.h"
30 : #include "jsboolinlines.h"
31 : #include "jsobjinlines.h"
32 : #include "jsscriptinlines.h"
33 :
34 : using namespace js;
35 : using namespace js::jit;
36 :
37 : using JS::ToInt32;
38 :
39 : using mozilla::CheckedInt;
40 : using mozilla::NumbersAreIdentical;
41 : using mozilla::IsFloat32Representable;
42 : using mozilla::IsNaN;
43 : using mozilla::IsPowerOfTwo;
44 : using mozilla::Maybe;
45 : using mozilla::DebugOnly;
46 :
47 : #ifdef DEBUG
48 478023 : size_t MUse::index() const
49 : {
50 478023 : return consumer()->indexOf(this);
51 : }
52 : #endif
53 :
54 : template<size_t Op> static void
55 0 : ConvertDefinitionToDouble(TempAllocator& alloc, MDefinition* def, MInstruction* consumer)
56 : {
57 0 : MInstruction* replace = MToDouble::New(alloc, def);
58 0 : consumer->replaceOperand(Op, replace);
59 0 : consumer->block()->insertBefore(consumer, replace);
60 0 : }
61 :
62 : static bool
63 0 : CheckUsesAreFloat32Consumers(const MInstruction* ins)
64 : {
65 0 : bool allConsumerUses = true;
66 0 : for (MUseDefIterator use(ins); allConsumerUses && use; use++)
67 0 : allConsumerUses &= use.def()->canConsumeFloat32(use.use());
68 0 : return allConsumerUses;
69 : }
70 :
71 : void
72 0 : MDefinition::PrintOpcodeName(GenericPrinter& out, MDefinition::Opcode op)
73 : {
74 : static const char * const names[] =
75 : {
76 : #define NAME(x) #x,
77 : MIR_OPCODE_LIST(NAME)
78 : #undef NAME
79 : };
80 0 : const char* name = names[op];
81 0 : size_t len = strlen(name);
82 0 : for (size_t i = 0; i < len; i++)
83 0 : out.printf("%c", tolower(name[i]));
84 0 : }
85 :
86 : static MConstant*
87 173 : EvaluateConstantOperands(TempAllocator& alloc, MBinaryInstruction* ins, bool* ptypeChange = nullptr)
88 : {
89 173 : MDefinition* left = ins->getOperand(0);
90 173 : MDefinition* right = ins->getOperand(1);
91 :
92 173 : MOZ_ASSERT(IsTypeRepresentableAsDouble(left->type()));
93 173 : MOZ_ASSERT(IsTypeRepresentableAsDouble(right->type()));
94 :
95 173 : if (!left->isConstant() || !right->isConstant())
96 168 : return nullptr;
97 :
98 5 : MConstant* lhs = left->toConstant();
99 5 : MConstant* rhs = right->toConstant();
100 5 : double ret = JS::GenericNaN();
101 :
102 5 : switch (ins->op()) {
103 : case MDefinition::Op_BitAnd:
104 0 : ret = double(lhs->toInt32() & rhs->toInt32());
105 0 : break;
106 : case MDefinition::Op_BitOr:
107 0 : ret = double(lhs->toInt32() | rhs->toInt32());
108 0 : break;
109 : case MDefinition::Op_BitXor:
110 0 : ret = double(lhs->toInt32() ^ rhs->toInt32());
111 0 : break;
112 : case MDefinition::Op_Lsh:
113 0 : ret = double(uint32_t(lhs->toInt32()) << (rhs->toInt32() & 0x1F));
114 0 : break;
115 : case MDefinition::Op_Rsh:
116 0 : ret = double(lhs->toInt32() >> (rhs->toInt32() & 0x1F));
117 0 : break;
118 : case MDefinition::Op_Ursh:
119 0 : ret = double(uint32_t(lhs->toInt32()) >> (rhs->toInt32() & 0x1F));
120 0 : break;
121 : case MDefinition::Op_Add:
122 5 : ret = lhs->numberToDouble() + rhs->numberToDouble();
123 5 : break;
124 : case MDefinition::Op_Sub:
125 0 : ret = lhs->numberToDouble() - rhs->numberToDouble();
126 0 : break;
127 : case MDefinition::Op_Mul:
128 0 : ret = lhs->numberToDouble() * rhs->numberToDouble();
129 0 : break;
130 : case MDefinition::Op_Div:
131 0 : if (ins->toDiv()->isUnsigned()) {
132 0 : if (rhs->isInt32(0)) {
133 0 : if (ins->toDiv()->trapOnError())
134 0 : return nullptr;
135 0 : ret = 0.0;
136 : } else {
137 0 : ret = double(uint32_t(lhs->toInt32()) / uint32_t(rhs->toInt32()));
138 : }
139 : } else {
140 0 : ret = NumberDiv(lhs->numberToDouble(), rhs->numberToDouble());
141 : }
142 0 : break;
143 : case MDefinition::Op_Mod:
144 0 : if (ins->toMod()->isUnsigned()) {
145 0 : if (rhs->isInt32(0)) {
146 0 : if (ins->toMod()->trapOnError())
147 0 : return nullptr;
148 0 : ret = 0.0;
149 : } else {
150 0 : ret = double(uint32_t(lhs->toInt32()) % uint32_t(rhs->toInt32()));
151 : }
152 : } else {
153 0 : ret = NumberMod(lhs->numberToDouble(), rhs->numberToDouble());
154 : }
155 0 : break;
156 : default:
157 0 : MOZ_CRASH("NYI");
158 : }
159 :
160 5 : if (ins->type() == MIRType::Float32)
161 0 : return MConstant::NewFloat32(alloc, float(ret));
162 5 : if (ins->type() == MIRType::Double)
163 4 : return MConstant::New(alloc, DoubleValue(ret));
164 :
165 1 : Value retVal;
166 1 : retVal.setNumber(JS::CanonicalizeNaN(ret));
167 :
168 : // If this was an int32 operation but the result isn't an int32 (for
169 : // example, a division where the numerator isn't evenly divisible by the
170 : // denominator), decline folding.
171 1 : MOZ_ASSERT(ins->type() == MIRType::Int32);
172 1 : if (!retVal.isInt32()) {
173 0 : if (ptypeChange)
174 0 : *ptypeChange = true;
175 0 : return nullptr;
176 : }
177 :
178 1 : return MConstant::New(alloc, retVal);
179 : }
180 :
181 : static MMul*
182 0 : EvaluateExactReciprocal(TempAllocator& alloc, MDiv* ins)
183 : {
184 : // we should fold only when it is a floating point operation
185 0 : if (!IsFloatingPointType(ins->type()))
186 0 : return nullptr;
187 :
188 0 : MDefinition* left = ins->getOperand(0);
189 0 : MDefinition* right = ins->getOperand(1);
190 :
191 0 : if (!right->isConstant())
192 0 : return nullptr;
193 :
194 : int32_t num;
195 0 : if (!mozilla::NumberIsInt32(right->toConstant()->numberToDouble(), &num))
196 0 : return nullptr;
197 :
198 : // check if rhs is a power of two
199 0 : if (mozilla::Abs(num) & (mozilla::Abs(num) - 1))
200 0 : return nullptr;
201 :
202 0 : Value ret;
203 0 : ret.setDouble(1.0 / double(num));
204 :
205 : MConstant* foldedRhs;
206 0 : if (ins->type() == MIRType::Float32)
207 0 : foldedRhs = MConstant::NewFloat32(alloc, ret.toDouble());
208 : else
209 0 : foldedRhs = MConstant::New(alloc, ret);
210 :
211 0 : MOZ_ASSERT(foldedRhs->type() == ins->type());
212 0 : ins->block()->insertBefore(ins, foldedRhs);
213 :
214 0 : MMul* mul = MMul::New(alloc, left, foldedRhs, ins->type());
215 0 : mul->setCommutative();
216 0 : mul->setMustPreserveNaN(ins->mustPreserveNaN());
217 0 : return mul;
218 : }
219 :
220 : void
221 0 : MDefinition::printName(GenericPrinter& out) const
222 : {
223 0 : PrintOpcodeName(out, op());
224 0 : out.printf("%u", id());
225 0 : }
226 :
227 : HashNumber
228 2626 : MDefinition::addU32ToHash(HashNumber hash, uint32_t data)
229 : {
230 2626 : return data + (hash << 6) + (hash << 16) - hash;
231 : }
232 :
233 : HashNumber
234 1525 : MDefinition::valueHash() const
235 : {
236 1525 : HashNumber out = op();
237 3914 : for (size_t i = 0, e = numOperands(); i < e; i++)
238 2389 : out = addU32ToHash(out, getOperand(i)->id());
239 1525 : if (MDefinition* dep = dependency())
240 217 : out = addU32ToHash(out, dep->id());
241 1525 : return out;
242 : }
243 :
244 : bool
245 1568 : MDefinition::congruentIfOperandsEqual(const MDefinition* ins) const
246 : {
247 1568 : if (op() != ins->op())
248 0 : return false;
249 :
250 1568 : if (type() != ins->type())
251 0 : return false;
252 :
253 1568 : if (isEffectful() || ins->isEffectful())
254 0 : return false;
255 :
256 1568 : if (numOperands() != ins->numOperands())
257 0 : return false;
258 :
259 3744 : for (size_t i = 0, e = numOperands(); i < e; i++) {
260 2176 : if (getOperand(i) != ins->getOperand(i))
261 0 : return false;
262 : }
263 :
264 1568 : return true;
265 : }
266 :
267 : MDefinition*
268 1992 : MDefinition::foldsTo(TempAllocator& alloc)
269 : {
270 : // In the default case, there are no constants to fold.
271 1992 : return this;
272 : }
273 :
274 : bool
275 34 : MDefinition::mightBeMagicType() const
276 : {
277 34 : if (IsMagicType(type()))
278 0 : return true;
279 :
280 34 : if (MIRType::Value != type())
281 15 : return false;
282 :
283 19 : return !resultTypeSet() || resultTypeSet()->hasType(TypeSet::MagicArgType());
284 : }
285 :
286 : MDefinition*
287 84 : MInstruction::foldsToStore(TempAllocator& alloc)
288 : {
289 84 : if (!dependency())
290 0 : return nullptr;
291 :
292 84 : MDefinition* store = dependency();
293 84 : if (mightAlias(store) != AliasType::MustAlias)
294 84 : return nullptr;
295 :
296 0 : if (!store->block()->dominates(block()))
297 0 : return nullptr;
298 :
299 : MDefinition* value;
300 0 : switch (store->op()) {
301 : case Op_StoreFixedSlot:
302 0 : value = store->toStoreFixedSlot()->value();
303 0 : break;
304 : case Op_StoreSlot:
305 0 : value = store->toStoreSlot()->value();
306 0 : break;
307 : case Op_StoreElement:
308 0 : value = store->toStoreElement()->value();
309 0 : break;
310 : case Op_StoreUnboxedObjectOrNull:
311 0 : value = store->toStoreUnboxedObjectOrNull()->value();
312 0 : break;
313 : default:
314 0 : MOZ_CRASH("unknown store");
315 : }
316 :
317 : // If the type are matching then we return the value which is used as
318 : // argument of the store.
319 0 : if (value->type() != type()) {
320 : // If we expect to read a type which is more generic than the type seen
321 : // by the store, then we box the value used by the store.
322 0 : if (type() != MIRType::Value)
323 0 : return nullptr;
324 : // We cannot unbox ObjectOrNull yet.
325 0 : if (value->type() == MIRType::ObjectOrNull)
326 0 : return nullptr;
327 :
328 0 : MOZ_ASSERT(value->type() < MIRType::Value);
329 0 : MBox* box = MBox::New(alloc, value);
330 0 : value = box;
331 : }
332 :
333 0 : return value;
334 : }
335 :
336 : void
337 1244 : MDefinition::analyzeEdgeCasesForward()
338 : {
339 1244 : }
340 :
341 : void
342 1471 : MDefinition::analyzeEdgeCasesBackward()
343 : {
344 1471 : }
345 :
346 : void
347 4969 : MInstruction::setResumePoint(MResumePoint* resumePoint)
348 : {
349 4969 : MOZ_ASSERT(!resumePoint_);
350 4969 : resumePoint_ = resumePoint;
351 4969 : resumePoint_->setInstruction(this);
352 4969 : }
353 :
354 : void
355 0 : MInstruction::stealResumePoint(MInstruction* ins)
356 : {
357 0 : MOZ_ASSERT(ins->resumePoint_->instruction() == ins);
358 0 : resumePoint_ = ins->resumePoint_;
359 0 : ins->resumePoint_ = nullptr;
360 0 : resumePoint_->replaceInstruction(this);
361 0 : }
362 :
363 : void
364 49 : MInstruction::moveResumePointAsEntry()
365 : {
366 49 : MOZ_ASSERT(isNop());
367 49 : block()->clearEntryResumePoint();
368 49 : block()->setEntryResumePoint(resumePoint_);
369 49 : resumePoint_->resetInstruction();
370 49 : resumePoint_ = nullptr;
371 49 : }
372 :
373 : void
374 0 : MInstruction::clearResumePoint()
375 : {
376 0 : resumePoint_->resetInstruction();
377 0 : block()->discardPreAllocatedResumePoint(resumePoint_);
378 0 : resumePoint_ = nullptr;
379 0 : }
380 :
381 : bool
382 1763 : MDefinition::maybeEmulatesUndefined(CompilerConstraintList* constraints)
383 : {
384 1763 : if (!mightBeType(MIRType::Object))
385 1750 : return false;
386 :
387 13 : TemporaryTypeSet* types = resultTypeSet();
388 13 : if (!types)
389 6 : return true;
390 :
391 7 : return types->maybeEmulatesUndefined(constraints);
392 : }
393 :
394 : static bool
395 3 : MaybeCallable(CompilerConstraintList* constraints, MDefinition* op)
396 : {
397 3 : if (!op->mightBeType(MIRType::Object))
398 3 : return false;
399 :
400 0 : TemporaryTypeSet* types = op->resultTypeSet();
401 0 : if (!types)
402 0 : return true;
403 :
404 0 : return types->maybeCallable(constraints);
405 : }
406 :
407 : /* static */ const char*
408 0 : AliasSet::Name(size_t flag)
409 : {
410 0 : switch(flag) {
411 0 : case 0: return "ObjectFields";
412 0 : case 1: return "Element";
413 0 : case 2: return "UnboxedElement";
414 0 : case 3: return "DynamicSlot";
415 0 : case 4: return "FixedSlot";
416 0 : case 5: return "DOMProperty";
417 0 : case 6: return "FrameArgument";
418 0 : case 7: return "WasmGlobalVar";
419 0 : case 8: return "WasmHeap";
420 0 : case 9: return "TypedArrayLength";
421 : default:
422 0 : MOZ_CRASH("Unknown flag");
423 : }
424 : }
425 :
426 : void
427 912 : MTest::cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints)
428 : {
429 912 : MOZ_ASSERT(operandMightEmulateUndefined());
430 :
431 912 : if (!getOperand(0)->maybeEmulatesUndefined(constraints))
432 910 : markNoOperandEmulatesUndefined();
433 912 : }
434 :
435 : MDefinition*
436 232 : MTest::foldsDoubleNegation(TempAllocator& alloc)
437 : {
438 232 : MDefinition* op = getOperand(0);
439 :
440 232 : if (op->isNot()) {
441 : // If the operand of the Not is itself a Not, they cancel out.
442 8 : MDefinition* opop = op->getOperand(0);
443 8 : if (opop->isNot())
444 0 : return MTest::New(alloc, opop->toNot()->input(), ifTrue(), ifFalse());
445 8 : return MTest::New(alloc, op->toNot()->input(), ifFalse(), ifTrue());
446 : }
447 224 : return nullptr;
448 : }
449 :
450 : MDefinition*
451 224 : MTest::foldsConstant(TempAllocator& alloc)
452 : {
453 224 : MDefinition* op = getOperand(0);
454 224 : if (MConstant* opConst = op->maybeConstantValue()) {
455 : bool b;
456 32 : if (opConst->valueToBoolean(&b))
457 32 : return MGoto::New(alloc, b ? ifTrue() : ifFalse());
458 : }
459 192 : return nullptr;
460 : }
461 :
462 : MDefinition*
463 192 : MTest::foldsTypes(TempAllocator& alloc)
464 : {
465 192 : MDefinition* op = getOperand(0);
466 :
467 192 : switch (op->type()) {
468 : case MIRType::Undefined:
469 : case MIRType::Null:
470 0 : return MGoto::New(alloc, ifFalse());
471 : case MIRType::Symbol:
472 0 : return MGoto::New(alloc, ifTrue());
473 : case MIRType::Object:
474 0 : if (!operandMightEmulateUndefined())
475 0 : return MGoto::New(alloc, ifTrue());
476 0 : break;
477 : default:
478 192 : break;
479 : }
480 192 : return nullptr;
481 : }
482 :
483 : MDefinition*
484 192 : MTest::foldsNeedlessControlFlow(TempAllocator& alloc)
485 : {
486 228 : for (MInstructionIterator iter(ifTrue()->begin()), end(ifTrue()->end()); iter != end; ) {
487 209 : MInstruction* ins = *iter++;
488 209 : if (ins->isNop() || ins->isGoto())
489 22 : continue;
490 187 : if (ins->hasUses())
491 320 : return nullptr;
492 40 : if (!DeadIfUnused(ins))
493 26 : return nullptr;
494 : }
495 :
496 38 : for (MInstructionIterator iter(ifFalse()->begin()), end(ifFalse()->end()); iter != end; ) {
497 19 : MInstruction* ins = *iter++;
498 19 : if (ins->isNop() || ins->isGoto())
499 0 : continue;
500 19 : if (ins->hasUses())
501 34 : return nullptr;
502 4 : if (!DeadIfUnused(ins))
503 4 : return nullptr;
504 : }
505 :
506 0 : if (ifTrue()->numSuccessors() != 1 || ifFalse()->numSuccessors() != 1)
507 0 : return nullptr;
508 0 : if (ifTrue()->getSuccessor(0) != ifFalse()->getSuccessor(0))
509 0 : return nullptr;
510 :
511 0 : if (ifTrue()->successorWithPhis())
512 0 : return nullptr;
513 :
514 0 : return MGoto::New(alloc, ifTrue());
515 : }
516 :
517 : MDefinition*
518 232 : MTest::foldsTo(TempAllocator& alloc)
519 : {
520 :
521 232 : if (MDefinition* def = foldsDoubleNegation(alloc))
522 8 : return def;
523 :
524 224 : if (MDefinition* def = foldsConstant(alloc))
525 32 : return def;
526 :
527 192 : if (MDefinition* def = foldsTypes(alloc))
528 0 : return def;
529 :
530 192 : if (MDefinition* def = foldsNeedlessControlFlow(alloc))
531 0 : return def;
532 :
533 192 : return this;
534 : }
535 :
536 : void
537 2 : MTest::filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
538 : bool* filtersNull)
539 : {
540 2 : MDefinition* ins = getOperand(0);
541 2 : if (ins->isCompare()) {
542 0 : ins->toCompare()->filtersUndefinedOrNull(trueBranch, subject, filtersUndefined, filtersNull);
543 0 : return;
544 : }
545 :
546 2 : if (!trueBranch && ins->isNot()) {
547 0 : *subject = ins->getOperand(0);
548 0 : *filtersUndefined = *filtersNull = true;
549 0 : return;
550 : }
551 :
552 2 : if (trueBranch) {
553 0 : *subject = ins;
554 0 : *filtersUndefined = *filtersNull = true;
555 0 : return;
556 : }
557 :
558 2 : *filtersUndefined = *filtersNull = false;
559 2 : *subject = nullptr;
560 : }
561 :
562 : void
563 0 : MDefinition::printOpcode(GenericPrinter& out) const
564 : {
565 0 : PrintOpcodeName(out, op());
566 0 : for (size_t j = 0, e = numOperands(); j < e; j++) {
567 0 : out.printf(" ");
568 0 : if (getUseFor(j)->hasProducer())
569 0 : getOperand(j)->printName(out);
570 : else
571 0 : out.printf("(null)");
572 : }
573 0 : }
574 :
575 : void
576 0 : MDefinition::dump(GenericPrinter& out) const
577 : {
578 0 : printName(out);
579 0 : out.printf(" = ");
580 0 : printOpcode(out);
581 0 : out.printf("\n");
582 :
583 0 : if (isInstruction()) {
584 0 : if (MResumePoint* resume = toInstruction()->resumePoint())
585 0 : resume->dump(out);
586 : }
587 0 : }
588 :
589 : void
590 0 : MDefinition::dump() const
591 : {
592 0 : Fprinter out(stderr);
593 0 : dump(out);
594 0 : out.finish();
595 0 : }
596 :
597 : void
598 0 : MDefinition::dumpLocation(GenericPrinter& out) const
599 : {
600 0 : MResumePoint* rp = nullptr;
601 0 : const char* linkWord = nullptr;
602 0 : if (isInstruction() && toInstruction()->resumePoint()) {
603 0 : rp = toInstruction()->resumePoint();
604 0 : linkWord = "at";
605 : } else {
606 0 : rp = block()->entryResumePoint();
607 0 : linkWord = "after";
608 : }
609 :
610 0 : while (rp) {
611 0 : JSScript* script = rp->block()->info().script();
612 0 : uint32_t lineno = PCToLineNumber(rp->block()->info().script(), rp->pc());
613 0 : out.printf(" %s %s:%d\n", linkWord, script->filename(), lineno);
614 0 : rp = rp->caller();
615 0 : linkWord = "in";
616 : }
617 0 : }
618 :
619 : void
620 0 : MDefinition::dumpLocation() const
621 : {
622 0 : Fprinter out(stderr);
623 0 : dumpLocation(out);
624 0 : out.finish();
625 0 : }
626 :
627 : #if defined(DEBUG) || defined(JS_JITSPEW)
628 : size_t
629 0 : MDefinition::useCount() const
630 : {
631 0 : size_t count = 0;
632 0 : for (MUseIterator i(uses_.begin()); i != uses_.end(); i++)
633 0 : count++;
634 0 : return count;
635 : }
636 :
637 : size_t
638 22401 : MDefinition::defUseCount() const
639 : {
640 22401 : size_t count = 0;
641 359865 : for (MUseIterator i(uses_.begin()); i != uses_.end(); i++)
642 337464 : if ((*i)->consumer()->isDefinition())
643 44807 : count++;
644 22401 : return count;
645 : }
646 : #endif
647 :
648 : bool
649 49 : MDefinition::hasOneUse() const
650 : {
651 49 : MUseIterator i(uses_.begin());
652 49 : if (i == uses_.end())
653 0 : return false;
654 49 : i++;
655 49 : return i == uses_.end();
656 : }
657 :
658 : bool
659 28 : MDefinition::hasOneDefUse() const
660 : {
661 28 : bool hasOneDefUse = false;
662 168 : for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
663 162 : if (!(*i)->consumer()->isDefinition())
664 112 : continue;
665 :
666 : // We already have a definition use. So 1+
667 50 : if (hasOneDefUse)
668 22 : return false;
669 :
670 : // We saw one definition. Loop to test if there is another.
671 28 : hasOneDefUse = true;
672 : }
673 :
674 6 : return hasOneDefUse;
675 : }
676 :
677 : bool
678 40 : MDefinition::hasDefUses() const
679 : {
680 44 : for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
681 30 : if ((*i)->consumer()->isDefinition())
682 26 : return true;
683 : }
684 :
685 14 : return false;
686 : }
687 :
688 : bool
689 1565 : MDefinition::hasLiveDefUses() const
690 : {
691 5573 : for (MUseIterator i(uses_.begin()); i != uses_.end(); i++) {
692 4008 : MNode* ins = (*i)->consumer();
693 4008 : if (ins->isDefinition()) {
694 696 : if (!ins->toDefinition()->isRecoveredOnBailout())
695 0 : return true;
696 : } else {
697 3312 : MOZ_ASSERT(ins->isResumePoint());
698 3312 : if (!ins->toResumePoint()->isRecoverableOperand(*i))
699 0 : return true;
700 : }
701 : }
702 :
703 1565 : return false;
704 : }
705 :
706 : void
707 23 : MDefinition::replaceAllUsesWith(MDefinition* dom)
708 : {
709 54 : for (size_t i = 0, e = numOperands(); i < e; ++i)
710 31 : getOperand(i)->setUseRemovedUnchecked();
711 :
712 23 : justReplaceAllUsesWith(dom);
713 23 : }
714 :
715 : void
716 2173 : MDefinition::justReplaceAllUsesWith(MDefinition* dom)
717 : {
718 2173 : MOZ_ASSERT(dom != nullptr);
719 2173 : MOZ_ASSERT(dom != this);
720 :
721 : // Carry over the fact the value has uses which are no longer inspectable
722 : // with the graph.
723 2173 : if (isUseRemoved())
724 77 : dom->setUseRemovedUnchecked();
725 :
726 36057 : for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i)
727 33884 : i->setProducerUnchecked(dom);
728 2173 : dom->uses_.takeElements(uses_);
729 2173 : }
730 :
731 : void
732 0 : MDefinition::justReplaceAllUsesWithExcept(MDefinition* dom)
733 : {
734 0 : MOZ_ASSERT(dom != nullptr);
735 0 : MOZ_ASSERT(dom != this);
736 :
737 : // Carry over the fact the value has uses which are no longer inspectable
738 : // with the graph.
739 0 : if (isUseRemoved())
740 0 : dom->setUseRemovedUnchecked();
741 :
742 : // Move all uses to new dom. Save the use of the dominating instruction.
743 0 : MUse *exceptUse = nullptr;
744 0 : for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ++i) {
745 0 : if (i->consumer() != dom) {
746 0 : i->setProducerUnchecked(dom);
747 : } else {
748 0 : MOZ_ASSERT(!exceptUse);
749 0 : exceptUse = *i;
750 : }
751 : }
752 0 : dom->uses_.takeElements(uses_);
753 :
754 : // Restore the use to the original definition.
755 0 : dom->uses_.remove(exceptUse);
756 0 : exceptUse->setProducerUnchecked(this);
757 0 : uses_.pushFront(exceptUse);
758 0 : }
759 :
760 : bool
761 677 : MDefinition::optimizeOutAllUses(TempAllocator& alloc)
762 : {
763 5141 : for (MUseIterator i(usesBegin()), e(usesEnd()); i != e;) {
764 4464 : MUse* use = *i++;
765 4464 : MConstant* constant = use->consumer()->block()->optimizedOutConstant(alloc);
766 4464 : if (!alloc.ensureBallast())
767 0 : return false;
768 :
769 : // Update the resume point operand to use the optimized-out constant.
770 4464 : use->setProducerUnchecked(constant);
771 4464 : constant->addUseUnchecked(use);
772 : }
773 :
774 : // Remove dangling pointers.
775 677 : this->uses_.clear();
776 677 : return true;
777 : }
778 :
779 : void
780 0 : MDefinition::replaceAllLiveUsesWith(MDefinition* dom)
781 : {
782 0 : for (MUseIterator i(usesBegin()), e(usesEnd()); i != e; ) {
783 0 : MUse* use = *i++;
784 0 : MNode* consumer = use->consumer();
785 0 : if (consumer->isResumePoint())
786 0 : continue;
787 0 : if (consumer->isDefinition() && consumer->toDefinition()->isRecoveredOnBailout())
788 0 : continue;
789 :
790 : // Update the operand to use the dominating definition.
791 0 : use->replaceProducer(dom);
792 : }
793 0 : }
794 :
795 : bool
796 184 : MDefinition::emptyResultTypeSet() const
797 : {
798 184 : return resultTypeSet() && resultTypeSet()->empty();
799 : }
800 :
801 : MConstant*
802 5855 : MConstant::New(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints)
803 : {
804 5855 : return new(alloc) MConstant(v, constraints);
805 : }
806 :
807 : MConstant*
808 0 : MConstant::New(TempAllocator::Fallible alloc, const Value& v, CompilerConstraintList* constraints)
809 : {
810 0 : return new(alloc) MConstant(v, constraints);
811 : }
812 :
813 : MConstant*
814 0 : MConstant::NewFloat32(TempAllocator& alloc, double d)
815 : {
816 0 : MOZ_ASSERT(IsNaN(d) || d == double(float(d)));
817 0 : return new(alloc) MConstant(float(d));
818 : }
819 :
820 : MConstant*
821 0 : MConstant::NewInt64(TempAllocator& alloc, int64_t i)
822 : {
823 0 : return new(alloc) MConstant(i);
824 : }
825 :
826 : MConstant*
827 0 : MConstant::New(TempAllocator& alloc, const Value& v, MIRType type)
828 : {
829 0 : if (type == MIRType::Float32)
830 0 : return NewFloat32(alloc, v.toNumber());
831 0 : MConstant* res = New(alloc, v);
832 0 : MOZ_ASSERT(res->type() == type);
833 0 : return res;
834 : }
835 :
836 : MConstant*
837 49 : MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v)
838 : {
839 49 : return new(alloc) MConstant(v);
840 : }
841 :
842 : static TemporaryTypeSet*
843 641 : MakeSingletonTypeSetFromKey(CompilerConstraintList* constraints, TypeSet::ObjectKey* key)
844 : {
845 : // Invalidate when this object's ObjectGroup gets unknown properties. This
846 : // happens for instance when we mutate an object's __proto__, in this case
847 : // we want to invalidate and mark this TypeSet as containing AnyObject
848 : // (because mutating __proto__ will change an object's ObjectGroup).
849 641 : MOZ_ASSERT(constraints);
850 641 : (void)key->hasStableClassAndProto(constraints);
851 :
852 641 : LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
853 641 : return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::ObjectType(key));
854 : }
855 :
856 : TemporaryTypeSet*
857 632 : jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj)
858 : {
859 632 : return MakeSingletonTypeSetFromKey(constraints, TypeSet::ObjectKey::get(obj));
860 : }
861 :
862 : TemporaryTypeSet*
863 9 : jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, ObjectGroup* obj)
864 : {
865 9 : return MakeSingletonTypeSetFromKey(constraints, TypeSet::ObjectKey::get(obj));
866 : }
867 :
868 : static TemporaryTypeSet*
869 19 : MakeUnknownTypeSet()
870 : {
871 19 : LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
872 19 : return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::UnknownType());
873 : }
874 :
875 : #ifdef DEBUG
876 :
877 : bool
878 0 : jit::IonCompilationCanUseNurseryPointers()
879 : {
880 : // If we are doing backend compilation, which could occur on a helper
881 : // thread but might actually be on the active thread, check the flag set on
882 : // the JSContext by AutoEnterIonCompilation.
883 0 : if (CurrentThreadIsIonCompiling())
884 0 : return !CurrentThreadIsIonCompilingSafeForMinorGC();
885 :
886 : // Otherwise, we must be on the active thread during MIR construction. The
887 : // store buffer must have been notified that minor GCs must cancel pending
888 : // or in progress Ion compilations.
889 0 : JSContext* cx = TlsContext.get();
890 0 : return cx->zone()->group()->storeBuffer().cancelIonCompilations();
891 : }
892 :
893 : #endif // DEBUG
894 :
895 5855 : MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints)
896 : {
897 5855 : setResultType(MIRTypeFromValue(vp));
898 :
899 5855 : MOZ_ASSERT(payload_.asBits == 0);
900 :
901 5855 : switch (type()) {
902 : case MIRType::Undefined:
903 : case MIRType::Null:
904 1697 : break;
905 : case MIRType::Boolean:
906 209 : payload_.b = vp.toBoolean();
907 209 : break;
908 : case MIRType::Int32:
909 2204 : payload_.i32 = vp.toInt32();
910 2204 : break;
911 : case MIRType::Double:
912 30 : payload_.d = vp.toDouble();
913 30 : break;
914 : case MIRType::String:
915 159 : MOZ_ASSERT(vp.toString()->isAtom());
916 159 : payload_.str = vp.toString();
917 159 : break;
918 : case MIRType::Symbol:
919 5 : payload_.sym = vp.toSymbol();
920 5 : break;
921 : case MIRType::Object:
922 581 : payload_.obj = &vp.toObject();
923 : // Create a singleton type set for the object. This isn't necessary for
924 : // other types as the result type encodes all needed information.
925 581 : MOZ_ASSERT_IF(IsInsideNursery(&vp.toObject()), IonCompilationCanUseNurseryPointers());
926 581 : setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject()));
927 581 : break;
928 : case MIRType::MagicOptimizedArguments:
929 : case MIRType::MagicOptimizedOut:
930 : case MIRType::MagicHole:
931 : case MIRType::MagicIsConstructing:
932 951 : break;
933 : case MIRType::MagicUninitializedLexical:
934 : // JS_UNINITIALIZED_LEXICAL does not escape to script and is not
935 : // observed in type sets. However, it may flow around freely during
936 : // Ion compilation. Give it an unknown typeset to poison any type sets
937 : // it merges with.
938 : //
939 : // TODO We could track uninitialized lexicals more precisely by tracking
940 : // them in type sets.
941 19 : setResultTypeSet(MakeUnknownTypeSet());
942 19 : break;
943 : default:
944 0 : MOZ_CRASH("Unexpected type");
945 : }
946 :
947 5855 : setMovable();
948 5855 : }
949 :
950 49 : MConstant::MConstant(JSObject* obj)
951 : {
952 49 : MOZ_ASSERT_IF(IsInsideNursery(obj), IonCompilationCanUseNurseryPointers());
953 49 : setResultType(MIRType::Object);
954 49 : payload_.obj = obj;
955 49 : setMovable();
956 49 : }
957 :
958 0 : MConstant::MConstant(float f)
959 : {
960 0 : setResultType(MIRType::Float32);
961 0 : payload_.f = f;
962 0 : setMovable();
963 0 : }
964 :
965 0 : MConstant::MConstant(int64_t i)
966 : {
967 0 : setResultType(MIRType::Int64);
968 0 : payload_.i64 = i;
969 0 : setMovable();
970 0 : }
971 :
972 : #ifdef DEBUG
973 : void
974 5352 : MConstant::assertInitializedPayload() const
975 : {
976 : // valueHash() and equals() expect the unused payload bits to be
977 : // initialized to zero. Assert this in debug builds.
978 :
979 5352 : switch (type()) {
980 : case MIRType::Int32:
981 : case MIRType::Float32:
982 575 : MOZ_ASSERT((payload_.asBits >> 32) == 0);
983 575 : break;
984 : case MIRType::Boolean:
985 916 : MOZ_ASSERT((payload_.asBits >> 1) == 0);
986 916 : break;
987 : case MIRType::Double:
988 : case MIRType::Int64:
989 5 : break;
990 : case MIRType::String:
991 : case MIRType::Object:
992 : case MIRType::Symbol:
993 : MOZ_ASSERT_IF(JS_BITS_PER_WORD == 32, (payload_.asBits >> 32) == 0);
994 761 : break;
995 : default:
996 3095 : MOZ_ASSERT(IsNullOrUndefined(type()) || IsMagicType(type()));
997 3095 : MOZ_ASSERT(payload_.asBits == 0);
998 3095 : break;
999 : }
1000 5352 : }
1001 : #endif
1002 :
1003 : static HashNumber
1004 1958 : ConstantValueHash(MIRType type, uint64_t payload)
1005 : {
1006 : // Build a 64-bit value holding both the payload and the type.
1007 : static const size_t TypeBits = 8;
1008 : static const size_t TypeShift = 64 - TypeBits;
1009 1958 : MOZ_ASSERT(uintptr_t(type) <= (1 << TypeBits) - 1);
1010 1958 : uint64_t bits = (uint64_t(type) << TypeShift) ^ payload;
1011 :
1012 : // Fold all 64 bits into the 32-bit result. It's tempting to just discard
1013 : // half of the bits, as this is just a hash, however there are many common
1014 : // patterns of values where only the low or the high bits vary, so
1015 : // discarding either side would lead to excessive hash collisions.
1016 1958 : return (HashNumber)bits ^ (HashNumber)(bits >> 32);
1017 : }
1018 :
1019 : HashNumber
1020 1958 : MConstant::valueHash() const
1021 : {
1022 : static_assert(sizeof(Payload) == sizeof(uint64_t),
1023 : "Code below assumes payload fits in 64 bits");
1024 :
1025 1958 : assertInitializedPayload();
1026 1958 : return ConstantValueHash(type(), payload_.asBits);
1027 : }
1028 :
1029 : bool
1030 3394 : MConstant::congruentTo(const MDefinition* ins) const
1031 : {
1032 3394 : return ins->isConstant() && equals(ins->toConstant());
1033 : }
1034 :
1035 : void
1036 0 : MConstant::printOpcode(GenericPrinter& out) const
1037 : {
1038 0 : PrintOpcodeName(out, op());
1039 0 : out.printf(" ");
1040 0 : switch (type()) {
1041 : case MIRType::Undefined:
1042 0 : out.printf("undefined");
1043 0 : break;
1044 : case MIRType::Null:
1045 0 : out.printf("null");
1046 0 : break;
1047 : case MIRType::Boolean:
1048 0 : out.printf(toBoolean() ? "true" : "false");
1049 0 : break;
1050 : case MIRType::Int32:
1051 0 : out.printf("0x%x", toInt32());
1052 0 : break;
1053 : case MIRType::Int64:
1054 0 : out.printf("0x%" PRIx64, toInt64());
1055 0 : break;
1056 : case MIRType::Double:
1057 0 : out.printf("%.16g", toDouble());
1058 0 : break;
1059 : case MIRType::Float32:
1060 : {
1061 0 : float val = toFloat32();
1062 0 : out.printf("%.16g", val);
1063 0 : break;
1064 : }
1065 : case MIRType::Object:
1066 0 : if (toObject().is<JSFunction>()) {
1067 0 : JSFunction* fun = &toObject().as<JSFunction>();
1068 0 : if (fun->displayAtom()) {
1069 0 : out.put("function ");
1070 0 : EscapedStringPrinter(out, fun->displayAtom(), 0);
1071 : } else {
1072 0 : out.put("unnamed function");
1073 : }
1074 0 : if (fun->hasScript()) {
1075 0 : JSScript* script = fun->nonLazyScript();
1076 0 : out.printf(" (%s:%" PRIuSIZE ")",
1077 0 : script->filename() ? script->filename() : "", script->lineno());
1078 : }
1079 0 : out.printf(" at %p", (void*) fun);
1080 0 : break;
1081 : }
1082 0 : out.printf("object %p (%s)", (void*)&toObject(), toObject().getClass()->name);
1083 0 : break;
1084 : case MIRType::Symbol:
1085 0 : out.printf("symbol at %p", (void*)toSymbol());
1086 0 : break;
1087 : case MIRType::String:
1088 0 : out.printf("string %p", (void*)toString());
1089 0 : break;
1090 : case MIRType::MagicOptimizedArguments:
1091 0 : out.printf("magic lazyargs");
1092 0 : break;
1093 : case MIRType::MagicHole:
1094 0 : out.printf("magic hole");
1095 0 : break;
1096 : case MIRType::MagicIsConstructing:
1097 0 : out.printf("magic is-constructing");
1098 0 : break;
1099 : case MIRType::MagicOptimizedOut:
1100 0 : out.printf("magic optimized-out");
1101 0 : break;
1102 : case MIRType::MagicUninitializedLexical:
1103 0 : out.printf("magic uninitialized-lexical");
1104 0 : break;
1105 : default:
1106 0 : MOZ_CRASH("unexpected type");
1107 : }
1108 0 : }
1109 :
1110 : bool
1111 88 : MConstant::canProduceFloat32() const
1112 : {
1113 88 : if (!isTypeRepresentableAsDouble())
1114 83 : return false;
1115 :
1116 5 : if (type() == MIRType::Int32)
1117 5 : return IsFloat32Representable(static_cast<double>(toInt32()));
1118 0 : if (type() == MIRType::Double)
1119 0 : return IsFloat32Representable(toDouble());
1120 0 : MOZ_ASSERT(type() == MIRType::Float32);
1121 0 : return true;
1122 : }
1123 :
1124 : Value
1125 571 : MConstant::toJSValue() const
1126 : {
1127 : // Wasm has types like int64 that cannot be stored as js::Value. It also
1128 : // doesn't want the NaN canonicalization enforced by js::Value.
1129 571 : MOZ_ASSERT(!IsCompilingWasm());
1130 :
1131 571 : switch (type()) {
1132 : case MIRType::Undefined:
1133 48 : return UndefinedValue();
1134 : case MIRType::Null:
1135 4 : return NullValue();
1136 : case MIRType::Boolean:
1137 189 : return BooleanValue(toBoolean());
1138 : case MIRType::Int32:
1139 106 : return Int32Value(toInt32());
1140 : case MIRType::Double:
1141 0 : return DoubleValue(toDouble());
1142 : case MIRType::Float32:
1143 0 : return Float32Value(toFloat32());
1144 : case MIRType::String:
1145 29 : return StringValue(toString());
1146 : case MIRType::Symbol:
1147 3 : return SymbolValue(toSymbol());
1148 : case MIRType::Object:
1149 188 : return ObjectValue(toObject());
1150 : case MIRType::MagicOptimizedArguments:
1151 0 : return MagicValue(JS_OPTIMIZED_ARGUMENTS);
1152 : case MIRType::MagicOptimizedOut:
1153 0 : return MagicValue(JS_OPTIMIZED_OUT);
1154 : case MIRType::MagicHole:
1155 0 : return MagicValue(JS_ELEMENTS_HOLE);
1156 : case MIRType::MagicIsConstructing:
1157 0 : return MagicValue(JS_IS_CONSTRUCTING);
1158 : case MIRType::MagicUninitializedLexical:
1159 4 : return MagicValue(JS_UNINITIALIZED_LEXICAL);
1160 : default:
1161 0 : MOZ_CRASH("Unexpected type");
1162 : }
1163 : }
1164 :
1165 : bool
1166 43 : MConstant::valueToBoolean(bool* res) const
1167 : {
1168 43 : switch (type()) {
1169 : case MIRType::Boolean:
1170 30 : *res = toBoolean();
1171 30 : return true;
1172 : case MIRType::Int32:
1173 0 : *res = toInt32() != 0;
1174 0 : return true;
1175 : case MIRType::Int64:
1176 0 : *res = toInt64() != 0;
1177 0 : return true;
1178 : case MIRType::Double:
1179 0 : *res = !mozilla::IsNaN(toDouble()) && toDouble() != 0.0;
1180 0 : return true;
1181 : case MIRType::Float32:
1182 0 : *res = !mozilla::IsNaN(toFloat32()) && toFloat32() != 0.0f;
1183 0 : return true;
1184 : case MIRType::Null:
1185 : case MIRType::Undefined:
1186 13 : *res = false;
1187 13 : return true;
1188 : case MIRType::Symbol:
1189 0 : *res = true;
1190 0 : return true;
1191 : case MIRType::String:
1192 0 : *res = toString()->length() != 0;
1193 0 : return true;
1194 : case MIRType::Object:
1195 0 : *res = !EmulatesUndefined(&toObject());
1196 0 : return true;
1197 : default:
1198 0 : MOZ_ASSERT(IsMagicType(type()));
1199 0 : return false;
1200 : }
1201 : }
1202 :
1203 : HashNumber
1204 0 : MWasmFloatConstant::valueHash() const
1205 : {
1206 0 : return ConstantValueHash(type(), u.bits_);
1207 : }
1208 :
1209 : bool
1210 0 : MWasmFloatConstant::congruentTo(const MDefinition* ins) const
1211 : {
1212 0 : return ins->isWasmFloatConstant() &&
1213 0 : type() == ins->type() &&
1214 0 : u.bits_ == ins->toWasmFloatConstant()->u.bits_;
1215 : }
1216 :
1217 : MDefinition*
1218 0 : MSimdValueX4::foldsTo(TempAllocator& alloc)
1219 : {
1220 : #ifdef DEBUG
1221 0 : MIRType laneType = SimdTypeToLaneArgumentType(type());
1222 : #endif
1223 0 : bool allConstants = true;
1224 0 : bool allSame = true;
1225 :
1226 0 : for (size_t i = 0; i < 4; ++i) {
1227 0 : MDefinition* op = getOperand(i);
1228 0 : MOZ_ASSERT(op->type() == laneType);
1229 0 : if (!op->isConstant())
1230 0 : allConstants = false;
1231 0 : if (i > 0 && op != getOperand(i - 1))
1232 0 : allSame = false;
1233 : }
1234 :
1235 0 : if (!allConstants && !allSame)
1236 0 : return this;
1237 :
1238 0 : if (allConstants) {
1239 : SimdConstant cst;
1240 0 : switch (type()) {
1241 : case MIRType::Bool32x4: {
1242 : int32_t a[4];
1243 0 : for (size_t i = 0; i < 4; ++i)
1244 0 : a[i] = getOperand(i)->toConstant()->valueToBooleanInfallible() ? -1 : 0;
1245 0 : cst = SimdConstant::CreateX4(a);
1246 0 : break;
1247 : }
1248 : case MIRType::Int32x4: {
1249 : int32_t a[4];
1250 0 : for (size_t i = 0; i < 4; ++i)
1251 0 : a[i] = getOperand(i)->toConstant()->toInt32();
1252 0 : cst = SimdConstant::CreateX4(a);
1253 0 : break;
1254 : }
1255 : case MIRType::Float32x4: {
1256 : float a[4];
1257 0 : for (size_t i = 0; i < 4; ++i)
1258 0 : a[i] = getOperand(i)->toConstant()->numberToDouble();
1259 0 : cst = SimdConstant::CreateX4(a);
1260 0 : break;
1261 : }
1262 0 : default: MOZ_CRASH("unexpected type in MSimdValueX4::foldsTo");
1263 : }
1264 :
1265 0 : return MSimdConstant::New(alloc, cst, type());
1266 : }
1267 :
1268 0 : MOZ_ASSERT(allSame);
1269 0 : return MSimdSplat::New(alloc, getOperand(0), type());
1270 : }
1271 :
1272 : MDefinition*
1273 0 : MSimdSplat::foldsTo(TempAllocator& alloc)
1274 : {
1275 : #ifdef DEBUG
1276 0 : MIRType laneType = SimdTypeToLaneArgumentType(type());
1277 : #endif
1278 0 : MDefinition* op = getOperand(0);
1279 0 : if (!op->isConstant())
1280 0 : return this;
1281 0 : MOZ_ASSERT(op->type() == laneType);
1282 :
1283 : SimdConstant cst;
1284 0 : switch (type()) {
1285 : case MIRType::Bool8x16: {
1286 0 : int8_t v = op->toConstant()->valueToBooleanInfallible() ? -1 : 0;
1287 0 : cst = SimdConstant::SplatX16(v);
1288 0 : break;
1289 : }
1290 : case MIRType::Bool16x8: {
1291 0 : int16_t v = op->toConstant()->valueToBooleanInfallible() ? -1 : 0;
1292 0 : cst = SimdConstant::SplatX8(v);
1293 0 : break;
1294 : }
1295 : case MIRType::Bool32x4: {
1296 0 : int32_t v = op->toConstant()->valueToBooleanInfallible() ? -1 : 0;
1297 0 : cst = SimdConstant::SplatX4(v);
1298 0 : break;
1299 : }
1300 : case MIRType::Int8x16: {
1301 0 : int32_t v = op->toConstant()->toInt32();
1302 0 : cst = SimdConstant::SplatX16(v);
1303 0 : break;
1304 : }
1305 : case MIRType::Int16x8: {
1306 0 : int32_t v = op->toConstant()->toInt32();
1307 0 : cst = SimdConstant::SplatX8(v);
1308 0 : break;
1309 : }
1310 : case MIRType::Int32x4: {
1311 0 : int32_t v = op->toConstant()->toInt32();
1312 0 : cst = SimdConstant::SplatX4(v);
1313 0 : break;
1314 : }
1315 : case MIRType::Float32x4: {
1316 0 : float v = op->toConstant()->numberToDouble();
1317 0 : cst = SimdConstant::SplatX4(v);
1318 0 : break;
1319 : }
1320 0 : default: MOZ_CRASH("unexpected type in MSimdSplat::foldsTo");
1321 : }
1322 :
1323 0 : return MSimdConstant::New(alloc, cst, type());
1324 : }
1325 :
1326 : MDefinition*
1327 0 : MSimdUnbox::foldsTo(TempAllocator& alloc)
1328 : {
1329 0 : MDefinition* in = input();
1330 :
1331 0 : if (in->isSimdBox()) {
1332 0 : MSimdBox* box = in->toSimdBox();
1333 : // If the operand is a MSimdBox, then we just reuse the operand of the
1334 : // MSimdBox as long as the type corresponds to what we are supposed to
1335 : // unbox.
1336 0 : in = box->input();
1337 0 : if (box->simdType() != simdType())
1338 0 : return this;
1339 0 : MOZ_ASSERT(in->type() == type());
1340 0 : return in;
1341 : }
1342 :
1343 0 : return this;
1344 : }
1345 :
1346 : MDefinition*
1347 0 : MSimdSwizzle::foldsTo(TempAllocator& alloc)
1348 : {
1349 0 : if (lanesMatch(0, 1, 2, 3))
1350 0 : return input();
1351 0 : return this;
1352 : }
1353 :
1354 : MDefinition*
1355 0 : MSimdGeneralShuffle::foldsTo(TempAllocator& alloc)
1356 : {
1357 0 : FixedList<uint8_t> lanes;
1358 0 : if (!lanes.init(alloc, numLanes()))
1359 0 : return this;
1360 :
1361 0 : for (size_t i = 0; i < numLanes(); i++) {
1362 0 : if (!lane(i)->isConstant() || lane(i)->type() != MIRType::Int32)
1363 0 : return this;
1364 0 : int32_t temp = lane(i)->toConstant()->toInt32();
1365 0 : if (temp < 0 || unsigned(temp) >= numLanes() * numVectors())
1366 0 : return this;
1367 0 : lanes[i] = uint8_t(temp);
1368 : }
1369 :
1370 0 : if (numVectors() == 1)
1371 0 : return MSimdSwizzle::New(alloc, vector(0), lanes.data());
1372 :
1373 0 : MOZ_ASSERT(numVectors() == 2);
1374 0 : return MSimdShuffle::New(alloc, vector(0), vector(1), lanes.data());
1375 : }
1376 :
1377 : MInstruction*
1378 0 : MSimdConvert::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* obj,
1379 : MIRType toType, SimdSign sign, wasm::BytecodeOffset bytecodeOffset)
1380 : {
1381 0 : MIRType fromType = obj->type();
1382 :
1383 0 : if (SupportsUint32x4FloatConversions || sign != SimdSign::Unsigned) {
1384 0 : MInstruction* ins = New(alloc, obj, toType, sign, bytecodeOffset);
1385 0 : addTo->add(ins);
1386 0 : return ins;
1387 : }
1388 :
1389 : // This architecture can't do Uint32x4 <-> Float32x4 conversions (Hi SSE!)
1390 0 : MOZ_ASSERT(sign == SimdSign::Unsigned);
1391 0 : if (fromType == MIRType::Int32x4 && toType == MIRType::Float32x4) {
1392 : // Converting Uint32x4 -> Float32x4. This algorithm is from LLVM.
1393 : //
1394 : // Split the input number into high and low parts:
1395 : //
1396 : // uint32_t hi = x >> 16;
1397 : // uint32_t lo = x & 0xffff;
1398 : //
1399 : // Insert these parts as the low mantissa bits in a float32 number with
1400 : // the corresponding exponent:
1401 : //
1402 : // float fhi = (bits-as-float)(hi | 0x53000000); // 0x1.0p39f + hi*2^16
1403 : // float flo = (bits-as-float)(lo | 0x4b000000); // 0x1.0p23f + lo
1404 : //
1405 : // Subtract the bias from the hi part:
1406 : //
1407 : // fhi -= (0x1.0p39 + 0x1.0p23) // hi*2^16 - 0x1.0p23
1408 : //
1409 : // And finally combine:
1410 : //
1411 : // result = flo + fhi // lo + hi*2^16.
1412 :
1413 : // Compute hi = obj >> 16 (lane-wise unsigned shift).
1414 0 : MInstruction* c16 = MConstant::New(alloc, Int32Value(16));
1415 0 : addTo->add(c16);
1416 0 : MInstruction* hi = MSimdShift::AddLegalized(alloc, addTo, obj, c16, MSimdShift::ursh);
1417 :
1418 : // Compute lo = obj & 0xffff (lane-wise).
1419 : MInstruction* m16 =
1420 0 : MSimdConstant::New(alloc, SimdConstant::SplatX4(0xffff), MIRType::Int32x4);
1421 0 : addTo->add(m16);
1422 0 : MInstruction* lo = MSimdBinaryBitwise::New(alloc, obj, m16, MSimdBinaryBitwise::and_);
1423 0 : addTo->add(lo);
1424 :
1425 : // Mix in the exponents.
1426 : MInstruction* exphi =
1427 0 : MSimdConstant::New(alloc, SimdConstant::SplatX4(0x53000000), MIRType::Int32x4);
1428 0 : addTo->add(exphi);
1429 0 : MInstruction* mhi = MSimdBinaryBitwise::New(alloc, hi, exphi, MSimdBinaryBitwise::or_);
1430 0 : addTo->add(mhi);
1431 : MInstruction* explo =
1432 0 : MSimdConstant::New(alloc, SimdConstant::SplatX4(0x4b000000), MIRType::Int32x4);
1433 0 : addTo->add(explo);
1434 0 : MInstruction* mlo = MSimdBinaryBitwise::New(alloc, lo, explo, MSimdBinaryBitwise::or_);
1435 0 : addTo->add(mlo);
1436 :
1437 : // Bit-cast both to Float32x4.
1438 0 : MInstruction* fhi = MSimdReinterpretCast::New(alloc, mhi, MIRType::Float32x4);
1439 0 : addTo->add(fhi);
1440 0 : MInstruction* flo = MSimdReinterpretCast::New(alloc, mlo, MIRType::Float32x4);
1441 0 : addTo->add(flo);
1442 :
1443 : // Subtract out the bias: 0x1.0p39f + 0x1.0p23f.
1444 : // MSVC doesn't support the hexadecimal float syntax.
1445 0 : const float BiasValue = 549755813888.f + 8388608.f;
1446 : MInstruction* bias =
1447 0 : MSimdConstant::New(alloc, SimdConstant::SplatX4(BiasValue), MIRType::Float32x4);
1448 0 : addTo->add(bias);
1449 : MInstruction* fhi_debiased =
1450 0 : MSimdBinaryArith::AddLegalized(alloc, addTo, fhi, bias, MSimdBinaryArith::Op_sub);
1451 :
1452 : // Compute the final result.
1453 : return MSimdBinaryArith::AddLegalized(alloc, addTo, fhi_debiased, flo,
1454 0 : MSimdBinaryArith::Op_add);
1455 : }
1456 :
1457 0 : if (fromType == MIRType::Float32x4 && toType == MIRType::Int32x4) {
1458 : // The Float32x4 -> Uint32x4 conversion can throw if the input is out of
1459 : // range. This is handled by the LFloat32x4ToUint32x4 expansion.
1460 0 : MInstruction* ins = New(alloc, obj, toType, sign, bytecodeOffset);
1461 0 : addTo->add(ins);
1462 0 : return ins;
1463 : }
1464 :
1465 0 : MOZ_CRASH("Unhandled SIMD type conversion");
1466 : }
1467 :
1468 : MInstruction*
1469 0 : MSimdBinaryComp::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
1470 : MDefinition* right, Operation op, SimdSign sign)
1471 : {
1472 0 : MOZ_ASSERT(left->type() == right->type());
1473 0 : MIRType opType = left->type();
1474 0 : MOZ_ASSERT(IsSimdType(opType));
1475 0 : bool IsEquality = op == equal || op == notEqual;
1476 :
1477 : // Check if this is an unsupported unsigned compare that needs to be biased.
1478 : // If so, put the bias vector in `bias`.
1479 0 : if (sign == SimdSign::Unsigned && !IsEquality) {
1480 0 : MInstruction* bias = nullptr;
1481 :
1482 : // This is an order comparison of Uint32x4 vectors which are not supported on this target.
1483 : // Simply offset |left| and |right| by INT_MIN, then do a signed comparison.
1484 0 : if (!SupportsUint32x4Compares && opType == MIRType::Int32x4)
1485 0 : bias = MSimdConstant::New(alloc, SimdConstant::SplatX4(int32_t(0x80000000)), opType);
1486 0 : else if (!SupportsUint16x8Compares && opType == MIRType::Int16x8)
1487 0 : bias = MSimdConstant::New(alloc, SimdConstant::SplatX8(int16_t(0x8000)), opType);
1488 0 : if (!SupportsUint8x16Compares && opType == MIRType::Int8x16)
1489 0 : bias = MSimdConstant::New(alloc, SimdConstant::SplatX16(int8_t(0x80)), opType);
1490 :
1491 0 : if (bias) {
1492 0 : addTo->add(bias);
1493 :
1494 : // Add the bias.
1495 : MInstruction* bleft =
1496 0 : MSimdBinaryArith::AddLegalized(alloc, addTo, left, bias, MSimdBinaryArith::Op_add);
1497 : MInstruction* bright =
1498 0 : MSimdBinaryArith::AddLegalized(alloc, addTo, right, bias, MSimdBinaryArith::Op_add);
1499 :
1500 : // Do the equivalent signed comparison.
1501 : MInstruction* result =
1502 0 : MSimdBinaryComp::New(alloc, bleft, bright, op, SimdSign::Signed);
1503 0 : addTo->add(result);
1504 :
1505 0 : return result;
1506 : }
1507 : }
1508 :
1509 0 : if (sign == SimdSign::Unsigned &&
1510 0 : ((!SupportsUint32x4Compares && opType == MIRType::Int32x4) ||
1511 0 : (!SupportsUint16x8Compares && opType == MIRType::Int16x8) ||
1512 0 : (!SupportsUint8x16Compares && opType == MIRType::Int8x16))) {
1513 : // The sign doesn't matter for equality tests. Flip it to make the
1514 : // backend assertions happy.
1515 0 : MOZ_ASSERT(IsEquality);
1516 0 : sign = SimdSign::Signed;
1517 : }
1518 :
1519 : // This is a legal operation already. Just create the instruction requested.
1520 0 : MInstruction* result = MSimdBinaryComp::New(alloc, left, right, op, sign);
1521 0 : addTo->add(result);
1522 0 : return result;
1523 : }
1524 :
1525 : MInstruction*
1526 0 : MSimdBinaryArith::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
1527 : MDefinition* right, Operation op)
1528 : {
1529 0 : MOZ_ASSERT(left->type() == right->type());
1530 0 : MIRType opType = left->type();
1531 0 : MOZ_ASSERT(IsSimdType(opType));
1532 :
1533 : // SSE does not have 8x16 multiply instructions.
1534 0 : if (opType == MIRType::Int8x16 && op == Op_mul) {
1535 : // Express the multiply in terms of Int16x8 multiplies by handling the
1536 : // even and odd lanes separately.
1537 :
1538 0 : MInstruction* wideL = MSimdReinterpretCast::New(alloc, left, MIRType::Int16x8);
1539 0 : addTo->add(wideL);
1540 0 : MInstruction* wideR = MSimdReinterpretCast::New(alloc, right, MIRType::Int16x8);
1541 0 : addTo->add(wideR);
1542 :
1543 : // wideL = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx
1544 : // wideR = bbaa bbaa bbaa bbaa bbaa bbaa bbaa bbaa
1545 :
1546 : // Shift the odd lanes down to the low bits of the 16x8 vectors.
1547 0 : MInstruction* eight = MConstant::New(alloc, Int32Value(8));
1548 0 : addTo->add(eight);
1549 0 : MInstruction* evenL = wideL;
1550 0 : MInstruction* evenR = wideR;
1551 : MInstruction* oddL =
1552 0 : MSimdShift::AddLegalized(alloc, addTo, wideL, eight, MSimdShift::ursh);
1553 : MInstruction* oddR =
1554 0 : MSimdShift::AddLegalized(alloc, addTo, wideR, eight, MSimdShift::ursh);
1555 :
1556 : // evenL = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx
1557 : // evenR = bbaa bbaa bbaa bbaa bbaa bbaa bbaa bbaa
1558 : // oddL = 00yy 00yy 00yy 00yy 00yy 00yy 00yy 00yy
1559 : // oddR = 00bb 00bb 00bb 00bb 00bb 00bb 00bb 00bb
1560 :
1561 : // Now do two 16x8 multiplications. We can use the low bits of each.
1562 0 : MInstruction* even = MSimdBinaryArith::AddLegalized(alloc, addTo, evenL, evenR, Op_mul);
1563 0 : MInstruction* odd = MSimdBinaryArith::AddLegalized(alloc, addTo, oddL, oddR, Op_mul);
1564 :
1565 : // even = ~~PP ~~PP ~~PP ~~PP ~~PP ~~PP ~~PP ~~PP
1566 : // odd = ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ
1567 :
1568 : MInstruction* mask =
1569 0 : MSimdConstant::New(alloc, SimdConstant::SplatX8(int16_t(0x00ff)), MIRType::Int16x8);
1570 0 : addTo->add(mask);
1571 0 : even = MSimdBinaryBitwise::New(alloc, even, mask, MSimdBinaryBitwise::and_);
1572 0 : addTo->add(even);
1573 0 : odd = MSimdShift::AddLegalized(alloc, addTo, odd, eight, MSimdShift::lsh);
1574 :
1575 : // even = 00PP 00PP 00PP 00PP 00PP 00PP 00PP 00PP
1576 : // odd = QQ00 QQ00 QQ00 QQ00 QQ00 QQ00 QQ00 QQ00
1577 :
1578 : // Combine:
1579 0 : MInstruction* result = MSimdBinaryBitwise::New(alloc, even, odd, MSimdBinaryBitwise::or_);
1580 0 : addTo->add(result);
1581 0 : result = MSimdReinterpretCast::New(alloc, result, opType);
1582 0 : addTo->add(result);
1583 0 : return result;
1584 : }
1585 :
1586 : // This is a legal operation already. Just create the instruction requested.
1587 0 : MInstruction* result = MSimdBinaryArith::New(alloc, left, right, op);
1588 0 : addTo->add(result);
1589 0 : return result;
1590 : }
1591 :
1592 : MInstruction*
1593 0 : MSimdShift::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
1594 : MDefinition* right, Operation op)
1595 : {
1596 0 : MIRType opType = left->type();
1597 0 : MOZ_ASSERT(IsIntegerSimdType(opType));
1598 :
1599 : // SSE does not provide 8x16 shift instructions.
1600 0 : if (opType == MIRType::Int8x16) {
1601 : // Express the shift in terms of Int16x8 shifts by splitting into even
1602 : // and odd lanes, place 8-bit lanes into the high bits of Int16x8
1603 : // vectors `even` and `odd`. Shift, mask, combine.
1604 : //
1605 : // wide = Int16x8.fromInt8x16Bits(left);
1606 : // shiftBy = right & 7
1607 : // mask = Int16x8.splat(0xff00);
1608 : //
1609 0 : MInstruction* wide = MSimdReinterpretCast::New(alloc, left, MIRType::Int16x8);
1610 0 : addTo->add(wide);
1611 :
1612 : // wide = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx
1613 :
1614 0 : MInstruction* shiftMask = MConstant::New(alloc, Int32Value(7));
1615 0 : addTo->add(shiftMask);
1616 0 : MBinaryBitwiseInstruction* shiftBy = MBitAnd::New(alloc, right, shiftMask);
1617 0 : shiftBy->setInt32Specialization();
1618 0 : addTo->add(shiftBy);
1619 :
1620 : // Move the even 8x16 lanes into the high bits of the 16x8 lanes.
1621 0 : MInstruction* eight = MConstant::New(alloc, Int32Value(8));
1622 0 : addTo->add(eight);
1623 0 : MInstruction* even = MSimdShift::AddLegalized(alloc, addTo, wide, eight, lsh);
1624 :
1625 : // Leave the odd lanes in place.
1626 0 : MInstruction* odd = wide;
1627 :
1628 : // even = xx00 xx00 xx00 xx00 xx00 xx00 xx00 xx00
1629 : // odd = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx
1630 :
1631 : MInstruction* mask =
1632 0 : MSimdConstant::New(alloc, SimdConstant::SplatX8(int16_t(0xff00)), MIRType::Int16x8);
1633 0 : addTo->add(mask);
1634 :
1635 : // Left-shift: Clear the low bits in `odd` before shifting.
1636 0 : if (op == lsh) {
1637 0 : odd = MSimdBinaryBitwise::New(alloc, odd, mask, MSimdBinaryBitwise::and_);
1638 0 : addTo->add(odd);
1639 : // odd = yy00 yy00 yy00 yy00 yy00 yy00 yy00 yy00
1640 : }
1641 :
1642 : // Do the real shift twice: once for the even lanes, once for the odd
1643 : // lanes. This is a recursive call, but with a different type.
1644 0 : even = MSimdShift::AddLegalized(alloc, addTo, even, shiftBy, op);
1645 0 : odd = MSimdShift::AddLegalized(alloc, addTo, odd, shiftBy, op);
1646 :
1647 : // even = XX~~ XX~~ XX~~ XX~~ XX~~ XX~~ XX~~ XX~~
1648 : // odd = YY~~ YY~~ YY~~ YY~~ YY~~ YY~~ YY~~ YY~~
1649 :
1650 : // Right-shift: Clear the low bits in `odd` after shifting.
1651 0 : if (op != lsh) {
1652 0 : odd = MSimdBinaryBitwise::New(alloc, odd, mask, MSimdBinaryBitwise::and_);
1653 0 : addTo->add(odd);
1654 : // odd = YY00 YY00 YY00 YY00 YY00 YY00 YY00 YY00
1655 : }
1656 :
1657 : // Move the even lanes back to their original place.
1658 0 : even = MSimdShift::AddLegalized(alloc, addTo, even, eight, ursh);
1659 :
1660 : // Now, `odd` contains the odd lanes properly shifted, and `even`
1661 : // contains the even lanes properly shifted:
1662 : //
1663 : // even = 00XX 00XX 00XX 00XX 00XX 00XX 00XX 00XX
1664 : // odd = YY00 YY00 YY00 YY00 YY00 YY00 YY00 YY00
1665 : //
1666 : // Combine:
1667 0 : MInstruction* result = MSimdBinaryBitwise::New(alloc, even, odd, MSimdBinaryBitwise::or_);
1668 0 : addTo->add(result);
1669 0 : result = MSimdReinterpretCast::New(alloc, result, opType);
1670 0 : addTo->add(result);
1671 0 : return result;
1672 : }
1673 :
1674 : // This is a legal operation already. Just create the instruction requested.
1675 0 : MInstruction* result = MSimdShift::New(alloc, left, right, op);
1676 0 : addTo->add(result);
1677 0 : return result;
1678 : }
1679 :
1680 : template <typename T>
1681 : static void
1682 0 : PrintOpcodeOperation(T* mir, GenericPrinter& out)
1683 : {
1684 0 : mir->MDefinition::printOpcode(out);
1685 0 : out.printf(" (%s)", T::OperationName(mir->operation()));
1686 0 : }
1687 :
1688 : void
1689 0 : MSimdBinaryArith::printOpcode(GenericPrinter& out) const
1690 : {
1691 0 : PrintOpcodeOperation(this, out);
1692 0 : }
1693 : void
1694 0 : MSimdBinarySaturating::printOpcode(GenericPrinter& out) const
1695 : {
1696 0 : PrintOpcodeOperation(this, out);
1697 0 : }
1698 : void
1699 0 : MSimdBinaryBitwise::printOpcode(GenericPrinter& out) const
1700 : {
1701 0 : PrintOpcodeOperation(this, out);
1702 0 : }
1703 : void
1704 0 : MSimdUnaryArith::printOpcode(GenericPrinter& out) const
1705 : {
1706 0 : PrintOpcodeOperation(this, out);
1707 0 : }
1708 : void
1709 0 : MSimdBinaryComp::printOpcode(GenericPrinter& out) const
1710 : {
1711 0 : PrintOpcodeOperation(this, out);
1712 0 : }
1713 : void
1714 0 : MSimdShift::printOpcode(GenericPrinter& out) const
1715 : {
1716 0 : PrintOpcodeOperation(this, out);
1717 0 : }
1718 :
1719 : void
1720 0 : MSimdInsertElement::printOpcode(GenericPrinter& out) const
1721 : {
1722 0 : MDefinition::printOpcode(out);
1723 0 : out.printf(" (lane %u)", lane());
1724 0 : }
1725 :
1726 : void
1727 0 : MSimdBox::printOpcode(GenericPrinter& out) const
1728 : {
1729 0 : MDefinition::printOpcode(out);
1730 0 : out.printf(" (%s%s)", SimdTypeToString(simdType()),
1731 0 : initialHeap() == gc::TenuredHeap ? ", tenured" : "");
1732 0 : }
1733 :
1734 : void
1735 0 : MSimdUnbox::printOpcode(GenericPrinter& out) const
1736 : {
1737 0 : MDefinition::printOpcode(out);
1738 0 : out.printf(" (%s)", SimdTypeToString(simdType()));
1739 0 : }
1740 :
1741 : void
1742 0 : MControlInstruction::printOpcode(GenericPrinter& out) const
1743 : {
1744 0 : MDefinition::printOpcode(out);
1745 0 : for (size_t j = 0; j < numSuccessors(); j++) {
1746 0 : if (getSuccessor(j))
1747 0 : out.printf(" block%u", getSuccessor(j)->id());
1748 : else
1749 0 : out.printf(" (null-to-be-patched)");
1750 : }
1751 0 : }
1752 :
1753 : void
1754 0 : MCompare::printOpcode(GenericPrinter& out) const
1755 : {
1756 0 : MDefinition::printOpcode(out);
1757 0 : out.printf(" %s", CodeName[jsop()]);
1758 0 : }
1759 :
1760 : void
1761 0 : MConstantElements::printOpcode(GenericPrinter& out) const
1762 : {
1763 0 : PrintOpcodeName(out, op());
1764 0 : out.printf(" 0x%" PRIxPTR, value().asValue());
1765 0 : }
1766 :
1767 : void
1768 0 : MLoadUnboxedScalar::printOpcode(GenericPrinter& out) const
1769 : {
1770 0 : MDefinition::printOpcode(out);
1771 0 : out.printf(" %s", ScalarTypeDescr::typeName(storageType()));
1772 0 : }
1773 :
1774 : void
1775 0 : MAssertRange::printOpcode(GenericPrinter& out) const
1776 : {
1777 0 : MDefinition::printOpcode(out);
1778 0 : out.put(" ");
1779 0 : assertedRange()->dump(out);
1780 0 : }
1781 :
1782 0 : void MNearbyInt::printOpcode(GenericPrinter& out) const
1783 : {
1784 0 : MDefinition::printOpcode(out);
1785 0 : const char* roundingModeStr = nullptr;
1786 0 : switch (roundingMode_) {
1787 0 : case RoundingMode::Up: roundingModeStr = "(up)"; break;
1788 0 : case RoundingMode::Down: roundingModeStr = "(down)"; break;
1789 0 : case RoundingMode::NearestTiesToEven: roundingModeStr = "(nearest ties even)"; break;
1790 0 : case RoundingMode::TowardsZero: roundingModeStr = "(towards zero)"; break;
1791 : }
1792 0 : out.printf(" %s", roundingModeStr);
1793 0 : }
1794 :
1795 : const char*
1796 0 : MMathFunction::FunctionName(Function function)
1797 : {
1798 0 : switch (function) {
1799 0 : case Log: return "Log";
1800 0 : case Sin: return "Sin";
1801 0 : case Cos: return "Cos";
1802 0 : case Exp: return "Exp";
1803 0 : case Tan: return "Tan";
1804 0 : case ACos: return "ACos";
1805 0 : case ASin: return "ASin";
1806 0 : case ATan: return "ATan";
1807 0 : case Log10: return "Log10";
1808 0 : case Log2: return "Log2";
1809 0 : case Log1P: return "Log1P";
1810 0 : case ExpM1: return "ExpM1";
1811 0 : case CosH: return "CosH";
1812 0 : case SinH: return "SinH";
1813 0 : case TanH: return "TanH";
1814 0 : case ACosH: return "ACosH";
1815 0 : case ASinH: return "ASinH";
1816 0 : case ATanH: return "ATanH";
1817 0 : case Sign: return "Sign";
1818 0 : case Trunc: return "Trunc";
1819 0 : case Cbrt: return "Cbrt";
1820 0 : case Floor: return "Floor";
1821 0 : case Ceil: return "Ceil";
1822 0 : case Round: return "Round";
1823 : default:
1824 0 : MOZ_CRASH("Unknown math function");
1825 : }
1826 : }
1827 :
1828 : void
1829 0 : MMathFunction::printOpcode(GenericPrinter& out) const
1830 : {
1831 0 : MDefinition::printOpcode(out);
1832 0 : out.printf(" %s", FunctionName(function()));
1833 0 : }
1834 :
1835 : MDefinition*
1836 0 : MMathFunction::foldsTo(TempAllocator& alloc)
1837 : {
1838 0 : MDefinition* input = getOperand(0);
1839 0 : if (!input->isConstant() || !input->toConstant()->isTypeRepresentableAsDouble())
1840 0 : return this;
1841 :
1842 0 : double in = input->toConstant()->numberToDouble();
1843 : double out;
1844 0 : switch (function_) {
1845 : case Log:
1846 0 : out = js::math_log_uncached(in);
1847 0 : break;
1848 : case Sin:
1849 0 : out = js::math_sin_uncached(in);
1850 0 : break;
1851 : case Cos:
1852 0 : out = js::math_cos_uncached(in);
1853 0 : break;
1854 : case Exp:
1855 0 : out = js::math_exp_uncached(in);
1856 0 : break;
1857 : case Tan:
1858 0 : out = js::math_tan_uncached(in);
1859 0 : break;
1860 : case ACos:
1861 0 : out = js::math_acos_uncached(in);
1862 0 : break;
1863 : case ASin:
1864 0 : out = js::math_asin_uncached(in);
1865 0 : break;
1866 : case ATan:
1867 0 : out = js::math_atan_uncached(in);
1868 0 : break;
1869 : case Log10:
1870 0 : out = js::math_log10_uncached(in);
1871 0 : break;
1872 : case Log2:
1873 0 : out = js::math_log2_uncached(in);
1874 0 : break;
1875 : case Log1P:
1876 0 : out = js::math_log1p_uncached(in);
1877 0 : break;
1878 : case ExpM1:
1879 0 : out = js::math_expm1_uncached(in);
1880 0 : break;
1881 : case CosH:
1882 0 : out = js::math_cosh_uncached(in);
1883 0 : break;
1884 : case SinH:
1885 0 : out = js::math_sinh_uncached(in);
1886 0 : break;
1887 : case TanH:
1888 0 : out = js::math_tanh_uncached(in);
1889 0 : break;
1890 : case ACosH:
1891 0 : out = js::math_acosh_uncached(in);
1892 0 : break;
1893 : case ASinH:
1894 0 : out = js::math_asinh_uncached(in);
1895 0 : break;
1896 : case ATanH:
1897 0 : out = js::math_atanh_uncached(in);
1898 0 : break;
1899 : case Sign:
1900 0 : out = js::math_sign_uncached(in);
1901 0 : break;
1902 : case Trunc:
1903 0 : out = js::math_trunc_uncached(in);
1904 0 : break;
1905 : case Cbrt:
1906 0 : out = js::math_cbrt_uncached(in);
1907 0 : break;
1908 : case Floor:
1909 0 : out = js::math_floor_impl(in);
1910 0 : break;
1911 : case Ceil:
1912 0 : out = js::math_ceil_impl(in);
1913 0 : break;
1914 : case Round:
1915 0 : out = js::math_round_impl(in);
1916 0 : break;
1917 : default:
1918 0 : return this;
1919 : }
1920 :
1921 0 : if (input->type() == MIRType::Float32)
1922 0 : return MConstant::NewFloat32(alloc, out);
1923 0 : return MConstant::New(alloc, DoubleValue(out));
1924 : }
1925 :
1926 : MDefinition*
1927 0 : MAtomicIsLockFree::foldsTo(TempAllocator& alloc)
1928 : {
1929 0 : MDefinition* input = getOperand(0);
1930 0 : if (!input->isConstant() || input->type() != MIRType::Int32)
1931 0 : return this;
1932 :
1933 0 : int32_t i = input->toConstant()->toInt32();
1934 0 : return MConstant::New(alloc, BooleanValue(AtomicOperations::isLockfree(i)));
1935 : }
1936 :
1937 : // Define |THIS_SLOT| as part of this translation unit, as it is used to
1938 : // specialized the parameterized |New| function calls introduced by
1939 : // TRIVIAL_NEW_WRAPPERS.
1940 : const int32_t MParameter::THIS_SLOT;
1941 :
1942 : void
1943 0 : MParameter::printOpcode(GenericPrinter& out) const
1944 : {
1945 0 : PrintOpcodeName(out, op());
1946 0 : if (index() == THIS_SLOT)
1947 0 : out.printf(" THIS_SLOT");
1948 : else
1949 0 : out.printf(" %d", index());
1950 0 : }
1951 :
1952 : HashNumber
1953 6 : MParameter::valueHash() const
1954 : {
1955 6 : HashNumber hash = MDefinition::valueHash();
1956 6 : hash = addU32ToHash(hash, index_);
1957 6 : return hash;
1958 : }
1959 :
1960 : bool
1961 0 : MParameter::congruentTo(const MDefinition* ins) const
1962 : {
1963 0 : if (!ins->isParameter())
1964 0 : return false;
1965 :
1966 0 : return ins->toParameter()->index() == index_;
1967 : }
1968 :
1969 263 : WrappedFunction::WrappedFunction(JSFunction* fun)
1970 : : fun_(fun),
1971 263 : nargs_(fun->nargs()),
1972 263 : isNative_(fun->isNative()),
1973 263 : isConstructor_(fun->isConstructor()),
1974 263 : isClassConstructor_(fun->isClassConstructor()),
1975 1315 : isSelfHostedBuiltin_(fun->isSelfHostedBuiltin())
1976 263 : {}
1977 :
1978 : MCall*
1979 1145 : MCall::New(TempAllocator& alloc, JSFunction* target, size_t maxArgc, size_t numActualArgs,
1980 : bool construct, bool ignoresReturnValue, bool isDOMCall)
1981 : {
1982 1145 : WrappedFunction* wrappedTarget = target ? new(alloc) WrappedFunction(target) : nullptr;
1983 1145 : MOZ_ASSERT(maxArgc >= numActualArgs);
1984 : MCall* ins;
1985 1145 : if (isDOMCall) {
1986 0 : MOZ_ASSERT(!construct);
1987 0 : ins = new(alloc) MCallDOMNative(wrappedTarget, numActualArgs);
1988 : } else {
1989 1145 : ins = new(alloc) MCall(wrappedTarget, numActualArgs, construct, ignoresReturnValue);
1990 : }
1991 1145 : if (!ins->init(alloc, maxArgc + NumNonArgumentOperands))
1992 0 : return nullptr;
1993 1145 : return ins;
1994 : }
1995 :
1996 : AliasSet
1997 0 : MCallDOMNative::getAliasSet() const
1998 : {
1999 0 : const JSJitInfo* jitInfo = getJitInfo();
2000 :
2001 : // If we don't know anything about the types of our arguments, we have to
2002 : // assume that type-coercions can have side-effects, so we need to alias
2003 : // everything.
2004 0 : if (jitInfo->aliasSet() == JSJitInfo::AliasEverything || !jitInfo->isTypedMethodJitInfo())
2005 0 : return AliasSet::Store(AliasSet::Any);
2006 :
2007 0 : uint32_t argIndex = 0;
2008 : const JSTypedMethodJitInfo* methodInfo =
2009 0 : reinterpret_cast<const JSTypedMethodJitInfo*>(jitInfo);
2010 0 : for (const JSJitInfo::ArgType* argType = methodInfo->argTypes;
2011 0 : *argType != JSJitInfo::ArgTypeListEnd;
2012 : ++argType, ++argIndex)
2013 : {
2014 0 : if (argIndex >= numActualArgs()) {
2015 : // Passing through undefined can't have side-effects
2016 0 : continue;
2017 : }
2018 : // getArg(0) is "this", so skip it
2019 0 : MDefinition* arg = getArg(argIndex+1);
2020 0 : MIRType actualType = arg->type();
2021 : // The only way to reliably avoid side-effects given the information we
2022 : // have here is if we're passing in a known primitive value to an
2023 : // argument that expects a primitive value.
2024 : //
2025 : // XXXbz maybe we need to communicate better information. For example,
2026 : // a sequence argument will sort of unavoidably have side effects, while
2027 : // a typed array argument won't have any, but both are claimed to be
2028 : // JSJitInfo::Object. But if we do that, we need to watch out for our
2029 : // movability/DCE-ability bits: if we have an arg type that can reliably
2030 : // throw an exception on conversion, that might not affect our alias set
2031 : // per se, but it should prevent us being moved or DCE-ed, unless we
2032 : // know the incoming things match that arg type and won't throw.
2033 : //
2034 0 : if ((actualType == MIRType::Value || actualType == MIRType::Object) ||
2035 0 : (*argType & JSJitInfo::Object))
2036 : {
2037 0 : return AliasSet::Store(AliasSet::Any);
2038 : }
2039 : }
2040 :
2041 : // We checked all the args, and they check out. So we only alias DOM
2042 : // mutations or alias nothing, depending on the alias set in the jitinfo.
2043 0 : if (jitInfo->aliasSet() == JSJitInfo::AliasNone)
2044 0 : return AliasSet::None();
2045 :
2046 0 : MOZ_ASSERT(jitInfo->aliasSet() == JSJitInfo::AliasDOMSets);
2047 0 : return AliasSet::Load(AliasSet::DOMProperty);
2048 : }
2049 :
2050 : void
2051 0 : MCallDOMNative::computeMovable()
2052 : {
2053 : // We are movable if the jitinfo says we can be and if we're also not
2054 : // effectful. The jitinfo can't check for the latter, since it depends on
2055 : // the types of our arguments.
2056 0 : const JSJitInfo* jitInfo = getJitInfo();
2057 :
2058 0 : MOZ_ASSERT_IF(jitInfo->isMovable,
2059 : jitInfo->aliasSet() != JSJitInfo::AliasEverything);
2060 :
2061 0 : if (jitInfo->isMovable && !isEffectful())
2062 0 : setMovable();
2063 0 : }
2064 :
2065 : bool
2066 0 : MCallDOMNative::congruentTo(const MDefinition* ins) const
2067 : {
2068 0 : if (!isMovable())
2069 0 : return false;
2070 :
2071 0 : if (!ins->isCall())
2072 0 : return false;
2073 :
2074 0 : const MCall* call = ins->toCall();
2075 :
2076 0 : if (!call->isCallDOMNative())
2077 0 : return false;
2078 :
2079 0 : if (getSingleTarget() != call->getSingleTarget())
2080 0 : return false;
2081 :
2082 0 : if (isConstructing() != call->isConstructing())
2083 0 : return false;
2084 :
2085 0 : if (numActualArgs() != call->numActualArgs())
2086 0 : return false;
2087 :
2088 0 : if (needsArgCheck() != call->needsArgCheck())
2089 0 : return false;
2090 :
2091 0 : if (!congruentIfOperandsEqual(call))
2092 0 : return false;
2093 :
2094 : // The other call had better be movable at this point!
2095 0 : MOZ_ASSERT(call->isMovable());
2096 :
2097 0 : return true;
2098 : }
2099 :
2100 : const JSJitInfo*
2101 0 : MCallDOMNative::getJitInfo() const
2102 : {
2103 0 : MOZ_ASSERT(getSingleTarget() && getSingleTarget()->isNative());
2104 :
2105 0 : const JSJitInfo* jitInfo = getSingleTarget()->jitInfo();
2106 0 : MOZ_ASSERT(jitInfo);
2107 :
2108 0 : return jitInfo;
2109 : }
2110 :
2111 : MDefinition*
2112 20 : MStringLength::foldsTo(TempAllocator& alloc)
2113 : {
2114 20 : if (type() == MIRType::Int32 && string()->isConstant()) {
2115 0 : JSAtom* atom = &string()->toConstant()->toString()->asAtom();
2116 0 : return MConstant::New(alloc, Int32Value(atom->length()));
2117 : }
2118 :
2119 20 : return this;
2120 : }
2121 :
2122 : MDefinition*
2123 24 : MConcat::foldsTo(TempAllocator& alloc)
2124 : {
2125 24 : if (lhs()->isConstant() && lhs()->toConstant()->toString()->empty())
2126 0 : return rhs();
2127 :
2128 24 : if (rhs()->isConstant() && rhs()->toConstant()->toString()->empty())
2129 0 : return lhs();
2130 :
2131 24 : return this;
2132 : }
2133 :
2134 : static bool
2135 0 : EnsureFloatInputOrConvert(MUnaryInstruction* owner, TempAllocator& alloc)
2136 : {
2137 0 : MDefinition* input = owner->input();
2138 0 : if (!input->canProduceFloat32()) {
2139 0 : if (input->type() == MIRType::Float32)
2140 0 : ConvertDefinitionToDouble<0>(alloc, input, owner);
2141 0 : return false;
2142 : }
2143 0 : return true;
2144 : }
2145 :
2146 : void
2147 0 : MFloor::trySpecializeFloat32(TempAllocator& alloc)
2148 : {
2149 0 : MOZ_ASSERT(type() == MIRType::Int32);
2150 0 : if (EnsureFloatInputOrConvert(this, alloc))
2151 0 : specialization_ = MIRType::Float32;
2152 0 : }
2153 :
2154 : void
2155 0 : MCeil::trySpecializeFloat32(TempAllocator& alloc)
2156 : {
2157 0 : MOZ_ASSERT(type() == MIRType::Int32);
2158 0 : if (EnsureFloatInputOrConvert(this, alloc))
2159 0 : specialization_ = MIRType::Float32;
2160 0 : }
2161 :
2162 : void
2163 0 : MRound::trySpecializeFloat32(TempAllocator& alloc)
2164 : {
2165 0 : MOZ_ASSERT(type() == MIRType::Int32);
2166 0 : if (EnsureFloatInputOrConvert(this, alloc))
2167 0 : specialization_ = MIRType::Float32;
2168 0 : }
2169 :
2170 : void
2171 0 : MNearbyInt::trySpecializeFloat32(TempAllocator& alloc)
2172 : {
2173 0 : if (EnsureFloatInputOrConvert(this, alloc)) {
2174 0 : specialization_ = MIRType::Float32;
2175 0 : setResultType(MIRType::Float32);
2176 : }
2177 0 : }
2178 :
2179 : MTableSwitch*
2180 44 : MTableSwitch::New(TempAllocator& alloc, MDefinition* ins, int32_t low, int32_t high)
2181 : {
2182 44 : return new(alloc) MTableSwitch(alloc, ins, low, high);
2183 : }
2184 :
2185 : MGoto*
2186 2531 : MGoto::New(TempAllocator& alloc, MBasicBlock* target)
2187 : {
2188 2531 : MOZ_ASSERT(target);
2189 2531 : return new(alloc) MGoto(target);
2190 : }
2191 :
2192 : MGoto*
2193 0 : MGoto::New(TempAllocator::Fallible alloc, MBasicBlock* target)
2194 : {
2195 0 : MOZ_ASSERT(target);
2196 0 : return new(alloc) MGoto(target);
2197 : }
2198 :
2199 : MGoto*
2200 0 : MGoto::New(TempAllocator& alloc)
2201 : {
2202 0 : return new(alloc) MGoto(nullptr);
2203 : }
2204 :
2205 : void
2206 0 : MUnbox::printOpcode(GenericPrinter& out) const
2207 : {
2208 0 : PrintOpcodeName(out, op());
2209 0 : out.printf(" ");
2210 0 : getOperand(0)->printName(out);
2211 0 : out.printf(" ");
2212 :
2213 0 : switch (type()) {
2214 0 : case MIRType::Int32: out.printf("to Int32"); break;
2215 0 : case MIRType::Double: out.printf("to Double"); break;
2216 0 : case MIRType::Boolean: out.printf("to Boolean"); break;
2217 0 : case MIRType::String: out.printf("to String"); break;
2218 0 : case MIRType::Symbol: out.printf("to Symbol"); break;
2219 0 : case MIRType::Object: out.printf("to Object"); break;
2220 0 : default: break;
2221 : }
2222 :
2223 0 : switch (mode()) {
2224 0 : case Fallible: out.printf(" (fallible)"); break;
2225 0 : case Infallible: out.printf(" (infallible)"); break;
2226 0 : case TypeBarrier: out.printf(" (typebarrier)"); break;
2227 0 : default: break;
2228 : }
2229 0 : }
2230 :
2231 : MDefinition*
2232 207 : MUnbox::foldsTo(TempAllocator &alloc)
2233 : {
2234 207 : if (!input()->isLoadFixedSlot())
2235 168 : return this;
2236 39 : MLoadFixedSlot* load = input()->toLoadFixedSlot();
2237 39 : if (load->type() != MIRType::Value)
2238 0 : return this;
2239 39 : if (type() != MIRType::Boolean && !IsNumberType(type()))
2240 38 : return this;
2241 : // Only optimize if the load comes immediately before the unbox, so it's
2242 : // safe to copy the load's dependency field.
2243 1 : MInstructionIterator iter(load->block()->begin(load));
2244 1 : ++iter;
2245 1 : if (*iter != this)
2246 0 : return this;
2247 :
2248 2 : MLoadFixedSlotAndUnbox* ins = MLoadFixedSlotAndUnbox::New(alloc, load->object(), load->slot(),
2249 3 : mode(), type(), bailoutKind());
2250 : // As GVN runs after the Alias Analysis, we have to set the dependency by hand
2251 1 : ins->setDependency(load->dependency());
2252 1 : return ins;
2253 : }
2254 :
2255 : void
2256 0 : MTypeBarrier::printOpcode(GenericPrinter& out) const
2257 : {
2258 0 : PrintOpcodeName(out, op());
2259 0 : out.printf(" ");
2260 0 : getOperand(0)->printName(out);
2261 0 : }
2262 :
2263 : bool
2264 246 : MTypeBarrier::congruentTo(const MDefinition* def) const
2265 : {
2266 246 : if (!def->isTypeBarrier())
2267 0 : return false;
2268 246 : const MTypeBarrier* other = def->toTypeBarrier();
2269 246 : if (barrierKind() != other->barrierKind() || isGuard() != other->isGuard())
2270 8 : return false;
2271 238 : if (!resultTypeSet()->equals(other->resultTypeSet()))
2272 0 : return false;
2273 238 : return congruentIfOperandsEqual(other);
2274 : }
2275 :
2276 : MDefinition*
2277 234 : MTypeBarrier::foldsTo(TempAllocator& alloc)
2278 : {
2279 234 : MIRType type = resultTypeSet()->getKnownMIRType();
2280 234 : if (type == MIRType::Value || type == MIRType::Object)
2281 117 : return this;
2282 :
2283 117 : if (!input()->isConstant())
2284 113 : return this;
2285 :
2286 4 : if (input()->type() != type)
2287 0 : return this;
2288 :
2289 4 : return input();
2290 : }
2291 :
2292 : #ifdef DEBUG
2293 : void
2294 46 : MPhi::assertLoopPhi() const
2295 : {
2296 : // getLoopPredecessorOperand and getLoopBackedgeOperand rely on these
2297 : // predecessors being at indices 0 and 1.
2298 46 : MBasicBlock* pred = block()->getPredecessor(0);
2299 46 : MBasicBlock* back = block()->getPredecessor(1);
2300 46 : MOZ_ASSERT(pred == block()->loopPredecessor());
2301 46 : MOZ_ASSERT(pred->successorWithPhis() == block());
2302 46 : MOZ_ASSERT(pred->positionInPhiSuccessor() == 0);
2303 46 : MOZ_ASSERT(back == block()->backedge());
2304 46 : MOZ_ASSERT(back->successorWithPhis() == block());
2305 46 : MOZ_ASSERT(back->positionInPhiSuccessor() == 1);
2306 46 : }
2307 : #endif
2308 :
2309 : void
2310 214 : MPhi::removeOperand(size_t index)
2311 : {
2312 214 : MOZ_ASSERT(index < numOperands());
2313 214 : MOZ_ASSERT(getUseFor(index)->index() == index);
2314 214 : MOZ_ASSERT(getUseFor(index)->consumer() == this);
2315 :
2316 : // If we have phi(..., a, b, c, d, ..., z) and we plan
2317 : // on removing a, then first shift downward so that we have
2318 : // phi(..., b, c, d, ..., z, z):
2319 214 : MUse* p = inputs_.begin() + index;
2320 214 : MUse* e = inputs_.end();
2321 214 : p->producer()->removeUse(p);
2322 1160 : for (; p < e - 1; ++p) {
2323 473 : MDefinition* producer = (p + 1)->producer();
2324 473 : p->setProducerUnchecked(producer);
2325 473 : producer->replaceUse(p + 1, p);
2326 : }
2327 :
2328 : // truncate the inputs_ list:
2329 214 : inputs_.popBack();
2330 214 : }
2331 :
2332 : void
2333 1991 : MPhi::removeAllOperands()
2334 : {
2335 5922 : for (MUse& p : inputs_)
2336 3931 : p.producer()->removeUse(&p);
2337 1991 : inputs_.clear();
2338 1991 : }
2339 :
2340 : MDefinition*
2341 338 : MPhi::foldsTernary(TempAllocator& alloc)
2342 : {
2343 : /* Look if this MPhi is a ternary construct.
2344 : * This is a very loose term as it actually only checks for
2345 : *
2346 : * MTest X
2347 : * / \
2348 : * ... ...
2349 : * \ /
2350 : * MPhi X Y
2351 : *
2352 : * Which we will simply call:
2353 : * x ? x : y or x ? y : x
2354 : */
2355 :
2356 338 : if (numOperands() != 2)
2357 32 : return nullptr;
2358 :
2359 306 : MOZ_ASSERT(block()->numPredecessors() == 2);
2360 :
2361 306 : MBasicBlock* pred = block()->immediateDominator();
2362 306 : if (!pred || !pred->lastIns()->isTest())
2363 150 : return nullptr;
2364 :
2365 156 : MTest* test = pred->lastIns()->toTest();
2366 :
2367 : // True branch may only dominate one edge of MPhi.
2368 312 : if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
2369 156 : test->ifTrue()->dominates(block()->getPredecessor(1)))
2370 : {
2371 0 : return nullptr;
2372 : }
2373 :
2374 : // False branch may only dominate one edge of MPhi.
2375 312 : if (test->ifFalse()->dominates(block()->getPredecessor(0)) ==
2376 156 : test->ifFalse()->dominates(block()->getPredecessor(1)))
2377 : {
2378 0 : return nullptr;
2379 : }
2380 :
2381 : // True and false branch must dominate different edges of MPhi.
2382 312 : if (test->ifTrue()->dominates(block()->getPredecessor(0)) ==
2383 156 : test->ifFalse()->dominates(block()->getPredecessor(0)))
2384 : {
2385 0 : return nullptr;
2386 : }
2387 :
2388 : // We found a ternary construct.
2389 156 : bool firstIsTrueBranch = test->ifTrue()->dominates(block()->getPredecessor(0));
2390 156 : MDefinition* trueDef = firstIsTrueBranch ? getOperand(0) : getOperand(1);
2391 156 : MDefinition* falseDef = firstIsTrueBranch ? getOperand(1) : getOperand(0);
2392 :
2393 : // Accept either
2394 : // testArg ? testArg : constant or
2395 : // testArg ? constant : testArg
2396 156 : if (!trueDef->isConstant() && !falseDef->isConstant())
2397 112 : return nullptr;
2398 :
2399 44 : MConstant* c = trueDef->isConstant() ? trueDef->toConstant() : falseDef->toConstant();
2400 44 : MDefinition* testArg = (trueDef == c) ? falseDef : trueDef;
2401 44 : if (testArg != test->input())
2402 36 : return nullptr;
2403 :
2404 : // This check should be a tautology, except that the constant might be the
2405 : // result of the removal of a branch. In such case the domination scope of
2406 : // the block which is holding the constant might be incomplete. This
2407 : // condition is used to prevent doing this optimization based on incomplete
2408 : // information.
2409 : //
2410 : // As GVN removed a branch, it will update the dominations rules before
2411 : // trying to fold this MPhi again. Thus, this condition does not inhibit
2412 : // this optimization.
2413 8 : MBasicBlock* truePred = block()->getPredecessor(firstIsTrueBranch ? 0 : 1);
2414 8 : MBasicBlock* falsePred = block()->getPredecessor(firstIsTrueBranch ? 1 : 0);
2415 16 : if (!trueDef->block()->dominates(truePred) ||
2416 8 : !falseDef->block()->dominates(falsePred))
2417 : {
2418 0 : return nullptr;
2419 : }
2420 :
2421 : // If testArg is an int32 type we can:
2422 : // - fold testArg ? testArg : 0 to testArg
2423 : // - fold testArg ? 0 : testArg to 0
2424 8 : if (testArg->type() == MIRType::Int32 && c->numberToDouble() == 0) {
2425 0 : testArg->setGuardRangeBailoutsUnchecked();
2426 :
2427 : // When folding to the constant we need to hoist it.
2428 0 : if (trueDef == c && !c->block()->dominates(block()))
2429 0 : c->block()->moveBefore(pred->lastIns(), c);
2430 0 : return trueDef;
2431 : }
2432 :
2433 : // If testArg is an double type we can:
2434 : // - fold testArg ? testArg : 0.0 to MNaNToZero(testArg)
2435 8 : if (testArg->type() == MIRType::Double && mozilla::IsPositiveZero(c->numberToDouble()) &&
2436 : c != trueDef)
2437 : {
2438 0 : MNaNToZero* replace = MNaNToZero::New(alloc, testArg);
2439 0 : test->block()->insertBefore(test, replace);
2440 0 : return replace;
2441 : }
2442 :
2443 : // If testArg is a string type we can:
2444 : // - fold testArg ? testArg : "" to testArg
2445 : // - fold testArg ? "" : testArg to ""
2446 8 : if (testArg->type() == MIRType::String &&
2447 0 : c->toString() == GetJitContext()->runtime->emptyString())
2448 : {
2449 : // When folding to the constant we need to hoist it.
2450 0 : if (trueDef == c && !c->block()->dominates(block()))
2451 0 : c->block()->moveBefore(pred->lastIns(), c);
2452 0 : return trueDef;
2453 : }
2454 :
2455 8 : return nullptr;
2456 : }
2457 :
2458 : MDefinition*
2459 3979 : MPhi::operandIfRedundant()
2460 : {
2461 3979 : if (inputs_.length() == 0)
2462 0 : return nullptr;
2463 :
2464 : // If this phi is redundant (e.g., phi(a,a) or b=phi(a,this)),
2465 : // returns the operand that it will always be equal to (a, in
2466 : // those two cases).
2467 3979 : MDefinition* first = getOperand(0);
2468 5648 : for (size_t i = 1, e = numOperands(); i < e; i++) {
2469 4081 : MDefinition* op = getOperand(i);
2470 4081 : if (op != first && op != this)
2471 2412 : return nullptr;
2472 : }
2473 1567 : return first;
2474 : }
2475 :
2476 : MDefinition*
2477 338 : MPhi::foldsFilterTypeSet()
2478 : {
2479 : // Fold phi with as operands a combination of 'subject' and
2480 : // MFilterTypeSet(subject) to 'subject'.
2481 :
2482 338 : if (inputs_.length() == 0)
2483 0 : return nullptr;
2484 :
2485 338 : MDefinition* subject = getOperand(0);
2486 338 : if (subject->isFilterTypeSet())
2487 4 : subject = subject->toFilterTypeSet()->input();
2488 :
2489 : // Not same type, don't fold.
2490 338 : if (subject->type() != type())
2491 0 : return nullptr;
2492 :
2493 : // Phi is better typed (has typeset). Don't fold.
2494 338 : if (resultTypeSet() && !subject->resultTypeSet())
2495 18 : return nullptr;
2496 :
2497 : // Phi is better typed (according to typeset). Don't fold.
2498 320 : if (subject->resultTypeSet() && resultTypeSet()) {
2499 263 : if (!subject->resultTypeSet()->isSubset(resultTypeSet()))
2500 4 : return nullptr;
2501 : }
2502 :
2503 364 : for (size_t i = 1, e = numOperands(); i < e; i++) {
2504 364 : MDefinition* op = getOperand(i);
2505 364 : if (op == subject)
2506 48 : continue;
2507 316 : if (op->isFilterTypeSet() && op->toFilterTypeSet()->input() == subject)
2508 0 : continue;
2509 :
2510 316 : return nullptr;
2511 : }
2512 :
2513 0 : return subject;
2514 : }
2515 :
2516 : MDefinition*
2517 365 : MPhi::foldsTo(TempAllocator& alloc)
2518 : {
2519 365 : if (MDefinition* def = operandIfRedundant())
2520 27 : return def;
2521 :
2522 338 : if (MDefinition* def = foldsTernary(alloc))
2523 0 : return def;
2524 :
2525 338 : if (MDefinition* def = foldsFilterTypeSet())
2526 0 : return def;
2527 :
2528 338 : return this;
2529 : }
2530 :
2531 : bool
2532 400 : MPhi::congruentTo(const MDefinition* ins) const
2533 : {
2534 400 : if (!ins->isPhi())
2535 0 : return false;
2536 :
2537 : // Phis in different blocks may have different control conditions.
2538 : // For example, these phis:
2539 : //
2540 : // if (p)
2541 : // goto a
2542 : // a:
2543 : // t = phi(x, y)
2544 : //
2545 : // if (q)
2546 : // goto b
2547 : // b:
2548 : // s = phi(x, y)
2549 : //
2550 : // have identical operands, but they are not equvalent because t is
2551 : // effectively p?x:y and s is effectively q?x:y.
2552 : //
2553 : // For now, consider phis in different blocks incongruent.
2554 400 : if (ins->block() != block())
2555 0 : return false;
2556 :
2557 400 : return congruentIfOperandsEqual(ins);
2558 : }
2559 :
2560 : static inline TemporaryTypeSet*
2561 432 : MakeMIRTypeSet(TempAllocator& alloc, MIRType type)
2562 : {
2563 432 : MOZ_ASSERT(type != MIRType::Value);
2564 : TypeSet::Type ntype = type == MIRType::Object
2565 : ? TypeSet::AnyObjectType()
2566 432 : : TypeSet::PrimitiveType(ValueTypeFromMIRType(type));
2567 432 : return alloc.lifoAlloc()->new_<TemporaryTypeSet>(alloc.lifoAlloc(), ntype);
2568 : }
2569 :
2570 : bool
2571 3108 : jit::MergeTypes(TempAllocator& alloc, MIRType* ptype, TemporaryTypeSet** ptypeSet,
2572 : MIRType newType, TemporaryTypeSet* newTypeSet)
2573 : {
2574 3108 : if (newTypeSet && newTypeSet->empty())
2575 973 : return true;
2576 4270 : LifoAlloc::AutoFallibleScope fallibleAllocator(alloc.lifoAlloc());
2577 2135 : if (newType != *ptype) {
2578 343 : if (IsTypeRepresentableAsDouble(newType) && IsTypeRepresentableAsDouble(*ptype)) {
2579 24 : *ptype = MIRType::Double;
2580 319 : } else if (*ptype != MIRType::Value) {
2581 127 : if (!*ptypeSet) {
2582 63 : *ptypeSet = MakeMIRTypeSet(alloc, *ptype);
2583 63 : if (!*ptypeSet)
2584 0 : return false;
2585 : }
2586 127 : *ptype = MIRType::Value;
2587 192 : } else if (*ptypeSet && (*ptypeSet)->empty()) {
2588 114 : *ptype = newType;
2589 : }
2590 : }
2591 2135 : if (*ptypeSet) {
2592 1046 : if (!newTypeSet && newType != MIRType::Value) {
2593 369 : newTypeSet = MakeMIRTypeSet(alloc, newType);
2594 369 : if (!newTypeSet)
2595 0 : return false;
2596 : }
2597 1046 : if (newTypeSet) {
2598 1038 : if (!newTypeSet->isSubset(*ptypeSet)) {
2599 237 : *ptypeSet = TypeSet::unionSets(*ptypeSet, newTypeSet, alloc.lifoAlloc());
2600 237 : if (!*ptypeSet)
2601 0 : return false;
2602 : }
2603 : } else {
2604 8 : *ptypeSet = nullptr;
2605 : }
2606 : }
2607 2135 : return true;
2608 : }
2609 :
2610 : // Tests whether 'types' includes all possible values represented by
2611 : // input/inputTypes.
2612 : bool
2613 120 : jit::TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes)
2614 : {
2615 120 : if (!types)
2616 7 : return inputTypes && inputTypes->empty();
2617 :
2618 113 : switch (input) {
2619 : case MIRType::Undefined:
2620 : case MIRType::Null:
2621 : case MIRType::Boolean:
2622 : case MIRType::Int32:
2623 : case MIRType::Double:
2624 : case MIRType::Float32:
2625 : case MIRType::String:
2626 : case MIRType::Symbol:
2627 : case MIRType::MagicOptimizedArguments:
2628 55 : return types->hasType(TypeSet::PrimitiveType(ValueTypeFromMIRType(input)));
2629 :
2630 : case MIRType::Object:
2631 7 : return types->unknownObject() || (inputTypes && inputTypes->isSubset(types));
2632 :
2633 : case MIRType::Value:
2634 51 : return types->unknown() || (inputTypes && inputTypes->isSubset(types));
2635 :
2636 : default:
2637 0 : MOZ_CRASH("Bad input type");
2638 : }
2639 : }
2640 :
2641 : // Tests if two type combos (type/typeset) are equal.
2642 : bool
2643 0 : jit::EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
2644 : MIRType type2, TemporaryTypeSet* typeset2)
2645 : {
2646 : // Types should equal.
2647 0 : if (type1 != type2)
2648 0 : return false;
2649 :
2650 : // Both have equal type and no typeset.
2651 0 : if (!typeset1 && !typeset2)
2652 0 : return true;
2653 :
2654 : // If only one instructions has a typeset.
2655 : // Test if the typset contains the same information as the MIRType.
2656 0 : if (typeset1 && !typeset2)
2657 0 : return TypeSetIncludes(typeset1, type2, nullptr);
2658 0 : if (!typeset1 && typeset2)
2659 0 : return TypeSetIncludes(typeset2, type1, nullptr);
2660 :
2661 : // Typesets should equal.
2662 0 : return typeset1->equals(typeset2);
2663 : }
2664 :
2665 : // Tests whether input/inputTypes can always be stored to an unboxed
2666 : // object/array property with the given unboxed type.
2667 : bool
2668 0 : jit::CanStoreUnboxedType(TempAllocator& alloc,
2669 : JSValueType unboxedType, MIRType input, TypeSet* inputTypes)
2670 : {
2671 0 : TemporaryTypeSet types;
2672 :
2673 0 : switch (unboxedType) {
2674 : case JSVAL_TYPE_BOOLEAN:
2675 : case JSVAL_TYPE_INT32:
2676 : case JSVAL_TYPE_DOUBLE:
2677 : case JSVAL_TYPE_STRING:
2678 0 : types.addType(TypeSet::PrimitiveType(unboxedType), alloc.lifoAlloc());
2679 0 : break;
2680 :
2681 : case JSVAL_TYPE_OBJECT:
2682 0 : types.addType(TypeSet::AnyObjectType(), alloc.lifoAlloc());
2683 0 : types.addType(TypeSet::NullType(), alloc.lifoAlloc());
2684 0 : break;
2685 :
2686 : default:
2687 0 : MOZ_CRASH("Bad unboxed type");
2688 : }
2689 :
2690 0 : return TypeSetIncludes(&types, input, inputTypes);
2691 : }
2692 :
2693 : static bool
2694 0 : CanStoreUnboxedType(TempAllocator& alloc, JSValueType unboxedType, MDefinition* value)
2695 : {
2696 0 : return CanStoreUnboxedType(alloc, unboxedType, value->type(), value->resultTypeSet());
2697 : }
2698 :
2699 : bool
2700 2635 : MPhi::specializeType(TempAllocator& alloc)
2701 : {
2702 : #ifdef DEBUG
2703 2635 : MOZ_ASSERT(!specialized_);
2704 2635 : specialized_ = true;
2705 : #endif
2706 :
2707 2635 : MOZ_ASSERT(!inputs_.empty());
2708 :
2709 : size_t start;
2710 2635 : if (hasBackedgeType_) {
2711 : // The type of this phi has already been populated with potential types
2712 : // that could come in via loop backedges.
2713 86 : start = 0;
2714 : } else {
2715 2549 : setResultType(getOperand(0)->type());
2716 2549 : setResultTypeSet(getOperand(0)->resultTypeSet());
2717 2549 : start = 1;
2718 : }
2719 :
2720 2635 : MIRType resultType = this->type();
2721 2635 : TemporaryTypeSet* resultTypeSet = this->resultTypeSet();
2722 :
2723 4115 : for (size_t i = start; i < inputs_.length(); i++) {
2724 1480 : MDefinition* def = getOperand(i);
2725 1480 : if (!MergeTypes(alloc, &resultType, &resultTypeSet, def->type(), def->resultTypeSet()))
2726 0 : return false;
2727 : }
2728 :
2729 2635 : setResultType(resultType);
2730 2635 : setResultTypeSet(resultTypeSet);
2731 2635 : return true;
2732 : }
2733 :
2734 : bool
2735 195 : MPhi::addBackedgeType(TempAllocator& alloc, MIRType type, TemporaryTypeSet* typeSet)
2736 : {
2737 195 : MOZ_ASSERT(!specialized_);
2738 :
2739 195 : if (hasBackedgeType_) {
2740 109 : MIRType resultType = this->type();
2741 109 : TemporaryTypeSet* resultTypeSet = this->resultTypeSet();
2742 :
2743 109 : if (!MergeTypes(alloc, &resultType, &resultTypeSet, type, typeSet))
2744 0 : return false;
2745 :
2746 109 : setResultType(resultType);
2747 109 : setResultTypeSet(resultTypeSet);
2748 : } else {
2749 86 : setResultType(type);
2750 86 : setResultTypeSet(typeSet);
2751 86 : hasBackedgeType_ = true;
2752 : }
2753 195 : return true;
2754 : }
2755 :
2756 : bool
2757 1 : MPhi::typeIncludes(MDefinition* def)
2758 : {
2759 1 : if (def->type() == MIRType::Int32 && this->type() == MIRType::Double)
2760 0 : return true;
2761 :
2762 1 : if (TemporaryTypeSet* types = def->resultTypeSet()) {
2763 1 : if (this->resultTypeSet())
2764 0 : return types->isSubset(this->resultTypeSet());
2765 1 : if (this->type() == MIRType::Value || types->empty())
2766 1 : return true;
2767 0 : return this->type() == types->getKnownMIRType();
2768 : }
2769 :
2770 0 : if (def->type() == MIRType::Value) {
2771 : // This phi must be able to be any value.
2772 0 : return this->type() == MIRType::Value
2773 0 : && (!this->resultTypeSet() || this->resultTypeSet()->unknown());
2774 : }
2775 :
2776 0 : return this->mightBeType(def->type());
2777 : }
2778 :
2779 : bool
2780 1519 : MPhi::checkForTypeChange(TempAllocator& alloc, MDefinition* ins, bool* ptypeChange)
2781 : {
2782 1519 : MIRType resultType = this->type();
2783 1519 : TemporaryTypeSet* resultTypeSet = this->resultTypeSet();
2784 :
2785 1519 : if (!MergeTypes(alloc, &resultType, &resultTypeSet, ins->type(), ins->resultTypeSet()))
2786 0 : return false;
2787 :
2788 1519 : if (resultType != this->type() || resultTypeSet != this->resultTypeSet()) {
2789 3 : *ptypeChange = true;
2790 3 : setResultType(resultType);
2791 3 : setResultTypeSet(resultTypeSet);
2792 : }
2793 1519 : return true;
2794 : }
2795 :
2796 : void
2797 3492 : MCall::addArg(size_t argnum, MDefinition* arg)
2798 : {
2799 : // The operand vector is initialized in reverse order by the IonBuilder.
2800 : // It cannot be checked for consistency until all arguments are added.
2801 : // FixedList doesn't initialize its elements, so do an unchecked init.
2802 3492 : initOperand(argnum + NumNonArgumentOperands, arg);
2803 3492 : }
2804 :
2805 : static inline bool
2806 24 : IsConstant(MDefinition* def, double v)
2807 : {
2808 24 : if (!def->isConstant())
2809 12 : return false;
2810 :
2811 12 : return NumbersAreIdentical(def->toConstant()->numberToDouble(), v);
2812 : }
2813 :
2814 : MDefinition*
2815 0 : MBinaryBitwiseInstruction::foldsTo(TempAllocator& alloc)
2816 : {
2817 0 : if (specialization_ != MIRType::Int32)
2818 0 : return this;
2819 :
2820 0 : if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
2821 0 : return folded;
2822 :
2823 0 : return this;
2824 : }
2825 :
2826 : MDefinition*
2827 0 : MBinaryBitwiseInstruction::foldUnnecessaryBitop()
2828 : {
2829 0 : if (specialization_ != MIRType::Int32)
2830 0 : return this;
2831 :
2832 : // Fold unsigned shift right operator when the second operand is zero and
2833 : // the only use is an unsigned modulo. Thus, the expression
2834 : // |(x >>> 0) % y| becomes |x % y|.
2835 0 : if (isUrsh() && hasOneDefUse() && IsUint32Type(this)) {
2836 0 : MUseDefIterator use(this);
2837 0 : if (use.def()->isMod() && use.def()->toMod()->isUnsigned())
2838 0 : return getOperand(0);
2839 0 : MOZ_ASSERT(!(++use));
2840 : }
2841 :
2842 : // Eliminate bitwise operations that are no-ops when used on integer
2843 : // inputs, such as (x | 0).
2844 :
2845 0 : MDefinition* lhs = getOperand(0);
2846 0 : MDefinition* rhs = getOperand(1);
2847 :
2848 0 : if (IsConstant(lhs, 0))
2849 0 : return foldIfZero(0);
2850 :
2851 0 : if (IsConstant(rhs, 0))
2852 0 : return foldIfZero(1);
2853 :
2854 0 : if (IsConstant(lhs, -1))
2855 0 : return foldIfNegOne(0);
2856 :
2857 0 : if (IsConstant(rhs, -1))
2858 0 : return foldIfNegOne(1);
2859 :
2860 0 : if (lhs == rhs)
2861 0 : return foldIfEqual();
2862 :
2863 0 : if (maskMatchesRightRange) {
2864 0 : MOZ_ASSERT(lhs->isConstant());
2865 0 : MOZ_ASSERT(lhs->type() == MIRType::Int32);
2866 0 : return foldIfAllBitsSet(0);
2867 : }
2868 :
2869 0 : if (maskMatchesLeftRange) {
2870 0 : MOZ_ASSERT(rhs->isConstant());
2871 0 : MOZ_ASSERT(rhs->type() == MIRType::Int32);
2872 0 : return foldIfAllBitsSet(1);
2873 : }
2874 :
2875 0 : return this;
2876 : }
2877 :
2878 : void
2879 2 : MBinaryBitwiseInstruction::infer(BaselineInspector*, jsbytecode*)
2880 : {
2881 8 : if (getOperand(0)->mightBeType(MIRType::Object) || getOperand(0)->mightBeType(MIRType::Symbol) ||
2882 6 : getOperand(1)->mightBeType(MIRType::Object) || getOperand(1)->mightBeType(MIRType::Symbol))
2883 : {
2884 0 : specialization_ = MIRType::None;
2885 : } else {
2886 2 : specializeAs(MIRType::Int32);
2887 : }
2888 2 : }
2889 :
2890 : void
2891 2 : MBinaryBitwiseInstruction::specializeAs(MIRType type)
2892 : {
2893 2 : MOZ_ASSERT(type == MIRType::Int32 || type == MIRType::Int64);
2894 2 : MOZ_ASSERT(this->type() == type);
2895 :
2896 2 : specialization_ = type;
2897 :
2898 2 : if (isBitOr() || isBitAnd() || isBitXor())
2899 2 : setCommutative();
2900 2 : }
2901 :
2902 : void
2903 0 : MShiftInstruction::infer(BaselineInspector*, jsbytecode*)
2904 : {
2905 0 : if (getOperand(0)->mightBeType(MIRType::Object) || getOperand(1)->mightBeType(MIRType::Object) ||
2906 0 : getOperand(0)->mightBeType(MIRType::Symbol) || getOperand(1)->mightBeType(MIRType::Symbol))
2907 0 : specialization_ = MIRType::None;
2908 : else
2909 0 : specialization_ = MIRType::Int32;
2910 0 : }
2911 :
2912 : void
2913 0 : MUrsh::infer(BaselineInspector* inspector, jsbytecode* pc)
2914 : {
2915 0 : if (getOperand(0)->mightBeType(MIRType::Object) || getOperand(1)->mightBeType(MIRType::Object) ||
2916 0 : getOperand(0)->mightBeType(MIRType::Symbol) || getOperand(1)->mightBeType(MIRType::Symbol))
2917 : {
2918 0 : specialization_ = MIRType::None;
2919 0 : setResultType(MIRType::Value);
2920 0 : return;
2921 : }
2922 :
2923 0 : if (inspector->hasSeenDoubleResult(pc)) {
2924 0 : specialization_ = MIRType::Double;
2925 0 : setResultType(MIRType::Double);
2926 0 : return;
2927 : }
2928 :
2929 0 : specialization_ = MIRType::Int32;
2930 0 : setResultType(MIRType::Int32);
2931 : }
2932 :
2933 : static inline bool
2934 0 : CanProduceNegativeZero(MDefinition* def)
2935 : {
2936 : // Test if this instruction can produce negative zero even when bailing out
2937 : // and changing types.
2938 0 : switch (def->op()) {
2939 : case MDefinition::Op_Constant:
2940 0 : if (def->type() == MIRType::Double && def->toConstant()->toDouble() == -0.0)
2941 0 : return true;
2942 : MOZ_FALLTHROUGH;
2943 : case MDefinition::Op_BitAnd:
2944 : case MDefinition::Op_BitOr:
2945 : case MDefinition::Op_BitXor:
2946 : case MDefinition::Op_BitNot:
2947 : case MDefinition::Op_Lsh:
2948 : case MDefinition::Op_Rsh:
2949 0 : return false;
2950 : default:
2951 0 : return true;
2952 : }
2953 : }
2954 :
2955 : static inline bool
2956 1 : NeedNegativeZeroCheck(MDefinition* def)
2957 : {
2958 1 : if (def->isGuardRangeBailouts())
2959 0 : return true;
2960 :
2961 : // Test if all uses have the same semantics for -0 and 0
2962 2 : for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) {
2963 1 : if (use->consumer()->isResumePoint())
2964 0 : continue;
2965 :
2966 1 : MDefinition* use_def = use->consumer()->toDefinition();
2967 1 : switch (use_def->op()) {
2968 : case MDefinition::Op_Add: {
2969 : // If add is truncating -0 and 0 are observed as the same.
2970 0 : if (use_def->toAdd()->isTruncated())
2971 0 : break;
2972 :
2973 : // x + y gives -0, when both x and y are -0
2974 :
2975 : // Figure out the order in which the addition's operands will
2976 : // execute. EdgeCaseAnalysis::analyzeLate has renumbered the MIR
2977 : // definitions for us so that this just requires comparing ids.
2978 0 : MDefinition* first = use_def->toAdd()->lhs();
2979 0 : MDefinition* second = use_def->toAdd()->rhs();
2980 0 : if (first->id() > second->id()) {
2981 0 : MDefinition* temp = first;
2982 0 : first = second;
2983 0 : second = temp;
2984 : }
2985 : // Negative zero checks can be removed on the first executed
2986 : // operand only if it is guaranteed the second executed operand
2987 : // will produce a value other than -0. While the second is
2988 : // typed as an int32, a bailout taken between execution of the
2989 : // operands may change that type and cause a -0 to flow to the
2990 : // second.
2991 : //
2992 : // There is no way to test whether there are any bailouts
2993 : // between execution of the operands, so remove negative
2994 : // zero checks from the first only if the second's type is
2995 : // independent from type changes that may occur after bailing.
2996 0 : if (def == first && CanProduceNegativeZero(second))
2997 0 : return true;
2998 :
2999 : // The negative zero check can always be removed on the second
3000 : // executed operand; by the time this executes the first will have
3001 : // been evaluated as int32 and the addition's result cannot be -0.
3002 0 : break;
3003 : }
3004 : case MDefinition::Op_Sub: {
3005 : // If sub is truncating -0 and 0 are observed as the same
3006 0 : if (use_def->toSub()->isTruncated())
3007 0 : break;
3008 :
3009 : // x + y gives -0, when x is -0 and y is 0
3010 :
3011 : // We can remove the negative zero check on the rhs, only if we
3012 : // are sure the lhs isn't negative zero.
3013 :
3014 : // The lhs is typed as integer (i.e. not -0.0), but it can bailout
3015 : // and change type. This should be fine if the lhs is executed
3016 : // first. However if the rhs is executed first, the lhs can bail,
3017 : // change type and become -0.0 while the rhs has already been
3018 : // optimized to not make a difference between zero and negative zero.
3019 0 : MDefinition* lhs = use_def->toSub()->lhs();
3020 0 : MDefinition* rhs = use_def->toSub()->rhs();
3021 0 : if (rhs->id() < lhs->id() && CanProduceNegativeZero(lhs))
3022 0 : return true;
3023 :
3024 : MOZ_FALLTHROUGH;
3025 : }
3026 : case MDefinition::Op_StoreElement:
3027 : case MDefinition::Op_StoreElementHole:
3028 : case MDefinition::Op_FallibleStoreElement:
3029 : case MDefinition::Op_LoadElement:
3030 : case MDefinition::Op_LoadElementHole:
3031 : case MDefinition::Op_LoadUnboxedScalar:
3032 : case MDefinition::Op_LoadTypedArrayElementHole:
3033 : case MDefinition::Op_CharCodeAt:
3034 : case MDefinition::Op_Mod:
3035 : // Only allowed to remove check when definition is the second operand
3036 0 : if (use_def->getOperand(0) == def)
3037 0 : return true;
3038 0 : for (size_t i = 2, e = use_def->numOperands(); i < e; i++) {
3039 0 : if (use_def->getOperand(i) == def)
3040 0 : return true;
3041 : }
3042 0 : break;
3043 : case MDefinition::Op_BoundsCheck:
3044 : // Only allowed to remove check when definition is the first operand
3045 0 : if (use_def->toBoundsCheck()->getOperand(1) == def)
3046 0 : return true;
3047 0 : break;
3048 : case MDefinition::Op_ToString:
3049 : case MDefinition::Op_FromCharCode:
3050 : case MDefinition::Op_TableSwitch:
3051 : case MDefinition::Op_Compare:
3052 : case MDefinition::Op_BitAnd:
3053 : case MDefinition::Op_BitOr:
3054 : case MDefinition::Op_BitXor:
3055 : case MDefinition::Op_Abs:
3056 : case MDefinition::Op_TruncateToInt32:
3057 : // Always allowed to remove check. No matter which operand.
3058 1 : break;
3059 : default:
3060 0 : return true;
3061 : }
3062 : }
3063 1 : return false;
3064 : }
3065 :
3066 : void
3067 0 : MBinaryArithInstruction::printOpcode(GenericPrinter& out) const
3068 : {
3069 0 : MDefinition::printOpcode(out);
3070 :
3071 0 : switch (type()) {
3072 : case MIRType::Int32:
3073 0 : if (isDiv())
3074 0 : out.printf(" [%s]", toDiv()->isUnsigned() ? "uint32" : "int32");
3075 0 : else if (isMod())
3076 0 : out.printf(" [%s]", toMod()->isUnsigned() ? "uint32" : "int32");
3077 : else
3078 0 : out.printf(" [int32]");
3079 0 : break;
3080 : case MIRType::Int64:
3081 0 : if (isDiv())
3082 0 : out.printf(" [%s]", toDiv()->isUnsigned() ? "uint64" : "int64");
3083 0 : else if (isMod())
3084 0 : out.printf(" [%s]", toMod()->isUnsigned() ? "uint64" : "int64");
3085 : else
3086 0 : out.printf(" [int64]");
3087 0 : break;
3088 : case MIRType::Float32:
3089 0 : out.printf(" [float]");
3090 0 : break;
3091 : case MIRType::Double:
3092 0 : out.printf(" [double]");
3093 0 : break;
3094 : default:
3095 0 : break;
3096 : }
3097 0 : }
3098 :
3099 : MBinaryArithInstruction*
3100 211 : MBinaryArithInstruction::New(TempAllocator& alloc, Opcode op,
3101 : MDefinition* left, MDefinition* right)
3102 : {
3103 211 : switch (op) {
3104 : case Op_Add:
3105 208 : return MAdd::New(alloc, left, right);
3106 : case Op_Sub:
3107 0 : return MSub::New(alloc, left, right);
3108 : case Op_Mul:
3109 3 : return MMul::New(alloc, left, right);
3110 : case Op_Div:
3111 0 : return MDiv::New(alloc, left, right);
3112 : case Op_Mod:
3113 0 : return MMod::New(alloc, left, right);
3114 : default:
3115 0 : MOZ_CRASH("unexpected binary opcode");
3116 : }
3117 : }
3118 :
3119 : void
3120 211 : MBinaryArithInstruction::setNumberSpecialization(TempAllocator& alloc, BaselineInspector* inspector,
3121 : jsbytecode* pc)
3122 : {
3123 211 : setSpecialization(MIRType::Double);
3124 :
3125 : // Try to specialize as int32.
3126 211 : if (getOperand(0)->type() == MIRType::Int32 && getOperand(1)->type() == MIRType::Int32) {
3127 160 : bool seenDouble = inspector->hasSeenDoubleResult(pc);
3128 :
3129 : // Use int32 specialization if the operation doesn't overflow on its
3130 : // constant operands and if the operation has never overflowed.
3131 160 : if (!seenDouble && !constantDoubleResult(alloc))
3132 160 : setInt32Specialization();
3133 : }
3134 211 : }
3135 :
3136 : bool
3137 160 : MBinaryArithInstruction::constantDoubleResult(TempAllocator& alloc)
3138 : {
3139 160 : bool typeChange = false;
3140 160 : EvaluateConstantOperands(alloc, this, &typeChange);
3141 160 : return typeChange;
3142 : }
3143 :
3144 : MDefinition*
3145 0 : MRsh::foldsTo(TempAllocator& alloc)
3146 : {
3147 0 : MDefinition* f = MBinaryBitwiseInstruction::foldsTo(alloc);
3148 :
3149 0 : if (f != this)
3150 0 : return f;
3151 :
3152 0 : MDefinition* lhs = getOperand(0);
3153 0 : MDefinition* rhs = getOperand(1);
3154 :
3155 0 : if (!lhs->isLsh() || !rhs->isConstant() || rhs->type() != MIRType::Int32)
3156 0 : return this;
3157 :
3158 0 : if (!lhs->getOperand(1)->isConstant() || lhs->getOperand(1)->type() != MIRType::Int32)
3159 0 : return this;
3160 :
3161 0 : uint32_t shift = rhs->toConstant()->toInt32();
3162 0 : uint32_t shift_lhs = lhs->getOperand(1)->toConstant()->toInt32();
3163 0 : if (shift != shift_lhs)
3164 0 : return this;
3165 :
3166 0 : switch (shift) {
3167 : case 16:
3168 0 : return MSignExtend::New(alloc, lhs->getOperand(0), MSignExtend::Half);
3169 : case 24:
3170 0 : return MSignExtend::New(alloc, lhs->getOperand(0), MSignExtend::Byte);
3171 : }
3172 :
3173 0 : return this;
3174 : }
3175 :
3176 : MDefinition*
3177 13 : MBinaryArithInstruction::foldsTo(TempAllocator& alloc)
3178 : {
3179 13 : if (specialization_ == MIRType::None)
3180 0 : return this;
3181 :
3182 13 : if (specialization_ == MIRType::Int64)
3183 0 : return this;
3184 :
3185 13 : MDefinition* lhs = getOperand(0);
3186 13 : MDefinition* rhs = getOperand(1);
3187 13 : if (MConstant* folded = EvaluateConstantOperands(alloc, this)) {
3188 1 : if (isTruncated()) {
3189 0 : if (!folded->block())
3190 0 : block()->insertBefore(this, folded);
3191 0 : if (folded->type() != MIRType::Int32)
3192 1 : return MTruncateToInt32::New(alloc, folded);
3193 : }
3194 1 : return folded;
3195 : }
3196 :
3197 12 : if (mustPreserveNaN_)
3198 0 : return this;
3199 :
3200 : // 0 + -0 = 0. So we can't remove addition
3201 12 : if (isAdd() && specialization_ != MIRType::Int32)
3202 0 : return this;
3203 :
3204 12 : if (IsConstant(rhs, getIdentity())) {
3205 0 : if (isTruncated())
3206 0 : return MTruncateToInt32::New(alloc, lhs);
3207 0 : return lhs;
3208 : }
3209 :
3210 : // subtraction isn't commutative. So we can't remove subtraction when lhs equals 0
3211 12 : if (isSub())
3212 0 : return this;
3213 :
3214 12 : if (IsConstant(lhs, getIdentity())) {
3215 0 : if (isTruncated())
3216 0 : return MTruncateToInt32::New(alloc, rhs);
3217 0 : return rhs; // x op id => x
3218 : }
3219 :
3220 12 : return this;
3221 : }
3222 :
3223 : void
3224 0 : MFilterTypeSet::trySpecializeFloat32(TempAllocator& alloc)
3225 : {
3226 0 : MDefinition* in = input();
3227 0 : if (in->type() != MIRType::Float32)
3228 0 : return;
3229 :
3230 0 : setResultType(MIRType::Float32);
3231 : }
3232 :
3233 : bool
3234 6 : MFilterTypeSet::canProduceFloat32() const
3235 : {
3236 : // A FilterTypeSet should be a producer if the input is a producer too.
3237 : // Also, be overly conservative by marking as not float32 producer when the
3238 : // input is a phi, as phis can be cyclic (phiA -> FilterTypeSet -> phiB ->
3239 : // phiA) and FilterTypeSet doesn't belong in the Float32 phi analysis.
3240 6 : return !input()->isPhi() && input()->canProduceFloat32();
3241 : }
3242 :
3243 : bool
3244 0 : MFilterTypeSet::canConsumeFloat32(MUse* operand) const
3245 : {
3246 0 : MOZ_ASSERT(getUseFor(0) == operand);
3247 : // A FilterTypeSet should be a consumer if all uses are consumer. See also
3248 : // comment below MFilterTypeSet::canProduceFloat32.
3249 0 : bool allConsumerUses = true;
3250 0 : for (MUseDefIterator use(this); allConsumerUses && use; use++)
3251 0 : allConsumerUses &= !use.def()->isPhi() && use.def()->canConsumeFloat32(use.use());
3252 0 : return allConsumerUses;
3253 : }
3254 :
3255 : void
3256 0 : MBinaryArithInstruction::trySpecializeFloat32(TempAllocator& alloc)
3257 : {
3258 : // Do not use Float32 if we can use int32.
3259 0 : if (specialization_ == MIRType::Int32)
3260 0 : return;
3261 0 : if (specialization_ == MIRType::None)
3262 0 : return;
3263 :
3264 0 : MDefinition* left = lhs();
3265 0 : MDefinition* right = rhs();
3266 :
3267 0 : if (!left->canProduceFloat32() || !right->canProduceFloat32() ||
3268 0 : !CheckUsesAreFloat32Consumers(this))
3269 : {
3270 0 : if (left->type() == MIRType::Float32)
3271 0 : ConvertDefinitionToDouble<0>(alloc, left, this);
3272 0 : if (right->type() == MIRType::Float32)
3273 0 : ConvertDefinitionToDouble<1>(alloc, right, this);
3274 0 : return;
3275 : }
3276 :
3277 0 : specialization_ = MIRType::Float32;
3278 0 : setResultType(MIRType::Float32);
3279 : }
3280 :
3281 : void
3282 0 : MMinMax::trySpecializeFloat32(TempAllocator& alloc)
3283 : {
3284 0 : if (specialization_ == MIRType::Int32)
3285 0 : return;
3286 :
3287 0 : MDefinition* left = lhs();
3288 0 : MDefinition* right = rhs();
3289 :
3290 0 : if (!(left->canProduceFloat32() || (left->isMinMax() && left->type() == MIRType::Float32)) ||
3291 0 : !(right->canProduceFloat32() || (right->isMinMax() && right->type() == MIRType::Float32)))
3292 : {
3293 0 : if (left->type() == MIRType::Float32)
3294 0 : ConvertDefinitionToDouble<0>(alloc, left, this);
3295 0 : if (right->type() == MIRType::Float32)
3296 0 : ConvertDefinitionToDouble<1>(alloc, right, this);
3297 0 : return;
3298 : }
3299 :
3300 0 : specialization_ = MIRType::Float32;
3301 0 : setResultType(MIRType::Float32);
3302 : }
3303 :
3304 : MDefinition*
3305 8 : MMinMax::foldsTo(TempAllocator& alloc)
3306 : {
3307 8 : if (!lhs()->isConstant() && !rhs()->isConstant())
3308 0 : return this;
3309 :
3310 : // Directly apply math utility to compare the rhs() and lhs() when
3311 : // they are both constants.
3312 8 : if (lhs()->isConstant() && rhs()->isConstant()) {
3313 0 : if (!lhs()->toConstant()->isTypeRepresentableAsDouble() ||
3314 0 : !rhs()->toConstant()->isTypeRepresentableAsDouble())
3315 : {
3316 0 : return this;
3317 : }
3318 :
3319 0 : double lnum = lhs()->toConstant()->numberToDouble();
3320 0 : double rnum = rhs()->toConstant()->numberToDouble();
3321 :
3322 : double result;
3323 0 : if (isMax())
3324 0 : result = js::math_max_impl(lnum, rnum);
3325 : else
3326 0 : result = js::math_min_impl(lnum, rnum);
3327 :
3328 : // The folded MConstant should maintain the same MIRType with
3329 : // the original MMinMax.
3330 0 : if (type() == MIRType::Int32) {
3331 : int32_t cast;
3332 0 : if (mozilla::NumberEqualsInt32(result, &cast))
3333 0 : return MConstant::New(alloc, Int32Value(cast));
3334 0 : } else if (type() == MIRType::Float32) {
3335 0 : return MConstant::NewFloat32(alloc, result);
3336 : } else {
3337 0 : MOZ_ASSERT(type() == MIRType::Double);
3338 0 : return MConstant::New(alloc, DoubleValue(result));
3339 : }
3340 : }
3341 :
3342 8 : MDefinition* operand = lhs()->isConstant() ? rhs() : lhs();
3343 8 : MConstant* constant = lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant();
3344 :
3345 8 : if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType::Int32) {
3346 : // min(int32, cte >= INT32_MAX) = int32
3347 0 : if (!isMax() &&
3348 0 : constant->isTypeRepresentableAsDouble() &&
3349 0 : constant->numberToDouble() >= INT32_MAX)
3350 : {
3351 : MLimitedTruncate* limit =
3352 0 : MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate);
3353 0 : block()->insertBefore(this, limit);
3354 0 : MToDouble* toDouble = MToDouble::New(alloc, limit);
3355 0 : return toDouble;
3356 : }
3357 :
3358 : // max(int32, cte <= INT32_MIN) = int32
3359 0 : if (isMax() &&
3360 0 : constant->isTypeRepresentableAsDouble() &&
3361 0 : constant->numberToDouble() <= INT32_MIN)
3362 : {
3363 : MLimitedTruncate* limit =
3364 0 : MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate);
3365 0 : block()->insertBefore(this, limit);
3366 0 : MToDouble* toDouble = MToDouble::New(alloc, limit);
3367 0 : return toDouble;
3368 : }
3369 : }
3370 :
3371 8 : if (operand->isArrayLength() && constant->type() == MIRType::Int32) {
3372 0 : MOZ_ASSERT(operand->type() == MIRType::Int32);
3373 :
3374 : // max(array.length, 0) = array.length
3375 : // ArrayLength is always >= 0, so just return it.
3376 0 : if (isMax() && constant->toInt32() <= 0)
3377 0 : return operand;
3378 : }
3379 :
3380 8 : return this;
3381 : }
3382 :
3383 : MDefinition*
3384 0 : MPow::foldsConstant(TempAllocator &alloc)
3385 : {
3386 : // Both `x` and `p` in `x^p` must be constants in order to precompute.
3387 0 : if(!(input()->isConstant() && power()->isConstant()))
3388 0 : return nullptr;
3389 0 : if(!power()->toConstant()->isTypeRepresentableAsDouble())
3390 0 : return nullptr;
3391 0 : if(!input()->toConstant()->isTypeRepresentableAsDouble())
3392 0 : return nullptr;
3393 :
3394 0 : double x = input()->toConstant()->numberToDouble();
3395 0 : double p = power()->toConstant()->numberToDouble();
3396 0 : return MConstant::New(alloc, DoubleValue(js::ecmaPow(x, p)));
3397 : }
3398 :
3399 : MDefinition*
3400 0 : MPow::foldsConstantPower(TempAllocator &alloc)
3401 : {
3402 : // If `p` in `x^p` isn't constant, we can't apply these folds.
3403 0 : if(!power()->isConstant())
3404 0 : return nullptr;
3405 0 : if(!power()->toConstant()->isTypeRepresentableAsDouble())
3406 0 : return nullptr;
3407 :
3408 0 : double pow = power()->toConstant()->numberToDouble();
3409 0 : MIRType outputType = type();
3410 :
3411 : // Math.pow(x, 0.5) is a sqrt with edge-case detection.
3412 0 : if (pow == 0.5)
3413 0 : return MPowHalf::New(alloc, input());
3414 :
3415 : // Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5), even for edge cases.
3416 0 : if (pow == -0.5) {
3417 0 : MPowHalf* half = MPowHalf::New(alloc, input());
3418 0 : block()->insertBefore(this, half);
3419 0 : MConstant* one = MConstant::New(alloc, DoubleValue(1.0));
3420 0 : block()->insertBefore(this, one);
3421 0 : return MDiv::New(alloc, one, half, MIRType::Double);
3422 : }
3423 :
3424 : // Math.pow(x, 1) == x.
3425 0 : if (pow == 1.0)
3426 0 : return input();
3427 :
3428 : // Math.pow(x, 2) == x*x.
3429 0 : if (pow == 2.0)
3430 0 : return MMul::New(alloc, input(), input(), outputType);
3431 :
3432 : // Math.pow(x, 3) == x*x*x.
3433 0 : if (pow == 3.0) {
3434 0 : MMul* mul1 = MMul::New(alloc, input(), input(), outputType);
3435 0 : block()->insertBefore(this, mul1);
3436 0 : return MMul::New(alloc, input(), mul1, outputType);
3437 : }
3438 :
3439 : // Math.pow(x, 4) == y*y, where y = x*x.
3440 0 : if (pow == 4.0) {
3441 0 : MMul* y = MMul::New(alloc, input(), input(), outputType);
3442 0 : block()->insertBefore(this, y);
3443 0 : return MMul::New(alloc, y, y, outputType);
3444 : }
3445 :
3446 : // No optimization
3447 0 : return nullptr;
3448 : }
3449 :
3450 : MDefinition*
3451 0 : MPow::foldsTo(TempAllocator& alloc)
3452 : {
3453 0 : if(MDefinition *def = foldsConstant(alloc))
3454 0 : return def;
3455 0 : if(MDefinition *def = foldsConstantPower(alloc))
3456 0 : return def;
3457 0 : return this;
3458 : }
3459 :
3460 : bool
3461 0 : MAbs::fallible() const
3462 : {
3463 0 : return !implicitTruncate_ && (!range() || !range()->hasInt32Bounds());
3464 : }
3465 :
3466 : void
3467 0 : MAbs::trySpecializeFloat32(TempAllocator& alloc)
3468 : {
3469 : // Do not use Float32 if we can use int32.
3470 0 : if (input()->type() == MIRType::Int32)
3471 0 : return;
3472 :
3473 0 : if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
3474 0 : if (input()->type() == MIRType::Float32)
3475 0 : ConvertDefinitionToDouble<0>(alloc, input(), this);
3476 0 : return;
3477 : }
3478 :
3479 0 : setResultType(MIRType::Float32);
3480 0 : specialization_ = MIRType::Float32;
3481 : }
3482 :
3483 : MDefinition*
3484 0 : MDiv::foldsTo(TempAllocator& alloc)
3485 : {
3486 0 : if (specialization_ == MIRType::None)
3487 0 : return this;
3488 :
3489 0 : if (specialization_ == MIRType::Int64)
3490 0 : return this;
3491 :
3492 0 : if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
3493 0 : return folded;
3494 :
3495 0 : if (MDefinition* folded = EvaluateExactReciprocal(alloc, this))
3496 0 : return folded;
3497 :
3498 0 : return this;
3499 : }
3500 :
3501 : void
3502 0 : MDiv::analyzeEdgeCasesForward()
3503 : {
3504 : // This is only meaningful when doing integer division.
3505 0 : if (specialization_ != MIRType::Int32)
3506 0 : return;
3507 :
3508 0 : MOZ_ASSERT(lhs()->type() == MIRType::Int32);
3509 0 : MOZ_ASSERT(rhs()->type() == MIRType::Int32);
3510 :
3511 : // Try removing divide by zero check
3512 0 : if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0))
3513 0 : canBeDivideByZero_ = false;
3514 :
3515 : // If lhs is a constant int != INT32_MIN, then
3516 : // negative overflow check can be skipped.
3517 0 : if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(INT32_MIN))
3518 0 : canBeNegativeOverflow_ = false;
3519 :
3520 : // If rhs is a constant int != -1, likewise.
3521 0 : if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(-1))
3522 0 : canBeNegativeOverflow_ = false;
3523 :
3524 : // If lhs is != 0, then negative zero check can be skipped.
3525 0 : if (lhs()->isConstant() && !lhs()->toConstant()->isInt32(0))
3526 0 : setCanBeNegativeZero(false);
3527 :
3528 : // If rhs is >= 0, likewise.
3529 0 : if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) {
3530 0 : if (rhs()->toConstant()->toInt32() >= 0)
3531 0 : setCanBeNegativeZero(false);
3532 : }
3533 : }
3534 :
3535 : void
3536 0 : MDiv::analyzeEdgeCasesBackward()
3537 : {
3538 0 : if (canBeNegativeZero() && !NeedNegativeZeroCheck(this))
3539 0 : setCanBeNegativeZero(false);
3540 0 : }
3541 :
3542 : bool
3543 0 : MDiv::fallible() const
3544 : {
3545 0 : return !isTruncated();
3546 : }
3547 :
3548 : MDefinition*
3549 0 : MMod::foldsTo(TempAllocator& alloc)
3550 : {
3551 0 : if (specialization_ == MIRType::None)
3552 0 : return this;
3553 :
3554 0 : if (specialization_ == MIRType::Int64)
3555 0 : return this;
3556 :
3557 0 : if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
3558 0 : return folded;
3559 :
3560 0 : return this;
3561 : }
3562 :
3563 : void
3564 0 : MMod::analyzeEdgeCasesForward()
3565 : {
3566 : // These optimizations make sense only for integer division
3567 0 : if (specialization_ != MIRType::Int32)
3568 0 : return;
3569 :
3570 0 : if (rhs()->isConstant() && !rhs()->toConstant()->isInt32(0))
3571 0 : canBeDivideByZero_ = false;
3572 :
3573 0 : if (rhs()->isConstant()) {
3574 0 : int32_t n = rhs()->toConstant()->toInt32();
3575 0 : if (n > 0 && !IsPowerOfTwo(uint32_t(n)))
3576 0 : canBePowerOfTwoDivisor_ = false;
3577 : }
3578 : }
3579 :
3580 : bool
3581 0 : MMod::fallible() const
3582 : {
3583 0 : return !isTruncated() &&
3584 0 : (isUnsigned() || canBeDivideByZero() || canBeNegativeDividend());
3585 : }
3586 :
3587 : void
3588 0 : MMathFunction::trySpecializeFloat32(TempAllocator& alloc)
3589 : {
3590 0 : if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
3591 0 : if (input()->type() == MIRType::Float32)
3592 0 : ConvertDefinitionToDouble<0>(alloc, input(), this);
3593 0 : return;
3594 : }
3595 :
3596 0 : setResultType(MIRType::Float32);
3597 0 : specialization_ = MIRType::Float32;
3598 : }
3599 :
3600 0 : MHypot* MHypot::New(TempAllocator& alloc, const MDefinitionVector & vector)
3601 : {
3602 0 : uint32_t length = vector.length();
3603 0 : MHypot * hypot = new(alloc) MHypot;
3604 0 : if (!hypot->init(alloc, length))
3605 0 : return nullptr;
3606 :
3607 0 : for (uint32_t i = 0; i < length; ++i)
3608 0 : hypot->initOperand(i, vector[i]);
3609 0 : return hypot;
3610 : }
3611 :
3612 : bool
3613 16 : MAdd::fallible() const
3614 : {
3615 : // the add is fallible if range analysis does not say that it is finite, AND
3616 : // either the truncation analysis shows that there are non-truncated uses.
3617 16 : if (truncateKind() >= IndirectTruncate)
3618 2 : return false;
3619 14 : if (range() && range()->hasInt32Bounds())
3620 12 : return false;
3621 2 : return true;
3622 : }
3623 :
3624 : bool
3625 0 : MSub::fallible() const
3626 : {
3627 : // see comment in MAdd::fallible()
3628 0 : if (truncateKind() >= IndirectTruncate)
3629 0 : return false;
3630 0 : if (range() && range()->hasInt32Bounds())
3631 0 : return false;
3632 0 : return true;
3633 : }
3634 :
3635 : MDefinition*
3636 0 : MMul::foldsTo(TempAllocator& alloc)
3637 : {
3638 0 : MDefinition* out = MBinaryArithInstruction::foldsTo(alloc);
3639 0 : if (out != this)
3640 0 : return out;
3641 :
3642 0 : if (specialization() != MIRType::Int32)
3643 0 : return this;
3644 :
3645 0 : if (lhs() == rhs())
3646 0 : setCanBeNegativeZero(false);
3647 :
3648 0 : return this;
3649 : }
3650 :
3651 : void
3652 0 : MMul::analyzeEdgeCasesForward()
3653 : {
3654 : // Try to remove the check for negative zero
3655 : // This only makes sense when using the integer multiplication
3656 0 : if (specialization() != MIRType::Int32)
3657 0 : return;
3658 :
3659 : // If lhs is > 0, no need for negative zero check.
3660 0 : if (lhs()->isConstant() && lhs()->type() == MIRType::Int32) {
3661 0 : if (lhs()->toConstant()->toInt32() > 0)
3662 0 : setCanBeNegativeZero(false);
3663 : }
3664 :
3665 : // If rhs is > 0, likewise.
3666 0 : if (rhs()->isConstant() && rhs()->type() == MIRType::Int32) {
3667 0 : if (rhs()->toConstant()->toInt32() > 0)
3668 0 : setCanBeNegativeZero(false);
3669 : }
3670 : }
3671 :
3672 : void
3673 0 : MMul::analyzeEdgeCasesBackward()
3674 : {
3675 0 : if (canBeNegativeZero() && !NeedNegativeZeroCheck(this))
3676 0 : setCanBeNegativeZero(false);
3677 0 : }
3678 :
3679 : bool
3680 0 : MMul::updateForReplacement(MDefinition* ins_)
3681 : {
3682 0 : MMul* ins = ins_->toMul();
3683 0 : bool negativeZero = canBeNegativeZero() || ins->canBeNegativeZero();
3684 0 : setCanBeNegativeZero(negativeZero);
3685 : // Remove the imul annotation when merging imul and normal multiplication.
3686 0 : if (mode_ == Integer && ins->mode() != Integer)
3687 0 : mode_ = Normal;
3688 0 : return true;
3689 : }
3690 :
3691 : bool
3692 0 : MMul::canOverflow() const
3693 : {
3694 0 : if (isTruncated())
3695 0 : return false;
3696 0 : return !range() || !range()->hasInt32Bounds();
3697 : }
3698 :
3699 : bool
3700 0 : MUrsh::fallible() const
3701 : {
3702 0 : if (bailoutsDisabled())
3703 0 : return false;
3704 0 : return !range() || !range()->hasInt32Bounds();
3705 : }
3706 :
3707 : static inline bool
3708 24 : SimpleArithOperand(MDefinition* op)
3709 : {
3710 24 : return !op->mightBeType(MIRType::Object)
3711 24 : && !op->mightBeType(MIRType::String)
3712 24 : && !op->mightBeType(MIRType::Symbol)
3713 24 : && !op->mightBeType(MIRType::MagicOptimizedArguments)
3714 24 : && !op->mightBeType(MIRType::MagicHole)
3715 48 : && !op->mightBeType(MIRType::MagicIsConstructing);
3716 : }
3717 :
3718 : static bool
3719 24 : SafelyCoercesToDouble(MDefinition* op)
3720 : {
3721 : // Strings and symbols are unhandled -- visitToDouble() doesn't support them yet.
3722 : // Null is unhandled -- ToDouble(null) == 0, but (0 == null) is false.
3723 24 : return SimpleArithOperand(op) && !op->mightBeType(MIRType::Null);
3724 : }
3725 :
3726 : MIRType
3727 61 : MCompare::inputType()
3728 : {
3729 61 : switch(compareType_) {
3730 : case Compare_Undefined:
3731 0 : return MIRType::Undefined;
3732 : case Compare_Null:
3733 8 : return MIRType::Null;
3734 : case Compare_Boolean:
3735 0 : return MIRType::Boolean;
3736 : case Compare_UInt32:
3737 : case Compare_Int32:
3738 : case Compare_Int32MaybeCoerceBoth:
3739 : case Compare_Int32MaybeCoerceLHS:
3740 : case Compare_Int32MaybeCoerceRHS:
3741 53 : return MIRType::Int32;
3742 : case Compare_Double:
3743 : case Compare_DoubleMaybeCoerceLHS:
3744 : case Compare_DoubleMaybeCoerceRHS:
3745 0 : return MIRType::Double;
3746 : case Compare_Float32:
3747 0 : return MIRType::Float32;
3748 : case Compare_String:
3749 : case Compare_StrictString:
3750 0 : return MIRType::String;
3751 : case Compare_Symbol:
3752 0 : return MIRType::Symbol;
3753 : case Compare_Object:
3754 0 : return MIRType::Object;
3755 : case Compare_Unknown:
3756 : case Compare_Bitwise:
3757 0 : return MIRType::Value;
3758 : default:
3759 0 : MOZ_CRASH("No known conversion");
3760 : }
3761 : }
3762 :
3763 : static inline bool
3764 486 : MustBeUInt32(MDefinition* def, MDefinition** pwrapped)
3765 : {
3766 486 : if (def->isUrsh()) {
3767 0 : *pwrapped = def->toUrsh()->lhs();
3768 0 : MDefinition* rhs = def->toUrsh()->rhs();
3769 0 : return def->toUrsh()->bailoutsDisabled() &&
3770 0 : rhs->maybeConstantValue() &&
3771 0 : rhs->maybeConstantValue()->isInt32(0);
3772 : }
3773 :
3774 486 : if (MConstant* defConst = def->maybeConstantValue()) {
3775 2 : *pwrapped = defConst;
3776 2 : return defConst->type() == MIRType::Int32 && defConst->toInt32() >= 0;
3777 : }
3778 :
3779 484 : *pwrapped = nullptr; // silence GCC warning
3780 484 : return false;
3781 : }
3782 :
3783 : /* static */ bool
3784 486 : MBinaryInstruction::unsignedOperands(MDefinition* left, MDefinition* right)
3785 : {
3786 : MDefinition* replace;
3787 486 : if (!MustBeUInt32(left, &replace))
3788 486 : return false;
3789 0 : if (replace->type() != MIRType::Int32)
3790 0 : return false;
3791 0 : if (!MustBeUInt32(right, &replace))
3792 0 : return false;
3793 0 : if (replace->type() != MIRType::Int32)
3794 0 : return false;
3795 0 : return true;
3796 : }
3797 :
3798 : bool
3799 0 : MBinaryInstruction::unsignedOperands()
3800 : {
3801 0 : return unsignedOperands(getOperand(0), getOperand(1));
3802 : }
3803 :
3804 : void
3805 0 : MBinaryInstruction::replaceWithUnsignedOperands()
3806 : {
3807 0 : MOZ_ASSERT(unsignedOperands());
3808 :
3809 0 : for (size_t i = 0; i < numOperands(); i++) {
3810 : MDefinition* replace;
3811 0 : MustBeUInt32(getOperand(i), &replace);
3812 0 : if (replace == getOperand(i))
3813 0 : continue;
3814 :
3815 0 : getOperand(i)->setImplicitlyUsedUnchecked();
3816 0 : replaceOperand(i, replace);
3817 : }
3818 0 : }
3819 :
3820 : MCompare::CompareType
3821 486 : MCompare::determineCompareType(JSOp op, MDefinition* left, MDefinition* right)
3822 : {
3823 486 : MIRType lhs = left->type();
3824 486 : MIRType rhs = right->type();
3825 :
3826 486 : bool looseEq = op == JSOP_EQ || op == JSOP_NE;
3827 486 : bool strictEq = op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
3828 486 : bool relationalEq = !(looseEq || strictEq);
3829 :
3830 : // Comparisons on unsigned integers may be treated as UInt32.
3831 486 : if (unsignedOperands(left, right))
3832 0 : return Compare_UInt32;
3833 :
3834 : // Integer to integer or boolean to boolean comparisons may be treated as Int32.
3835 486 : if ((lhs == MIRType::Int32 && rhs == MIRType::Int32) ||
3836 0 : (lhs == MIRType::Boolean && rhs == MIRType::Boolean))
3837 : {
3838 84 : return Compare_Int32MaybeCoerceBoth;
3839 : }
3840 :
3841 : // Loose/relational cross-integer/boolean comparisons may be treated as Int32.
3842 402 : if (!strictEq &&
3843 162 : (lhs == MIRType::Int32 || lhs == MIRType::Boolean) &&
3844 107 : (rhs == MIRType::Int32 || rhs == MIRType::Boolean))
3845 : {
3846 0 : return Compare_Int32MaybeCoerceBoth;
3847 : }
3848 :
3849 : // Numeric comparisons against a double coerce to double.
3850 402 : if (IsTypeRepresentableAsDouble(lhs) && IsTypeRepresentableAsDouble(rhs))
3851 42 : return Compare_Double;
3852 :
3853 : // Any comparison is allowed except strict eq.
3854 360 : if (!strictEq && IsFloatingPointType(rhs) && SafelyCoercesToDouble(left))
3855 0 : return Compare_DoubleMaybeCoerceLHS;
3856 360 : if (!strictEq && IsFloatingPointType(lhs) && SafelyCoercesToDouble(right))
3857 24 : return Compare_DoubleMaybeCoerceRHS;
3858 :
3859 : // Handle object comparison.
3860 336 : if (!relationalEq && lhs == MIRType::Object && rhs == MIRType::Object)
3861 0 : return Compare_Object;
3862 :
3863 : // Handle string comparisons. (Relational string compares are still unsupported).
3864 336 : if (!relationalEq && lhs == MIRType::String && rhs == MIRType::String)
3865 3 : return Compare_String;
3866 :
3867 : // Handle symbol comparisons. (Relaational compare will throw)
3868 333 : if (!relationalEq && lhs == MIRType::Symbol && rhs == MIRType::Symbol)
3869 0 : return Compare_Symbol;
3870 :
3871 : // Handle strict string compare.
3872 333 : if (strictEq && lhs == MIRType::String)
3873 0 : return Compare_StrictString;
3874 333 : if (strictEq && rhs == MIRType::String)
3875 0 : return Compare_StrictString;
3876 :
3877 : // Handle compare with lhs or rhs being Undefined or Null.
3878 333 : if (!relationalEq && IsNullOrUndefined(lhs))
3879 2 : return (lhs == MIRType::Null) ? Compare_Null : Compare_Undefined;
3880 331 : if (!relationalEq && IsNullOrUndefined(rhs))
3881 15 : return (rhs == MIRType::Null) ? Compare_Null : Compare_Undefined;
3882 :
3883 : // Handle strict comparison with lhs/rhs being typed Boolean.
3884 316 : if (strictEq && (lhs == MIRType::Boolean || rhs == MIRType::Boolean)) {
3885 : // bool/bool case got an int32 specialization earlier.
3886 0 : MOZ_ASSERT(!(lhs == MIRType::Boolean && rhs == MIRType::Boolean));
3887 0 : return Compare_Boolean;
3888 : }
3889 :
3890 316 : return Compare_Unknown;
3891 : }
3892 :
3893 : void
3894 269 : MCompare::cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints)
3895 : {
3896 269 : MOZ_ASSERT(operandMightEmulateUndefined());
3897 :
3898 269 : if (getOperand(0)->maybeEmulatesUndefined(constraints))
3899 11 : return;
3900 258 : if (getOperand(1)->maybeEmulatesUndefined(constraints))
3901 0 : return;
3902 :
3903 258 : markNoOperandEmulatesUndefined();
3904 : }
3905 :
3906 : MBitNot*
3907 0 : MBitNot::NewInt32(TempAllocator& alloc, MDefinition* input)
3908 : {
3909 0 : MBitNot* ins = new(alloc) MBitNot(input);
3910 0 : ins->specialization_ = MIRType::Int32;
3911 0 : MOZ_ASSERT(ins->type() == MIRType::Int32);
3912 0 : return ins;
3913 : }
3914 :
3915 : MDefinition*
3916 4 : MBitNot::foldsTo(TempAllocator& alloc)
3917 : {
3918 4 : if (specialization_ != MIRType::Int32)
3919 0 : return this;
3920 :
3921 4 : MDefinition* input = getOperand(0);
3922 :
3923 4 : if (input->isConstant()) {
3924 0 : js::Value v = Int32Value(~(input->toConstant()->toInt32()));
3925 0 : return MConstant::New(alloc, v);
3926 : }
3927 :
3928 4 : if (input->isBitNot() && input->toBitNot()->specialization_ == MIRType::Int32) {
3929 2 : MOZ_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType::Int32);
3930 2 : return MTruncateToInt32::New(alloc, input->toBitNot()->input()); // ~~x => x | 0
3931 : }
3932 :
3933 2 : return this;
3934 : }
3935 :
3936 : MDefinition*
3937 0 : MTypeOf::foldsTo(TempAllocator& alloc)
3938 : {
3939 : // Note: we can't use input->type() here, type analysis has
3940 : // boxed the input.
3941 0 : MOZ_ASSERT(input()->type() == MIRType::Value);
3942 :
3943 : JSType type;
3944 :
3945 0 : switch (inputType()) {
3946 : case MIRType::Double:
3947 : case MIRType::Float32:
3948 : case MIRType::Int32:
3949 0 : type = JSTYPE_NUMBER;
3950 0 : break;
3951 : case MIRType::String:
3952 0 : type = JSTYPE_STRING;
3953 0 : break;
3954 : case MIRType::Symbol:
3955 0 : type = JSTYPE_SYMBOL;
3956 0 : break;
3957 : case MIRType::Null:
3958 0 : type = JSTYPE_OBJECT;
3959 0 : break;
3960 : case MIRType::Undefined:
3961 0 : type = JSTYPE_UNDEFINED;
3962 0 : break;
3963 : case MIRType::Boolean:
3964 0 : type = JSTYPE_BOOLEAN;
3965 0 : break;
3966 : case MIRType::Object:
3967 0 : if (!inputMaybeCallableOrEmulatesUndefined()) {
3968 : // Object is not callable and does not emulate undefined, so it's
3969 : // safe to fold to "object".
3970 0 : type = JSTYPE_OBJECT;
3971 0 : break;
3972 : }
3973 : MOZ_FALLTHROUGH;
3974 : default:
3975 0 : return this;
3976 : }
3977 :
3978 0 : return MConstant::New(alloc, StringValue(TypeName(type, GetJitContext()->runtime->names())));
3979 : }
3980 :
3981 : void
3982 3 : MTypeOf::cacheInputMaybeCallableOrEmulatesUndefined(CompilerConstraintList* constraints)
3983 : {
3984 3 : MOZ_ASSERT(inputMaybeCallableOrEmulatesUndefined());
3985 :
3986 3 : if (!input()->maybeEmulatesUndefined(constraints) && !MaybeCallable(constraints, input()))
3987 3 : markInputNotCallableOrEmulatesUndefined();
3988 3 : }
3989 :
3990 : MBitAnd*
3991 0 : MBitAnd::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
3992 : {
3993 0 : return new(alloc) MBitAnd(left, right, MIRType::Int32);
3994 : }
3995 :
3996 : MBitAnd*
3997 0 : MBitAnd::New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
3998 : {
3999 0 : MBitAnd* ins = new(alloc) MBitAnd(left, right, type);
4000 0 : ins->specializeAs(type);
4001 0 : return ins;
4002 : }
4003 :
4004 : MBitOr*
4005 2 : MBitOr::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
4006 : {
4007 2 : return new(alloc) MBitOr(left, right, MIRType::Int32);
4008 : }
4009 :
4010 : MBitOr*
4011 0 : MBitOr::New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
4012 : {
4013 0 : MBitOr* ins = new(alloc) MBitOr(left, right, type);
4014 0 : ins->specializeAs(type);
4015 0 : return ins;
4016 : }
4017 :
4018 : MBitXor*
4019 0 : MBitXor::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
4020 : {
4021 0 : return new(alloc) MBitXor(left, right, MIRType::Int32);
4022 : }
4023 :
4024 : MBitXor*
4025 0 : MBitXor::New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
4026 : {
4027 0 : MBitXor* ins = new(alloc) MBitXor(left, right, type);
4028 0 : ins->specializeAs(type);
4029 0 : return ins;
4030 : }
4031 :
4032 : MLsh*
4033 0 : MLsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
4034 : {
4035 0 : return new(alloc) MLsh(left, right, MIRType::Int32);
4036 : }
4037 :
4038 : MLsh*
4039 0 : MLsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
4040 : {
4041 0 : MLsh* ins = new(alloc) MLsh(left, right, type);
4042 0 : ins->specializeAs(type);
4043 0 : return ins;
4044 : }
4045 :
4046 : MRsh*
4047 0 : MRsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
4048 : {
4049 0 : return new(alloc) MRsh(left, right, MIRType::Int32);
4050 : }
4051 :
4052 : MRsh*
4053 0 : MRsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
4054 : {
4055 0 : MRsh* ins = new(alloc) MRsh(left, right, type);
4056 0 : ins->specializeAs(type);
4057 0 : return ins;
4058 : }
4059 :
4060 : MUrsh*
4061 0 : MUrsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right)
4062 : {
4063 0 : return new(alloc) MUrsh(left, right, MIRType::Int32);
4064 : }
4065 :
4066 : MUrsh*
4067 0 : MUrsh::New(TempAllocator& alloc, MDefinition* left, MDefinition* right, MIRType type)
4068 : {
4069 0 : MUrsh* ins = new(alloc) MUrsh(left, right, type);
4070 0 : ins->specializeAs(type);
4071 :
4072 : // Since Ion has no UInt32 type, we use Int32 and we have a special
4073 : // exception to the type rules: we can return values in
4074 : // (INT32_MIN,UINT32_MAX] and still claim that we have an Int32 type
4075 : // without bailing out. This is necessary because Ion has no UInt32
4076 : // type and we can't have bailouts in wasm code.
4077 0 : ins->bailoutsDisabled_ = true;
4078 :
4079 0 : return ins;
4080 : }
4081 :
4082 : MResumePoint*
4083 4503 : MResumePoint::New(TempAllocator& alloc, MBasicBlock* block, jsbytecode* pc,
4084 : Mode mode)
4085 : {
4086 4503 : MResumePoint* resume = new(alloc) MResumePoint(block, pc, mode);
4087 4503 : if (!resume->init(alloc)) {
4088 0 : block->discardPreAllocatedResumePoint(resume);
4089 0 : return nullptr;
4090 : }
4091 4503 : resume->inherit(block);
4092 4503 : return resume;
4093 : }
4094 :
4095 : MResumePoint*
4096 0 : MResumePoint::New(TempAllocator& alloc, MBasicBlock* block, MResumePoint* model,
4097 : const MDefinitionVector& operands)
4098 : {
4099 0 : MResumePoint* resume = new(alloc) MResumePoint(block, model->pc(), model->mode());
4100 :
4101 : // Allocate the same number of operands as the original resume point, and
4102 : // copy operands from the operands vector and not the not from the current
4103 : // block stack.
4104 0 : if (!resume->operands_.init(alloc, model->numAllocatedOperands())) {
4105 0 : block->discardPreAllocatedResumePoint(resume);
4106 0 : return nullptr;
4107 : }
4108 :
4109 : // Copy the operands.
4110 0 : for (size_t i = 0; i < operands.length(); i++)
4111 0 : resume->initOperand(i, operands[i]);
4112 :
4113 0 : return resume;
4114 : }
4115 :
4116 : MResumePoint*
4117 533 : MResumePoint::Copy(TempAllocator& alloc, MResumePoint* src)
4118 : {
4119 533 : MResumePoint* resume = new(alloc) MResumePoint(src->block(), src->pc(),
4120 533 : src->mode());
4121 : // Copy the operands from the original resume point, and not from the
4122 : // current block stack.
4123 533 : if (!resume->operands_.init(alloc, src->numAllocatedOperands())) {
4124 0 : src->block()->discardPreAllocatedResumePoint(resume);
4125 0 : return nullptr;
4126 : }
4127 :
4128 : // Copy the operands.
4129 7422 : for (size_t i = 0; i < resume->numOperands(); i++)
4130 6889 : resume->initOperand(i, src->getOperand(i));
4131 533 : return resume;
4132 : }
4133 :
4134 8996 : MResumePoint::MResumePoint(MBasicBlock* block, jsbytecode* pc, Mode mode)
4135 : : MNode(block),
4136 : pc_(pc),
4137 : instruction_(nullptr),
4138 8996 : mode_(mode)
4139 : {
4140 8996 : block->addResumePoint(this);
4141 8996 : }
4142 :
4143 : bool
4144 8463 : MResumePoint::init(TempAllocator& alloc)
4145 : {
4146 8463 : return operands_.init(alloc, block()->stackDepth());
4147 : }
4148 :
4149 : MResumePoint*
4150 751 : MResumePoint::caller() const
4151 : {
4152 751 : return block_->callerResumePoint();
4153 : }
4154 :
4155 : void
4156 4503 : MResumePoint::inherit(MBasicBlock* block)
4157 : {
4158 : // FixedList doesn't initialize its elements, so do unchecked inits.
4159 62732 : for (size_t i = 0; i < stackDepth(); i++)
4160 58229 : initOperand(i, block->getSlot(i));
4161 4503 : }
4162 :
4163 : void
4164 658 : MResumePoint::addStore(TempAllocator& alloc, MDefinition* store, const MResumePoint* cache)
4165 : {
4166 658 : MOZ_ASSERT(block()->outerResumePoint() != this);
4167 658 : MOZ_ASSERT_IF(cache, !cache->stores_.empty());
4168 :
4169 658 : if (cache && cache->stores_.begin()->operand == store) {
4170 : // If the last resume point had the same side-effect stack, then we can
4171 : // reuse the current side effect without cloning it. This is a simple
4172 : // way to share common context by making a spaghetti stack.
4173 583 : if (++cache->stores_.begin() == stores_.begin()) {
4174 571 : stores_.copy(cache->stores_);
4175 571 : return;
4176 : }
4177 : }
4178 :
4179 : // Ensure that the store would not be deleted by DCE.
4180 87 : MOZ_ASSERT(store->isEffectful());
4181 :
4182 87 : MStoreToRecover* top = new(alloc) MStoreToRecover(store);
4183 87 : stores_.push(top);
4184 : }
4185 :
4186 : void
4187 0 : MResumePoint::dump(GenericPrinter& out) const
4188 : {
4189 0 : out.printf("resumepoint mode=");
4190 :
4191 0 : switch (mode()) {
4192 : case MResumePoint::ResumeAt:
4193 0 : if (instruction_)
4194 0 : out.printf("At(%d)", instruction_->id());
4195 : else
4196 0 : out.printf("At");
4197 0 : break;
4198 : case MResumePoint::ResumeAfter:
4199 0 : out.printf("After");
4200 0 : break;
4201 : case MResumePoint::Outer:
4202 0 : out.printf("Outer");
4203 0 : break;
4204 : }
4205 :
4206 0 : if (MResumePoint* c = caller())
4207 0 : out.printf(" (caller in block%u)", c->block()->id());
4208 :
4209 0 : for (size_t i = 0; i < numOperands(); i++) {
4210 0 : out.printf(" ");
4211 0 : if (operands_[i].hasProducer())
4212 0 : getOperand(i)->printName(out);
4213 : else
4214 0 : out.printf("(null)");
4215 : }
4216 0 : out.printf("\n");
4217 0 : }
4218 :
4219 : void
4220 0 : MResumePoint::dump() const
4221 : {
4222 0 : Fprinter out(stderr);
4223 0 : dump(out);
4224 0 : out.finish();
4225 0 : }
4226 :
4227 : bool
4228 9514 : MResumePoint::isObservableOperand(MUse* u) const
4229 : {
4230 9514 : return isObservableOperand(indexOf(u));
4231 : }
4232 :
4233 : bool
4234 9762 : MResumePoint::isObservableOperand(size_t index) const
4235 : {
4236 9762 : return block()->info().isObservableSlot(index);
4237 : }
4238 :
4239 : bool
4240 3957 : MResumePoint::isRecoverableOperand(MUse* u) const
4241 : {
4242 3957 : return block()->info().isRecoverableOperand(indexOf(u));
4243 : }
4244 :
4245 : MDefinition*
4246 14 : MToInt32::foldsTo(TempAllocator& alloc)
4247 : {
4248 14 : MDefinition* input = getOperand(0);
4249 :
4250 : // Fold this operation if the input operand is constant.
4251 14 : if (input->isConstant()) {
4252 1 : DebugOnly<MacroAssembler::IntConversionInputKind> convert = conversion();
4253 1 : switch (input->type()) {
4254 : case MIRType::Null:
4255 0 : MOZ_ASSERT(convert == MacroAssembler::IntConversion_Any);
4256 0 : return MConstant::New(alloc, Int32Value(0));
4257 : case MIRType::Boolean:
4258 0 : MOZ_ASSERT(convert == MacroAssembler::IntConversion_Any ||
4259 : convert == MacroAssembler::IntConversion_NumbersOrBoolsOnly);
4260 0 : return MConstant::New(alloc, Int32Value(input->toConstant()->toBoolean()));
4261 : case MIRType::Int32:
4262 1 : return MConstant::New(alloc, Int32Value(input->toConstant()->toInt32()));
4263 : case MIRType::Float32:
4264 : case MIRType::Double:
4265 : int32_t ival;
4266 : // Only the value within the range of Int32 can be substituted as constant.
4267 0 : if (mozilla::NumberIsInt32(input->toConstant()->numberToDouble(), &ival))
4268 0 : return MConstant::New(alloc, Int32Value(ival));
4269 0 : break;
4270 : default:
4271 0 : break;
4272 : }
4273 : }
4274 :
4275 : // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4276 : // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4277 : // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4278 : // is folded to a MTruncateToInt32 node, which will result in this MIR:
4279 : // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4280 : // the MUrsh node's type is int32 (since uint32 is not implemented), and
4281 : // that would fold the MTruncateToInt32 node. This will make the modulo
4282 : // unsigned, while is should have been signed.
4283 13 : if (input->type() == MIRType::Int32 && !IsUint32Type(input))
4284 11 : return input;
4285 :
4286 2 : return this;
4287 : }
4288 :
4289 : void
4290 1 : MToInt32::analyzeEdgeCasesBackward()
4291 : {
4292 1 : if (!NeedNegativeZeroCheck(this))
4293 1 : setCanBeNegativeZero(false);
4294 1 : }
4295 :
4296 : MDefinition*
4297 6 : MTruncateToInt32::foldsTo(TempAllocator& alloc)
4298 : {
4299 6 : MDefinition* input = getOperand(0);
4300 6 : if (input->isBox())
4301 0 : input = input->getOperand(0);
4302 :
4303 : // Do not fold the TruncateToInt32 node when the input is uint32 (e.g. ursh
4304 : // with a zero constant. Consider the test jit-test/tests/ion/bug1247880.js,
4305 : // where the relevant code is: |(imul(1, x >>> 0) % 2)|. The imul operator
4306 : // is folded to a MTruncateToInt32 node, which will result in this MIR:
4307 : // MMod(MTruncateToInt32(MUrsh(x, MConstant(0))), MConstant(2)). Note that
4308 : // the MUrsh node's type is int32 (since uint32 is not implemented), and
4309 : // that would fold the MTruncateToInt32 node. This will make the modulo
4310 : // unsigned, while is should have been signed.
4311 6 : if (input->type() == MIRType::Int32 && !IsUint32Type(input))
4312 2 : return input;
4313 :
4314 4 : if (input->type() == MIRType::Double && input->isConstant()) {
4315 0 : int32_t ret = ToInt32(input->toConstant()->toDouble());
4316 0 : return MConstant::New(alloc, Int32Value(ret));
4317 : }
4318 :
4319 4 : return this;
4320 : }
4321 :
4322 : MDefinition*
4323 0 : MWasmTruncateToInt32::foldsTo(TempAllocator& alloc)
4324 : {
4325 0 : MDefinition* input = getOperand(0);
4326 0 : if (input->type() == MIRType::Int32)
4327 0 : return input;
4328 :
4329 0 : if (input->type() == MIRType::Double && input->isConstant()) {
4330 0 : double d = input->toConstant()->toDouble();
4331 0 : if (IsNaN(d))
4332 0 : return this;
4333 :
4334 0 : if (!isUnsigned_ && d <= double(INT32_MAX) && d >= double(INT32_MIN))
4335 0 : return MConstant::New(alloc, Int32Value(ToInt32(d)));
4336 :
4337 0 : if (isUnsigned_ && d <= double(UINT32_MAX) && d >= 0)
4338 0 : return MConstant::New(alloc, Int32Value(ToInt32(d)));
4339 : }
4340 :
4341 0 : if (input->type() == MIRType::Float32 && input->isConstant()) {
4342 0 : double f = double(input->toConstant()->toFloat32());
4343 0 : if (IsNaN(f))
4344 0 : return this;
4345 :
4346 0 : if (!isUnsigned_ && f <= double(INT32_MAX) && f >= double(INT32_MIN))
4347 0 : return MConstant::New(alloc, Int32Value(ToInt32(f)));
4348 :
4349 0 : if (isUnsigned_ && f <= double(UINT32_MAX) && f >= 0)
4350 0 : return MConstant::New(alloc, Int32Value(ToInt32(f)));
4351 : }
4352 :
4353 0 : return this;
4354 : }
4355 :
4356 : MDefinition*
4357 0 : MWrapInt64ToInt32::foldsTo(TempAllocator& alloc)
4358 : {
4359 0 : MDefinition* input = this->input();
4360 0 : if (input->isConstant()) {
4361 0 : uint64_t c = input->toConstant()->toInt64();
4362 0 : int32_t output = bottomHalf() ? int32_t(c) : int32_t(c >> 32);
4363 0 : return MConstant::New(alloc, Int32Value(output));
4364 : }
4365 :
4366 0 : return this;
4367 : }
4368 :
4369 : MDefinition*
4370 0 : MExtendInt32ToInt64::foldsTo(TempAllocator& alloc)
4371 : {
4372 0 : MDefinition* input = this->input();
4373 0 : if (input->isConstant()) {
4374 0 : int32_t c = input->toConstant()->toInt32();
4375 0 : int64_t res = isUnsigned() ? int64_t(uint32_t(c)) : int64_t(c);
4376 0 : return MConstant::NewInt64(alloc, res);
4377 : }
4378 :
4379 0 : return this;
4380 : }
4381 :
4382 : MDefinition*
4383 0 : MToDouble::foldsTo(TempAllocator& alloc)
4384 : {
4385 0 : MDefinition* input = getOperand(0);
4386 0 : if (input->isBox())
4387 0 : input = input->getOperand(0);
4388 :
4389 0 : if (input->type() == MIRType::Double)
4390 0 : return input;
4391 :
4392 0 : if (input->isConstant() && input->toConstant()->isTypeRepresentableAsDouble())
4393 0 : return MConstant::New(alloc, DoubleValue(input->toConstant()->numberToDouble()));
4394 :
4395 0 : return this;
4396 : }
4397 :
4398 : MDefinition*
4399 0 : MToFloat32::foldsTo(TempAllocator& alloc)
4400 : {
4401 0 : MDefinition* input = getOperand(0);
4402 0 : if (input->isBox())
4403 0 : input = input->getOperand(0);
4404 :
4405 0 : if (input->type() == MIRType::Float32)
4406 0 : return input;
4407 :
4408 : // If x is a Float32, Float32(Double(x)) == x
4409 0 : if (!mustPreserveNaN_ &&
4410 0 : input->isToDouble() &&
4411 0 : input->toToDouble()->input()->type() == MIRType::Float32)
4412 : {
4413 0 : return input->toToDouble()->input();
4414 : }
4415 :
4416 0 : if (input->isConstant() && input->toConstant()->isTypeRepresentableAsDouble())
4417 0 : return MConstant::NewFloat32(alloc, float(input->toConstant()->numberToDouble()));
4418 :
4419 0 : return this;
4420 : }
4421 :
4422 : MDefinition*
4423 8 : MToString::foldsTo(TempAllocator& alloc)
4424 : {
4425 8 : MDefinition* in = input();
4426 8 : if (in->isBox())
4427 0 : in = in->getOperand(0);
4428 :
4429 8 : if (in->type() == MIRType::String)
4430 0 : return in;
4431 8 : return this;
4432 : }
4433 :
4434 : MDefinition*
4435 0 : MClampToUint8::foldsTo(TempAllocator& alloc)
4436 : {
4437 0 : if (MConstant* inputConst = input()->maybeConstantValue()) {
4438 0 : if (inputConst->isTypeRepresentableAsDouble()) {
4439 0 : int32_t clamped = ClampDoubleToUint8(inputConst->numberToDouble());
4440 0 : return MConstant::New(alloc, Int32Value(clamped));
4441 : }
4442 : }
4443 0 : return this;
4444 : }
4445 :
4446 : bool
4447 158 : MCompare::tryFoldEqualOperands(bool* result)
4448 : {
4449 158 : if (lhs() != rhs())
4450 154 : return false;
4451 :
4452 : // Intuitively somebody would think that if lhs == rhs,
4453 : // then we can just return true. (Or false for !==)
4454 : // However NaN !== NaN is true! So we spend some time trying
4455 : // to eliminate this case.
4456 :
4457 4 : if (jsop() != JSOP_STRICTEQ && jsop() != JSOP_STRICTNE)
4458 1 : return false;
4459 :
4460 3 : if (compareType_ == Compare_Unknown)
4461 0 : return false;
4462 :
4463 3 : MOZ_ASSERT(compareType_ == Compare_Undefined || compareType_ == Compare_Null ||
4464 : compareType_ == Compare_Boolean || compareType_ == Compare_Int32 ||
4465 : compareType_ == Compare_Int32MaybeCoerceBoth ||
4466 : compareType_ == Compare_Int32MaybeCoerceLHS ||
4467 : compareType_ == Compare_Int32MaybeCoerceRHS || compareType_ == Compare_UInt32 ||
4468 : compareType_ == Compare_Double || compareType_ == Compare_DoubleMaybeCoerceLHS ||
4469 : compareType_ == Compare_DoubleMaybeCoerceRHS || compareType_ == Compare_Float32 ||
4470 : compareType_ == Compare_String || compareType_ == Compare_StrictString ||
4471 : compareType_ == Compare_Object || compareType_ == Compare_Bitwise ||
4472 : compareType_ == Compare_Symbol);
4473 :
4474 3 : if (isDoubleComparison() || isFloat32Comparison()) {
4475 0 : if (!operandsAreNeverNaN())
4476 0 : return false;
4477 : }
4478 :
4479 3 : lhs()->setGuardRangeBailoutsUnchecked();
4480 :
4481 3 : *result = (jsop() == JSOP_STRICTEQ);
4482 3 : return true;
4483 : }
4484 :
4485 : bool
4486 155 : MCompare::tryFoldTypeOf(bool* result)
4487 : {
4488 155 : if (!lhs()->isTypeOf() && !rhs()->isTypeOf())
4489 155 : return false;
4490 0 : if (!lhs()->isConstant() && !rhs()->isConstant())
4491 0 : return false;
4492 :
4493 0 : MTypeOf* typeOf = lhs()->isTypeOf() ? lhs()->toTypeOf() : rhs()->toTypeOf();
4494 0 : MConstant* constant = lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant();
4495 :
4496 0 : if (constant->type() != MIRType::String)
4497 0 : return false;
4498 :
4499 0 : if (jsop() != JSOP_STRICTEQ && jsop() != JSOP_STRICTNE &&
4500 0 : jsop() != JSOP_EQ && jsop() != JSOP_NE)
4501 : {
4502 0 : return false;
4503 : }
4504 :
4505 0 : const JSAtomState& names = GetJitContext()->runtime->names();
4506 0 : if (constant->toString() == TypeName(JSTYPE_UNDEFINED, names)) {
4507 0 : if (!typeOf->input()->mightBeType(MIRType::Undefined) &&
4508 0 : !typeOf->inputMaybeCallableOrEmulatesUndefined())
4509 : {
4510 0 : *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
4511 0 : return true;
4512 : }
4513 0 : } else if (constant->toString() == TypeName(JSTYPE_BOOLEAN, names)) {
4514 0 : if (!typeOf->input()->mightBeType(MIRType::Boolean)) {
4515 0 : *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
4516 0 : return true;
4517 : }
4518 0 : } else if (constant->toString() == TypeName(JSTYPE_NUMBER, names)) {
4519 0 : if (!typeOf->input()->mightBeType(MIRType::Int32) &&
4520 0 : !typeOf->input()->mightBeType(MIRType::Float32) &&
4521 0 : !typeOf->input()->mightBeType(MIRType::Double))
4522 : {
4523 0 : *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
4524 0 : return true;
4525 : }
4526 0 : } else if (constant->toString() == TypeName(JSTYPE_STRING, names)) {
4527 0 : if (!typeOf->input()->mightBeType(MIRType::String)) {
4528 0 : *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
4529 0 : return true;
4530 : }
4531 0 : } else if (constant->toString() == TypeName(JSTYPE_SYMBOL, names)) {
4532 0 : if (!typeOf->input()->mightBeType(MIRType::Symbol)) {
4533 0 : *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
4534 0 : return true;
4535 : }
4536 0 : } else if (constant->toString() == TypeName(JSTYPE_OBJECT, names)) {
4537 0 : if (!typeOf->input()->mightBeType(MIRType::Object) &&
4538 0 : !typeOf->input()->mightBeType(MIRType::Null))
4539 : {
4540 0 : *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
4541 0 : return true;
4542 : }
4543 0 : } else if (constant->toString() == TypeName(JSTYPE_FUNCTION, names)) {
4544 0 : if (!typeOf->inputMaybeCallableOrEmulatesUndefined()) {
4545 0 : *result = (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE);
4546 0 : return true;
4547 : }
4548 : }
4549 :
4550 0 : return false;
4551 : }
4552 :
4553 : bool
4554 158 : MCompare::tryFold(bool* result)
4555 : {
4556 158 : JSOp op = jsop();
4557 :
4558 158 : if (tryFoldEqualOperands(result))
4559 3 : return true;
4560 :
4561 155 : if (tryFoldTypeOf(result))
4562 0 : return true;
4563 :
4564 155 : if (compareType_ == Compare_Null || compareType_ == Compare_Undefined) {
4565 : // The LHS is the value we want to test against null or undefined.
4566 5 : if (op == JSOP_STRICTEQ || op == JSOP_STRICTNE) {
4567 4 : if (lhs()->type() == inputType()) {
4568 0 : *result = (op == JSOP_STRICTEQ);
4569 0 : return true;
4570 : }
4571 4 : if (!lhs()->mightBeType(inputType())) {
4572 4 : *result = (op == JSOP_STRICTNE);
4573 4 : return true;
4574 : }
4575 : } else {
4576 1 : MOZ_ASSERT(op == JSOP_EQ || op == JSOP_NE);
4577 1 : if (IsNullOrUndefined(lhs()->type())) {
4578 1 : *result = (op == JSOP_EQ);
4579 1 : return true;
4580 : }
4581 0 : if (!lhs()->mightBeType(MIRType::Null) &&
4582 0 : !lhs()->mightBeType(MIRType::Undefined) &&
4583 0 : !(lhs()->mightBeType(MIRType::Object) && operandMightEmulateUndefined()))
4584 : {
4585 0 : *result = (op == JSOP_NE);
4586 0 : return true;
4587 : }
4588 : }
4589 0 : return false;
4590 : }
4591 :
4592 150 : if (compareType_ == Compare_Boolean) {
4593 0 : MOZ_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
4594 0 : MOZ_ASSERT(rhs()->type() == MIRType::Boolean);
4595 0 : MOZ_ASSERT(lhs()->type() != MIRType::Boolean, "Should use Int32 comparison");
4596 :
4597 0 : if (!lhs()->mightBeType(MIRType::Boolean)) {
4598 0 : *result = (op == JSOP_STRICTNE);
4599 0 : return true;
4600 : }
4601 0 : return false;
4602 : }
4603 :
4604 150 : if (compareType_ == Compare_StrictString) {
4605 0 : MOZ_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
4606 0 : MOZ_ASSERT(rhs()->type() == MIRType::String);
4607 0 : MOZ_ASSERT(lhs()->type() != MIRType::String, "Should use String comparison");
4608 :
4609 0 : if (!lhs()->mightBeType(MIRType::String)) {
4610 0 : *result = (op == JSOP_STRICTNE);
4611 0 : return true;
4612 : }
4613 0 : return false;
4614 : }
4615 :
4616 150 : return false;
4617 : }
4618 :
4619 : template <typename T>
4620 : static bool
4621 1 : FoldComparison(JSOp op, T left, T right)
4622 : {
4623 1 : switch (op) {
4624 0 : case JSOP_LT: return left < right;
4625 0 : case JSOP_LE: return left <= right;
4626 0 : case JSOP_GT: return left > right;
4627 0 : case JSOP_GE: return left >= right;
4628 1 : case JSOP_STRICTEQ: case JSOP_EQ: return left == right;
4629 0 : case JSOP_STRICTNE: case JSOP_NE: return left != right;
4630 0 : default: MOZ_CRASH("Unexpected op.");
4631 : }
4632 : }
4633 :
4634 : bool
4635 82 : MCompare::evaluateConstantOperands(TempAllocator& alloc, bool* result)
4636 : {
4637 82 : if (type() != MIRType::Boolean && type() != MIRType::Int32)
4638 0 : return false;
4639 :
4640 82 : MDefinition* left = getOperand(0);
4641 82 : MDefinition* right = getOperand(1);
4642 :
4643 82 : if (compareType() == Compare_Double) {
4644 : // Optimize "MCompare MConstant (MToDouble SomethingInInt32Range).
4645 : // In most cases the MToDouble was added, because the constant is
4646 : // a double.
4647 : // e.g. v < 9007199254740991, where v is an int32 is always true.
4648 0 : if (!lhs()->isConstant() && !rhs()->isConstant())
4649 0 : return false;
4650 :
4651 0 : MDefinition* operand = left->isConstant() ? right : left;
4652 0 : MConstant* constant = left->isConstant() ? left->toConstant() : right->toConstant();
4653 0 : MOZ_ASSERT(constant->type() == MIRType::Double);
4654 0 : double cte = constant->toDouble();
4655 :
4656 0 : if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType::Int32) {
4657 0 : bool replaced = false;
4658 0 : switch (jsop_) {
4659 : case JSOP_LT:
4660 0 : if (cte > INT32_MAX || cte < INT32_MIN) {
4661 0 : *result = !((constant == lhs()) ^ (cte < INT32_MIN));
4662 0 : replaced = true;
4663 : }
4664 0 : break;
4665 : case JSOP_LE:
4666 0 : if (constant == lhs()) {
4667 0 : if (cte > INT32_MAX || cte <= INT32_MIN) {
4668 0 : *result = (cte <= INT32_MIN);
4669 0 : replaced = true;
4670 : }
4671 : } else {
4672 0 : if (cte >= INT32_MAX || cte < INT32_MIN) {
4673 0 : *result = (cte >= INT32_MIN);
4674 0 : replaced = true;
4675 : }
4676 : }
4677 0 : break;
4678 : case JSOP_GT:
4679 0 : if (cte > INT32_MAX || cte < INT32_MIN) {
4680 0 : *result = !((constant == rhs()) ^ (cte < INT32_MIN));
4681 0 : replaced = true;
4682 : }
4683 0 : break;
4684 : case JSOP_GE:
4685 0 : if (constant == lhs()) {
4686 0 : if (cte >= INT32_MAX || cte < INT32_MIN) {
4687 0 : *result = (cte >= INT32_MAX);
4688 0 : replaced = true;
4689 : }
4690 : } else {
4691 0 : if (cte > INT32_MAX || cte <= INT32_MIN) {
4692 0 : *result = (cte <= INT32_MIN);
4693 0 : replaced = true;
4694 : }
4695 : }
4696 0 : break;
4697 : case JSOP_STRICTEQ: // Fall through.
4698 : case JSOP_EQ:
4699 0 : if (cte > INT32_MAX || cte < INT32_MIN) {
4700 0 : *result = false;
4701 0 : replaced = true;
4702 : }
4703 0 : break;
4704 : case JSOP_STRICTNE: // Fall through.
4705 : case JSOP_NE:
4706 0 : if (cte > INT32_MAX || cte < INT32_MIN) {
4707 0 : *result = true;
4708 0 : replaced = true;
4709 : }
4710 0 : break;
4711 : default:
4712 0 : MOZ_CRASH("Unexpected op.");
4713 : }
4714 0 : if (replaced) {
4715 : MLimitedTruncate* limit =
4716 0 : MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate);
4717 0 : limit->setGuardUnchecked();
4718 0 : block()->insertBefore(this, limit);
4719 0 : return true;
4720 : }
4721 : }
4722 : }
4723 :
4724 82 : if (!left->isConstant() || !right->isConstant())
4725 81 : return false;
4726 :
4727 1 : MConstant* lhs = left->toConstant();
4728 1 : MConstant* rhs = right->toConstant();
4729 :
4730 : // Fold away some String equality comparisons.
4731 1 : if (lhs->type() == MIRType::String && rhs->type() == MIRType::String) {
4732 0 : int32_t comp = 0; // Default to equal.
4733 0 : if (left != right)
4734 0 : comp = CompareAtoms(&lhs->toString()->asAtom(), &rhs->toString()->asAtom());
4735 0 : *result = FoldComparison(jsop_, comp, 0);
4736 0 : return true;
4737 : }
4738 :
4739 1 : if (compareType_ == Compare_UInt32) {
4740 0 : *result = FoldComparison(jsop_, uint32_t(lhs->toInt32()), uint32_t(rhs->toInt32()));
4741 0 : return true;
4742 : }
4743 :
4744 1 : if (compareType_ == Compare_Int64) {
4745 0 : *result = FoldComparison(jsop_, lhs->toInt64(), rhs->toInt64());
4746 0 : return true;
4747 : }
4748 :
4749 1 : if (compareType_ == Compare_UInt64) {
4750 0 : *result = FoldComparison(jsop_, uint64_t(lhs->toInt64()), uint64_t(rhs->toInt64()));
4751 0 : return true;
4752 : }
4753 :
4754 1 : if (lhs->isTypeRepresentableAsDouble() && rhs->isTypeRepresentableAsDouble()) {
4755 1 : *result = FoldComparison(jsop_, lhs->numberToDouble(), rhs->numberToDouble());
4756 1 : return true;
4757 : }
4758 :
4759 0 : return false;
4760 : }
4761 :
4762 : MDefinition*
4763 90 : MCompare::foldsTo(TempAllocator& alloc)
4764 : {
4765 : bool result;
4766 :
4767 90 : if (tryFold(&result) || evaluateConstantOperands(alloc, &result)) {
4768 9 : if (type() == MIRType::Int32)
4769 0 : return MConstant::New(alloc, Int32Value(result));
4770 :
4771 9 : MOZ_ASSERT(type() == MIRType::Boolean);
4772 9 : return MConstant::New(alloc, BooleanValue(result));
4773 : }
4774 :
4775 81 : return this;
4776 : }
4777 :
4778 : void
4779 0 : MCompare::trySpecializeFloat32(TempAllocator& alloc)
4780 : {
4781 0 : MDefinition* lhs = getOperand(0);
4782 0 : MDefinition* rhs = getOperand(1);
4783 :
4784 0 : if (lhs->canProduceFloat32() && rhs->canProduceFloat32() && compareType_ == Compare_Double) {
4785 0 : compareType_ = Compare_Float32;
4786 : } else {
4787 0 : if (lhs->type() == MIRType::Float32)
4788 0 : ConvertDefinitionToDouble<0>(alloc, lhs, this);
4789 0 : if (rhs->type() == MIRType::Float32)
4790 0 : ConvertDefinitionToDouble<1>(alloc, rhs, this);
4791 : }
4792 0 : }
4793 :
4794 : void
4795 0 : MCompare::filtersUndefinedOrNull(bool trueBranch, MDefinition** subject, bool* filtersUndefined,
4796 : bool* filtersNull)
4797 : {
4798 0 : *filtersNull = *filtersUndefined = false;
4799 0 : *subject = nullptr;
4800 :
4801 0 : if (compareType() != Compare_Undefined && compareType() != Compare_Null)
4802 0 : return;
4803 :
4804 0 : MOZ_ASSERT(jsop() == JSOP_STRICTNE || jsop() == JSOP_NE ||
4805 : jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ);
4806 :
4807 : // JSOP_*NE only removes undefined/null from if/true branch
4808 0 : if (!trueBranch && (jsop() == JSOP_STRICTNE || jsop() == JSOP_NE))
4809 0 : return;
4810 :
4811 : // JSOP_*EQ only removes undefined/null from else/false branch
4812 0 : if (trueBranch && (jsop() == JSOP_STRICTEQ || jsop() == JSOP_EQ))
4813 0 : return;
4814 :
4815 0 : if (jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE) {
4816 0 : *filtersUndefined = compareType() == Compare_Undefined;
4817 0 : *filtersNull = compareType() == Compare_Null;
4818 : } else {
4819 0 : *filtersUndefined = *filtersNull = true;
4820 : }
4821 :
4822 0 : *subject = lhs();
4823 : }
4824 :
4825 : void
4826 127 : MNot::cacheOperandMightEmulateUndefined(CompilerConstraintList* constraints)
4827 : {
4828 127 : MOZ_ASSERT(operandMightEmulateUndefined());
4829 :
4830 127 : if (!getOperand(0)->maybeEmulatesUndefined(constraints))
4831 127 : markNoOperandEmulatesUndefined();
4832 127 : }
4833 :
4834 : MDefinition*
4835 21 : MNot::foldsTo(TempAllocator& alloc)
4836 : {
4837 : // Fold if the input is constant
4838 21 : if (MConstant* inputConst = input()->maybeConstantValue()) {
4839 : bool b;
4840 11 : if (inputConst->valueToBoolean(&b)) {
4841 11 : if (type() == MIRType::Int32 || type() == MIRType::Int64)
4842 0 : return MConstant::New(alloc, Int32Value(!b));
4843 11 : return MConstant::New(alloc, BooleanValue(!b));
4844 : }
4845 : }
4846 :
4847 : // If the operand of the Not is itself a Not, they cancel out. But we can't
4848 : // always convert Not(Not(x)) to x because that may loose the conversion to
4849 : // boolean. We can simplify Not(Not(Not(x))) to Not(x) though.
4850 10 : MDefinition* op = getOperand(0);
4851 10 : if (op->isNot()) {
4852 0 : MDefinition* opop = op->getOperand(0);
4853 0 : if (opop->isNot())
4854 0 : return opop;
4855 : }
4856 :
4857 : // NOT of an undefined or null value is always true
4858 10 : if (input()->type() == MIRType::Undefined || input()->type() == MIRType::Null)
4859 0 : return MConstant::New(alloc, BooleanValue(true));
4860 :
4861 : // NOT of a symbol is always false.
4862 10 : if (input()->type() == MIRType::Symbol)
4863 0 : return MConstant::New(alloc, BooleanValue(false));
4864 :
4865 : // NOT of an object that can't emulate undefined is always false.
4866 10 : if (input()->type() == MIRType::Object && !operandMightEmulateUndefined())
4867 0 : return MConstant::New(alloc, BooleanValue(false));
4868 :
4869 10 : return this;
4870 : }
4871 :
4872 : void
4873 0 : MNot::trySpecializeFloat32(TempAllocator& alloc)
4874 : {
4875 0 : MDefinition* in = input();
4876 0 : if (!in->canProduceFloat32() && in->type() == MIRType::Float32)
4877 0 : ConvertDefinitionToDouble<0>(alloc, in, this);
4878 0 : }
4879 :
4880 : void
4881 0 : MBeta::printOpcode(GenericPrinter& out) const
4882 : {
4883 0 : MDefinition::printOpcode(out);
4884 :
4885 0 : out.printf(" ");
4886 0 : comparison_->dump(out);
4887 0 : }
4888 :
4889 : bool
4890 0 : MCreateThisWithTemplate::canRecoverOnBailout() const
4891 : {
4892 0 : MOZ_ASSERT(templateObject()->is<PlainObject>() || templateObject()->is<UnboxedPlainObject>());
4893 0 : MOZ_ASSERT_IF(templateObject()->is<PlainObject>(),
4894 : !templateObject()->as<PlainObject>().denseElementsAreCopyOnWrite());
4895 0 : return true;
4896 : }
4897 :
4898 : bool
4899 0 : OperandIndexMap::init(TempAllocator& alloc, JSObject* templateObject)
4900 : {
4901 : const UnboxedLayout& layout =
4902 0 : templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration();
4903 :
4904 0 : const UnboxedLayout::PropertyVector& properties = layout.properties();
4905 0 : MOZ_ASSERT(properties.length() < 255);
4906 :
4907 : // Allocate an array of indexes, where the top of each field correspond to
4908 : // the index of the operand in the MObjectState instance.
4909 0 : if (!map.init(alloc, layout.size()))
4910 0 : return false;
4911 :
4912 : // Reset all indexes to 0, which is an error code.
4913 0 : for (size_t i = 0; i < map.length(); i++)
4914 0 : map[i] = 0;
4915 :
4916 : // Map the property offsets to the indexes of MObjectState operands.
4917 0 : uint8_t index = 1;
4918 0 : for (size_t i = 0; i < properties.length(); i++, index++)
4919 0 : map[properties[i].offset] = index;
4920 :
4921 0 : return true;
4922 : }
4923 :
4924 64 : MObjectState::MObjectState(MObjectState* state)
4925 64 : : numSlots_(state->numSlots_),
4926 64 : numFixedSlots_(state->numFixedSlots_),
4927 192 : operandIndex_(state->operandIndex_)
4928 : {
4929 : // This instruction is only used as a summary for bailout paths.
4930 64 : setResultType(MIRType::Object);
4931 64 : setRecoveredOnBailout();
4932 64 : }
4933 :
4934 5 : MObjectState::MObjectState(JSObject *templateObject, OperandIndexMap* operandIndex)
4935 : {
4936 : // This instruction is only used as a summary for bailout paths.
4937 5 : setResultType(MIRType::Object);
4938 5 : setRecoveredOnBailout();
4939 :
4940 5 : if (templateObject->is<NativeObject>()) {
4941 5 : NativeObject* nativeObject = &templateObject->as<NativeObject>();
4942 5 : numSlots_ = nativeObject->slotSpan();
4943 5 : numFixedSlots_ = nativeObject->numFixedSlots();
4944 : } else {
4945 : const UnboxedLayout& layout =
4946 0 : templateObject->as<UnboxedPlainObject>().layoutDontCheckGeneration();
4947 : // Same as UnboxedLayout::makeNativeGroup
4948 0 : numSlots_ = layout.properties().length();
4949 0 : numFixedSlots_ = gc::GetGCKindSlots(layout.getAllocKind());
4950 : }
4951 :
4952 5 : operandIndex_ = operandIndex;
4953 5 : }
4954 :
4955 : JSObject*
4956 18 : MObjectState::templateObjectOf(MDefinition* obj)
4957 : {
4958 18 : if (obj->isNewObject())
4959 10 : return obj->toNewObject()->templateObject();
4960 8 : else if (obj->isCreateThisWithTemplate())
4961 0 : return obj->toCreateThisWithTemplate()->templateObject();
4962 8 : else if (obj->isNewCallObject())
4963 2 : return obj->toNewCallObject()->templateObject();
4964 6 : else if (obj->isNewIterator())
4965 6 : return obj->toNewIterator()->templateObject();
4966 :
4967 0 : MOZ_CRASH("unreachable");
4968 : }
4969 :
4970 : bool
4971 69 : MObjectState::init(TempAllocator& alloc, MDefinition* obj)
4972 : {
4973 69 : if (!MVariadicInstruction::init(alloc, numSlots() + 1))
4974 0 : return false;
4975 : // +1, for the Object.
4976 69 : initOperand(0, obj);
4977 69 : return true;
4978 : }
4979 :
4980 : bool
4981 5 : MObjectState::initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal)
4982 : {
4983 5 : JSObject* templateObject = templateObjectOf(object());
4984 :
4985 : // Initialize all the slots of the object state with the value contained in
4986 : // the template object. This is needed to account values which are baked in
4987 : // the template objects and not visible in IonMonkey, such as the
4988 : // uninitialized-lexical magic value of call objects.
4989 5 : if (templateObject->is<UnboxedPlainObject>()) {
4990 0 : UnboxedPlainObject& unboxedObject = templateObject->as<UnboxedPlainObject>();
4991 0 : const UnboxedLayout& layout = unboxedObject.layoutDontCheckGeneration();
4992 0 : const UnboxedLayout::PropertyVector& properties = layout.properties();
4993 :
4994 0 : for (size_t i = 0; i < properties.length(); i++) {
4995 0 : Value val = unboxedObject.getValue(properties[i], /* maybeUninitialized = */ true);
4996 0 : MDefinition *def = undefinedVal;
4997 0 : if (!val.isUndefined()) {
4998 0 : MConstant* ins = val.isObject() ?
4999 0 : MConstant::NewConstraintlessObject(alloc, &val.toObject()) :
5000 0 : MConstant::New(alloc, val);
5001 0 : block()->insertBefore(this, ins);
5002 0 : def = ins;
5003 : }
5004 0 : initSlot(i, def);
5005 : }
5006 : } else {
5007 5 : NativeObject& nativeObject = templateObject->as<NativeObject>();
5008 5 : MOZ_ASSERT(nativeObject.slotSpan() == numSlots());
5009 :
5010 17 : for (size_t i = 0; i < numSlots(); i++) {
5011 12 : Value val = nativeObject.getSlot(i);
5012 12 : MDefinition *def = undefinedVal;
5013 12 : if (!val.isUndefined()) {
5014 0 : MConstant* ins = val.isObject() ?
5015 0 : MConstant::NewConstraintlessObject(alloc, &val.toObject()) :
5016 0 : MConstant::New(alloc, val);
5017 0 : block()->insertBefore(this, ins);
5018 0 : def = ins;
5019 : }
5020 12 : initSlot(i, def);
5021 : }
5022 : }
5023 5 : return true;
5024 : }
5025 :
5026 : MObjectState*
5027 5 : MObjectState::New(TempAllocator& alloc, MDefinition* obj)
5028 : {
5029 5 : JSObject* templateObject = templateObjectOf(obj);
5030 5 : MOZ_ASSERT(templateObject, "Unexpected object creation.");
5031 :
5032 5 : OperandIndexMap* operandIndex = nullptr;
5033 5 : if (templateObject->is<UnboxedPlainObject>()) {
5034 0 : operandIndex = new(alloc) OperandIndexMap;
5035 0 : if (!operandIndex || !operandIndex->init(alloc, templateObject))
5036 0 : return nullptr;
5037 : }
5038 :
5039 5 : MObjectState* res = new(alloc) MObjectState(templateObject, operandIndex);
5040 5 : if (!res || !res->init(alloc, obj))
5041 0 : return nullptr;
5042 5 : return res;
5043 : }
5044 :
5045 : MObjectState*
5046 64 : MObjectState::Copy(TempAllocator& alloc, MObjectState* state)
5047 : {
5048 64 : MObjectState* res = new(alloc) MObjectState(state);
5049 64 : if (!res || !res->init(alloc, state->object()))
5050 0 : return nullptr;
5051 223 : for (size_t i = 0; i < res->numSlots(); i++)
5052 159 : res->initSlot(i, state->getSlot(i));
5053 64 : return res;
5054 : }
5055 :
5056 0 : MArrayState::MArrayState(MDefinition* arr)
5057 : {
5058 : // This instruction is only used as a summary for bailout paths.
5059 0 : setResultType(MIRType::Object);
5060 0 : setRecoveredOnBailout();
5061 0 : numElements_ = arr->toNewArray()->length();
5062 0 : }
5063 :
5064 : bool
5065 0 : MArrayState::init(TempAllocator& alloc, MDefinition* obj, MDefinition* len)
5066 : {
5067 0 : if (!MVariadicInstruction::init(alloc, numElements() + 2))
5068 0 : return false;
5069 : // +1, for the Array object.
5070 0 : initOperand(0, obj);
5071 : // +1, for the length value of the array.
5072 0 : initOperand(1, len);
5073 0 : return true;
5074 : }
5075 :
5076 : MArrayState*
5077 0 : MArrayState::New(TempAllocator& alloc, MDefinition* arr, MDefinition* undefinedVal,
5078 : MDefinition* initLength)
5079 : {
5080 0 : MArrayState* res = new(alloc) MArrayState(arr);
5081 0 : if (!res || !res->init(alloc, arr, initLength))
5082 0 : return nullptr;
5083 0 : for (size_t i = 0; i < res->numElements(); i++)
5084 0 : res->initElement(i, undefinedVal);
5085 0 : return res;
5086 : }
5087 :
5088 : MArrayState*
5089 0 : MArrayState::Copy(TempAllocator& alloc, MArrayState* state)
5090 : {
5091 0 : MDefinition* arr = state->array();
5092 0 : MDefinition* len = state->initializedLength();
5093 0 : MArrayState* res = new(alloc) MArrayState(arr);
5094 0 : if (!res || !res->init(alloc, arr, len))
5095 0 : return nullptr;
5096 0 : for (size_t i = 0; i < res->numElements(); i++)
5097 0 : res->initElement(i, state->getElement(i));
5098 0 : return res;
5099 : }
5100 :
5101 9 : MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t length, MConstant* templateConst,
5102 9 : gc::InitialHeap initialHeap, jsbytecode* pc, bool vmCall)
5103 : : MUnaryInstruction(templateConst),
5104 : length_(length),
5105 : initialHeap_(initialHeap),
5106 : convertDoubleElements_(false),
5107 : pc_(pc),
5108 9 : vmCall_(vmCall)
5109 : {
5110 9 : setResultType(MIRType::Object);
5111 9 : if (templateObject()) {
5112 9 : if (TemporaryTypeSet* types = MakeSingletonTypeSet(constraints, templateObject())) {
5113 9 : setResultTypeSet(types);
5114 9 : if (types->convertDoubleElements(constraints) == TemporaryTypeSet::AlwaysConvertToDoubles)
5115 0 : convertDoubleElements_ = true;
5116 : }
5117 : }
5118 9 : }
5119 :
5120 : MDefinition::AliasType
5121 343 : MLoadFixedSlot::mightAlias(const MDefinition* def) const
5122 : {
5123 343 : if (def->isStoreFixedSlot()) {
5124 121 : const MStoreFixedSlot* store = def->toStoreFixedSlot();
5125 121 : if (store->slot() != slot())
5126 100 : return AliasType::NoAlias;
5127 21 : if (store->object() != object())
5128 18 : return AliasType::MayAlias;
5129 3 : return AliasType::MustAlias;
5130 : }
5131 222 : return AliasType::MayAlias;
5132 : }
5133 :
5134 : MDefinition*
5135 70 : MLoadFixedSlot::foldsTo(TempAllocator& alloc)
5136 : {
5137 70 : if (MDefinition* def = foldsToStore(alloc))
5138 0 : return def;
5139 :
5140 70 : return this;
5141 : }
5142 :
5143 : MDefinition::AliasType
5144 0 : MLoadFixedSlotAndUnbox::mightAlias(const MDefinition* def) const
5145 : {
5146 0 : if (def->isStoreFixedSlot()) {
5147 0 : const MStoreFixedSlot* store = def->toStoreFixedSlot();
5148 0 : if (store->slot() != slot())
5149 0 : return AliasType::NoAlias;
5150 0 : if (store->object() != object())
5151 0 : return AliasType::MayAlias;
5152 0 : return AliasType::MustAlias;
5153 : }
5154 0 : return AliasType::MayAlias;
5155 : }
5156 :
5157 : MDefinition*
5158 0 : MLoadFixedSlotAndUnbox::foldsTo(TempAllocator& alloc)
5159 : {
5160 0 : if (MDefinition* def = foldsToStore(alloc))
5161 0 : return def;
5162 :
5163 0 : return this;
5164 : }
5165 :
5166 : MDefinition*
5167 0 : MWasmAddOffset::foldsTo(TempAllocator& alloc)
5168 : {
5169 0 : MDefinition* baseArg = base();
5170 0 : if (!baseArg->isConstant())
5171 0 : return this;
5172 :
5173 0 : MOZ_ASSERT(baseArg->type() == MIRType::Int32);
5174 0 : CheckedInt<uint32_t> ptr = baseArg->toConstant()->toInt32();
5175 :
5176 0 : ptr += offset();
5177 :
5178 0 : if (!ptr.isValid())
5179 0 : return this;
5180 :
5181 0 : return MConstant::New(alloc, Int32Value(ptr.value()));
5182 : }
5183 :
5184 : MDefinition::AliasType
5185 0 : MAsmJSLoadHeap::mightAlias(const MDefinition* def) const
5186 : {
5187 0 : if (def->isAsmJSStoreHeap()) {
5188 0 : const MAsmJSStoreHeap* store = def->toAsmJSStoreHeap();
5189 0 : if (store->accessType() != accessType())
5190 0 : return AliasType::MayAlias;
5191 0 : if (!base()->isConstant() || !store->base()->isConstant())
5192 0 : return AliasType::MayAlias;
5193 0 : const MConstant* otherBase = store->base()->toConstant();
5194 0 : if (base()->toConstant()->equals(otherBase) && offset() == store->offset())
5195 0 : return AliasType::MayAlias;
5196 0 : return AliasType::NoAlias;
5197 : }
5198 0 : return AliasType::MayAlias;
5199 : }
5200 :
5201 : bool
5202 0 : MAsmJSLoadHeap::congruentTo(const MDefinition* ins) const
5203 : {
5204 0 : if (!ins->isAsmJSLoadHeap())
5205 0 : return false;
5206 0 : const MAsmJSLoadHeap* load = ins->toAsmJSLoadHeap();
5207 0 : return load->accessType() == accessType() &&
5208 0 : load->offset() == offset() &&
5209 0 : congruentIfOperandsEqual(load);
5210 : }
5211 :
5212 : MDefinition::AliasType
5213 0 : MWasmLoadGlobalVar::mightAlias(const MDefinition* def) const
5214 : {
5215 0 : if (def->isWasmStoreGlobalVar()) {
5216 0 : const MWasmStoreGlobalVar* store = def->toWasmStoreGlobalVar();
5217 : // Global variables can't alias each other or be type-reinterpreted.
5218 0 : return (store->globalDataOffset() == globalDataOffset_) ? AliasType::MayAlias :
5219 0 : AliasType::NoAlias;
5220 : }
5221 0 : return AliasType::MayAlias;
5222 : }
5223 :
5224 : HashNumber
5225 0 : MWasmLoadGlobalVar::valueHash() const
5226 : {
5227 0 : HashNumber hash = MDefinition::valueHash();
5228 0 : hash = addU32ToHash(hash, globalDataOffset_);
5229 0 : return hash;
5230 : }
5231 :
5232 : bool
5233 0 : MWasmLoadGlobalVar::congruentTo(const MDefinition* ins) const
5234 : {
5235 0 : if (ins->isWasmLoadGlobalVar())
5236 0 : return globalDataOffset_ == ins->toWasmLoadGlobalVar()->globalDataOffset_;
5237 0 : return false;
5238 : }
5239 :
5240 : MDefinition*
5241 0 : MWasmLoadGlobalVar::foldsTo(TempAllocator& alloc)
5242 : {
5243 0 : if (!dependency() || !dependency()->isWasmStoreGlobalVar())
5244 0 : return this;
5245 :
5246 0 : MWasmStoreGlobalVar* store = dependency()->toWasmStoreGlobalVar();
5247 0 : if (!store->block()->dominates(block()))
5248 0 : return this;
5249 :
5250 0 : if (store->globalDataOffset() != globalDataOffset())
5251 0 : return this;
5252 :
5253 0 : if (store->value()->type() != type())
5254 0 : return this;
5255 :
5256 0 : return store->value();
5257 : }
5258 :
5259 : MDefinition::AliasType
5260 45 : MLoadSlot::mightAlias(const MDefinition* def) const
5261 : {
5262 45 : if (def->isStoreSlot()) {
5263 0 : const MStoreSlot* store = def->toStoreSlot();
5264 0 : if (store->slot() != slot())
5265 0 : return AliasType::NoAlias;
5266 :
5267 0 : if (store->slots() != slots())
5268 0 : return AliasType::MayAlias;
5269 :
5270 0 : return AliasType::MustAlias;
5271 : }
5272 45 : return AliasType::MayAlias;
5273 : }
5274 :
5275 : HashNumber
5276 14 : MLoadSlot::valueHash() const
5277 : {
5278 14 : HashNumber hash = MDefinition::valueHash();
5279 14 : hash = addU32ToHash(hash, slot_);
5280 14 : return hash;
5281 : }
5282 :
5283 : MDefinition*
5284 13 : MLoadSlot::foldsTo(TempAllocator& alloc)
5285 : {
5286 13 : if (MDefinition* def = foldsToStore(alloc))
5287 0 : return def;
5288 :
5289 13 : return this;
5290 : }
5291 :
5292 : void
5293 0 : MLoadSlot::printOpcode(GenericPrinter& out) const
5294 : {
5295 0 : MDefinition::printOpcode(out);
5296 0 : out.printf(" %d", slot());
5297 0 : }
5298 :
5299 : void
5300 0 : MStoreSlot::printOpcode(GenericPrinter& out) const
5301 : {
5302 0 : PrintOpcodeName(out, op());
5303 0 : out.printf(" ");
5304 0 : getOperand(0)->printName(out);
5305 0 : out.printf(" %d ", slot());
5306 0 : getOperand(1)->printName(out);
5307 0 : }
5308 :
5309 : MDefinition*
5310 11 : MFunctionEnvironment::foldsTo(TempAllocator& alloc)
5311 : {
5312 11 : if (!input()->isLambda())
5313 11 : return this;
5314 :
5315 0 : return input()->toLambda()->environmentChain();
5316 : }
5317 :
5318 : static bool
5319 0 : AddIsANonZeroAdditionOf(MAdd* add, MDefinition* ins)
5320 : {
5321 0 : if (add->lhs() != ins && add->rhs() != ins)
5322 0 : return false;
5323 0 : MDefinition* other = (add->lhs() == ins) ? add->rhs() : add->lhs();
5324 0 : if (!IsNumberType(other->type()))
5325 0 : return false;
5326 0 : if (!other->isConstant())
5327 0 : return false;
5328 0 : if (other->toConstant()->numberToDouble() == 0)
5329 0 : return false;
5330 0 : return true;
5331 : }
5332 :
5333 : static bool
5334 0 : DefinitelyDifferentValue(MDefinition* ins1, MDefinition* ins2)
5335 : {
5336 0 : if (ins1 == ins2)
5337 0 : return false;
5338 :
5339 : // Drop the MToInt32 added by the TypePolicy for double and float values.
5340 0 : if (ins1->isToInt32())
5341 0 : return DefinitelyDifferentValue(ins1->toToInt32()->input(), ins2);
5342 0 : if (ins2->isToInt32())
5343 0 : return DefinitelyDifferentValue(ins2->toToInt32()->input(), ins1);
5344 :
5345 : // Ignore the bounds check, which in most cases will contain the same info.
5346 0 : if (ins1->isBoundsCheck())
5347 0 : return DefinitelyDifferentValue(ins1->toBoundsCheck()->index(), ins2);
5348 0 : if (ins2->isBoundsCheck())
5349 0 : return DefinitelyDifferentValue(ins2->toBoundsCheck()->index(), ins1);
5350 :
5351 : // For constants check they are not equal.
5352 0 : if (ins1->isConstant() && ins2->isConstant())
5353 0 : return !ins1->toConstant()->equals(ins2->toConstant());
5354 :
5355 : // Check if "ins1 = ins2 + cte", which would make both instructions
5356 : // have different values.
5357 0 : if (ins1->isAdd()) {
5358 0 : if (AddIsANonZeroAdditionOf(ins1->toAdd(), ins2))
5359 0 : return true;
5360 : }
5361 0 : if (ins2->isAdd()) {
5362 0 : if (AddIsANonZeroAdditionOf(ins2->toAdd(), ins1))
5363 0 : return true;
5364 : }
5365 :
5366 0 : return false;
5367 : }
5368 :
5369 : MDefinition::AliasType
5370 4 : MLoadElement::mightAlias(const MDefinition* def) const
5371 : {
5372 4 : if (def->isStoreElement()) {
5373 0 : const MStoreElement* store = def->toStoreElement();
5374 0 : if (store->index() != index()) {
5375 0 : if (DefinitelyDifferentValue(store->index(), index()))
5376 0 : return AliasType::NoAlias;
5377 0 : return AliasType::MayAlias;
5378 : }
5379 :
5380 0 : if (store->elements() != elements())
5381 0 : return AliasType::MayAlias;
5382 :
5383 0 : return AliasType::MustAlias;
5384 : }
5385 4 : return AliasType::MayAlias;
5386 : }
5387 :
5388 : MDefinition*
5389 1 : MLoadElement::foldsTo(TempAllocator& alloc)
5390 : {
5391 1 : if (MDefinition* def = foldsToStore(alloc))
5392 0 : return def;
5393 :
5394 1 : return this;
5395 : }
5396 :
5397 : MDefinition::AliasType
5398 0 : MLoadUnboxedObjectOrNull::mightAlias(const MDefinition* def) const
5399 : {
5400 0 : if (def->isStoreUnboxedObjectOrNull()) {
5401 0 : const MStoreUnboxedObjectOrNull* store = def->toStoreUnboxedObjectOrNull();
5402 0 : if (store->index() != index()) {
5403 0 : if (DefinitelyDifferentValue(store->index(), index()))
5404 0 : return AliasType::NoAlias;
5405 0 : return AliasType::MayAlias;
5406 : }
5407 :
5408 0 : if (store->elements() != elements())
5409 0 : return AliasType::MayAlias;
5410 :
5411 0 : if (store->offsetAdjustment() != offsetAdjustment())
5412 0 : return AliasType::MayAlias;
5413 :
5414 0 : return AliasType::MustAlias;
5415 : }
5416 0 : return AliasType::MayAlias;
5417 : }
5418 :
5419 : MDefinition*
5420 0 : MLoadUnboxedObjectOrNull::foldsTo(TempAllocator& alloc)
5421 : {
5422 0 : if (MDefinition* def = foldsToStore(alloc))
5423 0 : return def;
5424 :
5425 0 : return this;
5426 : }
5427 :
5428 : bool
5429 0 : MGuardReceiverPolymorphic::congruentTo(const MDefinition* ins) const
5430 : {
5431 0 : if (!ins->isGuardReceiverPolymorphic())
5432 0 : return false;
5433 :
5434 0 : const MGuardReceiverPolymorphic* other = ins->toGuardReceiverPolymorphic();
5435 :
5436 0 : if (numReceivers() != other->numReceivers())
5437 0 : return false;
5438 0 : for (size_t i = 0; i < numReceivers(); i++) {
5439 0 : if (receiver(i) != other->receiver(i))
5440 0 : return false;
5441 : }
5442 :
5443 0 : return congruentIfOperandsEqual(ins);
5444 : }
5445 :
5446 : void
5447 0 : InlinePropertyTable::trimTo(const InliningTargets& targets, const BoolVector& choiceSet)
5448 : {
5449 0 : for (size_t i = 0; i < targets.length(); i++) {
5450 : // If the target was inlined, don't erase the entry.
5451 0 : if (choiceSet[i])
5452 0 : continue;
5453 :
5454 : // If the target wasn't a function we would have veto'ed it
5455 : // and it will not be in the entries list.
5456 0 : if (!targets[i].target->is<JSFunction>())
5457 0 : continue;
5458 :
5459 0 : JSFunction* target = &targets[i].target->as<JSFunction>();
5460 :
5461 : // Eliminate all entries containing the vetoed function from the map.
5462 0 : size_t j = 0;
5463 0 : while (j < numEntries()) {
5464 0 : if (entries_[j]->func == target)
5465 0 : entries_.erase(&entries_[j]);
5466 : else
5467 0 : j++;
5468 : }
5469 : }
5470 0 : }
5471 :
5472 : void
5473 0 : InlinePropertyTable::trimToTargets(const InliningTargets& targets)
5474 : {
5475 0 : JitSpew(JitSpew_Inlining, "Got inlineable property cache with %d cases",
5476 0 : (int)numEntries());
5477 :
5478 0 : size_t i = 0;
5479 0 : while (i < numEntries()) {
5480 0 : bool foundFunc = false;
5481 0 : for (size_t j = 0; j < targets.length(); j++) {
5482 0 : if (entries_[i]->func == targets[j].target) {
5483 0 : foundFunc = true;
5484 0 : break;
5485 : }
5486 : }
5487 0 : if (!foundFunc)
5488 0 : entries_.erase(&(entries_[i]));
5489 : else
5490 0 : i++;
5491 : }
5492 :
5493 0 : JitSpew(JitSpew_Inlining, "%d inlineable cases left after trimming to %d targets",
5494 0 : (int)numEntries(), (int)targets.length());
5495 0 : }
5496 :
5497 : bool
5498 0 : InlinePropertyTable::hasFunction(JSFunction* func) const
5499 : {
5500 0 : for (size_t i = 0; i < numEntries(); i++) {
5501 0 : if (entries_[i]->func == func)
5502 0 : return true;
5503 : }
5504 0 : return false;
5505 : }
5506 :
5507 : bool
5508 0 : InlinePropertyTable::hasObjectGroup(ObjectGroup* group) const
5509 : {
5510 0 : for (size_t i = 0; i < numEntries(); i++) {
5511 0 : if (entries_[i]->group == group)
5512 0 : return true;
5513 : }
5514 0 : return false;
5515 : }
5516 :
5517 : TemporaryTypeSet*
5518 0 : InlinePropertyTable::buildTypeSetForFunction(JSFunction* func) const
5519 : {
5520 0 : LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
5521 0 : TemporaryTypeSet* types = alloc->new_<TemporaryTypeSet>();
5522 0 : if (!types)
5523 0 : return nullptr;
5524 0 : for (size_t i = 0; i < numEntries(); i++) {
5525 0 : if (entries_[i]->func == func)
5526 0 : types->addType(TypeSet::ObjectType(entries_[i]->group), alloc);
5527 : }
5528 0 : return types;
5529 : }
5530 :
5531 : bool
5532 0 : InlinePropertyTable::appendRoots(MRootList& roots) const
5533 : {
5534 0 : for (const Entry* entry : entries_) {
5535 0 : if (!entry->appendRoots(roots))
5536 0 : return false;
5537 : }
5538 0 : return true;
5539 : }
5540 :
5541 : SharedMem<void*>
5542 0 : MLoadTypedArrayElementStatic::base() const
5543 : {
5544 0 : return someTypedArray_->as<TypedArrayObject>().viewDataEither();
5545 : }
5546 :
5547 : size_t
5548 0 : MLoadTypedArrayElementStatic::length() const
5549 : {
5550 0 : return someTypedArray_->as<TypedArrayObject>().byteLength();
5551 : }
5552 :
5553 : bool
5554 0 : MLoadTypedArrayElementStatic::congruentTo(const MDefinition* ins) const
5555 : {
5556 0 : if (!ins->isLoadTypedArrayElementStatic())
5557 0 : return false;
5558 0 : const MLoadTypedArrayElementStatic* other = ins->toLoadTypedArrayElementStatic();
5559 0 : if (offset() != other->offset())
5560 0 : return false;
5561 0 : if (needsBoundsCheck() != other->needsBoundsCheck())
5562 0 : return false;
5563 0 : if (accessType() != other->accessType())
5564 0 : return false;
5565 0 : if (base() != other->base())
5566 0 : return false;
5567 0 : return congruentIfOperandsEqual(other);
5568 : }
5569 :
5570 : SharedMem<void*>
5571 0 : MStoreTypedArrayElementStatic::base() const
5572 : {
5573 0 : return someTypedArray_->as<TypedArrayObject>().viewDataEither();
5574 : }
5575 :
5576 : bool
5577 23 : MGetPropertyCache::allowDoubleResult() const
5578 : {
5579 23 : if (!resultTypeSet())
5580 17 : return true;
5581 :
5582 6 : return resultTypeSet()->hasType(TypeSet::DoubleType());
5583 : }
5584 :
5585 : size_t
5586 0 : MStoreTypedArrayElementStatic::length() const
5587 : {
5588 0 : return someTypedArray_->as<TypedArrayObject>().byteLength();
5589 : }
5590 :
5591 : MDefinition::AliasType
5592 0 : MGetPropertyPolymorphic::mightAlias(const MDefinition* store) const
5593 : {
5594 : // Allow hoisting this instruction if the store does not write to a
5595 : // slot read by this instruction.
5596 :
5597 0 : if (!store->isStoreFixedSlot() && !store->isStoreSlot())
5598 0 : return AliasType::MayAlias;
5599 :
5600 0 : for (size_t i = 0; i < numReceivers(); i++) {
5601 0 : const Shape* shape = this->shape(i);
5602 0 : if (!shape)
5603 0 : continue;
5604 0 : if (shape->slot() < shape->numFixedSlots()) {
5605 : // Fixed slot.
5606 0 : uint32_t slot = shape->slot();
5607 0 : if (store->isStoreFixedSlot() && store->toStoreFixedSlot()->slot() != slot)
5608 0 : continue;
5609 0 : if (store->isStoreSlot())
5610 0 : continue;
5611 : } else {
5612 : // Dynamic slot.
5613 0 : uint32_t slot = shape->slot() - shape->numFixedSlots();
5614 0 : if (store->isStoreSlot() && store->toStoreSlot()->slot() != slot)
5615 0 : continue;
5616 0 : if (store->isStoreFixedSlot())
5617 0 : continue;
5618 : }
5619 :
5620 0 : return AliasType::MayAlias;
5621 : }
5622 :
5623 0 : return AliasType::NoAlias;
5624 : }
5625 :
5626 : bool
5627 0 : MGetPropertyPolymorphic::appendRoots(MRootList& roots) const
5628 : {
5629 0 : if (!roots.append(name_))
5630 0 : return false;
5631 :
5632 0 : for (const PolymorphicEntry& entry : receivers_) {
5633 0 : if (!entry.appendRoots(roots))
5634 0 : return false;
5635 : }
5636 :
5637 0 : return true;
5638 : }
5639 :
5640 : bool
5641 0 : MSetPropertyPolymorphic::appendRoots(MRootList& roots) const
5642 : {
5643 0 : if (!roots.append(name_))
5644 0 : return false;
5645 :
5646 0 : for (const PolymorphicEntry& entry : receivers_) {
5647 0 : if (!entry.appendRoots(roots))
5648 0 : return false;
5649 : }
5650 :
5651 0 : return true;
5652 : }
5653 :
5654 : bool
5655 0 : MGuardReceiverPolymorphic::appendRoots(MRootList& roots) const
5656 : {
5657 0 : for (const ReceiverGuard& guard : receivers_) {
5658 0 : if (!roots.append(guard))
5659 0 : return false;
5660 : }
5661 0 : return true;
5662 : }
5663 :
5664 : bool
5665 3 : MDispatchInstruction::appendRoots(MRootList& roots) const
5666 : {
5667 6 : for (const Entry& entry : map_) {
5668 3 : if (!entry.appendRoots(roots))
5669 0 : return false;
5670 : }
5671 3 : return true;
5672 : }
5673 :
5674 : bool
5675 0 : MObjectGroupDispatch::appendRoots(MRootList& roots) const
5676 : {
5677 0 : if (inlinePropertyTable_ && !inlinePropertyTable_->appendRoots(roots))
5678 0 : return false;
5679 0 : return MDispatchInstruction::appendRoots(roots);
5680 : }
5681 :
5682 : bool
5683 3 : MFunctionDispatch::appendRoots(MRootList& roots) const
5684 : {
5685 3 : return MDispatchInstruction::appendRoots(roots);
5686 : }
5687 :
5688 : bool
5689 704 : MConstant::appendRoots(MRootList& roots) const
5690 : {
5691 704 : switch (type()) {
5692 : case MIRType::String:
5693 56 : return roots.append(toString());
5694 : case MIRType::Symbol:
5695 2 : return roots.append(toSymbol());
5696 : case MIRType::Object:
5697 190 : return roots.append(&toObject());
5698 : case MIRType::Undefined:
5699 : case MIRType::Null:
5700 : case MIRType::Boolean:
5701 : case MIRType::Int32:
5702 : case MIRType::Double:
5703 : case MIRType::Float32:
5704 : case MIRType::MagicOptimizedArguments:
5705 : case MIRType::MagicOptimizedOut:
5706 : case MIRType::MagicHole:
5707 : case MIRType::MagicIsConstructing:
5708 : case MIRType::MagicUninitializedLexical:
5709 456 : return true;
5710 : default:
5711 0 : MOZ_CRASH("Unexpected type");
5712 : }
5713 : }
5714 :
5715 : MDefinition*
5716 0 : MWasmUnsignedToDouble::foldsTo(TempAllocator& alloc)
5717 : {
5718 0 : if (input()->isConstant() && input()->type() == MIRType::Int32)
5719 0 : return MConstant::New(alloc, DoubleValue(uint32_t(input()->toConstant()->toInt32())));
5720 :
5721 0 : return this;
5722 : }
5723 :
5724 : MDefinition*
5725 0 : MWasmUnsignedToFloat32::foldsTo(TempAllocator& alloc)
5726 : {
5727 0 : if (input()->isConstant() && input()->type() == MIRType::Int32) {
5728 0 : double dval = double(uint32_t(input()->toConstant()->toInt32()));
5729 0 : if (IsFloat32Representable(dval))
5730 0 : return MConstant::NewFloat32(alloc, float(dval));
5731 : }
5732 :
5733 0 : return this;
5734 : }
5735 :
5736 : MWasmCall*
5737 0 : MWasmCall::New(TempAllocator& alloc, const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee,
5738 : const Args& args, MIRType resultType, uint32_t spIncrement, MDefinition* tableIndex)
5739 : {
5740 0 : MWasmCall* call = new(alloc) MWasmCall(desc, callee, spIncrement);
5741 0 : call->setResultType(resultType);
5742 :
5743 0 : if (!call->argRegs_.init(alloc, args.length()))
5744 0 : return nullptr;
5745 0 : for (size_t i = 0; i < call->argRegs_.length(); i++)
5746 0 : call->argRegs_[i] = args[i].reg;
5747 :
5748 0 : if (!call->init(alloc, call->argRegs_.length() + (callee.isTable() ? 1 : 0)))
5749 0 : return nullptr;
5750 : // FixedList doesn't initialize its elements, so do an unchecked init.
5751 0 : for (size_t i = 0; i < call->argRegs_.length(); i++)
5752 0 : call->initOperand(i, args[i].def);
5753 0 : if (callee.isTable())
5754 0 : call->initOperand(call->argRegs_.length(), tableIndex);
5755 :
5756 0 : return call;
5757 : }
5758 :
5759 : MWasmCall*
5760 0 : MWasmCall::NewBuiltinInstanceMethodCall(TempAllocator& alloc,
5761 : const wasm::CallSiteDesc& desc,
5762 : const wasm::SymbolicAddress builtin,
5763 : const ABIArg& instanceArg,
5764 : const Args& args,
5765 : MIRType resultType,
5766 : uint32_t spIncrement)
5767 : {
5768 0 : auto callee = wasm::CalleeDesc::builtinInstanceMethod(builtin);
5769 0 : MWasmCall* call = MWasmCall::New(alloc, desc, callee, args, resultType, spIncrement, nullptr);
5770 0 : if (!call)
5771 0 : return nullptr;
5772 :
5773 0 : MOZ_ASSERT(instanceArg != ABIArg());
5774 0 : call->instanceArg_ = instanceArg;
5775 0 : return call;
5776 : }
5777 :
5778 : void
5779 0 : MSqrt::trySpecializeFloat32(TempAllocator& alloc) {
5780 0 : if (!input()->canProduceFloat32() || !CheckUsesAreFloat32Consumers(this)) {
5781 0 : if (input()->type() == MIRType::Float32)
5782 0 : ConvertDefinitionToDouble<0>(alloc, input(), this);
5783 0 : return;
5784 : }
5785 :
5786 0 : setResultType(MIRType::Float32);
5787 0 : specialization_ = MIRType::Float32;
5788 : }
5789 :
5790 : MDefinition*
5791 0 : MClz::foldsTo(TempAllocator& alloc)
5792 : {
5793 0 : if (num()->isConstant()) {
5794 0 : MConstant* c = num()->toConstant();
5795 0 : if (type() == MIRType::Int32) {
5796 0 : int32_t n = c->toInt32();
5797 0 : if (n == 0)
5798 0 : return MConstant::New(alloc, Int32Value(32));
5799 0 : return MConstant::New(alloc, Int32Value(mozilla::CountLeadingZeroes32(n)));
5800 : }
5801 0 : int64_t n = c->toInt64();
5802 0 : if (n == 0)
5803 0 : return MConstant::NewInt64(alloc, int64_t(64));
5804 0 : return MConstant::NewInt64(alloc, int64_t(mozilla::CountLeadingZeroes64(n)));
5805 : }
5806 :
5807 0 : return this;
5808 : }
5809 :
5810 : MDefinition*
5811 0 : MCtz::foldsTo(TempAllocator& alloc)
5812 : {
5813 0 : if (num()->isConstant()) {
5814 0 : MConstant* c = num()->toConstant();
5815 0 : if (type() == MIRType::Int32) {
5816 0 : int32_t n = num()->toConstant()->toInt32();
5817 0 : if (n == 0)
5818 0 : return MConstant::New(alloc, Int32Value(32));
5819 0 : return MConstant::New(alloc, Int32Value(mozilla::CountTrailingZeroes32(n)));
5820 : }
5821 0 : int64_t n = c->toInt64();
5822 0 : if (n == 0)
5823 0 : return MConstant::NewInt64(alloc, int64_t(64));
5824 0 : return MConstant::NewInt64(alloc, int64_t(mozilla::CountTrailingZeroes64(n)));
5825 : }
5826 :
5827 0 : return this;
5828 : }
5829 :
5830 : MDefinition*
5831 0 : MPopcnt::foldsTo(TempAllocator& alloc)
5832 : {
5833 0 : if (num()->isConstant()) {
5834 0 : MConstant* c = num()->toConstant();
5835 0 : if (type() == MIRType::Int32) {
5836 0 : int32_t n = num()->toConstant()->toInt32();
5837 0 : return MConstant::New(alloc, Int32Value(mozilla::CountPopulation32(n)));
5838 : }
5839 0 : int64_t n = c->toInt64();
5840 0 : return MConstant::NewInt64(alloc, int64_t(mozilla::CountPopulation64(n)));
5841 : }
5842 :
5843 0 : return this;
5844 : }
5845 :
5846 : MDefinition*
5847 8 : MBoundsCheck::foldsTo(TempAllocator& alloc)
5848 : {
5849 8 : if (index()->isConstant() && length()->isConstant()) {
5850 0 : uint32_t len = length()->toConstant()->toInt32();
5851 0 : uint32_t idx = index()->toConstant()->toInt32();
5852 0 : if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len)
5853 0 : return index();
5854 : }
5855 :
5856 8 : return this;
5857 : }
5858 :
5859 : MDefinition*
5860 0 : MTableSwitch::foldsTo(TempAllocator& alloc)
5861 : {
5862 0 : MDefinition* op = getOperand(0);
5863 :
5864 : // If we only have one successor, convert to a plain goto to the only
5865 : // successor. TableSwitch indices are numeric; other types will always go to
5866 : // the only successor.
5867 0 : if (numSuccessors() == 1 || (op->type() != MIRType::Value && !IsNumberType(op->type())))
5868 0 : return MGoto::New(alloc, getDefault());
5869 :
5870 0 : if (MConstant* opConst = op->maybeConstantValue()) {
5871 0 : if (op->type() == MIRType::Int32) {
5872 0 : int32_t i = opConst->toInt32() - low_;
5873 : MBasicBlock* target;
5874 0 : if (size_t(i) < numCases())
5875 0 : target = getCase(size_t(i));
5876 : else
5877 0 : target = getDefault();
5878 0 : MOZ_ASSERT(target);
5879 0 : return MGoto::New(alloc, target);
5880 : }
5881 : }
5882 :
5883 0 : return this;
5884 : }
5885 :
5886 : MDefinition*
5887 2 : MArrayJoin::foldsTo(TempAllocator& alloc)
5888 : {
5889 2 : MDefinition* arr = array();
5890 :
5891 2 : if (!arr->isStringSplit())
5892 2 : return this;
5893 :
5894 0 : setRecoveredOnBailout();
5895 0 : if (arr->hasLiveDefUses()) {
5896 0 : setNotRecoveredOnBailout();
5897 0 : return this;
5898 : }
5899 :
5900 : // The MStringSplit won't generate any code.
5901 0 : arr->setRecoveredOnBailout();
5902 :
5903 : // We're replacing foo.split(bar).join(baz) by
5904 : // foo.replace(bar, baz). MStringSplit could be recovered by
5905 : // a bailout. As we are removing its last use, and its result
5906 : // could be captured by a resume point, this MStringSplit will
5907 : // be executed on the bailout path.
5908 0 : MDefinition* string = arr->toStringSplit()->string();
5909 0 : MDefinition* pattern = arr->toStringSplit()->separator();
5910 0 : MDefinition* replacement = sep();
5911 :
5912 0 : MStringReplace *substr = MStringReplace::New(alloc, string, pattern, replacement);
5913 0 : substr->setFlatReplacement();
5914 0 : return substr;
5915 : }
5916 :
5917 : MDefinition*
5918 0 : MGetFirstDollarIndex::foldsTo(TempAllocator& alloc)
5919 : {
5920 0 : MDefinition* strArg = str();
5921 0 : if (!strArg->isConstant())
5922 0 : return this;
5923 :
5924 0 : JSAtom* atom = &strArg->toConstant()->toString()->asAtom();
5925 0 : int32_t index = GetFirstDollarIndexRawFlat(atom);
5926 0 : return MConstant::New(alloc, Int32Value(index));
5927 : }
5928 :
5929 : MConvertUnboxedObjectToNative*
5930 0 : MConvertUnboxedObjectToNative::New(TempAllocator& alloc, MDefinition* obj, ObjectGroup* group)
5931 : {
5932 0 : MConvertUnboxedObjectToNative* res = new(alloc) MConvertUnboxedObjectToNative(obj, group);
5933 :
5934 0 : ObjectGroup* nativeGroup = group->unboxedLayout().nativeGroup();
5935 :
5936 : // Make a new type set for the result of this instruction which replaces
5937 : // the input group with the native group we will convert it to.
5938 0 : TemporaryTypeSet* types = obj->resultTypeSet();
5939 0 : if (types && !types->unknownObject()) {
5940 0 : TemporaryTypeSet* newTypes = types->cloneWithoutObjects(alloc.lifoAlloc());
5941 0 : if (newTypes) {
5942 0 : for (size_t i = 0; i < types->getObjectCount(); i++) {
5943 0 : TypeSet::ObjectKey* key = types->getObject(i);
5944 0 : if (!key)
5945 0 : continue;
5946 0 : if (key->unknownProperties() || !key->isGroup() || key->group() != group)
5947 0 : newTypes->addType(TypeSet::ObjectType(key), alloc.lifoAlloc());
5948 : else
5949 0 : newTypes->addType(TypeSet::ObjectType(nativeGroup), alloc.lifoAlloc());
5950 : }
5951 0 : res->setResultTypeSet(newTypes);
5952 : }
5953 : }
5954 :
5955 0 : return res;
5956 : }
5957 :
5958 : bool
5959 172 : jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints,
5960 : MDefinition* obj, MDefinition* id)
5961 : {
5962 172 : if (obj->mightBeType(MIRType::String))
5963 3 : return false;
5964 :
5965 169 : if (id->type() != MIRType::Int32 && id->type() != MIRType::Double)
5966 9 : return false;
5967 :
5968 160 : TemporaryTypeSet* types = obj->resultTypeSet();
5969 160 : if (!types)
5970 0 : return false;
5971 :
5972 : // Typed arrays are native classes but do not have dense elements.
5973 160 : const Class* clasp = types->getKnownClass(constraints);
5974 160 : return clasp && clasp->isNative() && !IsTypedArrayClass(clasp);
5975 : }
5976 :
5977 : JSValueType
5978 180 : jit::UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj,
5979 : MDefinition* id)
5980 : {
5981 180 : if (obj->mightBeType(MIRType::String))
5982 3 : return JSVAL_TYPE_MAGIC;
5983 :
5984 177 : if (id && id->type() != MIRType::Int32 && id->type() != MIRType::Double)
5985 9 : return JSVAL_TYPE_MAGIC;
5986 :
5987 168 : TemporaryTypeSet* types = obj->resultTypeSet();
5988 168 : if (!types || types->unknownObject())
5989 24 : return JSVAL_TYPE_MAGIC;
5990 :
5991 144 : JSValueType elementType = JSVAL_TYPE_MAGIC;
5992 144 : for (unsigned i = 0; i < types->getObjectCount(); i++) {
5993 2 : TypeSet::ObjectKey* key = types->getObject(i);
5994 2 : if (!key)
5995 0 : continue;
5996 :
5997 2 : if (key->unknownProperties() || !key->isGroup())
5998 0 : return JSVAL_TYPE_MAGIC;
5999 :
6000 2 : if (key->clasp() != &UnboxedArrayObject::class_)
6001 2 : return JSVAL_TYPE_MAGIC;
6002 :
6003 0 : const UnboxedLayout &layout = key->group()->unboxedLayout();
6004 :
6005 0 : if (layout.nativeGroup())
6006 0 : return JSVAL_TYPE_MAGIC;
6007 :
6008 0 : if (elementType == layout.elementType() || elementType == JSVAL_TYPE_MAGIC)
6009 0 : elementType = layout.elementType();
6010 : else
6011 0 : return JSVAL_TYPE_MAGIC;
6012 :
6013 0 : key->watchStateChangeForUnboxedConvertedToNative(constraints);
6014 : }
6015 :
6016 142 : return elementType;
6017 : }
6018 :
6019 : bool
6020 52 : jit::ElementAccessIsTypedArray(CompilerConstraintList* constraints,
6021 : MDefinition* obj, MDefinition* id,
6022 : Scalar::Type* arrayType)
6023 : {
6024 52 : if (obj->mightBeType(MIRType::String))
6025 6 : return false;
6026 :
6027 46 : if (id->type() != MIRType::Int32 && id->type() != MIRType::Double)
6028 16 : return false;
6029 :
6030 30 : TemporaryTypeSet* types = obj->resultTypeSet();
6031 30 : if (!types)
6032 0 : return false;
6033 :
6034 30 : *arrayType = types->getTypedArrayType(constraints);
6035 30 : return *arrayType != Scalar::MaxTypedArrayViewType;
6036 : }
6037 :
6038 : bool
6039 2 : jit::ElementAccessIsPacked(CompilerConstraintList* constraints, MDefinition* obj)
6040 : {
6041 2 : TemporaryTypeSet* types = obj->resultTypeSet();
6042 2 : return types && !types->hasObjectFlags(constraints, OBJECT_FLAG_NON_PACKED);
6043 : }
6044 :
6045 : bool
6046 3 : jit::ElementAccessMightBeCopyOnWrite(CompilerConstraintList* constraints, MDefinition* obj)
6047 : {
6048 3 : TemporaryTypeSet* types = obj->resultTypeSet();
6049 3 : return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_COPY_ON_WRITE);
6050 : }
6051 :
6052 : bool
6053 1 : jit::ElementAccessMightBeFrozen(CompilerConstraintList* constraints, MDefinition* obj)
6054 : {
6055 1 : TemporaryTypeSet* types = obj->resultTypeSet();
6056 1 : return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_FROZEN_ELEMENTS);
6057 : }
6058 :
6059 : AbortReasonOr<bool>
6060 3 : jit::ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj)
6061 : {
6062 3 : TemporaryTypeSet* types = obj->resultTypeSet();
6063 :
6064 3 : if (!types || types->hasObjectFlags(builder->constraints(), OBJECT_FLAG_LENGTH_OVERFLOW))
6065 0 : return true;
6066 :
6067 3 : return TypeCanHaveExtraIndexedProperties(builder, types);
6068 : }
6069 :
6070 : MIRType
6071 1 : jit::DenseNativeElementType(CompilerConstraintList* constraints, MDefinition* obj)
6072 : {
6073 1 : TemporaryTypeSet* types = obj->resultTypeSet();
6074 1 : MIRType elementType = MIRType::None;
6075 1 : unsigned count = types->getObjectCount();
6076 :
6077 2 : for (unsigned i = 0; i < count; i++) {
6078 1 : TypeSet::ObjectKey* key = types->getObject(i);
6079 1 : if (!key)
6080 0 : continue;
6081 :
6082 1 : if (key->unknownProperties())
6083 0 : return MIRType::None;
6084 :
6085 1 : HeapTypeSetKey elementTypes = key->property(JSID_VOID);
6086 :
6087 1 : MIRType type = elementTypes.knownMIRType(constraints);
6088 1 : if (type == MIRType::None)
6089 0 : return MIRType::None;
6090 :
6091 1 : if (elementType == MIRType::None)
6092 1 : elementType = type;
6093 0 : else if (elementType != type)
6094 0 : return MIRType::None;
6095 : }
6096 :
6097 1 : return elementType;
6098 : }
6099 :
6100 : static BarrierKind
6101 82 : PropertyReadNeedsTypeBarrier(CompilerConstraintList* constraints,
6102 : TypeSet::ObjectKey* key, PropertyName* name,
6103 : TypeSet* observed)
6104 : {
6105 : // If the object being read from has types for the property which haven't
6106 : // been observed at this access site, the read could produce a new type and
6107 : // a barrier is needed. Note that this only covers reads from properties
6108 : // which are accounted for by type information, i.e. native data properties
6109 : // and elements.
6110 : //
6111 : // We also need a barrier if the object is a proxy, because then all bets
6112 : // are off, just as if it has unknown properties.
6113 159 : if (key->unknownProperties() || observed->empty() ||
6114 77 : key->clasp()->isProxy())
6115 : {
6116 5 : return BarrierKind::TypeSet;
6117 : }
6118 :
6119 77 : jsid id = name ? NameToId(name) : JSID_VOID;
6120 77 : HeapTypeSetKey property = key->property(id);
6121 77 : if (property.maybeTypes()) {
6122 42 : if (!TypeSetIncludes(observed, MIRType::Value, property.maybeTypes())) {
6123 : // If all possible objects have been observed, we don't have to
6124 : // guard on the specific object types.
6125 6 : if (property.maybeTypes()->objectsAreSubset(observed)) {
6126 0 : property.freeze(constraints);
6127 0 : return BarrierKind::TypeTagOnly;
6128 : }
6129 6 : return BarrierKind::TypeSet;
6130 : }
6131 : }
6132 :
6133 : // Type information for global objects is not required to reflect the
6134 : // initial 'undefined' value for properties, in particular global
6135 : // variables declared with 'var'. Until the property is assigned a value
6136 : // other than undefined, a barrier is required.
6137 71 : if (key->isSingleton()) {
6138 21 : JSObject* obj = key->singleton();
6139 26 : if (name && CanHaveEmptyPropertyTypesForOwnProperty(obj) &&
6140 10 : (!property.maybeTypes() || property.maybeTypes()->empty()))
6141 : {
6142 0 : return BarrierKind::TypeSet;
6143 : }
6144 : }
6145 :
6146 71 : property.freeze(constraints);
6147 71 : return BarrierKind::NoBarrier;
6148 : }
6149 :
6150 : static bool
6151 19 : ObjectSubsumes(TypeSet::ObjectKey* first, TypeSet::ObjectKey* second)
6152 : {
6153 54 : if (first->isSingleton() ||
6154 32 : second->isSingleton() ||
6155 32 : first->clasp() != second->clasp() ||
6156 51 : first->unknownProperties() ||
6157 16 : second->unknownProperties())
6158 : {
6159 3 : return false;
6160 : }
6161 :
6162 16 : if (first->clasp() == &ArrayObject::class_) {
6163 0 : HeapTypeSetKey firstElements = first->property(JSID_VOID);
6164 0 : HeapTypeSetKey secondElements = second->property(JSID_VOID);
6165 :
6166 0 : return firstElements.maybeTypes() && secondElements.maybeTypes() &&
6167 0 : firstElements.maybeTypes()->equals(secondElements.maybeTypes());
6168 : }
6169 :
6170 16 : if (first->clasp() == &UnboxedArrayObject::class_) {
6171 0 : return first->group()->unboxedLayout().elementType() ==
6172 0 : second->group()->unboxedLayout().elementType();
6173 : }
6174 :
6175 16 : return false;
6176 : }
6177 :
6178 : BarrierKind
6179 70 : jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx,
6180 : CompilerConstraintList* constraints,
6181 : TypeSet::ObjectKey* key, PropertyName* name,
6182 : TemporaryTypeSet* observed, bool updateObserved)
6183 : {
6184 70 : if (!updateObserved)
6185 0 : return PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
6186 :
6187 : // If this access has never executed, try to add types to the observed set
6188 : // according to any property which exists on the object or its prototype.
6189 70 : if (observed->empty() && name) {
6190 : JSObject* obj;
6191 7 : if (key->isSingleton())
6192 2 : obj = key->singleton();
6193 : else
6194 5 : obj = key->proto().isDynamic() ? nullptr : key->proto().toObjectOrNull();
6195 :
6196 19 : while (obj) {
6197 8 : if (!obj->getClass()->isNative())
6198 0 : break;
6199 :
6200 8 : TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(obj);
6201 8 : if (propertycx)
6202 0 : key->ensureTrackedProperty(propertycx, NameToId(name));
6203 :
6204 8 : if (!key->unknownProperties()) {
6205 8 : HeapTypeSetKey property = key->property(NameToId(name));
6206 8 : if (property.maybeTypes()) {
6207 2 : TypeSet::TypeList types;
6208 2 : if (!property.maybeTypes()->enumerateTypes(&types))
6209 0 : break;
6210 2 : if (types.length() == 1) {
6211 : // Note: the return value here is ignored.
6212 2 : observed->addType(types[0], GetJitContext()->temp->lifoAlloc());
6213 2 : break;
6214 : }
6215 : }
6216 : }
6217 :
6218 6 : obj = obj->staticPrototype();
6219 : }
6220 : }
6221 :
6222 : // If any objects which could be observed are similar to ones that have
6223 : // already been observed, add them to the observed type set.
6224 70 : if (!key->unknownProperties()) {
6225 70 : HeapTypeSetKey property = key->property(name ? NameToId(name) : JSID_VOID);
6226 :
6227 70 : if (property.maybeTypes() && !property.maybeTypes()->unknownObject()) {
6228 57 : for (size_t i = 0; i < property.maybeTypes()->getObjectCount(); i++) {
6229 19 : TypeSet::ObjectKey* key = property.maybeTypes()->getObject(i);
6230 19 : if (!key || observed->unknownObject())
6231 0 : continue;
6232 :
6233 38 : for (size_t j = 0; j < observed->getObjectCount(); j++) {
6234 19 : TypeSet::ObjectKey* observedKey = observed->getObject(j);
6235 19 : if (observedKey && ObjectSubsumes(observedKey, key)) {
6236 : // Note: the return value here is ignored.
6237 0 : observed->addType(TypeSet::ObjectType(key),
6238 0 : GetJitContext()->temp->lifoAlloc());
6239 0 : break;
6240 : }
6241 : }
6242 : }
6243 : }
6244 : }
6245 :
6246 70 : return PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
6247 : }
6248 :
6249 : BarrierKind
6250 496 : jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx,
6251 : CompilerConstraintList* constraints,
6252 : MDefinition* obj, PropertyName* name,
6253 : TemporaryTypeSet* observed)
6254 : {
6255 496 : if (observed->unknown())
6256 0 : return BarrierKind::NoBarrier;
6257 :
6258 496 : TypeSet* types = obj->resultTypeSet();
6259 496 : if (!types || types->unknownObject())
6260 244 : return BarrierKind::TypeSet;
6261 :
6262 252 : BarrierKind res = BarrierKind::NoBarrier;
6263 :
6264 252 : bool updateObserved = types->getObjectCount() == 1;
6265 302 : for (size_t i = 0; i < types->getObjectCount(); i++) {
6266 61 : if (TypeSet::ObjectKey* key = types->getObject(i)) {
6267 61 : BarrierKind kind = PropertyReadNeedsTypeBarrier(propertycx, constraints, key, name,
6268 61 : observed, updateObserved);
6269 61 : if (kind == BarrierKind::TypeSet)
6270 11 : return BarrierKind::TypeSet;
6271 :
6272 50 : if (kind == BarrierKind::TypeTagOnly) {
6273 0 : MOZ_ASSERT(res == BarrierKind::NoBarrier || res == BarrierKind::TypeTagOnly);
6274 0 : res = BarrierKind::TypeTagOnly;
6275 : } else {
6276 50 : MOZ_ASSERT(kind == BarrierKind::NoBarrier);
6277 : }
6278 : }
6279 : }
6280 :
6281 241 : return res;
6282 : }
6283 :
6284 : AbortReasonOr<BarrierKind>
6285 6 : jit::PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder* builder,
6286 : MDefinition* obj, PropertyName* name,
6287 : TemporaryTypeSet* observed)
6288 : {
6289 6 : if (observed->unknown())
6290 0 : return BarrierKind::NoBarrier;
6291 :
6292 6 : TypeSet* types = obj->resultTypeSet();
6293 6 : if (!types || types->unknownObject())
6294 0 : return BarrierKind::TypeSet;
6295 :
6296 6 : BarrierKind res = BarrierKind::NoBarrier;
6297 :
6298 12 : for (size_t i = 0; i < types->getObjectCount(); i++) {
6299 6 : TypeSet::ObjectKey* key = types->getObject(i);
6300 6 : if (!key)
6301 0 : continue;
6302 : while (true) {
6303 18 : if (!builder->alloc().ensureBallast())
6304 0 : return builder->abort(AbortReason::Alloc);
6305 18 : if (!key->hasStableClassAndProto(builder->constraints()))
6306 0 : return BarrierKind::TypeSet;
6307 18 : if (!key->proto().isObject())
6308 6 : break;
6309 12 : JSObject* proto = builder->checkNurseryObject(key->proto().toObject());
6310 12 : key = TypeSet::ObjectKey::get(proto);
6311 12 : BarrierKind kind = PropertyReadNeedsTypeBarrier(builder->constraints(),
6312 12 : key, name, observed);
6313 12 : if (kind == BarrierKind::TypeSet)
6314 0 : return BarrierKind::TypeSet;
6315 :
6316 12 : if (kind == BarrierKind::TypeTagOnly) {
6317 0 : MOZ_ASSERT(res == BarrierKind::NoBarrier || res == BarrierKind::TypeTagOnly);
6318 0 : res = BarrierKind::TypeTagOnly;
6319 : } else {
6320 12 : MOZ_ASSERT(kind == BarrierKind::NoBarrier);
6321 : }
6322 12 : }
6323 : }
6324 :
6325 6 : return res;
6326 : }
6327 :
6328 : bool
6329 14 : jit::PropertyReadIsIdempotent(CompilerConstraintList* constraints,
6330 : MDefinition* obj, PropertyName* name)
6331 : {
6332 : // Determine if reading a property from obj is likely to be idempotent.
6333 :
6334 14 : TypeSet* types = obj->resultTypeSet();
6335 14 : if (!types || types->unknownObject())
6336 8 : return false;
6337 :
6338 9 : for (size_t i = 0; i < types->getObjectCount(); i++) {
6339 6 : if (TypeSet::ObjectKey* key = types->getObject(i)) {
6340 6 : if (key->unknownProperties())
6341 3 : return false;
6342 :
6343 : // Check if the property has been reconfigured or is a getter.
6344 6 : HeapTypeSetKey property = key->property(NameToId(name));
6345 6 : if (property.nonData(constraints))
6346 3 : return false;
6347 : }
6348 : }
6349 :
6350 3 : return true;
6351 : }
6352 :
6353 : void
6354 0 : jit::AddObjectsForPropertyRead(MDefinition* obj, PropertyName* name,
6355 : TemporaryTypeSet* observed)
6356 : {
6357 : // Add objects to observed which *could* be observed by reading name from obj,
6358 : // to hopefully avoid unnecessary type barriers and code invalidations.
6359 :
6360 0 : LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
6361 :
6362 0 : TemporaryTypeSet* types = obj->resultTypeSet();
6363 0 : if (!types || types->unknownObject()) {
6364 0 : observed->addType(TypeSet::AnyObjectType(), alloc);
6365 0 : return;
6366 : }
6367 :
6368 0 : for (size_t i = 0; i < types->getObjectCount(); i++) {
6369 0 : TypeSet::ObjectKey* key = types->getObject(i);
6370 0 : if (!key)
6371 0 : continue;
6372 :
6373 0 : if (key->unknownProperties()) {
6374 0 : observed->addType(TypeSet::AnyObjectType(), alloc);
6375 0 : return;
6376 : }
6377 :
6378 0 : jsid id = name ? NameToId(name) : JSID_VOID;
6379 0 : HeapTypeSetKey property = key->property(id);
6380 0 : HeapTypeSet* types = property.maybeTypes();
6381 0 : if (!types)
6382 0 : continue;
6383 :
6384 0 : if (types->unknownObject()) {
6385 0 : observed->addType(TypeSet::AnyObjectType(), alloc);
6386 0 : return;
6387 : }
6388 :
6389 0 : for (size_t i = 0; i < types->getObjectCount(); i++) {
6390 0 : if (TypeSet::ObjectKey* key = types->getObject(i))
6391 0 : observed->addType(TypeSet::ObjectType(key), alloc);
6392 : }
6393 : }
6394 : }
6395 :
6396 : AbortReasonOr<bool>
6397 10 : PrototypeHasIndexedProperty(IonBuilder* builder, JSObject* obj)
6398 : {
6399 5 : do {
6400 10 : TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(builder->checkNurseryObject(obj));
6401 10 : if (ClassCanHaveExtraProperties(key->clasp()))
6402 0 : return true;
6403 10 : if (key->unknownProperties())
6404 0 : return true;
6405 10 : HeapTypeSetKey index = key->property(JSID_VOID);
6406 10 : if (index.nonData(builder->constraints()) || index.isOwnProperty(builder->constraints()))
6407 0 : return true;
6408 10 : obj = obj->staticPrototype();
6409 10 : if (!builder->alloc().ensureBallast())
6410 0 : return builder->abort(AbortReason::Alloc);
6411 10 : } while (obj);
6412 :
6413 5 : return false;
6414 : }
6415 :
6416 : // Whether Array.prototype, or an object on its proto chain, has an indexed property.
6417 : AbortReasonOr<bool>
6418 2 : jit::ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script)
6419 : {
6420 2 : if (JSObject* proto = script->global().maybeGetArrayPrototype())
6421 2 : return PrototypeHasIndexedProperty(builder, proto);
6422 0 : return true;
6423 : }
6424 :
6425 : // Whether obj or any of its prototypes have an indexed property.
6426 : AbortReasonOr<bool>
6427 3 : jit::TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types)
6428 : {
6429 3 : const Class* clasp = types->getKnownClass(builder->constraints());
6430 :
6431 : // Note: typed arrays have indexed properties not accounted for by type
6432 : // information, though these are all in bounds and will be accounted for
6433 : // by JIT paths.
6434 3 : if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsTypedArrayClass(clasp)))
6435 0 : return true;
6436 :
6437 3 : if (types->hasObjectFlags(builder->constraints(), OBJECT_FLAG_SPARSE_INDEXES))
6438 0 : return true;
6439 :
6440 : JSObject* proto;
6441 3 : if (!types->getCommonPrototype(builder->constraints(), &proto))
6442 0 : return true;
6443 :
6444 3 : if (!proto)
6445 0 : return false;
6446 :
6447 3 : return PrototypeHasIndexedProperty(builder, proto);
6448 : }
6449 :
6450 : static bool
6451 64 : PropertyTypeIncludes(TempAllocator& alloc, HeapTypeSetKey property,
6452 : MDefinition* value, MIRType implicitType)
6453 : {
6454 : // If implicitType is not MIRType::None, it is an additional type which the
6455 : // property implicitly includes. In this case, make a new type set which
6456 : // explicitly contains the type.
6457 64 : TypeSet* types = property.maybeTypes();
6458 64 : if (implicitType != MIRType::None) {
6459 0 : TypeSet::Type newType = TypeSet::PrimitiveType(ValueTypeFromMIRType(implicitType));
6460 0 : if (types)
6461 0 : types = types->clone(alloc.lifoAlloc());
6462 : else
6463 0 : types = alloc.lifoAlloc()->new_<TemporaryTypeSet>();
6464 0 : if (!types) {
6465 0 : return false;
6466 : }
6467 0 : types->addType(newType, alloc.lifoAlloc());
6468 : }
6469 :
6470 64 : return TypeSetIncludes(types, value->type(), value->resultTypeSet());
6471 : }
6472 :
6473 : static bool
6474 3 : TryAddTypeBarrierForWrite(TempAllocator& alloc, CompilerConstraintList* constraints,
6475 : MBasicBlock* current, TemporaryTypeSet* objTypes,
6476 : PropertyName* name, MDefinition** pvalue, MIRType implicitType)
6477 : {
6478 : // Return whether pvalue was modified to include a type barrier ensuring
6479 : // that writing the value to objTypes/id will not require changing type
6480 : // information.
6481 :
6482 : // All objects in the set must have the same types for name. Otherwise, we
6483 : // could bail out without subsequently triggering a type change that
6484 : // invalidates the compiled code.
6485 6 : Maybe<HeapTypeSetKey> aggregateProperty;
6486 :
6487 3 : for (size_t i = 0; i < objTypes->getObjectCount(); i++) {
6488 3 : TypeSet::ObjectKey* key = objTypes->getObject(i);
6489 3 : if (!key)
6490 0 : continue;
6491 :
6492 3 : if (key->unknownProperties())
6493 3 : return false;
6494 :
6495 3 : jsid id = name ? NameToId(name) : JSID_VOID;
6496 3 : HeapTypeSetKey property = key->property(id);
6497 3 : if (!property.maybeTypes() || property.couldBeConstant(constraints))
6498 3 : return false;
6499 :
6500 0 : if (PropertyTypeIncludes(alloc, property, *pvalue, implicitType))
6501 0 : return false;
6502 :
6503 : // This freeze is not required for correctness, but ensures that we
6504 : // will recompile if the property types change and the barrier can
6505 : // potentially be removed.
6506 0 : property.freeze(constraints);
6507 :
6508 0 : if (!aggregateProperty) {
6509 0 : aggregateProperty.emplace(property);
6510 : } else {
6511 0 : if (!aggregateProperty->maybeTypes()->equals(property.maybeTypes()))
6512 0 : return false;
6513 : }
6514 : }
6515 :
6516 0 : MOZ_ASSERT(aggregateProperty);
6517 :
6518 0 : MIRType propertyType = aggregateProperty->knownMIRType(constraints);
6519 0 : switch (propertyType) {
6520 : case MIRType::Boolean:
6521 : case MIRType::Int32:
6522 : case MIRType::Double:
6523 : case MIRType::String:
6524 : case MIRType::Symbol: {
6525 : // The property is a particular primitive type, guard by unboxing the
6526 : // value before the write.
6527 0 : if (!(*pvalue)->mightBeType(propertyType)) {
6528 : // The value's type does not match the property type. Just do a VM
6529 : // call as it will always trigger invalidation of the compiled code.
6530 0 : MOZ_ASSERT_IF((*pvalue)->type() != MIRType::Value, (*pvalue)->type() != propertyType);
6531 0 : return false;
6532 : }
6533 0 : MInstruction* ins = MUnbox::New(alloc, *pvalue, propertyType, MUnbox::Fallible);
6534 0 : current->add(ins);
6535 0 : *pvalue = ins;
6536 0 : return true;
6537 : }
6538 : default:;
6539 : }
6540 :
6541 0 : if ((*pvalue)->type() != MIRType::Value)
6542 0 : return false;
6543 :
6544 0 : TemporaryTypeSet* types = aggregateProperty->maybeTypes()->clone(alloc.lifoAlloc());
6545 0 : if (!types)
6546 0 : return false;
6547 :
6548 : // If all possible objects can be stored without a barrier, we don't have to
6549 : // guard on the specific object types.
6550 0 : BarrierKind kind = BarrierKind::TypeSet;
6551 0 : if ((*pvalue)->resultTypeSet() && (*pvalue)->resultTypeSet()->objectsAreSubset(types))
6552 0 : kind = BarrierKind::TypeTagOnly;
6553 :
6554 0 : MInstruction* ins = MMonitorTypes::New(alloc, *pvalue, types, kind);
6555 0 : current->add(ins);
6556 0 : return true;
6557 : }
6558 :
6559 : static MInstruction*
6560 0 : AddGroupGuard(TempAllocator& alloc, MBasicBlock* current, MDefinition* obj,
6561 : TypeSet::ObjectKey* key, bool bailOnEquality)
6562 : {
6563 : MInstruction* guard;
6564 :
6565 0 : if (key->isGroup()) {
6566 0 : guard = MGuardObjectGroup::New(alloc, obj, key->group(), bailOnEquality,
6567 0 : Bailout_ObjectIdentityOrTypeGuard);
6568 : } else {
6569 0 : MConstant* singletonConst = MConstant::NewConstraintlessObject(alloc, key->singleton());
6570 0 : current->add(singletonConst);
6571 0 : guard = MGuardObjectIdentity::New(alloc, obj, singletonConst, bailOnEquality);
6572 : }
6573 :
6574 0 : current->add(guard);
6575 :
6576 : // For now, never move object group / identity guards.
6577 0 : guard->setNotMovable();
6578 :
6579 0 : return guard;
6580 : }
6581 :
6582 : // Whether value can be written to property without changing type information.
6583 : bool
6584 64 : jit::CanWriteProperty(TempAllocator& alloc, CompilerConstraintList* constraints,
6585 : HeapTypeSetKey property, MDefinition* value,
6586 : MIRType implicitType /* = MIRType::None */)
6587 : {
6588 64 : if (property.couldBeConstant(constraints))
6589 0 : return false;
6590 64 : return PropertyTypeIncludes(alloc, property, value, implicitType);
6591 : }
6592 :
6593 : bool
6594 64 : jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* constraints,
6595 : MBasicBlock* current, MDefinition** pobj,
6596 : PropertyName* name, MDefinition** pvalue,
6597 : bool canModify, MIRType implicitType)
6598 : {
6599 : // If any value being written is not reflected in the type information for
6600 : // objects which obj could represent, a type barrier is needed when writing
6601 : // the value. As for propertyReadNeedsTypeBarrier, this only applies for
6602 : // properties that are accounted for by type information, i.e. normal data
6603 : // properties and elements.
6604 :
6605 64 : TemporaryTypeSet* types = (*pobj)->resultTypeSet();
6606 64 : if (!types || types->unknownObject())
6607 0 : return true;
6608 :
6609 : // If all of the objects being written to have property types which already
6610 : // reflect the value, no barrier at all is needed. Additionally, if all
6611 : // objects being written to have the same types for the property, and those
6612 : // types do *not* reflect the value, add a type barrier for the value.
6613 :
6614 64 : bool success = true;
6615 123 : for (size_t i = 0; i < types->getObjectCount(); i++) {
6616 64 : TypeSet::ObjectKey* key = types->getObject(i);
6617 64 : if (!key || key->unknownProperties())
6618 0 : continue;
6619 :
6620 : // TI doesn't track TypedArray indexes and should never insert a type
6621 : // barrier for them.
6622 64 : if (!name && IsTypedArrayClass(key->clasp()))
6623 0 : continue;
6624 :
6625 64 : jsid id = name ? NameToId(name) : JSID_VOID;
6626 64 : HeapTypeSetKey property = key->property(id);
6627 64 : if (!CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) {
6628 : // Either pobj or pvalue needs to be modified to filter out the
6629 : // types which the value could have but are not in the property,
6630 : // or a VM call is required. A VM call is always required if pobj
6631 : // and pvalue cannot be modified.
6632 5 : if (!canModify)
6633 2 : return true;
6634 : success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue,
6635 3 : implicitType);
6636 3 : break;
6637 : }
6638 : }
6639 :
6640 : // Perform additional filtering to make sure that any unboxed property
6641 : // being written can accommodate the value.
6642 124 : for (size_t i = 0; i < types->getObjectCount(); i++) {
6643 62 : TypeSet::ObjectKey* key = types->getObject(i);
6644 62 : if (key && key->isGroup() && key->group()->maybeUnboxedLayout()) {
6645 0 : const UnboxedLayout& layout = key->group()->unboxedLayout();
6646 0 : if (name) {
6647 0 : const UnboxedLayout::Property* property = layout.lookup(name);
6648 0 : if (property && !CanStoreUnboxedType(alloc, property->type, *pvalue))
6649 0 : return true;
6650 : } else {
6651 0 : if (layout.isArray() && !CanStoreUnboxedType(alloc, layout.elementType(), *pvalue))
6652 0 : return true;
6653 : }
6654 : }
6655 : }
6656 :
6657 62 : if (success)
6658 59 : return false;
6659 :
6660 : // If all of the objects except one have property types which reflect the
6661 : // value, and the remaining object has no types at all for the property,
6662 : // add a guard that the object does not have that remaining object's type.
6663 :
6664 3 : if (types->getObjectCount() <= 1)
6665 3 : return true;
6666 :
6667 0 : TypeSet::ObjectKey* excluded = nullptr;
6668 0 : for (size_t i = 0; i < types->getObjectCount(); i++) {
6669 0 : TypeSet::ObjectKey* key = types->getObject(i);
6670 0 : if (!key || key->unknownProperties())
6671 0 : continue;
6672 0 : if (!name && IsTypedArrayClass(key->clasp()))
6673 0 : continue;
6674 :
6675 0 : jsid id = name ? NameToId(name) : JSID_VOID;
6676 0 : HeapTypeSetKey property = key->property(id);
6677 0 : if (CanWriteProperty(alloc, constraints, property, *pvalue, implicitType))
6678 0 : continue;
6679 :
6680 0 : if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded)
6681 0 : return true;
6682 0 : excluded = key;
6683 : }
6684 :
6685 0 : MOZ_ASSERT(excluded);
6686 :
6687 : // If the excluded object is a group with an unboxed layout, make sure it
6688 : // does not have a corresponding native group. Objects with the native
6689 : // group might appear even though they are not in the type set.
6690 0 : if (excluded->isGroup()) {
6691 0 : if (UnboxedLayout* layout = excluded->group()->maybeUnboxedLayout()) {
6692 0 : if (layout->nativeGroup())
6693 0 : return true;
6694 0 : excluded->watchStateChangeForUnboxedConvertedToNative(constraints);
6695 : }
6696 : }
6697 :
6698 0 : *pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true);
6699 0 : return false;
6700 : }
|