WarpBuilder.cpp (116245B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "jit/WarpBuilder.h" 8 9 #include "mozilla/DebugOnly.h" 10 11 #include "jit/BaselineFrame.h" 12 #include "jit/CacheIR.h" 13 #include "jit/CompileInfo.h" 14 #include "jit/InlineScriptTree.h" 15 #include "jit/MIR-wasm.h" 16 #include "jit/MIR.h" 17 #include "jit/MIRGenerator.h" 18 #include "jit/MIRGraph.h" 19 #include "jit/WarpCacheIRTranspiler.h" 20 #include "jit/WarpSnapshot.h" 21 #include "js/friend/ErrorMessages.h" // JSMSG_BAD_CONST_ASSIGN 22 #include "vm/ConstantCompareOperand.h" 23 #include "vm/GeneratorObject.h" 24 #include "vm/Interpreter.h" 25 #include "vm/Opcodes.h" 26 #include "vm/TypeofEqOperand.h" // TypeofEqOperand 27 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 28 # include "vm/UsingHint.h" 29 #endif 30 31 #include "gc/ObjectKind-inl.h" 32 #include "vm/BytecodeIterator-inl.h" 33 #include "vm/BytecodeLocation-inl.h" 34 #include "vm/JSObject-inl.h" 35 36 using namespace js; 37 using namespace js::jit; 38 39 // Used for building the outermost script. 40 WarpBuilder::WarpBuilder(WarpSnapshot& snapshot, MIRGenerator& mirGen, 41 WarpCompilation* warpCompilation) 42 : WarpBuilderShared(snapshot, mirGen, nullptr), 43 warpCompilation_(warpCompilation), 44 graph_(mirGen.graph()), 45 info_(mirGen.outerInfo()), 46 scriptSnapshot_(snapshot.rootScript()), 47 script_(snapshot.rootScript()->script()), 48 loopStack_(mirGen.alloc()) { 49 opSnapshotIter_ = scriptSnapshot_->opSnapshots().getFirst(); 50 } 51 52 // Used for building inlined scripts. 53 WarpBuilder::WarpBuilder(WarpBuilder* caller, WarpScriptSnapshot* snapshot, 54 CompileInfo& compileInfo, CallInfo* inlineCallInfo, 55 MResumePoint* callerResumePoint) 56 : WarpBuilderShared(caller->snapshot(), caller->mirGen(), nullptr), 57 warpCompilation_(caller->warpCompilation()), 58 graph_(caller->mirGen().graph()), 59 info_(compileInfo), 60 scriptSnapshot_(snapshot), 61 script_(snapshot->script()), 62 loopStack_(caller->mirGen().alloc()), 63 callerBuilder_(caller), 64 callerResumePoint_(callerResumePoint), 65 inlineCallInfo_(inlineCallInfo) { 66 opSnapshotIter_ = snapshot->opSnapshots().getFirst(); 67 } 68 69 BytecodeSite* WarpBuilder::newBytecodeSite(BytecodeLocation loc) { 70 jsbytecode* pc = loc.toRawBytecode(); 71 MOZ_ASSERT(info().inlineScriptTree()->script()->containsPC(pc)); 72 return new (alloc()) BytecodeSite(info().inlineScriptTree(), pc); 73 } 74 75 const WarpOpSnapshot* WarpBuilder::getOpSnapshotImpl( 76 BytecodeLocation loc, WarpOpSnapshot::Kind kind) { 77 uint32_t offset = loc.bytecodeToOffset(script_); 78 79 // Skip snapshots until we get to a snapshot with offset >= offset. This is 80 // a loop because WarpBuilder can skip unreachable bytecode ops. 81 while (opSnapshotIter_ && opSnapshotIter_->offset() < offset) { 82 opSnapshotIter_ = opSnapshotIter_->getNext(); 83 } 84 85 if (!opSnapshotIter_ || opSnapshotIter_->offset() != offset || 86 opSnapshotIter_->kind() != kind) { 87 return nullptr; 88 } 89 90 return opSnapshotIter_; 91 } 92 93 void WarpBuilder::initBlock(MBasicBlock* block) { 94 graph().addBlock(block); 95 96 block->setLoopDepth(loopDepth()); 97 98 current = block; 99 } 100 101 bool WarpBuilder::startNewBlock(MBasicBlock* predecessor, BytecodeLocation loc, 102 size_t numToPop) { 103 MBasicBlock* block = 104 MBasicBlock::NewPopN(graph(), info(), predecessor, newBytecodeSite(loc), 105 MBasicBlock::NORMAL, numToPop); 106 if (!block) { 107 return false; 108 } 109 110 initBlock(block); 111 return true; 112 } 113 114 bool WarpBuilder::startNewEntryBlock(size_t stackDepth, BytecodeLocation loc) { 115 MBasicBlock* block = 116 MBasicBlock::New(graph(), stackDepth, info(), /* maybePred = */ nullptr, 117 newBytecodeSite(loc), MBasicBlock::NORMAL); 118 if (!block) { 119 return false; 120 } 121 122 initBlock(block); 123 return true; 124 } 125 126 bool WarpBuilder::startNewLoopHeaderBlock(BytecodeLocation loopHead) { 127 MBasicBlock* header = MBasicBlock::NewPendingLoopHeader( 128 graph(), info(), current, newBytecodeSite(loopHead)); 129 if (!header) { 130 return false; 131 } 132 133 initBlock(header); 134 return loopStack_.emplaceBack(header); 135 } 136 137 bool WarpBuilder::startNewOsrPreHeaderBlock(BytecodeLocation loopHead) { 138 MOZ_ASSERT(loopHead.is(JSOp::LoopHead)); 139 MOZ_ASSERT(loopHead.toRawBytecode() == info().osrPc()); 140 141 // Create two blocks: 142 // * The OSR entry block. This is always the graph's second block and has no 143 // predecessors. This is the entry point for OSR from the Baseline JIT. 144 // * The OSR preheader block. This has two predecessors: the OSR entry block 145 // and the current block. 146 147 MBasicBlock* pred = current; 148 149 // Create the OSR entry block. 150 if (!startNewEntryBlock(pred->stackDepth(), loopHead)) { 151 return false; 152 } 153 154 MBasicBlock* osrBlock = current; 155 graph().setOsrBlock(osrBlock); 156 graph().moveBlockAfter(*graph().begin(), osrBlock); 157 158 MOsrEntry* entry = MOsrEntry::New(alloc()); 159 osrBlock->add(entry); 160 161 // Initialize environment chain. 162 { 163 uint32_t slot = info().environmentChainSlot(); 164 MInstruction* envv; 165 if (usesEnvironmentChain()) { 166 envv = MOsrEnvironmentChain::New(alloc(), entry); 167 } else { 168 // Use an undefined value if the script does not need its environment 169 // chain, to match the main entry point. 170 envv = MConstant::NewUndefined(alloc()); 171 } 172 osrBlock->add(envv); 173 osrBlock->initSlot(slot, envv); 174 } 175 176 // Initialize return value. 177 { 178 MInstruction* returnValue; 179 if (!script_->noScriptRval()) { 180 returnValue = MOsrReturnValue::New(alloc(), entry); 181 } else { 182 returnValue = MConstant::NewUndefined(alloc()); 183 } 184 osrBlock->add(returnValue); 185 osrBlock->initSlot(info().returnValueSlot(), returnValue); 186 } 187 188 // Initialize arguments object. 189 MInstruction* argsObj = nullptr; 190 if (info().needsArgsObj()) { 191 argsObj = MOsrArgumentsObject::New(alloc(), entry); 192 osrBlock->add(argsObj); 193 osrBlock->initSlot(info().argsObjSlot(), argsObj); 194 } 195 196 if (info().hasFunMaybeLazy()) { 197 // Initialize |this| parameter. 198 MParameter* thisv = MParameter::New(alloc(), MParameter::THIS_SLOT); 199 osrBlock->add(thisv); 200 osrBlock->initSlot(info().thisSlot(), thisv); 201 202 // Initialize arguments. There are three cases: 203 // 204 // 1) There's no ArgumentsObject or it doesn't alias formals. In this case 205 // we can just use the frame's argument slot. 206 // 2) The ArgumentsObject aliases formals and the argument is stored in the 207 // CallObject. Use |undefined| because we can't load from the arguments 208 // object and code will use the CallObject anyway. 209 // 3) The ArgumentsObject aliases formals and the argument isn't stored in 210 // the CallObject. We have to load it from the ArgumentsObject. 211 for (uint32_t i = 0; i < info().nargs(); i++) { 212 uint32_t slot = info().argSlotUnchecked(i); 213 MInstruction* osrv; 214 if (!info().argsObjAliasesFormals()) { 215 osrv = MParameter::New(alloc().fallible(), i); 216 } else if (script_->formalIsAliased(i)) { 217 osrv = MConstant::New(alloc().fallible(), UndefinedValue()); 218 } else { 219 osrv = MGetArgumentsObjectArg::New(alloc().fallible(), argsObj, i); 220 } 221 if (!osrv) { 222 return false; 223 } 224 current->add(osrv); 225 current->initSlot(slot, osrv); 226 } 227 } 228 229 // Initialize locals. 230 uint32_t nlocals = info().nlocals(); 231 for (uint32_t i = 0; i < nlocals; i++) { 232 uint32_t slot = info().localSlot(i); 233 ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(i); 234 MOsrValue* osrv = MOsrValue::New(alloc().fallible(), entry, offset); 235 if (!osrv) { 236 return false; 237 } 238 current->add(osrv); 239 current->initSlot(slot, osrv); 240 } 241 242 // Initialize expression stack slots. 243 uint32_t numStackSlots = current->stackDepth() - info().firstStackSlot(); 244 for (uint32_t i = 0; i < numStackSlots; i++) { 245 uint32_t slot = info().stackSlot(i); 246 ptrdiff_t offset = BaselineFrame::reverseOffsetOfLocal(nlocals + i); 247 MOsrValue* osrv = MOsrValue::New(alloc().fallible(), entry, offset); 248 if (!osrv) { 249 return false; 250 } 251 current->add(osrv); 252 current->initSlot(slot, osrv); 253 } 254 255 MStart* start = MStart::New(alloc()); 256 current->add(start); 257 258 // Note: phi specialization can add type guard instructions to the OSR entry 259 // block if needed. See TypeAnalyzer::shouldSpecializeOsrPhis. 260 261 // Create the preheader block, with the predecessor block and OSR block as 262 // predecessors. 263 if (!startNewBlock(pred, loopHead)) { 264 return false; 265 } 266 267 pred->end(MGoto::New(alloc(), current)); 268 osrBlock->end(MGoto::New(alloc(), current)); 269 270 if (!current->addPredecessor(alloc(), osrBlock)) { 271 return false; 272 } 273 274 return true; 275 } 276 277 bool WarpBuilder::addPendingEdge(BytecodeLocation target, MBasicBlock* block, 278 uint32_t successor, uint32_t numToPop) { 279 MOZ_ASSERT(successor < block->lastIns()->numSuccessors()); 280 MOZ_ASSERT(numToPop <= block->stackDepth()); 281 282 jsbytecode* targetPC = target.toRawBytecode(); 283 PendingEdgesMap::AddPtr p = pendingEdges_.lookupForAdd(targetPC); 284 if (p) { 285 return p->value().emplaceBack(block, successor, numToPop); 286 } 287 288 PendingEdges edges; 289 static_assert(PendingEdges::InlineLength >= 1, 290 "Appending one element should be infallible"); 291 MOZ_ALWAYS_TRUE(edges.emplaceBack(block, successor, numToPop)); 292 293 return pendingEdges_.add(p, targetPC, std::move(edges)); 294 } 295 296 bool WarpBuilder::build() { 297 if (!buildPrologue()) { 298 return false; 299 } 300 301 if (!buildBody()) { 302 return false; 303 } 304 305 if (!MPhi::markIteratorPhis(*iterators())) { 306 return false; 307 } 308 309 MOZ_ASSERT_IF(info().osrPc(), graph().osrBlock()); 310 MOZ_ASSERT(loopStack_.empty()); 311 MOZ_ASSERT(loopDepth() == 0); 312 313 return true; 314 } 315 316 bool WarpBuilder::buildInline() { 317 if (!buildInlinePrologue()) { 318 return false; 319 } 320 321 if (!buildBody()) { 322 return false; 323 } 324 325 MOZ_ASSERT(loopStack_.empty()); 326 return true; 327 } 328 329 MInstruction* WarpBuilder::buildNamedLambdaEnv(MDefinition* callee, 330 MDefinition* env, 331 NamedLambdaObject* templateObj, 332 gc::Heap initialHeap) { 333 MOZ_ASSERT(templateObj->numDynamicSlots() == 0); 334 335 MInstruction* namedLambda = 336 MNewNamedLambdaObject::New(alloc(), templateObj, initialHeap); 337 current->add(namedLambda); 338 339 // Initialize the object's reserved slots. 340 if (initialHeap == gc::Heap::Default) { 341 // No post barrier is needed here: the object will be allocated in the 342 // nursery if possible, and if the tenured heap is used instead, a minor 343 // collection will have been performed that moved env/callee to the tenured 344 // heap. 345 #ifdef DEBUG 346 current->add( 347 MAssertCanElidePostWriteBarrier::New(alloc(), namedLambda, env)); 348 current->add( 349 MAssertCanElidePostWriteBarrier::New(alloc(), namedLambda, callee)); 350 #endif 351 } else { 352 current->add(MPostWriteBarrier::New(alloc(), namedLambda, env)); 353 current->add(MPostWriteBarrier::New(alloc(), namedLambda, callee)); 354 } 355 356 size_t enclosingSlot = NamedLambdaObject::enclosingEnvironmentSlot(); 357 size_t lambdaSlot = NamedLambdaObject::lambdaSlot(); 358 current->add(MStoreFixedSlot::NewUnbarriered(alloc(), namedLambda, 359 enclosingSlot, env)); 360 current->add(MStoreFixedSlot::NewUnbarriered(alloc(), namedLambda, lambdaSlot, 361 callee)); 362 363 return namedLambda; 364 } 365 366 MInstruction* WarpBuilder::buildCallObject(MDefinition* callee, 367 MDefinition* env, 368 CallObject* templateObj, 369 gc::Heap initialHeap) { 370 MConstant* templateCst = constant(ObjectValue(*templateObj)); 371 372 MNewCallObject* callObj = 373 MNewCallObject::New(alloc(), templateCst, initialHeap); 374 current->add(callObj); 375 376 // Initialize the object's reserved slots. 377 if (initialHeap == gc::Heap::Default) { 378 // No post barrier is needed here, for the same reason as in 379 // buildNamedLambdaEnv. 380 #ifdef DEBUG 381 current->add(MAssertCanElidePostWriteBarrier::New(alloc(), callObj, env)); 382 current->add( 383 MAssertCanElidePostWriteBarrier::New(alloc(), callObj, callee)); 384 #endif 385 } else { 386 current->add(MPostWriteBarrier::New(alloc(), callObj, env)); 387 current->add(MPostWriteBarrier::New(alloc(), callObj, callee)); 388 } 389 390 size_t enclosingSlot = CallObject::enclosingEnvironmentSlot(); 391 size_t calleeSlot = CallObject::calleeSlot(); 392 current->add( 393 MStoreFixedSlot::NewUnbarriered(alloc(), callObj, enclosingSlot, env)); 394 current->add( 395 MStoreFixedSlot::NewUnbarriered(alloc(), callObj, calleeSlot, callee)); 396 397 return callObj; 398 } 399 400 bool WarpBuilder::buildEnvironmentChain() { 401 const WarpEnvironment& env = scriptSnapshot()->environment(); 402 403 if (env.is<NoEnvironment>()) { 404 return true; 405 } 406 407 MInstruction* envDef = env.match( 408 [](const NoEnvironment&) -> MInstruction* { 409 MOZ_CRASH("Already handled"); 410 }, 411 [this](JSObject* obj) -> MInstruction* { 412 return constant(ObjectValue(*obj)); 413 }, 414 [this](const FunctionEnvironment& env) -> MInstruction* { 415 MDefinition* callee = getCallee(); 416 MInstruction* envDef = MFunctionEnvironment::New(alloc(), callee); 417 current->add(envDef); 418 if (NamedLambdaObject* obj = env.namedLambdaTemplate) { 419 envDef = buildNamedLambdaEnv(callee, envDef, obj, env.initialHeap); 420 } 421 if (CallObject* obj = env.callObjectTemplate) { 422 envDef = buildCallObject(callee, envDef, obj, env.initialHeap); 423 if (!envDef) { 424 return nullptr; 425 } 426 } 427 return envDef; 428 }); 429 if (!envDef) { 430 return false; 431 } 432 433 // Update the environment slot from UndefinedValue only after the initial 434 // environment is created so that bailout doesn't see a partial environment. 435 // See: |BaselineStackBuilder::buildBaselineFrame| 436 current->setEnvironmentChain(envDef); 437 return true; 438 } 439 440 bool WarpBuilder::buildPrologue() { 441 BytecodeLocation startLoc(script_, script_->code()); 442 if (!startNewEntryBlock(info().firstStackSlot(), startLoc)) { 443 return false; 444 } 445 446 if (info().hasFunMaybeLazy()) { 447 // Initialize |this|. 448 MParameter* param = MParameter::New(alloc(), MParameter::THIS_SLOT); 449 current->add(param); 450 current->initSlot(info().thisSlot(), param); 451 452 // Initialize arguments. 453 for (uint32_t i = 0; i < info().nargs(); i++) { 454 MParameter* param = MParameter::New(alloc().fallible(), i); 455 if (!param) { 456 return false; 457 } 458 current->add(param); 459 current->initSlot(info().argSlotUnchecked(i), param); 460 } 461 } 462 463 MConstant* undef = constant(UndefinedValue()); 464 465 // Initialize local slots. 466 for (uint32_t i = 0; i < info().nlocals(); i++) { 467 current->initSlot(info().localSlot(i), undef); 468 } 469 470 // Initialize the environment chain, return value, and arguments object slots. 471 current->initSlot(info().environmentChainSlot(), undef); 472 current->initSlot(info().returnValueSlot(), undef); 473 if (info().needsArgsObj()) { 474 current->initSlot(info().argsObjSlot(), undef); 475 } 476 477 current->add(MStart::New(alloc())); 478 479 // Guard against over-recursion. 480 MCheckOverRecursed* check = MCheckOverRecursed::New(alloc()); 481 current->add(check); 482 483 if (!buildEnvironmentChain()) { 484 return false; 485 } 486 487 #ifdef JS_CACHEIR_SPEW 488 if (snapshot().needsFinalWarmUpCount()) { 489 MIncrementWarmUpCounter* ins = 490 MIncrementWarmUpCounter::New(alloc(), script_); 491 current->add(ins); 492 } 493 #endif 494 495 return true; 496 } 497 498 bool WarpBuilder::buildInlinePrologue() { 499 // Generate entry block. 500 BytecodeLocation startLoc(script_, script_->code()); 501 if (!startNewEntryBlock(info().firstStackSlot(), startLoc)) { 502 return false; 503 } 504 current->setCallerResumePoint(callerResumePoint()); 505 506 // Connect the entry block to the last block in the caller's graph. 507 MBasicBlock* pred = callerBuilder()->current; 508 MOZ_ASSERT(pred == callerResumePoint()->block()); 509 510 pred->end(MGoto::New(alloc(), current)); 511 if (!current->addPredecessorWithoutPhis(pred)) { 512 return false; 513 } 514 515 MConstant* undef = constant(UndefinedValue()); 516 517 // Initialize env chain slot to Undefined. It's set later by 518 // |buildEnvironmentChain|. 519 current->initSlot(info().environmentChainSlot(), undef); 520 521 // Initialize |return value| slot. 522 current->initSlot(info().returnValueSlot(), undef); 523 524 // Initialize |arguments| slot if needed. 525 if (info().needsArgsObj()) { 526 current->initSlot(info().argsObjSlot(), undef); 527 } 528 529 // Initialize |this| slot. 530 current->initSlot(info().thisSlot(), inlineCallInfo()->thisArg()); 531 532 uint32_t callerArgs = inlineCallInfo()->argc(); 533 uint32_t actualArgs = info().nargs(); 534 uint32_t passedArgs = std::min<uint32_t>(callerArgs, actualArgs); 535 536 // Initialize actually set arguments. 537 for (uint32_t i = 0; i < passedArgs; i++) { 538 MDefinition* arg = inlineCallInfo()->getArg(i); 539 current->initSlot(info().argSlotUnchecked(i), arg); 540 } 541 542 // Pass undefined for missing arguments. 543 for (uint32_t i = passedArgs; i < actualArgs; i++) { 544 current->initSlot(info().argSlotUnchecked(i), undef); 545 } 546 547 // Initialize local slots. 548 for (uint32_t i = 0; i < info().nlocals(); i++) { 549 current->initSlot(info().localSlot(i), undef); 550 } 551 552 MOZ_ASSERT(current->entryResumePoint()->stackDepth() == info().totalSlots()); 553 554 if (!buildEnvironmentChain()) { 555 return false; 556 } 557 558 return true; 559 } 560 561 #ifdef DEBUG 562 // In debug builds, after compiling a bytecode op, this class is used to check 563 // that all values popped by this opcode either: 564 // 565 // (1) Have the ImplicitlyUsed flag set on them. 566 // (2) Have more uses than before compiling this op (the value is 567 // used as operand of a new MIR instruction). 568 // 569 // This is used to catch problems where WarpBuilder pops a value without 570 // adding any SSA uses and doesn't call setImplicitlyUsedUnchecked on it. 571 class MOZ_RAII WarpPoppedValueUseChecker { 572 Vector<MDefinition*, 4, SystemAllocPolicy> popped_; 573 Vector<size_t, 4, SystemAllocPolicy> poppedUses_; 574 MBasicBlock* current_; 575 BytecodeLocation loc_; 576 577 public: 578 WarpPoppedValueUseChecker(MBasicBlock* current, BytecodeLocation loc) 579 : current_(current), loc_(loc) {} 580 581 [[nodiscard]] bool init() { 582 // Don't require SSA uses for values popped by these ops. 583 switch (loc_.getOp()) { 584 case JSOp::Pop: 585 case JSOp::PopN: 586 case JSOp::DupAt: 587 case JSOp::Dup: 588 case JSOp::Dup2: 589 case JSOp::Pick: 590 case JSOp::Unpick: 591 case JSOp::Swap: 592 case JSOp::SetArg: 593 case JSOp::SetLocal: 594 case JSOp::InitLexical: 595 case JSOp::SetRval: 596 case JSOp::Void: 597 // Basic stack/local/argument management opcodes. 598 return true; 599 600 case JSOp::Case: 601 case JSOp::Default: 602 // These ops have to pop the switch value when branching but don't 603 // actually use it. 604 return true; 605 606 default: 607 break; 608 } 609 610 unsigned nuses = loc_.useCount(); 611 612 for (unsigned i = 0; i < nuses; i++) { 613 MDefinition* def = current_->peek(-int32_t(i + 1)); 614 if (!popped_.append(def) || !poppedUses_.append(def->defUseCount())) { 615 return false; 616 } 617 } 618 619 return true; 620 } 621 622 void checkAfterOp() { 623 for (size_t i = 0; i < popped_.length(); i++) { 624 // First value popped by JSOp::EndIter is not used at all, it's similar 625 // to JSOp::Pop above. 626 if (loc_.is(JSOp::EndIter) && i == 0) { 627 continue; 628 } 629 MOZ_ASSERT(popped_[i]->isImplicitlyUsed() || 630 popped_[i]->defUseCount() > poppedUses_[i]); 631 } 632 } 633 }; 634 #endif 635 636 bool WarpBuilder::buildBody() { 637 for (BytecodeLocation loc : AllBytecodesIterable(script_)) { 638 if (mirGen().shouldCancel("WarpBuilder (opcode loop)")) { 639 return false; 640 } 641 642 // Skip unreachable ops (for example code after a 'return' or 'throw') until 643 // we get to the next jump target. 644 if (hasTerminatedBlock()) { 645 // Finish any "broken" loops with an unreachable backedge. For example: 646 // 647 // do { 648 // ... 649 // return; 650 // ... 651 // } while (x); 652 // 653 // This loop never actually loops. 654 if (loc.isBackedge() && !loopStack_.empty()) { 655 BytecodeLocation loopHead(script_, 656 loopStack_.back().header()->entryPC()); 657 if (loc.isBackedgeForLoophead(loopHead)) { 658 decLoopDepth(); 659 loopStack_.popBack(); 660 } 661 } 662 if (!loc.isJumpTarget()) { 663 continue; 664 } 665 } 666 667 if (!alloc().ensureBallast()) { 668 return false; 669 } 670 671 #ifdef DEBUG 672 WarpPoppedValueUseChecker useChecker(current, loc); 673 if (!useChecker.init()) { 674 return false; 675 } 676 #endif 677 bool wantPreciseLineNumbers = js::jit::PerfEnabled(); 678 if (wantPreciseLineNumbers && !hasTerminatedBlock()) { 679 current->updateTrackedSite(newBytecodeSite(loc)); 680 } 681 682 JSOp op = loc.getOp(); 683 684 #define BUILD_OP(OP, ...) \ 685 case JSOp::OP: \ 686 if (MOZ_UNLIKELY(!this->build_##OP(loc))) { \ 687 return false; \ 688 } \ 689 break; 690 switch (op) { FOR_EACH_OPCODE(BUILD_OP) } 691 #undef BUILD_OP 692 693 #ifdef DEBUG 694 useChecker.checkAfterOp(); 695 #endif 696 } 697 698 return true; 699 } 700 701 #define DEF_OP(OP) \ 702 bool WarpBuilder::build_##OP(BytecodeLocation) { \ 703 MOZ_CRASH("Unsupported op"); \ 704 } 705 WARP_UNSUPPORTED_OPCODE_LIST(DEF_OP) 706 #undef DEF_OP 707 708 bool WarpBuilder::build_Nop(BytecodeLocation) { return true; } 709 710 bool WarpBuilder::build_NopDestructuring(BytecodeLocation) { return true; } 711 712 bool WarpBuilder::build_NopIsAssignOp(BytecodeLocation) { return true; } 713 714 bool WarpBuilder::build_TryDestructuring(BytecodeLocation) { 715 // Set the hasTryBlock flag to turn off optimizations that eliminate dead 716 // resume points operands because the exception handler code for 717 // TryNoteKind::Destructuring is effectively a (specialized) catch-block. 718 graph().setHasTryBlock(); 719 return true; 720 } 721 722 bool WarpBuilder::build_Lineno(BytecodeLocation) { return true; } 723 724 bool WarpBuilder::build_DebugLeaveLexicalEnv(BytecodeLocation) { return true; } 725 726 bool WarpBuilder::build_Undefined(BytecodeLocation) { 727 pushConstant(UndefinedValue()); 728 return true; 729 } 730 731 bool WarpBuilder::build_Void(BytecodeLocation) { 732 current->pop(); 733 pushConstant(UndefinedValue()); 734 return true; 735 } 736 737 bool WarpBuilder::build_Null(BytecodeLocation) { 738 pushConstant(NullValue()); 739 return true; 740 } 741 742 bool WarpBuilder::build_Hole(BytecodeLocation) { 743 pushConstant(MagicValue(JS_ELEMENTS_HOLE)); 744 return true; 745 } 746 747 bool WarpBuilder::build_Uninitialized(BytecodeLocation) { 748 pushConstant(MagicValue(JS_UNINITIALIZED_LEXICAL)); 749 return true; 750 } 751 752 bool WarpBuilder::build_IsConstructing(BytecodeLocation) { 753 pushConstant(MagicValue(JS_IS_CONSTRUCTING)); 754 return true; 755 } 756 757 bool WarpBuilder::build_False(BytecodeLocation) { 758 pushConstant(BooleanValue(false)); 759 return true; 760 } 761 762 bool WarpBuilder::build_True(BytecodeLocation) { 763 pushConstant(BooleanValue(true)); 764 return true; 765 } 766 767 bool WarpBuilder::build_Pop(BytecodeLocation) { 768 current->pop(); 769 return true; 770 } 771 772 bool WarpBuilder::build_PopN(BytecodeLocation loc) { 773 for (uint32_t i = 0, n = loc.getPopCount(); i < n; i++) { 774 current->pop(); 775 } 776 return true; 777 } 778 779 bool WarpBuilder::build_Dup(BytecodeLocation) { 780 current->pushSlot(current->stackDepth() - 1); 781 return true; 782 } 783 784 bool WarpBuilder::build_Dup2(BytecodeLocation) { 785 uint32_t lhsSlot = current->stackDepth() - 2; 786 uint32_t rhsSlot = current->stackDepth() - 1; 787 current->pushSlot(lhsSlot); 788 current->pushSlot(rhsSlot); 789 return true; 790 } 791 792 bool WarpBuilder::build_DupAt(BytecodeLocation loc) { 793 current->pushSlot(current->stackDepth() - 1 - loc.getDupAtIndex()); 794 return true; 795 } 796 797 bool WarpBuilder::build_Swap(BytecodeLocation) { 798 current->swapAt(-1); 799 return true; 800 } 801 802 bool WarpBuilder::build_Pick(BytecodeLocation loc) { 803 int32_t depth = -int32_t(loc.getPickDepth()); 804 current->pick(depth); 805 return true; 806 } 807 808 bool WarpBuilder::build_Unpick(BytecodeLocation loc) { 809 int32_t depth = -int32_t(loc.getUnpickDepth()); 810 current->unpick(depth); 811 return true; 812 } 813 814 bool WarpBuilder::build_Zero(BytecodeLocation) { 815 pushConstant(Int32Value(0)); 816 return true; 817 } 818 819 bool WarpBuilder::build_One(BytecodeLocation) { 820 pushConstant(Int32Value(1)); 821 return true; 822 } 823 824 bool WarpBuilder::build_Int8(BytecodeLocation loc) { 825 pushConstant(Int32Value(loc.getInt8())); 826 return true; 827 } 828 829 bool WarpBuilder::build_Uint16(BytecodeLocation loc) { 830 pushConstant(Int32Value(loc.getUint16())); 831 return true; 832 } 833 834 bool WarpBuilder::build_Uint24(BytecodeLocation loc) { 835 pushConstant(Int32Value(loc.getUint24())); 836 return true; 837 } 838 839 bool WarpBuilder::build_Int32(BytecodeLocation loc) { 840 pushConstant(Int32Value(loc.getInt32())); 841 return true; 842 } 843 844 bool WarpBuilder::build_Double(BytecodeLocation loc) { 845 pushConstant(loc.getInlineValue()); 846 return true; 847 } 848 849 bool WarpBuilder::build_BigInt(BytecodeLocation loc) { 850 BigInt* bi = loc.getBigInt(script_); 851 pushConstant(BigIntValue(bi)); 852 return true; 853 } 854 855 bool WarpBuilder::build_String(BytecodeLocation loc) { 856 JSString* str = loc.getString(script_); 857 pushConstant(StringValue(str)); 858 return true; 859 } 860 861 bool WarpBuilder::build_Symbol(BytecodeLocation loc) { 862 uint32_t which = loc.getSymbolIndex(); 863 JS::Symbol* sym = mirGen().runtime->wellKnownSymbols().get(which); 864 pushConstant(SymbolValue(sym)); 865 return true; 866 } 867 868 bool WarpBuilder::build_RegExp(BytecodeLocation loc) { 869 RegExpObject* reObj = loc.getRegExp(script_); 870 871 auto* snapshot = getOpSnapshot<WarpRegExp>(loc); 872 873 MRegExp* regexp = MRegExp::New(alloc(), reObj, snapshot->hasShared()); 874 current->add(regexp); 875 current->push(regexp); 876 877 return true; 878 } 879 880 bool WarpBuilder::build_Return(BytecodeLocation) { 881 MDefinition* def = current->pop(); 882 883 MReturn* ret = MReturn::New(alloc(), def); 884 current->end(ret); 885 886 if (!graph().addReturn(current)) { 887 return false; 888 } 889 890 setTerminatedBlock(); 891 return true; 892 } 893 894 bool WarpBuilder::build_RetRval(BytecodeLocation) { 895 MDefinition* rval; 896 if (script_->noScriptRval()) { 897 rval = constant(UndefinedValue()); 898 } else { 899 rval = current->getSlot(info().returnValueSlot()); 900 } 901 902 MReturn* ret = MReturn::New(alloc(), rval); 903 current->end(ret); 904 905 if (!graph().addReturn(current)) { 906 return false; 907 } 908 909 setTerminatedBlock(); 910 return true; 911 } 912 913 bool WarpBuilder::build_SetRval(BytecodeLocation) { 914 MOZ_ASSERT(!script_->noScriptRval()); 915 MDefinition* rval = current->pop(); 916 current->setSlot(info().returnValueSlot(), rval); 917 return true; 918 } 919 920 bool WarpBuilder::build_GetRval(BytecodeLocation) { 921 MOZ_ASSERT(!script_->noScriptRval()); 922 MDefinition* rval = current->getSlot(info().returnValueSlot()); 923 current->push(rval); 924 return true; 925 } 926 927 bool WarpBuilder::build_GetLocal(BytecodeLocation loc) { 928 current->pushLocal(loc.local()); 929 return true; 930 } 931 932 bool WarpBuilder::build_SetLocal(BytecodeLocation loc) { 933 current->setLocal(loc.local()); 934 return true; 935 } 936 937 bool WarpBuilder::build_InitLexical(BytecodeLocation loc) { 938 current->setLocal(loc.local()); 939 return true; 940 } 941 942 bool WarpBuilder::build_GetArg(BytecodeLocation loc) { 943 uint32_t arg = loc.arg(); 944 if (info().argsObjAliasesFormals()) { 945 MDefinition* argsObj = current->argumentsObject(); 946 auto* getArg = MGetArgumentsObjectArg::New(alloc(), argsObj, arg); 947 current->add(getArg); 948 current->push(getArg); 949 } else { 950 current->pushArg(arg); 951 } 952 return true; 953 } 954 955 bool WarpBuilder::build_GetFrameArg(BytecodeLocation loc) { 956 current->pushArgUnchecked(loc.arg()); 957 return true; 958 } 959 960 bool WarpBuilder::build_SetArg(BytecodeLocation loc) { 961 uint32_t arg = loc.arg(); 962 MDefinition* val = current->peek(-1); 963 964 if (!info().argsObjAliasesFormals()) { 965 // Either |arguments| is never referenced within this function, or 966 // it doesn't map to the actual arguments values. Either way, we 967 // don't need to worry about synchronizing the argument values 968 // when writing to them. 969 current->setArg(arg); 970 return true; 971 } 972 973 // If an arguments object is in use, and it aliases formals, then all SetArgs 974 // must go through the arguments object. 975 MDefinition* argsObj = current->argumentsObject(); 976 current->add(MPostWriteBarrier::New(alloc(), argsObj, val)); 977 auto* ins = MSetArgumentsObjectArg::New(alloc(), argsObj, val, arg); 978 current->add(ins); 979 return resumeAfter(ins, loc); 980 } 981 982 bool WarpBuilder::build_ArgumentsLength(BytecodeLocation) { 983 if (inlineCallInfo()) { 984 pushConstant(Int32Value(inlineCallInfo()->argc())); 985 } else { 986 auto* argsLength = MArgumentsLength::New(alloc()); 987 current->add(argsLength); 988 current->push(argsLength); 989 } 990 return true; 991 } 992 993 bool WarpBuilder::build_GetActualArg(BytecodeLocation) { 994 MDefinition* index = current->pop(); 995 MInstruction* arg; 996 if (inlineCallInfo()) { 997 arg = MGetInlinedArgument::New(alloc(), index, *inlineCallInfo()); 998 if (!arg) { 999 return false; 1000 } 1001 } else { 1002 arg = MGetFrameArgument::New(alloc(), index); 1003 } 1004 current->add(arg); 1005 current->push(arg); 1006 return true; 1007 } 1008 1009 bool WarpBuilder::build_ToNumeric(BytecodeLocation loc) { 1010 return buildUnaryOp(loc); 1011 } 1012 1013 bool WarpBuilder::buildUnaryOp(BytecodeLocation loc) { 1014 MDefinition* value = current->pop(); 1015 return buildIC(loc, CacheKind::UnaryArith, {value}); 1016 } 1017 1018 bool WarpBuilder::build_Inc(BytecodeLocation loc) { return buildUnaryOp(loc); } 1019 1020 bool WarpBuilder::build_Dec(BytecodeLocation loc) { return buildUnaryOp(loc); } 1021 1022 bool WarpBuilder::build_Pos(BytecodeLocation loc) { return buildUnaryOp(loc); } 1023 1024 bool WarpBuilder::build_Neg(BytecodeLocation loc) { return buildUnaryOp(loc); } 1025 1026 bool WarpBuilder::build_BitNot(BytecodeLocation loc) { 1027 return buildUnaryOp(loc); 1028 } 1029 1030 bool WarpBuilder::buildBinaryOp(BytecodeLocation loc) { 1031 MDefinition* right = current->pop(); 1032 MDefinition* left = current->pop(); 1033 return buildIC(loc, CacheKind::BinaryArith, {left, right}); 1034 } 1035 1036 bool WarpBuilder::build_Add(BytecodeLocation loc) { return buildBinaryOp(loc); } 1037 1038 bool WarpBuilder::build_Sub(BytecodeLocation loc) { return buildBinaryOp(loc); } 1039 1040 bool WarpBuilder::build_Mul(BytecodeLocation loc) { return buildBinaryOp(loc); } 1041 1042 bool WarpBuilder::build_Div(BytecodeLocation loc) { return buildBinaryOp(loc); } 1043 1044 bool WarpBuilder::build_Mod(BytecodeLocation loc) { return buildBinaryOp(loc); } 1045 1046 bool WarpBuilder::build_Pow(BytecodeLocation loc) { return buildBinaryOp(loc); } 1047 1048 bool WarpBuilder::build_BitAnd(BytecodeLocation loc) { 1049 return buildBinaryOp(loc); 1050 } 1051 1052 bool WarpBuilder::build_BitOr(BytecodeLocation loc) { 1053 return buildBinaryOp(loc); 1054 } 1055 1056 bool WarpBuilder::build_BitXor(BytecodeLocation loc) { 1057 return buildBinaryOp(loc); 1058 } 1059 1060 bool WarpBuilder::build_Lsh(BytecodeLocation loc) { return buildBinaryOp(loc); } 1061 1062 bool WarpBuilder::build_Rsh(BytecodeLocation loc) { return buildBinaryOp(loc); } 1063 1064 bool WarpBuilder::build_Ursh(BytecodeLocation loc) { 1065 return buildBinaryOp(loc); 1066 } 1067 1068 bool WarpBuilder::buildCompareOp(BytecodeLocation loc) { 1069 MDefinition* right = current->pop(); 1070 MDefinition* left = current->pop(); 1071 return buildIC(loc, CacheKind::Compare, {left, right}); 1072 } 1073 1074 bool WarpBuilder::build_Eq(BytecodeLocation loc) { return buildCompareOp(loc); } 1075 1076 bool WarpBuilder::build_Ne(BytecodeLocation loc) { return buildCompareOp(loc); } 1077 1078 bool WarpBuilder::build_Lt(BytecodeLocation loc) { return buildCompareOp(loc); } 1079 1080 bool WarpBuilder::build_Le(BytecodeLocation loc) { return buildCompareOp(loc); } 1081 1082 bool WarpBuilder::build_Gt(BytecodeLocation loc) { return buildCompareOp(loc); } 1083 1084 bool WarpBuilder::build_Ge(BytecodeLocation loc) { return buildCompareOp(loc); } 1085 1086 bool WarpBuilder::build_StrictEq(BytecodeLocation loc) { 1087 return buildCompareOp(loc); 1088 } 1089 1090 bool WarpBuilder::build_StrictNe(BytecodeLocation loc) { 1091 return buildCompareOp(loc); 1092 } 1093 1094 bool WarpBuilder::buildStrictConstantEqOp(BytecodeLocation loc, 1095 JSOp compareOp) { 1096 auto operand = loc.getConstantCompareOperand(); 1097 MDefinition* value = current->pop(); 1098 switch (operand.type()) { 1099 case ConstantCompareOperand::EncodedType::Int32: { 1100 if (value->type() == MIRType::Int32) { 1101 MConstant* constant = MConstant::NewInt32(alloc(), operand.toInt32()); 1102 current->add(constant); 1103 1104 auto* compare = MCompare::New(alloc(), value, constant, compareOp, 1105 MCompare::Compare_Int32); 1106 current->add(compare); 1107 current->push(compare); 1108 return true; 1109 } 1110 1111 auto* ins = MStrictConstantCompareInt32::New( 1112 alloc(), value, operand.toInt32(), compareOp); 1113 current->add(ins); 1114 current->push(ins); 1115 return true; 1116 } 1117 1118 case ConstantCompareOperand::EncodedType::Boolean: { 1119 if (value->type() == MIRType::Boolean) { 1120 MConstant* constant = MConstant::NewInt32(alloc(), operand.toBoolean()); 1121 current->add(constant); 1122 1123 auto* toBoolToInt32 = MBooleanToInt32::New(alloc(), value); 1124 current->add(toBoolToInt32); 1125 1126 auto* compare = MCompare::New(alloc(), toBoolToInt32, constant, 1127 compareOp, MCompare::Compare_Int32); 1128 current->add(compare); 1129 current->push(compare); 1130 return true; 1131 } 1132 1133 auto* ins = MStrictConstantCompareBoolean::New( 1134 alloc(), value, operand.toBoolean(), compareOp); 1135 current->add(ins); 1136 current->push(ins); 1137 return true; 1138 } 1139 1140 case ConstantCompareOperand::EncodedType::Null: { 1141 MConstant* constant = MConstant::NewNull(alloc()); 1142 current->add(constant); 1143 1144 auto* ins = MCompare::New(alloc(), value, constant, compareOp, 1145 MCompare::Compare_Null); 1146 current->add(ins); 1147 current->push(ins); 1148 return true; 1149 } 1150 1151 case ConstantCompareOperand::EncodedType::Undefined: { 1152 MConstant* constant = MConstant::NewUndefined(alloc()); 1153 current->add(constant); 1154 1155 auto* ins = MCompare::New(alloc(), value, constant, compareOp, 1156 MCompare::Compare_Undefined); 1157 current->add(ins); 1158 current->push(ins); 1159 return true; 1160 } 1161 } 1162 return true; 1163 } 1164 1165 bool WarpBuilder::build_StrictConstantEq(BytecodeLocation loc) { 1166 return buildStrictConstantEqOp(loc, JSOp::StrictEq); 1167 } 1168 1169 bool WarpBuilder::build_StrictConstantNe(BytecodeLocation loc) { 1170 return buildStrictConstantEqOp(loc, JSOp::StrictNe); 1171 } 1172 1173 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 1174 bool WarpBuilder::build_AddDisposable(BytecodeLocation loc) { 1175 MOZ_ASSERT(usesEnvironmentChain()); 1176 1177 UsingHint hint = loc.getUsingHint(); 1178 MDefinition* needsClosure = current->pop(); 1179 MDefinition* method = current->pop(); 1180 MDefinition* obj = current->pop(); 1181 MDefinition* env = current->environmentChain(); 1182 1183 MAddDisposableResource* ins = MAddDisposableResource::New( 1184 alloc(), env, obj, method, needsClosure, uint8_t(hint)); 1185 current->add(ins); 1186 return resumeAfter(ins, loc); 1187 } 1188 1189 bool WarpBuilder::build_TakeDisposeCapability(BytecodeLocation loc) { 1190 MOZ_ASSERT(usesEnvironmentChain()); 1191 MDefinition* env = current->environmentChain(); 1192 1193 MTakeDisposeCapability* ins = MTakeDisposeCapability::New(alloc(), env); 1194 current->add(ins); 1195 current->push(ins); 1196 return resumeAfter(ins, loc); 1197 } 1198 1199 bool WarpBuilder::build_CreateSuppressedError(BytecodeLocation loc) { 1200 MDefinition* suppressed = current->pop(); 1201 MDefinition* error = current->pop(); 1202 1203 MCreateSuppressedError* ins = 1204 MCreateSuppressedError::New(alloc(), error, suppressed); 1205 current->add(ins); 1206 current->push(ins); 1207 return true; 1208 } 1209 #endif 1210 1211 // Returns true iff the MTest added for |op| has a true-target corresponding 1212 // with the join point in the bytecode. 1213 static bool TestTrueTargetIsJoinPoint(JSOp op) { 1214 switch (op) { 1215 case JSOp::JumpIfTrue: 1216 case JSOp::Or: 1217 case JSOp::Case: 1218 return true; 1219 1220 case JSOp::JumpIfFalse: 1221 case JSOp::And: 1222 case JSOp::Coalesce: 1223 return false; 1224 1225 default: 1226 MOZ_CRASH("Unexpected op"); 1227 } 1228 } 1229 1230 bool WarpBuilder::build_JumpTarget(BytecodeLocation loc) { 1231 PendingEdgesMap::Ptr p = pendingEdges_.lookup(loc.toRawBytecode()); 1232 if (!p) { 1233 // No (reachable) jumps so this is just a no-op. 1234 return true; 1235 } 1236 1237 PendingEdges edges(std::move(p->value())); 1238 pendingEdges_.remove(p); 1239 1240 MOZ_ASSERT(!edges.empty()); 1241 1242 // Create join block if there's fall-through from the previous bytecode op. 1243 if (!hasTerminatedBlock()) { 1244 MBasicBlock* pred = current; 1245 if (!startNewBlock(pred, loc)) { 1246 return false; 1247 } 1248 pred->end(MGoto::New(alloc(), current)); 1249 } 1250 1251 for (const PendingEdge& edge : edges) { 1252 MBasicBlock* source = edge.block(); 1253 uint32_t numToPop = edge.numToPop(); 1254 1255 if (hasTerminatedBlock()) { 1256 if (!startNewBlock(source, loc, numToPop)) { 1257 return false; 1258 } 1259 } else { 1260 MOZ_ASSERT(source->stackDepth() - numToPop == current->stackDepth()); 1261 if (!current->addPredecessorPopN(alloc(), source, numToPop)) { 1262 return false; 1263 } 1264 } 1265 1266 MOZ_ASSERT(source->lastIns()->isTest() || source->lastIns()->isGoto() || 1267 source->lastIns()->isTableSwitch()); 1268 source->lastIns()->initSuccessor(edge.successor(), current); 1269 } 1270 1271 MOZ_ASSERT(!hasTerminatedBlock()); 1272 return true; 1273 } 1274 1275 bool WarpBuilder::addIteratorLoopPhis(BytecodeLocation loopHead) { 1276 // When unwinding the stack for a thrown exception, the exception handler must 1277 // close live iterators. For ForIn and Destructuring loops, the exception 1278 // handler needs access to values on the stack. To prevent them from being 1279 // optimized away (and replaced with the JS_OPTIMIZED_OUT MagicValue), we need 1280 // to mark the phis (and phis they flow into) as having implicit uses. 1281 // See ProcessTryNotes in vm/Interpreter.cpp and CloseLiveIteratorIon in 1282 // jit/JitFrames.cpp 1283 1284 MOZ_ASSERT(current->stackDepth() >= info().firstStackSlot()); 1285 1286 bool emptyStack = current->stackDepth() == info().firstStackSlot(); 1287 if (emptyStack) { 1288 return true; 1289 } 1290 1291 jsbytecode* loopHeadPC = loopHead.toRawBytecode(); 1292 1293 for (TryNoteIterAllNoGC tni(script_, loopHeadPC); !tni.done(); ++tni) { 1294 const TryNote& tn = **tni; 1295 1296 // Stop if we reach an outer loop because outer loops were already 1297 // processed when we visited their loop headers. 1298 if (tn.isLoop()) { 1299 BytecodeLocation tnStart = script_->offsetToLocation(tn.start); 1300 if (tnStart != loopHead) { 1301 MOZ_ASSERT(tnStart.is(JSOp::LoopHead)); 1302 MOZ_ASSERT(tnStart < loopHead); 1303 return true; 1304 } 1305 } 1306 1307 switch (tn.kind()) { 1308 case TryNoteKind::Destructuring: 1309 case TryNoteKind::ForIn: { 1310 // For for-in loops we add the iterator object to iterators(). For 1311 // destructuring loops we add the "done" value that's on top of the 1312 // stack and used in the exception handler. 1313 MOZ_ASSERT(tn.stackDepth >= 1); 1314 uint32_t slot = info().stackSlot(tn.stackDepth - 1); 1315 MPhi* phi = current->getSlot(slot)->toPhi(); 1316 if (!iterators()->append(phi)) { 1317 return false; 1318 } 1319 break; 1320 } 1321 case TryNoteKind::Loop: 1322 case TryNoteKind::ForOf: 1323 // Regular loops do not have iterators to close. ForOf loops handle 1324 // unwinding using catch blocks. 1325 break; 1326 default: 1327 break; 1328 } 1329 } 1330 1331 return true; 1332 } 1333 1334 bool WarpBuilder::build_LoopHead(BytecodeLocation loc) { 1335 // All loops have the following bytecode structure: 1336 // 1337 // LoopHead 1338 // ... 1339 // JumpIfTrue/Goto to LoopHead 1340 1341 if (hasTerminatedBlock()) { 1342 // The whole loop is unreachable. 1343 return true; 1344 } 1345 1346 // Handle OSR from Baseline JIT code. 1347 if (loc.toRawBytecode() == info().osrPc()) { 1348 if (!startNewOsrPreHeaderBlock(loc)) { 1349 return false; 1350 } 1351 } 1352 1353 incLoopDepth(); 1354 1355 MBasicBlock* pred = current; 1356 if (!startNewLoopHeaderBlock(loc)) { 1357 return false; 1358 } 1359 1360 pred->end(MGoto::New(alloc(), current)); 1361 1362 if (!addIteratorLoopPhis(loc)) { 1363 return false; 1364 } 1365 1366 MInterruptCheck* check = MInterruptCheck::New(alloc()); 1367 current->add(check); 1368 1369 #ifdef JS_CACHEIR_SPEW 1370 if (snapshot().needsFinalWarmUpCount()) { 1371 MIncrementWarmUpCounter* ins = 1372 MIncrementWarmUpCounter::New(alloc(), script_); 1373 current->add(ins); 1374 } 1375 #endif 1376 1377 return true; 1378 } 1379 1380 bool WarpBuilder::buildTestOp(BytecodeLocation loc) { 1381 MDefinition* originalValue = current->peek(-1); 1382 1383 if (auto* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc)) { 1384 // If we have CacheIR, we can use it to refine the input. Note that 1385 // the transpiler doesn't generate any control instructions. Instead, 1386 // we fall through and generate them below. 1387 MDefinition* value = current->pop(); 1388 if (!TranspileCacheIRToMIR(this, loc, cacheIRSnapshot, {value})) { 1389 return false; 1390 } 1391 } 1392 1393 if (loc.isBackedge()) { 1394 return buildTestBackedge(loc); 1395 } 1396 1397 JSOp op = loc.getOp(); 1398 BytecodeLocation target1 = loc.next(); 1399 BytecodeLocation target2 = loc.getJumpTarget(); 1400 1401 if (TestTrueTargetIsJoinPoint(op)) { 1402 std::swap(target1, target2); 1403 } 1404 1405 MDefinition* value = current->pop(); 1406 1407 // JSOp::And and JSOp::Or leave the top stack value unchanged. The 1408 // top stack value may have been converted to bool by a transpiled 1409 // ToBool IC, so we push the original value. 1410 bool mustKeepCondition = (op == JSOp::And || op == JSOp::Or); 1411 if (mustKeepCondition) { 1412 current->push(originalValue); 1413 } 1414 1415 // If this op always branches to the same location we treat this as a 1416 // JSOp::Goto. 1417 if (target1 == target2) { 1418 value->setImplicitlyUsedUnchecked(); 1419 return buildForwardGoto(target1); 1420 } 1421 1422 MTest* test = MTest::New(alloc(), value, /* ifTrue = */ nullptr, 1423 /* ifFalse = */ nullptr); 1424 current->end(test); 1425 1426 // JSOp::Case must pop a second value on the true-branch (the input to the 1427 // switch-statement). 1428 uint32_t numToPop = (loc.getOp() == JSOp::Case) ? 1 : 0; 1429 1430 if (!addPendingEdge(target1, current, MTest::TrueBranchIndex, numToPop)) { 1431 return false; 1432 } 1433 if (!addPendingEdge(target2, current, MTest::FalseBranchIndex)) { 1434 return false; 1435 } 1436 1437 if (const auto* typesSnapshot = getOpSnapshot<WarpPolymorphicTypes>(loc)) { 1438 test->setObservedTypes(typesSnapshot->list()); 1439 } 1440 1441 setTerminatedBlock(); 1442 return true; 1443 } 1444 1445 bool WarpBuilder::buildTestBackedge(BytecodeLocation loc) { 1446 MOZ_ASSERT(loc.is(JSOp::JumpIfTrue)); 1447 MOZ_ASSERT(loopDepth() > 0); 1448 1449 MDefinition* value = current->pop(); 1450 1451 BytecodeLocation loopHead = loc.getJumpTarget(); 1452 MOZ_ASSERT(loopHead.is(JSOp::LoopHead)); 1453 1454 BytecodeLocation successor = loc.next(); 1455 1456 // We can finish the loop now. Use the loophead pc instead of the current pc 1457 // because the stack depth at the start of that op matches the current stack 1458 // depth (after popping our operand). 1459 MBasicBlock* pred = current; 1460 if (!startNewBlock(current, loopHead)) { 1461 return false; 1462 } 1463 1464 MTest* test = MTest::New(alloc(), value, /* ifTrue = */ current, 1465 /* ifFalse = */ nullptr); 1466 pred->end(test); 1467 1468 if (const auto* typesSnapshot = getOpSnapshot<WarpPolymorphicTypes>(loc)) { 1469 test->setObservedTypes(typesSnapshot->list()); 1470 } 1471 1472 if (!addPendingEdge(successor, pred, MTest::FalseBranchIndex)) { 1473 return false; 1474 } 1475 1476 return buildBackedge(); 1477 } 1478 1479 bool WarpBuilder::build_JumpIfFalse(BytecodeLocation loc) { 1480 return buildTestOp(loc); 1481 } 1482 1483 bool WarpBuilder::build_JumpIfTrue(BytecodeLocation loc) { 1484 return buildTestOp(loc); 1485 } 1486 1487 bool WarpBuilder::build_And(BytecodeLocation loc) { return buildTestOp(loc); } 1488 1489 bool WarpBuilder::build_Or(BytecodeLocation loc) { return buildTestOp(loc); } 1490 1491 bool WarpBuilder::build_Case(BytecodeLocation loc) { return buildTestOp(loc); } 1492 1493 bool WarpBuilder::build_Default(BytecodeLocation loc) { 1494 current->pop(); 1495 return buildForwardGoto(loc.getJumpTarget()); 1496 } 1497 1498 bool WarpBuilder::build_Coalesce(BytecodeLocation loc) { 1499 BytecodeLocation target1 = loc.next(); 1500 BytecodeLocation target2 = loc.getJumpTarget(); 1501 MOZ_ASSERT(target2 > target1); 1502 1503 MDefinition* value = current->peek(-1); 1504 1505 MInstruction* isNullOrUndefined = MIsNullOrUndefined::New(alloc(), value); 1506 current->add(isNullOrUndefined); 1507 1508 current->end(MTest::New(alloc(), isNullOrUndefined, /* ifTrue = */ nullptr, 1509 /* ifFalse = */ nullptr)); 1510 1511 if (!addPendingEdge(target1, current, MTest::TrueBranchIndex)) { 1512 return false; 1513 } 1514 if (!addPendingEdge(target2, current, MTest::FalseBranchIndex)) { 1515 return false; 1516 } 1517 1518 setTerminatedBlock(); 1519 return true; 1520 } 1521 1522 bool WarpBuilder::buildBackedge() { 1523 decLoopDepth(); 1524 1525 MBasicBlock* header = loopStack_.popCopy().header(); 1526 current->end(MGoto::New(alloc(), header)); 1527 1528 if (!header->setBackedge(current)) { 1529 return false; 1530 } 1531 1532 setTerminatedBlock(); 1533 return true; 1534 } 1535 1536 bool WarpBuilder::buildForwardGoto(BytecodeLocation target) { 1537 current->end(MGoto::New(alloc(), nullptr)); 1538 1539 if (!addPendingEdge(target, current, MGoto::TargetIndex)) { 1540 return false; 1541 } 1542 1543 setTerminatedBlock(); 1544 return true; 1545 } 1546 1547 bool WarpBuilder::build_Goto(BytecodeLocation loc) { 1548 if (loc.isBackedge()) { 1549 return buildBackedge(); 1550 } 1551 1552 return buildForwardGoto(loc.getJumpTarget()); 1553 } 1554 1555 bool WarpBuilder::build_IsNullOrUndefined(BytecodeLocation loc) { 1556 MDefinition* value = current->peek(-1); 1557 auto* isNullOrUndef = MIsNullOrUndefined::New(alloc(), value); 1558 current->add(isNullOrUndef); 1559 current->push(isNullOrUndef); 1560 return true; 1561 } 1562 1563 bool WarpBuilder::build_DebugCheckSelfHosted(BytecodeLocation loc) { 1564 #ifdef DEBUG 1565 MDefinition* val = current->pop(); 1566 MDebugCheckSelfHosted* check = MDebugCheckSelfHosted::New(alloc(), val); 1567 current->add(check); 1568 current->push(check); 1569 if (!resumeAfter(check, loc)) { 1570 return false; 1571 } 1572 #endif 1573 return true; 1574 } 1575 1576 bool WarpBuilder::build_DynamicImport(BytecodeLocation loc) { 1577 MDefinition* options = current->pop(); 1578 MDefinition* specifier = current->pop(); 1579 MDynamicImport* ins = MDynamicImport::New(alloc(), specifier, options); 1580 current->add(ins); 1581 current->push(ins); 1582 return resumeAfter(ins, loc); 1583 } 1584 1585 bool WarpBuilder::build_Not(BytecodeLocation loc) { 1586 if (auto* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc)) { 1587 // If we have CacheIR, we can use it to refine the input before 1588 // emitting the MNot. 1589 MDefinition* value = current->pop(); 1590 if (!TranspileCacheIRToMIR(this, loc, cacheIRSnapshot, {value})) { 1591 return false; 1592 } 1593 } 1594 1595 MDefinition* value = current->pop(); 1596 MNot* ins = MNot::New(alloc(), value); 1597 current->add(ins); 1598 current->push(ins); 1599 1600 if (const auto* typesSnapshot = getOpSnapshot<WarpPolymorphicTypes>(loc)) { 1601 ins->setObservedTypes(typesSnapshot->list()); 1602 } 1603 1604 return true; 1605 } 1606 1607 bool WarpBuilder::build_ToString(BytecodeLocation loc) { 1608 MDefinition* value = current->pop(); 1609 1610 if (value->type() == MIRType::String) { 1611 value->setImplicitlyUsedUnchecked(); 1612 current->push(value); 1613 return true; 1614 } 1615 1616 MToString* ins = 1617 MToString::New(alloc(), value, MToString::SideEffectHandling::Supported); 1618 current->add(ins); 1619 current->push(ins); 1620 if (ins->isEffectful()) { 1621 return resumeAfter(ins, loc); 1622 } 1623 return true; 1624 } 1625 1626 bool WarpBuilder::usesEnvironmentChain() const { 1627 return script_->jitScript()->usesEnvironmentChain(); 1628 } 1629 1630 bool WarpBuilder::build_GlobalOrEvalDeclInstantiation(BytecodeLocation loc) { 1631 MOZ_ASSERT(!script_->isForEval(), "Eval scripts not supported"); 1632 auto* redeclCheck = MGlobalDeclInstantiation::New(alloc()); 1633 current->add(redeclCheck); 1634 return resumeAfter(redeclCheck, loc); 1635 } 1636 1637 bool WarpBuilder::build_BindVar(BytecodeLocation) { 1638 MOZ_ASSERT(usesEnvironmentChain()); 1639 1640 MDefinition* env = current->environmentChain(); 1641 MCallBindVar* ins = MCallBindVar::New(alloc(), env); 1642 current->add(ins); 1643 current->push(ins); 1644 return true; 1645 } 1646 1647 bool WarpBuilder::build_MutateProto(BytecodeLocation loc) { 1648 MDefinition* value = current->pop(); 1649 MDefinition* obj = current->peek(-1); 1650 MMutateProto* mutate = MMutateProto::New(alloc(), obj, value); 1651 current->add(mutate); 1652 return resumeAfter(mutate, loc); 1653 } 1654 1655 MDefinition* WarpBuilder::getCallee() { 1656 if (inlineCallInfo()) { 1657 return inlineCallInfo()->callee(); 1658 } 1659 1660 MInstruction* callee = MCallee::New(alloc()); 1661 current->add(callee); 1662 return callee; 1663 } 1664 1665 bool WarpBuilder::build_Callee(BytecodeLocation) { 1666 MDefinition* callee = getCallee(); 1667 current->push(callee); 1668 return true; 1669 } 1670 1671 bool WarpBuilder::build_ToAsyncIter(BytecodeLocation loc) { 1672 MDefinition* nextMethod = current->pop(); 1673 MDefinition* iterator = current->pop(); 1674 MToAsyncIter* ins = MToAsyncIter::New(alloc(), iterator, nextMethod); 1675 current->add(ins); 1676 current->push(ins); 1677 return resumeAfter(ins, loc); 1678 } 1679 1680 bool WarpBuilder::build_ToPropertyKey(BytecodeLocation loc) { 1681 MDefinition* value = current->pop(); 1682 return buildIC(loc, CacheKind::ToPropertyKey, {value}); 1683 } 1684 1685 bool WarpBuilder::build_Typeof(BytecodeLocation loc) { 1686 MDefinition* input = current->pop(); 1687 1688 if (const auto* typesSnapshot = getOpSnapshot<WarpPolymorphicTypes>(loc)) { 1689 auto* typeOf = MTypeOf::New(alloc(), input); 1690 typeOf->setObservedTypes(typesSnapshot->list()); 1691 current->add(typeOf); 1692 1693 auto* ins = MTypeOfName::New(alloc(), typeOf); 1694 current->add(ins); 1695 current->push(ins); 1696 return true; 1697 } 1698 1699 return buildIC(loc, CacheKind::TypeOf, {input}); 1700 } 1701 1702 bool WarpBuilder::build_TypeofExpr(BytecodeLocation loc) { 1703 return build_Typeof(loc); 1704 } 1705 1706 bool WarpBuilder::build_TypeofEq(BytecodeLocation loc) { 1707 auto operand = loc.getTypeofEqOperand(); 1708 JSType type = operand.type(); 1709 JSOp compareOp = operand.compareOp(); 1710 MDefinition* input = current->pop(); 1711 1712 if (const auto* typesSnapshot = getOpSnapshot<WarpPolymorphicTypes>(loc)) { 1713 auto* typeOf = MTypeOf::New(alloc(), input); 1714 typeOf->setObservedTypes(typesSnapshot->list()); 1715 current->add(typeOf); 1716 1717 auto* typeInt = MConstant::NewInt32(alloc(), type); 1718 current->add(typeInt); 1719 1720 auto* ins = MCompare::New(alloc(), typeOf, typeInt, compareOp, 1721 MCompare::Compare_Int32); 1722 current->add(ins); 1723 current->push(ins); 1724 return true; 1725 } 1726 1727 return buildIC(loc, CacheKind::TypeOfEq, {input}); 1728 } 1729 1730 bool WarpBuilder::build_Arguments(BytecodeLocation loc) { 1731 auto* snapshot = getOpSnapshot<WarpArguments>(loc); 1732 MOZ_ASSERT(info().needsArgsObj()); 1733 MOZ_ASSERT(snapshot); 1734 MOZ_ASSERT(usesEnvironmentChain()); 1735 1736 ArgumentsObject* templateObj = snapshot->templateObj(); 1737 MDefinition* env = current->environmentChain(); 1738 1739 MInstruction* argsObj; 1740 if (inlineCallInfo()) { 1741 argsObj = MCreateInlinedArgumentsObject::New( 1742 alloc(), env, getCallee(), inlineCallInfo()->argv(), templateObj); 1743 if (!argsObj) { 1744 return false; 1745 } 1746 } else { 1747 argsObj = MCreateArgumentsObject::New(alloc(), env, templateObj); 1748 } 1749 current->add(argsObj); 1750 current->setArgumentsObject(argsObj); 1751 current->push(argsObj); 1752 1753 return true; 1754 } 1755 1756 bool WarpBuilder::build_ObjWithProto(BytecodeLocation loc) { 1757 MDefinition* proto = current->pop(); 1758 MInstruction* ins = MObjectWithProto::New(alloc(), proto); 1759 current->add(ins); 1760 current->push(ins); 1761 return resumeAfter(ins, loc); 1762 } 1763 1764 MDefinition* WarpBuilder::walkEnvironmentChain(uint32_t numHops) { 1765 MDefinition* env = current->environmentChain(); 1766 1767 for (uint32_t i = 0; i < numHops; i++) { 1768 if (!alloc().ensureBallast()) { 1769 return nullptr; 1770 } 1771 1772 MInstruction* ins = MEnclosingEnvironment::New(alloc(), env); 1773 current->add(ins); 1774 env = ins; 1775 } 1776 1777 return env; 1778 } 1779 1780 bool WarpBuilder::build_GetAliasedVar(BytecodeLocation loc) { 1781 EnvironmentCoordinate ec = loc.getEnvironmentCoordinate(); 1782 MDefinition* obj = walkEnvironmentChain(ec.hops()); 1783 if (!obj) { 1784 return false; 1785 } 1786 1787 MInstruction* load; 1788 if (EnvironmentObject::nonExtensibleIsFixedSlot(ec)) { 1789 load = MLoadFixedSlot::New(alloc(), obj, ec.slot()); 1790 } else { 1791 MInstruction* slots = MSlots::New(alloc(), obj); 1792 current->add(slots); 1793 1794 uint32_t slot = EnvironmentObject::nonExtensibleDynamicSlotIndex(ec); 1795 load = MLoadDynamicSlot::New(alloc(), slots, slot); 1796 } 1797 1798 current->add(load); 1799 current->push(load); 1800 return true; 1801 } 1802 1803 bool WarpBuilder::build_SetAliasedVar(BytecodeLocation loc) { 1804 EnvironmentCoordinate ec = loc.getEnvironmentCoordinate(); 1805 MDefinition* val = current->peek(-1); 1806 MDefinition* obj = walkEnvironmentChain(ec.hops()); 1807 if (!obj) { 1808 return false; 1809 } 1810 1811 current->add(MPostWriteBarrier::New(alloc(), obj, val)); 1812 1813 MInstruction* store; 1814 if (EnvironmentObject::nonExtensibleIsFixedSlot(ec)) { 1815 store = MStoreFixedSlot::NewBarriered(alloc(), obj, ec.slot(), val); 1816 } else { 1817 MInstruction* slots = MSlots::New(alloc(), obj); 1818 current->add(slots); 1819 1820 uint32_t slot = EnvironmentObject::nonExtensibleDynamicSlotIndex(ec); 1821 store = MStoreDynamicSlot::NewBarriered(alloc(), slots, slot, val); 1822 } 1823 1824 current->add(store); 1825 return resumeAfter(store, loc); 1826 } 1827 1828 bool WarpBuilder::build_InitAliasedLexical(BytecodeLocation loc) { 1829 return build_SetAliasedVar(loc); 1830 } 1831 1832 bool WarpBuilder::build_EnvCallee(BytecodeLocation loc) { 1833 uint32_t numHops = loc.getEnvCalleeNumHops(); 1834 MDefinition* env = walkEnvironmentChain(numHops); 1835 if (!env) { 1836 return false; 1837 } 1838 1839 auto* callee = MLoadFixedSlot::New(alloc(), env, CallObject::calleeSlot()); 1840 current->add(callee); 1841 current->push(callee); 1842 return true; 1843 } 1844 1845 bool WarpBuilder::build_Iter(BytecodeLocation loc) { 1846 MDefinition* obj = current->pop(); 1847 return buildIC(loc, CacheKind::GetIterator, {obj}); 1848 } 1849 1850 bool WarpBuilder::build_MoreIter(BytecodeLocation loc) { 1851 MDefinition* iter = current->peek(-1); 1852 MInstruction* ins = MIteratorMore::New(alloc(), iter); 1853 current->add(ins); 1854 current->push(ins); 1855 return resumeAfter(ins, loc); 1856 } 1857 1858 bool WarpBuilder::build_EndIter(BytecodeLocation loc) { 1859 current->pop(); // Iterator value is not used. 1860 MDefinition* iter = current->pop(); 1861 MInstruction* ins = MIteratorEnd::New(alloc(), iter); 1862 current->add(ins); 1863 return resumeAfter(ins, loc); 1864 } 1865 1866 bool WarpBuilder::build_CloseIter(BytecodeLocation loc) { 1867 MDefinition* iter = current->pop(); 1868 iter = unboxObjectInfallible(iter, IsMovable::No); 1869 return buildIC(loc, CacheKind::CloseIter, {iter}); 1870 } 1871 1872 bool WarpBuilder::build_IsNoIter(BytecodeLocation) { 1873 MDefinition* def = current->peek(-1); 1874 MOZ_ASSERT(def->isIteratorMore()); 1875 MInstruction* ins = MIsNoIter::New(alloc(), def); 1876 current->add(ins); 1877 current->push(ins); 1878 return true; 1879 } 1880 1881 bool WarpBuilder::build_OptimizeGetIterator(BytecodeLocation loc) { 1882 MDefinition* value = current->pop(); 1883 return buildIC(loc, CacheKind::OptimizeGetIterator, {value}); 1884 } 1885 1886 bool WarpBuilder::transpileCall(BytecodeLocation loc, 1887 const WarpCacheIR* cacheIRSnapshot, 1888 CallInfo* callInfo) { 1889 // Synthesize the constant number of arguments for this call op. 1890 auto* argc = MConstant::NewInt32(alloc(), callInfo->argc()); 1891 current->add(argc); 1892 1893 return TranspileCacheIRToMIR(this, loc, cacheIRSnapshot, {argc}, callInfo); 1894 } 1895 1896 void WarpBuilder::buildCreateThis(CallInfo& callInfo) { 1897 MOZ_ASSERT(callInfo.constructing()); 1898 1899 // Inline the this-object allocation on the caller-side. 1900 MDefinition* callee = callInfo.callee(); 1901 MDefinition* newTarget = callInfo.getNewTarget(); 1902 auto* createThis = MCreateThis::New(alloc(), callee, newTarget); 1903 current->add(createThis); 1904 callInfo.thisArg()->setImplicitlyUsedUnchecked(); 1905 callInfo.setThis(createThis); 1906 } 1907 1908 bool WarpBuilder::buildCallOp(BytecodeLocation loc) { 1909 uint32_t argc = loc.getCallArgc(); 1910 JSOp op = loc.getOp(); 1911 bool constructing = IsConstructOp(op); 1912 bool ignoresReturnValue = (op == JSOp::CallIgnoresRv || loc.resultIsPopped()); 1913 1914 CallInfo callInfo(alloc(), constructing, ignoresReturnValue); 1915 if (!callInfo.init(current, argc)) { 1916 return false; 1917 } 1918 1919 if (const auto* inliningSnapshot = getOpSnapshot<WarpInlinedCall>(loc)) { 1920 // Transpile the CacheIR to generate the correct guards before 1921 // inlining. In this case, CacheOp::CallInlinedFunction updates 1922 // the CallInfo, but does not generate a call. 1923 callInfo.markAsInlined(); 1924 if (!transpileCall(loc, inliningSnapshot->cacheIRSnapshot(), &callInfo)) { 1925 return false; 1926 } 1927 1928 // Generate the body of the inlined function. 1929 return buildInlinedCall(loc, inliningSnapshot, callInfo); 1930 } 1931 1932 if (auto* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc)) { 1933 return transpileCall(loc, cacheIRSnapshot, &callInfo); 1934 } 1935 1936 if (getOpSnapshot<WarpBailout>(loc)) { 1937 callInfo.setImplicitlyUsedUnchecked(); 1938 return buildBailoutForColdIC(loc, CacheKind::Call); 1939 } 1940 1941 bool needsThisCheck = false; 1942 if (callInfo.constructing()) { 1943 buildCreateThis(callInfo); 1944 needsThisCheck = true; 1945 } 1946 1947 MCall* call = makeCall(callInfo, needsThisCheck); 1948 if (!call) { 1949 return false; 1950 } 1951 1952 current->add(call); 1953 current->push(call); 1954 return resumeAfter(call, loc); 1955 } 1956 1957 bool WarpBuilder::build_Call(BytecodeLocation loc) { return buildCallOp(loc); } 1958 1959 bool WarpBuilder::build_CallContent(BytecodeLocation loc) { 1960 return buildCallOp(loc); 1961 } 1962 1963 bool WarpBuilder::build_CallIgnoresRv(BytecodeLocation loc) { 1964 return buildCallOp(loc); 1965 } 1966 1967 bool WarpBuilder::build_CallIter(BytecodeLocation loc) { 1968 return buildCallOp(loc); 1969 } 1970 1971 bool WarpBuilder::build_CallContentIter(BytecodeLocation loc) { 1972 return buildCallOp(loc); 1973 } 1974 1975 bool WarpBuilder::build_New(BytecodeLocation loc) { return buildCallOp(loc); } 1976 1977 bool WarpBuilder::build_NewContent(BytecodeLocation loc) { 1978 return buildCallOp(loc); 1979 } 1980 1981 bool WarpBuilder::build_SuperCall(BytecodeLocation loc) { 1982 return buildCallOp(loc); 1983 } 1984 1985 bool WarpBuilder::build_FunctionThis(BytecodeLocation loc) { 1986 MOZ_ASSERT(info().hasFunMaybeLazy()); 1987 1988 if (script_->strict()) { 1989 // No need to wrap primitive |this| in strict mode. 1990 current->pushSlot(info().thisSlot()); 1991 return true; 1992 } 1993 1994 MOZ_ASSERT(!script_->hasNonSyntacticScope(), 1995 "WarpOracle should have aborted compilation"); 1996 1997 MDefinition* def = current->getSlot(info().thisSlot()); 1998 JSObject* globalThis = snapshot().globalLexicalEnvThis(); 1999 2000 auto* thisObj = MBoxNonStrictThis::New(alloc(), def, globalThis); 2001 current->add(thisObj); 2002 current->push(thisObj); 2003 2004 return true; 2005 } 2006 2007 bool WarpBuilder::build_GlobalThis(BytecodeLocation loc) { 2008 MOZ_ASSERT(!script_->hasNonSyntacticScope()); 2009 JSObject* obj = snapshot().globalLexicalEnvThis(); 2010 pushConstant(ObjectValue(*obj)); 2011 return true; 2012 } 2013 2014 MConstant* WarpBuilder::globalLexicalEnvConstant() { 2015 JSObject* globalLexical = snapshot().globalLexicalEnv(); 2016 return constant(ObjectValue(*globalLexical)); 2017 } 2018 2019 bool WarpBuilder::build_GetName(BytecodeLocation loc) { 2020 MOZ_ASSERT(usesEnvironmentChain()); 2021 2022 MDefinition* env = current->environmentChain(); 2023 env = unboxObjectInfallible(env, IsMovable::Yes); 2024 return buildIC(loc, CacheKind::GetName, {env}); 2025 } 2026 2027 bool WarpBuilder::build_GetGName(BytecodeLocation loc) { 2028 MOZ_ASSERT(!script_->hasNonSyntacticScope()); 2029 2030 MDefinition* env = globalLexicalEnvConstant(); 2031 return buildIC(loc, CacheKind::GetName, {env}); 2032 } 2033 2034 bool WarpBuilder::build_BindName(BytecodeLocation loc) { 2035 MOZ_ASSERT(usesEnvironmentChain()); 2036 2037 MDefinition* env = current->environmentChain(); 2038 env = unboxObjectInfallible(env, IsMovable::Yes); 2039 return buildIC(loc, CacheKind::BindName, {env}); 2040 } 2041 2042 bool WarpBuilder::build_BindUnqualifiedName(BytecodeLocation loc) { 2043 MOZ_ASSERT(usesEnvironmentChain()); 2044 2045 MDefinition* env = current->environmentChain(); 2046 env = unboxObjectInfallible(env, IsMovable::Yes); 2047 return buildIC(loc, CacheKind::BindName, {env}); 2048 } 2049 2050 bool WarpBuilder::build_BindUnqualifiedGName(BytecodeLocation loc) { 2051 MOZ_ASSERT(!script_->hasNonSyntacticScope()); 2052 2053 if (const auto* snapshot = getOpSnapshot<WarpBindUnqualifiedGName>(loc)) { 2054 JSObject* globalEnv = snapshot->globalEnv(); 2055 pushConstant(ObjectValue(*globalEnv)); 2056 return true; 2057 } 2058 2059 MDefinition* env = globalLexicalEnvConstant(); 2060 return buildIC(loc, CacheKind::BindName, {env}); 2061 } 2062 2063 bool WarpBuilder::build_GetProp(BytecodeLocation loc) { 2064 MDefinition* val = current->pop(); 2065 return buildIC(loc, CacheKind::GetProp, {val}); 2066 } 2067 2068 bool WarpBuilder::build_GetBoundName(BytecodeLocation loc) { 2069 MDefinition* val = current->pop(); 2070 return buildIC(loc, CacheKind::GetProp, {val}); 2071 } 2072 2073 bool WarpBuilder::build_GetElem(BytecodeLocation loc) { 2074 MDefinition* id = current->pop(); 2075 MDefinition* val = current->pop(); 2076 return buildIC(loc, CacheKind::GetElem, {val, id}); 2077 } 2078 2079 bool WarpBuilder::build_SetProp(BytecodeLocation loc) { 2080 MDefinition* val = current->pop(); 2081 MDefinition* obj = current->pop(); 2082 current->push(val); 2083 return buildIC(loc, CacheKind::SetProp, {obj, val}); 2084 } 2085 2086 bool WarpBuilder::build_StrictSetProp(BytecodeLocation loc) { 2087 return build_SetProp(loc); 2088 } 2089 2090 bool WarpBuilder::build_SetName(BytecodeLocation loc) { 2091 return build_SetProp(loc); 2092 } 2093 2094 bool WarpBuilder::build_StrictSetName(BytecodeLocation loc) { 2095 return build_SetProp(loc); 2096 } 2097 2098 bool WarpBuilder::build_SetGName(BytecodeLocation loc) { 2099 return build_SetProp(loc); 2100 } 2101 2102 bool WarpBuilder::build_StrictSetGName(BytecodeLocation loc) { 2103 return build_SetProp(loc); 2104 } 2105 2106 bool WarpBuilder::build_InitGLexical(BytecodeLocation loc) { 2107 MOZ_ASSERT(!script_->hasNonSyntacticScope()); 2108 2109 MDefinition* globalLexical = globalLexicalEnvConstant(); 2110 MDefinition* val = current->peek(-1); 2111 2112 return buildIC(loc, CacheKind::SetProp, {globalLexical, val}); 2113 } 2114 2115 bool WarpBuilder::build_SetElem(BytecodeLocation loc) { 2116 MDefinition* val = current->pop(); 2117 MDefinition* id = current->pop(); 2118 MDefinition* obj = current->pop(); 2119 current->push(val); 2120 return buildIC(loc, CacheKind::SetElem, {obj, id, val}); 2121 } 2122 2123 bool WarpBuilder::build_StrictSetElem(BytecodeLocation loc) { 2124 return build_SetElem(loc); 2125 } 2126 2127 bool WarpBuilder::build_DelProp(BytecodeLocation loc) { 2128 PropertyName* name = loc.getPropertyName(script_); 2129 MDefinition* obj = current->pop(); 2130 bool strict = loc.getOp() == JSOp::StrictDelProp; 2131 2132 MInstruction* ins = MDeleteProperty::New(alloc(), obj, name, strict); 2133 current->add(ins); 2134 current->push(ins); 2135 return resumeAfter(ins, loc); 2136 } 2137 2138 bool WarpBuilder::build_StrictDelProp(BytecodeLocation loc) { 2139 return build_DelProp(loc); 2140 } 2141 2142 bool WarpBuilder::build_DelElem(BytecodeLocation loc) { 2143 MDefinition* id = current->pop(); 2144 MDefinition* obj = current->pop(); 2145 bool strict = loc.getOp() == JSOp::StrictDelElem; 2146 2147 MInstruction* ins = MDeleteElement::New(alloc(), obj, id, strict); 2148 current->add(ins); 2149 current->push(ins); 2150 return resumeAfter(ins, loc); 2151 } 2152 2153 bool WarpBuilder::build_StrictDelElem(BytecodeLocation loc) { 2154 return build_DelElem(loc); 2155 } 2156 2157 bool WarpBuilder::build_SetFunName(BytecodeLocation loc) { 2158 FunctionPrefixKind prefixKind = loc.getFunctionPrefixKind(); 2159 MDefinition* name = current->pop(); 2160 MDefinition* fun = current->pop(); 2161 2162 MSetFunName* ins = MSetFunName::New(alloc(), fun, name, uint8_t(prefixKind)); 2163 current->add(ins); 2164 current->push(fun); 2165 return resumeAfter(ins, loc); 2166 } 2167 2168 bool WarpBuilder::build_PushLexicalEnv(BytecodeLocation loc) { 2169 MOZ_ASSERT(usesEnvironmentChain()); 2170 2171 const auto* snapshot = getOpSnapshot<WarpLexicalEnvironment>(loc); 2172 MOZ_ASSERT(snapshot); 2173 2174 MDefinition* env = current->environmentChain(); 2175 MConstant* templateCst = constant(ObjectValue(*snapshot->templateObj())); 2176 2177 auto* ins = MNewLexicalEnvironmentObject::New(alloc(), templateCst); 2178 current->add(ins); 2179 2180 #ifdef DEBUG 2181 // Assert in debug mode we can elide the post write barrier. 2182 current->add(MAssertCanElidePostWriteBarrier::New(alloc(), ins, env)); 2183 #endif 2184 2185 // Initialize the object's reserved slots. No post barrier is needed here, 2186 // for the same reason as in buildNamedLambdaEnv. 2187 current->add(MStoreFixedSlot::NewUnbarriered( 2188 alloc(), ins, EnvironmentObject::enclosingEnvironmentSlot(), env)); 2189 2190 current->setEnvironmentChain(ins); 2191 return true; 2192 } 2193 2194 bool WarpBuilder::build_PushClassBodyEnv(BytecodeLocation loc) { 2195 MOZ_ASSERT(usesEnvironmentChain()); 2196 2197 const auto* snapshot = getOpSnapshot<WarpClassBodyEnvironment>(loc); 2198 MOZ_ASSERT(snapshot); 2199 2200 MDefinition* env = current->environmentChain(); 2201 MConstant* templateCst = constant(ObjectValue(*snapshot->templateObj())); 2202 2203 auto* ins = MNewClassBodyEnvironmentObject::New(alloc(), templateCst); 2204 current->add(ins); 2205 2206 #ifdef DEBUG 2207 // Assert in debug mode we can elide the post write barrier. 2208 current->add(MAssertCanElidePostWriteBarrier::New(alloc(), ins, env)); 2209 #endif 2210 2211 // Initialize the object's reserved slots. No post barrier is needed here, 2212 // for the same reason as in buildNamedLambdaEnv. 2213 current->add(MStoreFixedSlot::NewUnbarriered( 2214 alloc(), ins, EnvironmentObject::enclosingEnvironmentSlot(), env)); 2215 2216 current->setEnvironmentChain(ins); 2217 return true; 2218 } 2219 2220 bool WarpBuilder::build_PopLexicalEnv(BytecodeLocation) { 2221 MDefinition* enclosingEnv = walkEnvironmentChain(1); 2222 if (!enclosingEnv) { 2223 return false; 2224 } 2225 current->setEnvironmentChain(enclosingEnv); 2226 return true; 2227 } 2228 2229 bool WarpBuilder::build_FreshenLexicalEnv(BytecodeLocation loc) { 2230 MOZ_ASSERT(usesEnvironmentChain()); 2231 2232 const auto* snapshot = getOpSnapshot<WarpLexicalEnvironment>(loc); 2233 MOZ_ASSERT(snapshot); 2234 2235 MDefinition* enclosingEnv = walkEnvironmentChain(1); 2236 if (!enclosingEnv) { 2237 return false; 2238 } 2239 2240 MDefinition* env = current->environmentChain(); 2241 MConstant* templateCst = constant(ObjectValue(*snapshot->templateObj())); 2242 2243 auto* templateObj = snapshot->templateObj(); 2244 auto* scope = &templateObj->scope(); 2245 MOZ_ASSERT(scope->hasEnvironment()); 2246 2247 auto* ins = MNewLexicalEnvironmentObject::New(alloc(), templateCst); 2248 current->add(ins); 2249 2250 #ifdef DEBUG 2251 // Assert in debug mode we can elide the post write barrier. 2252 current->add( 2253 MAssertCanElidePostWriteBarrier::New(alloc(), ins, enclosingEnv)); 2254 #endif 2255 2256 // Initialize the object's reserved slots. No post barrier is needed here, 2257 // for the same reason as in buildNamedLambdaEnv. 2258 current->add(MStoreFixedSlot::NewUnbarriered( 2259 alloc(), ins, EnvironmentObject::enclosingEnvironmentSlot(), 2260 enclosingEnv)); 2261 2262 // Copy environment slots. 2263 MSlots* envSlots = nullptr; 2264 MSlots* slots = nullptr; 2265 for (BindingIter iter(scope); iter; iter++) { 2266 auto loc = iter.location(); 2267 if (loc.kind() != BindingLocation::Kind::Environment) { 2268 MOZ_ASSERT(loc.kind() == BindingLocation::Kind::Frame); 2269 continue; 2270 } 2271 2272 if (!alloc().ensureBallast()) { 2273 return false; 2274 } 2275 2276 uint32_t slot = loc.slot(); 2277 uint32_t numFixedSlots = templateObj->numFixedSlots(); 2278 if (slot >= numFixedSlots) { 2279 if (!envSlots) { 2280 envSlots = MSlots::New(alloc(), env); 2281 current->add(envSlots); 2282 } 2283 if (!slots) { 2284 slots = MSlots::New(alloc(), ins); 2285 current->add(slots); 2286 } 2287 2288 uint32_t dynamicSlot = slot - numFixedSlots; 2289 2290 auto* load = MLoadDynamicSlot::New(alloc(), envSlots, dynamicSlot); 2291 current->add(load); 2292 2293 #ifdef DEBUG 2294 // Assert in debug mode we can elide the post write barrier. 2295 current->add(MAssertCanElidePostWriteBarrier::New(alloc(), ins, load)); 2296 #endif 2297 2298 current->add( 2299 MStoreDynamicSlot::NewUnbarriered(alloc(), slots, dynamicSlot, load)); 2300 } else { 2301 auto* load = MLoadFixedSlot::New(alloc(), env, slot); 2302 current->add(load); 2303 2304 #ifdef DEBUG 2305 // Assert in debug mode we can elide the post write barrier. 2306 current->add(MAssertCanElidePostWriteBarrier::New(alloc(), ins, load)); 2307 #endif 2308 2309 current->add(MStoreFixedSlot::NewUnbarriered(alloc(), ins, slot, load)); 2310 } 2311 } 2312 2313 current->setEnvironmentChain(ins); 2314 return true; 2315 } 2316 2317 bool WarpBuilder::build_RecreateLexicalEnv(BytecodeLocation loc) { 2318 MOZ_ASSERT(usesEnvironmentChain()); 2319 2320 const auto* snapshot = getOpSnapshot<WarpLexicalEnvironment>(loc); 2321 MOZ_ASSERT(snapshot); 2322 2323 MDefinition* enclosingEnv = walkEnvironmentChain(1); 2324 if (!enclosingEnv) { 2325 return false; 2326 } 2327 2328 MConstant* templateCst = constant(ObjectValue(*snapshot->templateObj())); 2329 2330 auto* ins = MNewLexicalEnvironmentObject::New(alloc(), templateCst); 2331 current->add(ins); 2332 2333 #ifdef DEBUG 2334 // Assert in debug mode we can elide the post write barrier. 2335 current->add( 2336 MAssertCanElidePostWriteBarrier::New(alloc(), ins, enclosingEnv)); 2337 #endif 2338 2339 // Initialize the object's reserved slots. No post barrier is needed here, 2340 // for the same reason as in buildNamedLambdaEnv. 2341 current->add(MStoreFixedSlot::NewUnbarriered( 2342 alloc(), ins, EnvironmentObject::enclosingEnvironmentSlot(), 2343 enclosingEnv)); 2344 2345 current->setEnvironmentChain(ins); 2346 return true; 2347 } 2348 2349 bool WarpBuilder::build_PushVarEnv(BytecodeLocation loc) { 2350 MOZ_ASSERT(usesEnvironmentChain()); 2351 2352 const auto* snapshot = getOpSnapshot<WarpVarEnvironment>(loc); 2353 MOZ_ASSERT(snapshot); 2354 2355 MDefinition* env = current->environmentChain(); 2356 MConstant* templateCst = constant(ObjectValue(*snapshot->templateObj())); 2357 2358 auto* ins = MNewVarEnvironmentObject::New(alloc(), templateCst); 2359 current->add(ins); 2360 2361 #ifdef DEBUG 2362 // Assert in debug mode we can elide the post write barrier. 2363 current->add(MAssertCanElidePostWriteBarrier::New(alloc(), ins, env)); 2364 #endif 2365 2366 // Initialize the object's reserved slots. No post barrier is needed here, 2367 // for the same reason as in buildNamedLambdaEnv. 2368 current->add(MStoreFixedSlot::NewUnbarriered( 2369 alloc(), ins, EnvironmentObject::enclosingEnvironmentSlot(), env)); 2370 2371 current->setEnvironmentChain(ins); 2372 return true; 2373 } 2374 2375 bool WarpBuilder::build_ImplicitThis(BytecodeLocation loc) { 2376 MDefinition* env = current->pop(); 2377 2378 auto* ins = MImplicitThis::New(alloc(), env); 2379 current->add(ins); 2380 current->push(ins); 2381 return resumeAfter(ins, loc); 2382 } 2383 2384 bool WarpBuilder::build_CheckClassHeritage(BytecodeLocation loc) { 2385 MDefinition* def = current->pop(); 2386 auto* ins = MCheckClassHeritage::New(alloc(), def); 2387 current->add(ins); 2388 current->push(ins); 2389 return resumeAfter(ins, loc); 2390 } 2391 2392 bool WarpBuilder::build_CheckThis(BytecodeLocation loc) { 2393 MDefinition* def = current->pop(); 2394 auto* ins = MCheckThis::New(alloc(), def); 2395 current->add(ins); 2396 current->push(ins); 2397 return resumeAfter(ins, loc); 2398 } 2399 2400 bool WarpBuilder::build_CheckThisReinit(BytecodeLocation loc) { 2401 MDefinition* def = current->pop(); 2402 auto* ins = MCheckThisReinit::New(alloc(), def); 2403 current->add(ins); 2404 current->push(ins); 2405 return resumeAfter(ins, loc); 2406 } 2407 2408 bool WarpBuilder::build_Generator(BytecodeLocation loc) { 2409 MOZ_ASSERT(usesEnvironmentChain()); 2410 2411 MDefinition* callee = getCallee(); 2412 MDefinition* environmentChain = current->environmentChain(); 2413 MDefinition* argsObj = info().needsArgsObj() ? current->argumentsObject() 2414 : constant(Int32Value(0)); 2415 2416 MGenerator* generator = 2417 MGenerator::New(alloc(), callee, environmentChain, argsObj); 2418 2419 current->add(generator); 2420 current->push(generator); 2421 return resumeAfter(generator, loc); 2422 } 2423 2424 bool WarpBuilder::build_AfterYield(BytecodeLocation loc) { 2425 // Unreachable blocks don't need to generate a bail. 2426 if (hasTerminatedBlock()) { 2427 return true; 2428 } 2429 2430 // This comes after a yield, which we generate as a return, 2431 // so we know this should be unreachable code. 2432 // 2433 // We emit an unreachable bail for this, which will assert if we 2434 // ever execute this. 2435 // 2436 // An Unreachable bail, instead of MUnreachable, because MUnreachable 2437 // is a control instruction, and injecting it in the middle of a block 2438 // causes various graph state assertions to fail. 2439 MBail* bail = MBail::New(alloc(), BailoutKind::Unreachable); 2440 current->add(bail); 2441 2442 return true; 2443 } 2444 2445 bool WarpBuilder::build_FinalYieldRval(BytecodeLocation loc) { 2446 MDefinition* gen = current->pop(); 2447 2448 auto setSlotNull = [this, gen](size_t slot) { 2449 auto* ins = MStoreFixedSlot::NewBarriered(alloc(), gen, slot, 2450 constant(NullValue())); 2451 current->add(ins); 2452 }; 2453 2454 // Close the generator 2455 setSlotNull(AbstractGeneratorObject::calleeSlot()); 2456 setSlotNull(AbstractGeneratorObject::envChainSlot()); 2457 setSlotNull(AbstractGeneratorObject::argsObjectSlot()); 2458 setSlotNull(AbstractGeneratorObject::stackStorageSlot()); 2459 setSlotNull(AbstractGeneratorObject::resumeIndexSlot()); 2460 2461 // Return 2462 return build_RetRval(loc); 2463 } 2464 2465 bool WarpBuilder::build_AsyncResolve(BytecodeLocation loc) { 2466 MDefinition* generator = current->pop(); 2467 MDefinition* value = current->pop(); 2468 2469 auto* resolve = MAsyncResolve::New(alloc(), generator, value); 2470 current->add(resolve); 2471 current->push(resolve); 2472 return resumeAfter(resolve, loc); 2473 } 2474 2475 bool WarpBuilder::build_AsyncReject(BytecodeLocation loc) { 2476 MDefinition* generator = current->pop(); 2477 MDefinition* stack = current->pop(); 2478 MDefinition* reason = current->pop(); 2479 2480 auto* reject = MAsyncReject::New(alloc(), generator, reason, stack); 2481 current->add(reject); 2482 current->push(reject); 2483 return resumeAfter(reject, loc); 2484 } 2485 2486 bool WarpBuilder::build_ResumeKind(BytecodeLocation loc) { 2487 GeneratorResumeKind resumeKind = loc.resumeKind(); 2488 2489 current->push(constant(Int32Value(static_cast<int32_t>(resumeKind)))); 2490 return true; 2491 } 2492 2493 bool WarpBuilder::build_CheckResumeKind(BytecodeLocation loc) { 2494 // Outside of `yield*`, this is normally unreachable code in Warp, 2495 // so we just manipulate the stack appropriately to ensure correct 2496 // MIR generation. 2497 // 2498 // However, `yield*` emits a forced generator return which can be 2499 // warp compiled, so in order to correctly handle these semantics 2500 // we also generate a bailout, so that the forced generator return 2501 // runs in baseline. 2502 MDefinition* resumeKind = current->pop(); 2503 MDefinition* gen = current->pop(); 2504 MDefinition* rval = current->peek(-1); 2505 2506 // Mark operands as implicitly used. 2507 resumeKind->setImplicitlyUsedUnchecked(); 2508 gen->setImplicitlyUsedUnchecked(); 2509 rval->setImplicitlyUsedUnchecked(); 2510 2511 // Bail out if we encounter CheckResumeKind. 2512 MBail* bail = MBail::New(alloc(), BailoutKind::Inevitable); 2513 current->add(bail); 2514 current->setAlwaysBails(); 2515 2516 return true; 2517 } 2518 2519 bool WarpBuilder::build_CanSkipAwait(BytecodeLocation loc) { 2520 MDefinition* val = current->pop(); 2521 2522 MCanSkipAwait* canSkip = MCanSkipAwait::New(alloc(), val); 2523 current->add(canSkip); 2524 2525 current->push(val); 2526 current->push(canSkip); 2527 2528 return resumeAfter(canSkip, loc); 2529 } 2530 2531 bool WarpBuilder::build_MaybeExtractAwaitValue(BytecodeLocation loc) { 2532 MDefinition* canSkip = current->pop(); 2533 MDefinition* value = current->pop(); 2534 2535 MMaybeExtractAwaitValue* extracted = 2536 MMaybeExtractAwaitValue::New(alloc(), value, canSkip); 2537 current->add(extracted); 2538 2539 current->push(extracted); 2540 current->push(canSkip); 2541 2542 return resumeAfter(extracted, loc); 2543 } 2544 2545 bool WarpBuilder::build_InitialYield(BytecodeLocation loc) { 2546 MDefinition* gen = current->pop(); 2547 return buildSuspend(loc, gen, gen); 2548 } 2549 2550 bool WarpBuilder::build_Await(BytecodeLocation loc) { 2551 MDefinition* gen = current->pop(); 2552 MDefinition* promiseOrGenerator = current->pop(); 2553 2554 return buildSuspend(loc, gen, promiseOrGenerator); 2555 } 2556 bool WarpBuilder::build_Yield(BytecodeLocation loc) { return build_Await(loc); } 2557 2558 bool WarpBuilder::buildSuspend(BytecodeLocation loc, MDefinition* gen, 2559 MDefinition* retVal) { 2560 // If required, unbox the generator object explicitly and infallibly. 2561 // 2562 // This is done to avoid fuzz-bugs where ApplyTypeInformation does the 2563 // unboxing, and generates fallible unboxes which can lead to torn object 2564 // state due to `bailAfter`. 2565 MDefinition* genObj = gen; 2566 if (genObj->type() != MIRType::Object) { 2567 auto* unbox = 2568 MUnbox::New(alloc(), gen, MIRType::Object, MUnbox::Mode::Infallible); 2569 current->add(unbox); 2570 2571 genObj = unbox; 2572 } 2573 2574 int32_t slotsToCopy = current->stackDepth() - info().firstLocalSlot(); 2575 MOZ_ASSERT(slotsToCopy >= 0); 2576 if (slotsToCopy > 0) { 2577 auto* arrayObj = MLoadFixedSlotAndUnbox::New( 2578 alloc(), genObj, AbstractGeneratorObject::stackStorageSlot(), 2579 MUnbox::Mode::Infallible, MIRType::Object); 2580 current->add(arrayObj); 2581 2582 auto* stackStorage = MElements::New(alloc(), arrayObj); 2583 current->add(stackStorage); 2584 2585 for (int32_t i = 0; i < slotsToCopy; i++) { 2586 if (!alloc().ensureBallast()) { 2587 return false; 2588 } 2589 // Use peekUnchecked because we're also writing out the argument slots 2590 int32_t peek = -slotsToCopy + i; 2591 MDefinition* stackElem = current->peekUnchecked(peek); 2592 auto* store = MStoreElement::NewUnbarriered( 2593 alloc(), stackStorage, constant(Int32Value(i)), stackElem, 2594 /* needsHoleCheck = */ false); 2595 2596 current->add(store); 2597 current->add(MPostWriteBarrier::New(alloc(), arrayObj, stackElem)); 2598 } 2599 2600 auto* len = constant(Int32Value(slotsToCopy - 1)); 2601 2602 auto* setInitLength = 2603 MSetInitializedLength::New(alloc(), stackStorage, len); 2604 current->add(setInitLength); 2605 2606 auto* setLength = MSetArrayLength::New(alloc(), stackStorage, len); 2607 current->add(setLength); 2608 } 2609 2610 // Update Generator Object state 2611 uint32_t resumeIndex = loc.getResumeIndex(); 2612 2613 // This store is unbarriered, as it's only ever storing an integer, and as 2614 // such doesn't partake of object tracing. 2615 current->add(MStoreFixedSlot::NewUnbarriered( 2616 alloc(), genObj, AbstractGeneratorObject::resumeIndexSlot(), 2617 constant(Int32Value(resumeIndex)))); 2618 2619 // This store is barriered because it stores an object value. 2620 current->add(MStoreFixedSlot::NewBarriered( 2621 alloc(), genObj, AbstractGeneratorObject::envChainSlot(), 2622 current->environmentChain())); 2623 2624 current->add( 2625 MPostWriteBarrier::New(alloc(), genObj, current->environmentChain())); 2626 2627 // GeneratorReturn will return from the method, however to support MIR 2628 // generation isn't treated like the end of a block 2629 MGeneratorReturn* ret = MGeneratorReturn::New(alloc(), retVal); 2630 current->add(ret); 2631 2632 // To ensure the rest of the MIR generation looks correct, fill the stack with 2633 // the appropriately typed MUnreachable's for the stack pushes from this 2634 // opcode. 2635 auto* unreachableResumeKind = 2636 MUnreachableResult::New(alloc(), MIRType::Int32); 2637 current->add(unreachableResumeKind); 2638 current->push(unreachableResumeKind); 2639 2640 auto* unreachableGenerator = 2641 MUnreachableResult::New(alloc(), MIRType::Object); 2642 current->add(unreachableGenerator); 2643 current->push(unreachableGenerator); 2644 2645 auto* unreachableRval = MUnreachableResult::New(alloc(), MIRType::Value); 2646 current->add(unreachableRval); 2647 current->push(unreachableRval); 2648 2649 return true; 2650 } 2651 2652 bool WarpBuilder::build_AsyncAwait(BytecodeLocation loc) { 2653 MDefinition* gen = current->pop(); 2654 MDefinition* value = current->pop(); 2655 2656 MAsyncAwait* asyncAwait = MAsyncAwait::New(alloc(), value, gen); 2657 current->add(asyncAwait); 2658 current->push(asyncAwait); 2659 return resumeAfter(asyncAwait, loc); 2660 } 2661 2662 bool WarpBuilder::build_CheckReturn(BytecodeLocation loc) { 2663 MOZ_ASSERT(!script_->noScriptRval()); 2664 2665 MDefinition* returnValue = current->getSlot(info().returnValueSlot()); 2666 MDefinition* thisValue = current->pop(); 2667 2668 auto* ins = MCheckReturn::New(alloc(), returnValue, thisValue); 2669 current->add(ins); 2670 current->push(ins); 2671 return resumeAfter(ins, loc); 2672 } 2673 2674 void WarpBuilder::buildCheckLexicalOp(BytecodeLocation loc) { 2675 JSOp op = loc.getOp(); 2676 MOZ_ASSERT(op == JSOp::CheckLexical || op == JSOp::CheckAliasedLexical); 2677 2678 MDefinition* input = current->pop(); 2679 MInstruction* lexicalCheck = MLexicalCheck::New(alloc(), input); 2680 current->add(lexicalCheck); 2681 current->push(lexicalCheck); 2682 2683 if (snapshot().bailoutInfo().failedLexicalCheck()) { 2684 // If we have previously had a failed lexical check in Ion, we want to avoid 2685 // hoisting any lexical checks, which can cause spurious failures. In this 2686 // case, we also have to be careful not to hoist any loads of this lexical 2687 // past the check. For unaliased lexical variables, we can set the local 2688 // slot to create a dependency (see below). For aliased lexicals, that 2689 // doesn't work, so we disable LICM instead. 2690 lexicalCheck->setNotMovable(); 2691 if (op == JSOp::CheckAliasedLexical) { 2692 mirGen().disableLICM(); 2693 } 2694 } 2695 2696 if (op == JSOp::CheckLexical) { 2697 // Set the local slot so that a subsequent GetLocal without a CheckLexical 2698 // (the frontend can elide lexical checks) doesn't let a definition with 2699 // MIRType::MagicUninitializedLexical escape to arbitrary MIR instructions. 2700 // Note that in this case the GetLocal would be unreachable because we throw 2701 // an exception here, but we still generate MIR instructions for it. 2702 uint32_t slot = info().localSlot(loc.local()); 2703 current->setSlot(slot, lexicalCheck); 2704 } 2705 } 2706 2707 bool WarpBuilder::build_CheckLexical(BytecodeLocation loc) { 2708 buildCheckLexicalOp(loc); 2709 return true; 2710 } 2711 2712 bool WarpBuilder::build_CheckAliasedLexical(BytecodeLocation loc) { 2713 buildCheckLexicalOp(loc); 2714 return true; 2715 } 2716 2717 bool WarpBuilder::build_InitHomeObject(BytecodeLocation loc) { 2718 MDefinition* homeObject = current->pop(); 2719 MDefinition* function = current->pop(); 2720 2721 current->add(MPostWriteBarrier::New(alloc(), function, homeObject)); 2722 2723 auto* ins = MInitHomeObject::New(alloc(), function, homeObject); 2724 current->add(ins); 2725 current->push(ins); 2726 return true; 2727 } 2728 2729 bool WarpBuilder::build_SuperBase(BytecodeLocation) { 2730 MDefinition* callee = current->pop(); 2731 2732 auto* homeObject = MHomeObject::New(alloc(), callee); 2733 current->add(homeObject); 2734 2735 auto* superBase = MHomeObjectSuperBase::New(alloc(), homeObject); 2736 current->add(superBase); 2737 current->push(superBase); 2738 return true; 2739 } 2740 2741 bool WarpBuilder::build_SuperFun(BytecodeLocation) { 2742 MDefinition* callee = current->pop(); 2743 auto* ins = MSuperFunction::New(alloc(), callee); 2744 current->add(ins); 2745 current->push(ins); 2746 return true; 2747 } 2748 2749 bool WarpBuilder::build_BuiltinObject(BytecodeLocation loc) { 2750 if (auto* snapshot = getOpSnapshot<WarpBuiltinObject>(loc)) { 2751 JSObject* builtin = snapshot->builtin(); 2752 pushConstant(ObjectValue(*builtin)); 2753 return true; 2754 } 2755 2756 auto kind = loc.getBuiltinObjectKind(); 2757 auto* ins = MBuiltinObject::New(alloc(), kind); 2758 current->add(ins); 2759 current->push(ins); 2760 return resumeAfter(ins, loc); 2761 } 2762 2763 bool WarpBuilder::build_GetIntrinsic(BytecodeLocation loc) { 2764 if (auto* snapshot = getOpSnapshot<WarpGetIntrinsic>(loc)) { 2765 Value intrinsic = snapshot->intrinsic(); 2766 pushConstant(intrinsic); 2767 return true; 2768 } 2769 2770 PropertyName* name = loc.getPropertyName(script_); 2771 MCallGetIntrinsicValue* ins = MCallGetIntrinsicValue::New(alloc(), name); 2772 current->add(ins); 2773 current->push(ins); 2774 return resumeAfter(ins, loc); 2775 } 2776 2777 bool WarpBuilder::build_ImportMeta(BytecodeLocation loc) { 2778 ModuleObject* moduleObj = scriptSnapshot()->moduleObject(); 2779 MOZ_ASSERT(moduleObj); 2780 2781 MModuleMetadata* ins = MModuleMetadata::New(alloc(), moduleObj); 2782 current->add(ins); 2783 current->push(ins); 2784 return resumeAfter(ins, loc); 2785 } 2786 2787 bool WarpBuilder::build_CallSiteObj(BytecodeLocation loc) { 2788 return build_Object(loc); 2789 } 2790 2791 bool WarpBuilder::build_NewArray(BytecodeLocation loc) { 2792 return buildIC(loc, CacheKind::NewArray, {}); 2793 } 2794 2795 bool WarpBuilder::build_NewObject(BytecodeLocation loc) { 2796 return buildIC(loc, CacheKind::NewObject, {}); 2797 } 2798 2799 bool WarpBuilder::build_NewInit(BytecodeLocation loc) { 2800 return build_NewObject(loc); 2801 } 2802 2803 bool WarpBuilder::build_Object(BytecodeLocation loc) { 2804 JSObject* obj = loc.getObject(script_); 2805 MConstant* objConst = constant(ObjectValue(*obj)); 2806 2807 current->push(objConst); 2808 return true; 2809 } 2810 2811 bool WarpBuilder::buildInitPropGetterSetterOp(BytecodeLocation loc) { 2812 PropertyName* name = loc.getPropertyName(script_); 2813 MDefinition* value = current->pop(); 2814 MDefinition* obj = current->peek(-1); 2815 2816 auto* ins = MInitPropGetterSetter::New(alloc(), obj, value, name); 2817 current->add(ins); 2818 return resumeAfter(ins, loc); 2819 } 2820 2821 bool WarpBuilder::build_InitPropGetter(BytecodeLocation loc) { 2822 return buildInitPropGetterSetterOp(loc); 2823 } 2824 2825 bool WarpBuilder::build_InitPropSetter(BytecodeLocation loc) { 2826 return buildInitPropGetterSetterOp(loc); 2827 } 2828 2829 bool WarpBuilder::build_InitHiddenPropGetter(BytecodeLocation loc) { 2830 return buildInitPropGetterSetterOp(loc); 2831 } 2832 2833 bool WarpBuilder::build_InitHiddenPropSetter(BytecodeLocation loc) { 2834 return buildInitPropGetterSetterOp(loc); 2835 } 2836 2837 bool WarpBuilder::buildInitElemGetterSetterOp(BytecodeLocation loc) { 2838 MDefinition* value = current->pop(); 2839 MDefinition* id = current->pop(); 2840 MDefinition* obj = current->peek(-1); 2841 2842 auto* ins = MInitElemGetterSetter::New(alloc(), obj, id, value); 2843 current->add(ins); 2844 return resumeAfter(ins, loc); 2845 } 2846 2847 bool WarpBuilder::build_InitElemGetter(BytecodeLocation loc) { 2848 return buildInitElemGetterSetterOp(loc); 2849 } 2850 2851 bool WarpBuilder::build_InitElemSetter(BytecodeLocation loc) { 2852 return buildInitElemGetterSetterOp(loc); 2853 } 2854 2855 bool WarpBuilder::build_InitHiddenElemGetter(BytecodeLocation loc) { 2856 return buildInitElemGetterSetterOp(loc); 2857 } 2858 2859 bool WarpBuilder::build_InitHiddenElemSetter(BytecodeLocation loc) { 2860 return buildInitElemGetterSetterOp(loc); 2861 } 2862 2863 bool WarpBuilder::build_In(BytecodeLocation loc) { 2864 MDefinition* obj = current->pop(); 2865 MDefinition* id = current->pop(); 2866 return buildIC(loc, CacheKind::In, {id, obj}); 2867 } 2868 2869 bool WarpBuilder::build_HasOwn(BytecodeLocation loc) { 2870 MDefinition* obj = current->pop(); 2871 MDefinition* id = current->pop(); 2872 return buildIC(loc, CacheKind::HasOwn, {id, obj}); 2873 } 2874 2875 bool WarpBuilder::build_CheckPrivateField(BytecodeLocation loc) { 2876 MDefinition* id = current->peek(-1); 2877 MDefinition* obj = current->peek(-2); 2878 return buildIC(loc, CacheKind::CheckPrivateField, {obj, id}); 2879 } 2880 2881 bool WarpBuilder::build_NewPrivateName(BytecodeLocation loc) { 2882 JSAtom* name = loc.getAtom(script_); 2883 2884 auto* ins = MNewPrivateName::New(alloc(), &name->asOffThreadAtom()); 2885 current->add(ins); 2886 current->push(ins); 2887 return resumeAfter(ins, loc); 2888 } 2889 2890 bool WarpBuilder::build_Instanceof(BytecodeLocation loc) { 2891 MDefinition* rhs = current->pop(); 2892 MDefinition* obj = current->pop(); 2893 return buildIC(loc, CacheKind::InstanceOf, {obj, rhs}); 2894 } 2895 2896 bool WarpBuilder::build_NewTarget(BytecodeLocation loc) { 2897 MOZ_ASSERT(script_->isFunction()); 2898 MOZ_ASSERT(info().hasFunMaybeLazy()); 2899 MOZ_ASSERT(!scriptSnapshot()->isArrowFunction()); 2900 2901 if (inlineCallInfo()) { 2902 if (inlineCallInfo()->constructing()) { 2903 current->push(inlineCallInfo()->getNewTarget()); 2904 } else { 2905 pushConstant(UndefinedValue()); 2906 } 2907 return true; 2908 } 2909 2910 MNewTarget* ins = MNewTarget::New(alloc()); 2911 current->add(ins); 2912 current->push(ins); 2913 return true; 2914 } 2915 2916 bool WarpBuilder::build_CheckIsObj(BytecodeLocation loc) { 2917 CheckIsObjectKind kind = loc.getCheckIsObjectKind(); 2918 2919 MDefinition* toCheck = current->peek(-1); 2920 if (toCheck->type() == MIRType::Object) { 2921 toCheck->setImplicitlyUsedUnchecked(); 2922 return true; 2923 } 2924 2925 MDefinition* val = current->pop(); 2926 MCheckIsObj* ins = MCheckIsObj::New(alloc(), val, uint8_t(kind)); 2927 current->add(ins); 2928 current->push(ins); 2929 return resumeAfter(ins, loc); 2930 } 2931 2932 bool WarpBuilder::build_CheckObjCoercible(BytecodeLocation loc) { 2933 MDefinition* val = current->pop(); 2934 MCheckObjCoercible* ins = MCheckObjCoercible::New(alloc(), val); 2935 current->add(ins); 2936 current->push(ins); 2937 return resumeAfter(ins, loc); 2938 } 2939 2940 MInstruction* WarpBuilder::buildLoadSlot(MDefinition* obj, 2941 uint32_t numFixedSlots, 2942 uint32_t slot) { 2943 if (slot < numFixedSlots) { 2944 MLoadFixedSlot* load = MLoadFixedSlot::New(alloc(), obj, slot); 2945 current->add(load); 2946 return load; 2947 } 2948 2949 MSlots* slots = MSlots::New(alloc(), obj); 2950 current->add(slots); 2951 2952 MLoadDynamicSlot* load = 2953 MLoadDynamicSlot::New(alloc(), slots, slot - numFixedSlots); 2954 current->add(load); 2955 return load; 2956 } 2957 2958 bool WarpBuilder::build_GetImport(BytecodeLocation loc) { 2959 auto* snapshot = getOpSnapshot<WarpGetImport>(loc); 2960 2961 ModuleEnvironmentObject* targetEnv = snapshot->targetEnv(); 2962 2963 // Load the target environment slot. 2964 MConstant* obj = constant(ObjectValue(*targetEnv)); 2965 auto* load = buildLoadSlot(obj, snapshot->numFixedSlots(), snapshot->slot()); 2966 2967 if (snapshot->needsLexicalCheck()) { 2968 // TODO: IonBuilder has code to mark non-movable. See buildCheckLexicalOp. 2969 MInstruction* lexicalCheck = MLexicalCheck::New(alloc(), load); 2970 current->add(lexicalCheck); 2971 current->push(lexicalCheck); 2972 } else { 2973 current->push(load); 2974 } 2975 2976 return true; 2977 } 2978 2979 bool WarpBuilder::build_GetPropSuper(BytecodeLocation loc) { 2980 MDefinition* obj = current->pop(); 2981 MDefinition* receiver = current->pop(); 2982 return buildIC(loc, CacheKind::GetPropSuper, {obj, receiver}); 2983 } 2984 2985 bool WarpBuilder::build_GetElemSuper(BytecodeLocation loc) { 2986 MDefinition* obj = current->pop(); 2987 MDefinition* id = current->pop(); 2988 MDefinition* receiver = current->pop(); 2989 return buildIC(loc, CacheKind::GetElemSuper, {obj, id, receiver}); 2990 } 2991 2992 bool WarpBuilder::build_InitProp(BytecodeLocation loc) { 2993 MDefinition* val = current->pop(); 2994 MDefinition* obj = current->peek(-1); 2995 return buildIC(loc, CacheKind::SetProp, {obj, val}); 2996 } 2997 2998 bool WarpBuilder::build_InitLockedProp(BytecodeLocation loc) { 2999 return build_InitProp(loc); 3000 } 3001 3002 bool WarpBuilder::build_InitHiddenProp(BytecodeLocation loc) { 3003 return build_InitProp(loc); 3004 } 3005 3006 bool WarpBuilder::build_InitElem(BytecodeLocation loc) { 3007 MDefinition* val = current->pop(); 3008 MDefinition* id = current->pop(); 3009 MDefinition* obj = current->peek(-1); 3010 return buildIC(loc, CacheKind::SetElem, {obj, id, val}); 3011 } 3012 3013 bool WarpBuilder::build_InitLockedElem(BytecodeLocation loc) { 3014 return build_InitElem(loc); 3015 } 3016 3017 bool WarpBuilder::build_InitHiddenElem(BytecodeLocation loc) { 3018 return build_InitElem(loc); 3019 } 3020 3021 bool WarpBuilder::build_InitElemArray(BytecodeLocation loc) { 3022 MDefinition* val = current->pop(); 3023 MDefinition* obj = current->peek(-1); 3024 3025 // Note: getInitElemArrayIndex asserts the index fits in int32_t. 3026 uint32_t index = loc.getInitElemArrayIndex(); 3027 MConstant* indexConst = constant(Int32Value(index)); 3028 3029 // Note: InitArrayElemOperation asserts the index does not exceed the array's 3030 // dense element capacity. 3031 3032 auto* elements = MElements::New(alloc(), obj); 3033 current->add(elements); 3034 3035 if (val->type() == MIRType::MagicHole) { 3036 val->setImplicitlyUsedUnchecked(); 3037 auto* store = MStoreHoleValueElement::New(alloc(), elements, indexConst); 3038 current->add(store); 3039 } else { 3040 current->add(MPostWriteBarrier::New(alloc(), obj, val)); 3041 auto* store = 3042 MStoreElement::NewUnbarriered(alloc(), elements, indexConst, val, 3043 /* needsHoleCheck = */ false); 3044 current->add(store); 3045 } 3046 3047 auto* setLength = MSetInitializedLength::New(alloc(), elements, indexConst); 3048 current->add(setLength); 3049 3050 return resumeAfter(setLength, loc); 3051 } 3052 3053 bool WarpBuilder::build_InitElemInc(BytecodeLocation loc) { 3054 MDefinition* val = current->pop(); 3055 MDefinition* index = current->pop(); 3056 MDefinition* obj = current->peek(-1); 3057 3058 // Push index + 1. 3059 MConstant* constOne = constant(Int32Value(1)); 3060 MAdd* nextIndex = MAdd::New(alloc(), index, constOne, TruncateKind::Truncate); 3061 current->add(nextIndex); 3062 current->push(nextIndex); 3063 3064 return buildIC(loc, CacheKind::SetElem, {obj, index, val}); 3065 } 3066 3067 bool WarpBuilder::build_Lambda(BytecodeLocation loc) { 3068 MOZ_ASSERT(usesEnvironmentChain()); 3069 return buildIC(loc, CacheKind::Lambda, {}); 3070 } 3071 3072 bool WarpBuilder::build_FunWithProto(BytecodeLocation loc) { 3073 MOZ_ASSERT(usesEnvironmentChain()); 3074 3075 MDefinition* proto = current->pop(); 3076 MDefinition* env = current->environmentChain(); 3077 3078 JSFunction* fun = loc.getFunction(script_); 3079 MConstant* funConst = constant(ObjectValue(*fun)); 3080 3081 auto* ins = MFunctionWithProto::New(alloc(), env, proto, funConst); 3082 current->add(ins); 3083 current->push(ins); 3084 return resumeAfter(ins, loc); 3085 } 3086 3087 bool WarpBuilder::build_SpreadCall(BytecodeLocation loc) { 3088 bool constructing = false; 3089 CallInfo callInfo(alloc(), constructing, loc.resultIsPopped()); 3090 callInfo.initForSpreadCall(current); 3091 3092 // The argument must be an array object. Add an infallible MUnbox if needed, 3093 // but ensure it's not loop hoisted before the branch in the bytecode guarding 3094 // that it's not undefined. 3095 MOZ_ASSERT(callInfo.argc() == 1); 3096 callInfo.setArg(0, unboxObjectInfallible(callInfo.getArg(0), IsMovable::No)); 3097 3098 if (auto* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc)) { 3099 return transpileCall(loc, cacheIRSnapshot, &callInfo); 3100 } 3101 3102 bool needsThisCheck = false; 3103 MInstruction* call = makeSpreadCall(callInfo, needsThisCheck); 3104 if (!call) { 3105 return false; 3106 } 3107 call->setBailoutKind(BailoutKind::TooManyArguments); 3108 current->add(call); 3109 current->push(call); 3110 return resumeAfter(call, loc); 3111 } 3112 3113 bool WarpBuilder::build_SpreadNew(BytecodeLocation loc) { 3114 bool constructing = true; 3115 CallInfo callInfo(alloc(), constructing, loc.resultIsPopped()); 3116 callInfo.initForSpreadCall(current); 3117 3118 // See build_SpreadCall. 3119 MOZ_ASSERT(callInfo.argc() == 1); 3120 callInfo.setArg(0, unboxObjectInfallible(callInfo.getArg(0), IsMovable::No)); 3121 3122 if (auto* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc)) { 3123 return transpileCall(loc, cacheIRSnapshot, &callInfo); 3124 } 3125 3126 buildCreateThis(callInfo); 3127 3128 bool needsThisCheck = true; 3129 MInstruction* call = makeSpreadCall(callInfo, needsThisCheck); 3130 if (!call) { 3131 return false; 3132 } 3133 call->setBailoutKind(BailoutKind::TooManyArguments); 3134 current->add(call); 3135 current->push(call); 3136 return resumeAfter(call, loc); 3137 } 3138 3139 bool WarpBuilder::build_SpreadSuperCall(BytecodeLocation loc) { 3140 return build_SpreadNew(loc); 3141 } 3142 3143 bool WarpBuilder::build_OptimizeSpreadCall(BytecodeLocation loc) { 3144 MDefinition* value = current->pop(); 3145 return buildIC(loc, CacheKind::OptimizeSpreadCall, {value}); 3146 } 3147 3148 bool WarpBuilder::build_Debugger(BytecodeLocation loc) { 3149 // The |debugger;| statement will bail out to Baseline if the realm is a 3150 // debuggee realm with an onDebuggerStatement hook. 3151 MDebugger* debugger = MDebugger::New(alloc()); 3152 current->add(debugger); 3153 return resumeAfter(debugger, loc); 3154 } 3155 3156 bool WarpBuilder::build_TableSwitch(BytecodeLocation loc) { 3157 int32_t low = loc.getTableSwitchLow(); 3158 int32_t high = loc.getTableSwitchHigh(); 3159 size_t numCases = high - low + 1; 3160 3161 MDefinition* input = current->pop(); 3162 MTableSwitch* tableswitch = MTableSwitch::New(alloc(), input, low, high); 3163 current->end(tableswitch); 3164 3165 // Table mapping from target bytecode offset to MTableSwitch successor index. 3166 // This prevents adding multiple predecessor/successor edges to the same 3167 // target block, which isn't valid in MIR. 3168 using TargetToSuccessorMap = 3169 InlineMap<uint32_t, uint32_t, 8, DefaultHasher<uint32_t>, 3170 SystemAllocPolicy>; 3171 TargetToSuccessorMap targetToSuccessor; 3172 3173 // Create |default| edge. 3174 { 3175 BytecodeLocation defaultLoc = loc.getTableSwitchDefaultTarget(); 3176 uint32_t defaultOffset = defaultLoc.bytecodeToOffset(script_); 3177 3178 size_t index; 3179 if (!tableswitch->addDefault(nullptr, &index)) { 3180 return false; 3181 } 3182 if (!addPendingEdge(defaultLoc, current, index)) { 3183 return false; 3184 } 3185 if (!targetToSuccessor.put(defaultOffset, index)) { 3186 return false; 3187 } 3188 } 3189 3190 // Add all cases. 3191 for (size_t i = 0; i < numCases; i++) { 3192 BytecodeLocation caseLoc = loc.getTableSwitchCaseTarget(script_, i); 3193 uint32_t caseOffset = caseLoc.bytecodeToOffset(script_); 3194 3195 size_t index; 3196 if (auto p = targetToSuccessor.lookupForAdd(caseOffset)) { 3197 index = p->value(); 3198 } else { 3199 if (!tableswitch->addSuccessor(nullptr, &index)) { 3200 return false; 3201 } 3202 if (!addPendingEdge(caseLoc, current, index)) { 3203 return false; 3204 } 3205 if (!targetToSuccessor.add(p, caseOffset, index)) { 3206 return false; 3207 } 3208 } 3209 if (!tableswitch->addCase(index)) { 3210 return false; 3211 } 3212 } 3213 3214 setTerminatedBlock(); 3215 return true; 3216 } 3217 3218 bool WarpBuilder::build_Rest(BytecodeLocation loc) { 3219 auto* snapshot = getOpSnapshot<WarpRest>(loc); 3220 Shape* shape = snapshot ? snapshot->shape() : nullptr; 3221 3222 // NOTE: Keep this code in sync with |ArgumentsReplacer|. 3223 3224 if (inlineCallInfo()) { 3225 // If we are inlining, we know the actual arguments. 3226 unsigned numActuals = inlineCallInfo()->argc(); 3227 unsigned numFormals = info().nargs() - 1; 3228 unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0; 3229 3230 // TODO: support pre-tenuring. 3231 gc::Heap heap = gc::Heap::Default; 3232 3233 // Allocate an array of the correct size. 3234 MInstruction* newArray; 3235 if (shape && gc::CanUseFixedElementsForArray(numRest)) { 3236 auto* shapeConstant = MConstant::NewShape(alloc(), shape); 3237 current->add(shapeConstant); 3238 newArray = MNewArrayObject::New(alloc(), shapeConstant, numRest, heap); 3239 } else { 3240 MConstant* templateConst = constant(NullValue()); 3241 newArray = MNewArray::NewVM(alloc(), numRest, templateConst, heap); 3242 } 3243 current->add(newArray); 3244 current->push(newArray); 3245 3246 if (numRest == 0) { 3247 // No more updating to do. 3248 return true; 3249 } 3250 3251 MElements* elements = MElements::New(alloc(), newArray); 3252 current->add(elements); 3253 3254 // Unroll the argument copy loop. We don't need to do any bounds or hole 3255 // checking here. 3256 MConstant* index = nullptr; 3257 for (uint32_t i = numFormals; i < numActuals; i++) { 3258 if (!alloc().ensureBallast()) { 3259 return false; 3260 } 3261 3262 index = MConstant::NewInt32(alloc(), i - numFormals); 3263 current->add(index); 3264 3265 MDefinition* arg = inlineCallInfo()->argv()[i]; 3266 MStoreElement* store = 3267 MStoreElement::NewUnbarriered(alloc(), elements, index, arg, 3268 /* needsHoleCheck = */ false); 3269 current->add(store); 3270 current->add(MPostWriteBarrier::New(alloc(), newArray, arg)); 3271 } 3272 3273 // Update the initialized length for all the (necessarily non-hole) 3274 // elements added. 3275 MSetInitializedLength* initLength = 3276 MSetInitializedLength::New(alloc(), elements, index); 3277 current->add(initLength); 3278 3279 return true; 3280 } 3281 3282 MArgumentsLength* numActuals = MArgumentsLength::New(alloc()); 3283 current->add(numActuals); 3284 3285 // Pass in the number of actual arguments, the number of formals (not 3286 // including the rest parameter slot itself), and the shape. 3287 unsigned numFormals = info().nargs() - 1; 3288 MRest* rest = MRest::New(alloc(), numActuals, numFormals, shape); 3289 current->add(rest); 3290 current->push(rest); 3291 return true; 3292 } 3293 3294 bool WarpBuilder::build_Try(BytecodeLocation loc) { 3295 graph().setHasTryBlock(); 3296 3297 MBasicBlock* pred = current; 3298 if (!startNewBlock(pred, loc.next())) { 3299 return false; 3300 } 3301 3302 pred->end(MGoto::New(alloc(), current)); 3303 return true; 3304 } 3305 3306 bool WarpBuilder::build_Finally(BytecodeLocation loc) { 3307 MOZ_ASSERT(graph().hasTryBlock()); 3308 return true; 3309 } 3310 3311 bool WarpBuilder::build_Exception(BytecodeLocation) { 3312 MOZ_CRASH("Unreachable because we skip catch-blocks"); 3313 } 3314 3315 bool WarpBuilder::build_ExceptionAndStack(BytecodeLocation) { 3316 MOZ_CRASH("Unreachable because we skip catch-blocks"); 3317 } 3318 3319 bool WarpBuilder::build_Throw(BytecodeLocation loc) { 3320 MDefinition* def = current->pop(); 3321 3322 MThrow* ins = MThrow::New(alloc(), def); 3323 current->add(ins); 3324 if (!resumeAfter(ins, loc)) { 3325 return false; 3326 } 3327 3328 // Terminate the block. 3329 current->end(MUnreachable::New(alloc())); 3330 setTerminatedBlock(); 3331 return true; 3332 } 3333 3334 bool WarpBuilder::build_ThrowWithStack(BytecodeLocation loc) { 3335 MDefinition* stack = current->pop(); 3336 MDefinition* value = current->pop(); 3337 3338 auto* ins = MThrowWithStack::New(alloc(), value, stack); 3339 current->add(ins); 3340 if (!resumeAfter(ins, loc)) { 3341 return false; 3342 } 3343 3344 // Terminate the block. 3345 current->end(MUnreachable::New(alloc())); 3346 setTerminatedBlock(); 3347 return true; 3348 } 3349 3350 bool WarpBuilder::build_ThrowSetConst(BytecodeLocation loc) { 3351 auto* ins = MThrowRuntimeLexicalError::New(alloc(), JSMSG_BAD_CONST_ASSIGN); 3352 current->add(ins); 3353 if (!resumeAfter(ins, loc)) { 3354 return false; 3355 } 3356 3357 // Terminate the block. 3358 current->end(MUnreachable::New(alloc())); 3359 setTerminatedBlock(); 3360 return true; 3361 } 3362 3363 bool WarpBuilder::build_ThrowMsg(BytecodeLocation loc) { 3364 auto* ins = MThrowMsg::New(alloc(), loc.throwMsgKind()); 3365 current->add(ins); 3366 if (!resumeAfter(ins, loc)) { 3367 return false; 3368 } 3369 3370 // Terminate the block. 3371 current->end(MUnreachable::New(alloc())); 3372 setTerminatedBlock(); 3373 return true; 3374 } 3375 3376 bool WarpBuilder::buildIC(BytecodeLocation loc, CacheKind kind, 3377 std::initializer_list<MDefinition*> inputs) { 3378 MOZ_ASSERT(loc.opHasIC()); 3379 3380 mozilla::DebugOnly<size_t> numInputs = inputs.size(); 3381 MOZ_ASSERT(numInputs == NumInputsForCacheKind(kind)); 3382 3383 const WarpCacheIRBase* cacheIRSnapshot = getOpSnapshot<WarpCacheIR>(loc); 3384 if (!cacheIRSnapshot) { 3385 cacheIRSnapshot = getOpSnapshot<WarpCacheIRWithShapeList>(loc); 3386 if (!cacheIRSnapshot) { 3387 cacheIRSnapshot = getOpSnapshot<WarpCacheIRWithShapeListAndOffsets>(loc); 3388 } 3389 } 3390 if (cacheIRSnapshot) { 3391 return TranspileCacheIRToMIR(this, loc, cacheIRSnapshot, inputs); 3392 } 3393 3394 if (getOpSnapshot<WarpBailout>(loc)) { 3395 for (MDefinition* input : inputs) { 3396 input->setImplicitlyUsedUnchecked(); 3397 } 3398 return buildBailoutForColdIC(loc, kind); 3399 } 3400 3401 if (const auto* inliningSnapshot = getOpSnapshot<WarpInlinedCall>(loc)) { 3402 // The CallInfo will be initialized by the transpiler. 3403 bool ignoresRval = BytecodeIsPopped(loc.toRawBytecode()); 3404 CallInfo callInfo(alloc(), /*constructing =*/false, ignoresRval); 3405 callInfo.markAsInlined(); 3406 3407 if (!TranspileCacheIRToMIR(this, loc, inliningSnapshot->cacheIRSnapshot(), 3408 inputs, &callInfo)) { 3409 return false; 3410 } 3411 return buildInlinedCall(loc, inliningSnapshot, callInfo); 3412 } 3413 3414 // Work around std::initializer_list not defining operator[]. 3415 auto getInput = [&](size_t index) -> MDefinition* { 3416 MOZ_ASSERT(index < numInputs); 3417 return inputs.begin()[index]; 3418 }; 3419 3420 switch (kind) { 3421 case CacheKind::UnaryArith: { 3422 MOZ_ASSERT(numInputs == 1); 3423 auto* ins = MUnaryCache::New(alloc(), getInput(0)); 3424 current->add(ins); 3425 current->push(ins); 3426 return resumeAfter(ins, loc); 3427 } 3428 case CacheKind::ToPropertyKey: { 3429 MOZ_ASSERT(numInputs == 1); 3430 auto* ins = MToPropertyKeyCache::New(alloc(), getInput(0)); 3431 current->add(ins); 3432 current->push(ins); 3433 return resumeAfter(ins, loc); 3434 } 3435 case CacheKind::BinaryArith: { 3436 MOZ_ASSERT(numInputs == 2); 3437 auto* ins = 3438 MBinaryCache::New(alloc(), getInput(0), getInput(1), MIRType::Value); 3439 current->add(ins); 3440 current->push(ins); 3441 return resumeAfter(ins, loc); 3442 } 3443 case CacheKind::Compare: { 3444 MOZ_ASSERT(numInputs == 2); 3445 auto* ins = MBinaryCache::New(alloc(), getInput(0), getInput(1), 3446 MIRType::Boolean); 3447 current->add(ins); 3448 current->push(ins); 3449 return resumeAfter(ins, loc); 3450 } 3451 case CacheKind::In: { 3452 MOZ_ASSERT(numInputs == 2); 3453 auto* ins = MInCache::New(alloc(), getInput(0), getInput(1)); 3454 current->add(ins); 3455 current->push(ins); 3456 return resumeAfter(ins, loc); 3457 } 3458 case CacheKind::HasOwn: { 3459 MOZ_ASSERT(numInputs == 2); 3460 // Note: the MHasOwnCache constructor takes obj/id instead of id/obj. 3461 auto* ins = MHasOwnCache::New(alloc(), getInput(1), getInput(0)); 3462 current->add(ins); 3463 current->push(ins); 3464 return resumeAfter(ins, loc); 3465 } 3466 case CacheKind::CheckPrivateField: { 3467 MOZ_ASSERT(numInputs == 2); 3468 auto* ins = 3469 MCheckPrivateFieldCache::New(alloc(), getInput(0), getInput(1)); 3470 current->add(ins); 3471 current->push(ins); 3472 return resumeAfter(ins, loc); 3473 } 3474 case CacheKind::InstanceOf: { 3475 MOZ_ASSERT(numInputs == 2); 3476 auto* ins = MInstanceOfCache::New(alloc(), getInput(0), getInput(1)); 3477 current->add(ins); 3478 current->push(ins); 3479 return resumeAfter(ins, loc); 3480 } 3481 case CacheKind::BindName: { 3482 MOZ_ASSERT(numInputs == 1); 3483 auto* ins = MBindNameCache::New(alloc(), getInput(0)); 3484 current->add(ins); 3485 current->push(ins); 3486 return resumeAfter(ins, loc); 3487 } 3488 case CacheKind::GetIterator: { 3489 MOZ_ASSERT(numInputs == 1); 3490 auto* ins = MGetIteratorCache::New(alloc(), getInput(0)); 3491 current->add(ins); 3492 current->push(ins); 3493 return resumeAfter(ins, loc); 3494 } 3495 case CacheKind::GetName: { 3496 MOZ_ASSERT(numInputs == 1); 3497 auto* ins = MGetNameCache::New(alloc(), getInput(0)); 3498 current->add(ins); 3499 current->push(ins); 3500 return resumeAfter(ins, loc); 3501 } 3502 case CacheKind::GetProp: { 3503 MOZ_ASSERT(numInputs == 1); 3504 PropertyName* name = loc.getPropertyName(script_); 3505 MConstant* id = constant(StringValue(name)); 3506 MDefinition* val = getInput(0); 3507 auto* ins = MGetPropertyCache::New(alloc(), val, id); 3508 current->add(ins); 3509 current->push(ins); 3510 return resumeAfter(ins, loc); 3511 } 3512 case CacheKind::GetElem: { 3513 MOZ_ASSERT(numInputs == 2); 3514 MDefinition* val = getInput(0); 3515 auto* ins = MGetPropertyCache::New(alloc(), val, getInput(1)); 3516 current->add(ins); 3517 current->push(ins); 3518 return resumeAfter(ins, loc); 3519 } 3520 case CacheKind::SetProp: { 3521 MOZ_ASSERT(numInputs == 2); 3522 PropertyName* name = loc.getPropertyName(script_); 3523 MConstant* id = constant(StringValue(name)); 3524 bool strict = loc.isStrictSetOp(); 3525 auto* ins = 3526 MSetPropertyCache::New(alloc(), getInput(0), id, getInput(1), strict); 3527 current->add(ins); 3528 return resumeAfter(ins, loc); 3529 } 3530 case CacheKind::SetElem: { 3531 MOZ_ASSERT(numInputs == 3); 3532 bool strict = loc.isStrictSetOp(); 3533 auto* ins = MSetPropertyCache::New(alloc(), getInput(0), getInput(1), 3534 getInput(2), strict); 3535 current->add(ins); 3536 return resumeAfter(ins, loc); 3537 } 3538 case CacheKind::GetPropSuper: { 3539 MOZ_ASSERT(numInputs == 2); 3540 PropertyName* name = loc.getPropertyName(script_); 3541 MConstant* id = constant(StringValue(name)); 3542 auto* ins = 3543 MGetPropSuperCache::New(alloc(), getInput(0), getInput(1), id); 3544 current->add(ins); 3545 current->push(ins); 3546 return resumeAfter(ins, loc); 3547 } 3548 case CacheKind::GetElemSuper: { 3549 MOZ_ASSERT(numInputs == 3); 3550 // Note: CacheIR expects obj/id/receiver but MGetPropSuperCache takes 3551 // obj/receiver/id so swap the last two inputs. 3552 auto* ins = MGetPropSuperCache::New(alloc(), getInput(0), getInput(2), 3553 getInput(1)); 3554 current->add(ins); 3555 current->push(ins); 3556 return resumeAfter(ins, loc); 3557 } 3558 case CacheKind::OptimizeSpreadCall: { 3559 MOZ_ASSERT(numInputs == 1); 3560 auto* ins = MOptimizeSpreadCallCache::New(alloc(), getInput(0)); 3561 current->add(ins); 3562 current->push(ins); 3563 return resumeAfter(ins, loc); 3564 } 3565 case CacheKind::TypeOf: { 3566 // Note: Warp does not have a TypeOf IC, it just inlines the operation. 3567 MOZ_ASSERT(numInputs == 1); 3568 auto* typeOf = MTypeOf::New(alloc(), getInput(0)); 3569 current->add(typeOf); 3570 3571 auto* ins = MTypeOfName::New(alloc(), typeOf); 3572 current->add(ins); 3573 current->push(ins); 3574 return true; 3575 } 3576 case CacheKind::TypeOfEq: { 3577 MOZ_ASSERT(numInputs == 1); 3578 auto operand = loc.getTypeofEqOperand(); 3579 JSType type = operand.type(); 3580 JSOp compareOp = operand.compareOp(); 3581 auto* typeOf = MTypeOf::New(alloc(), getInput(0)); 3582 current->add(typeOf); 3583 3584 auto* typeInt = MConstant::NewInt32(alloc(), type); 3585 current->add(typeInt); 3586 3587 auto* ins = MCompare::New(alloc(), typeOf, typeInt, compareOp, 3588 MCompare::Compare_Int32); 3589 current->add(ins); 3590 current->push(ins); 3591 return true; 3592 } 3593 case CacheKind::NewObject: { 3594 auto* templateConst = constant(NullValue()); 3595 MNewObject* ins = MNewObject::NewVM( 3596 alloc(), templateConst, gc::Heap::Default, MNewObject::ObjectLiteral); 3597 current->add(ins); 3598 current->push(ins); 3599 return resumeAfter(ins, loc); 3600 } 3601 case CacheKind::NewArray: { 3602 uint32_t length = loc.getNewArrayLength(); 3603 MConstant* templateConst = constant(NullValue()); 3604 MNewArray* ins = 3605 MNewArray::NewVM(alloc(), length, templateConst, gc::Heap::Default); 3606 current->add(ins); 3607 current->push(ins); 3608 return true; 3609 } 3610 case CacheKind::CloseIter: { 3611 MOZ_ASSERT(numInputs == 1); 3612 static_assert(sizeof(CompletionKind) == sizeof(uint8_t)); 3613 CompletionKind kind = loc.getCompletionKind(); 3614 auto* ins = MCloseIterCache::New(alloc(), getInput(0), uint8_t(kind)); 3615 current->add(ins); 3616 return resumeAfter(ins, loc); 3617 } 3618 case CacheKind::OptimizeGetIterator: { 3619 MOZ_ASSERT(numInputs == 1); 3620 auto* ins = MOptimizeGetIteratorCache::New(alloc(), getInput(0)); 3621 current->add(ins); 3622 current->push(ins); 3623 return resumeAfter(ins, loc); 3624 } 3625 case CacheKind::Lambda: { 3626 MDefinition* env = current->environmentChain(); 3627 JSFunction* fun = loc.getFunction(script_); 3628 MConstant* funConst = constant(ObjectValue(*fun)); 3629 auto* ins = MLambda::New(alloc(), env, funConst, gc::Heap::Default); 3630 current->add(ins); 3631 current->push(ins); 3632 return resumeAfter(ins, loc); 3633 } 3634 case CacheKind::LazyConstant: 3635 case CacheKind::ToBool: 3636 case CacheKind::Call: 3637 case CacheKind::GetImport: 3638 // We're currently not using an IC or transpiling CacheIR for these kinds. 3639 MOZ_CRASH("Unexpected kind"); 3640 } 3641 3642 return true; 3643 } 3644 3645 bool WarpBuilder::buildBailoutForColdIC(BytecodeLocation loc, CacheKind kind) { 3646 MOZ_ASSERT(loc.opHasIC()); 3647 3648 MBail* bail = MBail::New(alloc(), BailoutKind::FirstExecution); 3649 current->add(bail); 3650 current->setAlwaysBails(); 3651 3652 MIRType resultType; 3653 switch (kind) { 3654 case CacheKind::UnaryArith: 3655 case CacheKind::BinaryArith: 3656 case CacheKind::GetName: 3657 case CacheKind::GetProp: 3658 case CacheKind::GetElem: 3659 case CacheKind::GetPropSuper: 3660 case CacheKind::GetElemSuper: 3661 case CacheKind::GetImport: 3662 case CacheKind::LazyConstant: 3663 case CacheKind::Call: 3664 case CacheKind::ToPropertyKey: 3665 case CacheKind::OptimizeSpreadCall: 3666 resultType = MIRType::Value; 3667 break; 3668 case CacheKind::BindName: 3669 case CacheKind::GetIterator: 3670 case CacheKind::NewArray: 3671 case CacheKind::NewObject: 3672 case CacheKind::Lambda: 3673 resultType = MIRType::Object; 3674 break; 3675 case CacheKind::TypeOf: 3676 resultType = MIRType::String; 3677 break; 3678 case CacheKind::ToBool: 3679 case CacheKind::Compare: 3680 case CacheKind::In: 3681 case CacheKind::HasOwn: 3682 case CacheKind::CheckPrivateField: 3683 case CacheKind::InstanceOf: 3684 case CacheKind::OptimizeGetIterator: 3685 case CacheKind::TypeOfEq: 3686 resultType = MIRType::Boolean; 3687 break; 3688 case CacheKind::SetProp: 3689 case CacheKind::SetElem: 3690 case CacheKind::CloseIter: 3691 return true; // No result. 3692 } 3693 3694 auto* ins = MUnreachableResult::New(alloc(), resultType); 3695 current->add(ins); 3696 current->push(ins); 3697 3698 return true; 3699 } 3700 3701 class MOZ_RAII AutoAccumulateReturns { 3702 MIRGraph& graph_; 3703 MIRGraphReturns* prev_; 3704 3705 public: 3706 AutoAccumulateReturns(MIRGraph& graph, MIRGraphReturns& returns) 3707 : graph_(graph) { 3708 prev_ = graph_.returnAccumulator(); 3709 graph_.setReturnAccumulator(&returns); 3710 } 3711 ~AutoAccumulateReturns() { graph_.setReturnAccumulator(prev_); } 3712 }; 3713 3714 bool WarpBuilder::buildInlinedCall(BytecodeLocation loc, 3715 const WarpInlinedCall* inlineSnapshot, 3716 CallInfo& callInfo) { 3717 jsbytecode* pc = loc.toRawBytecode(); 3718 3719 if (callInfo.isSetter()) { 3720 // build_SetProp pushes the rhs argument onto the stack. Remove it 3721 // in preparation for pushCallStack. 3722 current->pop(); 3723 } 3724 3725 callInfo.setImplicitlyUsedUnchecked(); 3726 3727 // Capture formals in the outer resume point. 3728 if (!callInfo.pushCallStack(current)) { 3729 return false; 3730 } 3731 MResumePoint* outerResumePoint = 3732 MResumePoint::New(alloc(), current, pc, callInfo.inliningResumeMode()); 3733 if (!outerResumePoint) { 3734 return false; 3735 } 3736 current->setOuterResumePoint(outerResumePoint); 3737 3738 // Pop formals again, except leave |callee| on stack for duration of call. 3739 callInfo.popCallStack(current); 3740 current->push(callInfo.callee()); 3741 3742 // Build the graph. 3743 CompileInfo* calleeCompileInfo = inlineSnapshot->info(); 3744 MIRGraphReturns returns(alloc()); 3745 AutoAccumulateReturns aar(graph(), returns); 3746 WarpBuilder inlineBuilder(this, inlineSnapshot->scriptSnapshot(), 3747 *calleeCompileInfo, &callInfo, outerResumePoint); 3748 if (!inlineBuilder.buildInline()) { 3749 // Note: Inlining only aborts on OOM. If inlining would fail for 3750 // any other reason, we detect it in advance and don't inline. 3751 return false; 3752 } 3753 3754 // We mark scripts as uninlineable in BytecodeAnalysis if we cannot 3755 // reach a return statement (without going through a catch/finally). 3756 MOZ_ASSERT(!returns.empty()); 3757 3758 // Create return block 3759 BytecodeLocation postCall = loc.next(); 3760 MBasicBlock* prev = current; 3761 if (!startNewEntryBlock(prev->stackDepth(), postCall)) { 3762 return false; 3763 } 3764 // Restore previous value of callerResumePoint. 3765 current->setCallerResumePoint(callerResumePoint()); 3766 current->inheritSlots(prev); 3767 3768 // Pop |callee|. 3769 current->pop(); 3770 3771 // Accumulate return values. 3772 MDefinition* returnValue = 3773 patchInlinedReturns(calleeCompileInfo, callInfo, returns, current); 3774 if (!returnValue) { 3775 return false; 3776 } 3777 current->push(returnValue); 3778 3779 // Initialize entry slots 3780 if (!current->initEntrySlots(alloc())) { 3781 return false; 3782 } 3783 3784 return true; 3785 } 3786 3787 MDefinition* WarpBuilder::patchInlinedReturns(CompileInfo* calleeCompileInfo, 3788 CallInfo& callInfo, 3789 MIRGraphReturns& exits, 3790 MBasicBlock* returnBlock) { 3791 if (exits.length() == 1) { 3792 return patchInlinedReturn(calleeCompileInfo, callInfo, exits[0], 3793 returnBlock); 3794 } 3795 3796 // Accumulate multiple returns with a phi. 3797 MPhi* phi = MPhi::New(alloc()); 3798 if (!phi->reserveLength(exits.length())) { 3799 return nullptr; 3800 } 3801 3802 for (auto* exit : exits) { 3803 MDefinition* rdef = 3804 patchInlinedReturn(calleeCompileInfo, callInfo, exit, returnBlock); 3805 if (!rdef) { 3806 return nullptr; 3807 } 3808 phi->addInput(rdef); 3809 } 3810 returnBlock->addPhi(phi); 3811 return phi; 3812 } 3813 3814 MDefinition* WarpBuilder::patchInlinedReturn(CompileInfo* calleeCompileInfo, 3815 CallInfo& callInfo, 3816 MBasicBlock* exit, 3817 MBasicBlock* returnBlock) { 3818 // Replace the MReturn in the exit block with an MGoto branching to 3819 // the return block. 3820 MDefinition* rdef = exit->lastIns()->toReturn()->input(); 3821 exit->discardLastIns(); 3822 3823 // Constructors must be patched by the caller to always return an object. 3824 // Derived class constructors contain extra bytecode to ensure an object 3825 // is always returned, so no additional patching is needed. 3826 if (callInfo.constructing() && 3827 !calleeCompileInfo->isDerivedClassConstructor()) { 3828 auto* filter = MReturnFromCtor::New(alloc(), rdef, callInfo.thisArg()); 3829 exit->add(filter); 3830 rdef = filter; 3831 } else if (callInfo.isSetter()) { 3832 // Setters return the rhs argument, not whatever value is returned. 3833 rdef = callInfo.getArg(0); 3834 } 3835 3836 exit->end(MGoto::New(alloc(), returnBlock)); 3837 if (!returnBlock->addPredecessorWithoutPhis(exit)) { 3838 return nullptr; 3839 } 3840 3841 return rdef; 3842 }