bool MBasicBlock::inherit(MBasicBlock *pred, uint32_t popped) { if (pred) { stackPosition_ = pred->stackPosition_; JS_ASSERT(stackPosition_ >= popped); stackPosition_ -= popped; if (kind_ != PENDING_LOOP_HEADER) copySlots(pred); } else if (pc()) { uint32_t stackDepth = info().script()->analysis()->getCode(pc()).stackDepth; stackPosition_ = info().firstStackSlot() + stackDepth; JS_ASSERT(stackPosition_ >= popped); stackPosition_ -= popped; } else { stackPosition_ = info().firstStackSlot(); } JS_ASSERT(info_.nslots() >= stackPosition_); JS_ASSERT(!entryResumePoint_); if (pc()) { // Propagate the caller resume point from the inherited block. MResumePoint *callerResumePoint = pred ? pred->callerResumePoint() : NULL; // Create a resume point using our initial stack state. entryResumePoint_ = new MResumePoint(this, pc(), callerResumePoint, MResumePoint::ResumeAt); if (!entryResumePoint_->init()) return false; } if (pred) { if (!predecessors_.append(pred)) return false; if (kind_ == PENDING_LOOP_HEADER) { for (size_t i = 0; i < stackDepth(); i++) { MPhi *phi = MPhi::New(i); if (!phi->addInputSlow(pred->getSlot(i))) return false; addPhi(phi); setSlot(i, phi); if (entryResumePoint()) entryResumePoint()->setOperand(i, phi); } } else if (entryResumePoint()) { for (size_t i = 0; i < stackDepth(); i++) entryResumePoint()->setOperand(i, getSlot(i)); } } else if (entryResumePoint()) { /* * Don't leave the operands uninitialized for the caller, as it may not * initialize them later on. */ for (size_t i = 0; i < stackDepth(); i++) entryResumePoint()->clearOperand(i); } return true; }
AbortReason MBasicBlock::setBackedge(MBasicBlock *pred) { // Predecessors must be finished, and at the correct stack depth. JS_ASSERT(lastIns_); JS_ASSERT(pred->lastIns_); JS_ASSERT(pred->stackDepth() == entryResumePoint()->stackDepth()); // We must be a pending loop header JS_ASSERT(kind_ == PENDING_LOOP_HEADER); bool hadTypeChange = false; // Add exit definitions to each corresponding phi at the entry. for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++) { MPhi *entryDef = *phi; MDefinition *exitDef = pred->slots_[entryDef->slot()]; // Assert that we already placed phis for each slot. JS_ASSERT(entryDef->block() == this); if (entryDef == exitDef) { // If the exit def is the same as the entry def, make a redundant // phi. Since loop headers have exactly two incoming edges, we // know that that's just the first input. // // Note that we eliminate later rather than now, to avoid any // weirdness around pending continue edges which might still hold // onto phis. exitDef = entryDef->getOperand(0); } bool typeChange = false; if (!entryDef->addInputSlow(exitDef, &typeChange)) return AbortReason_Alloc; hadTypeChange |= typeChange; JS_ASSERT(entryDef->slot() < pred->stackDepth()); setSlot(entryDef->slot(), entryDef); } if (hadTypeChange) { for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++) phi->removeOperand(phi->numOperands() - 1); return AbortReason_Disable; } // We are now a loop header proper kind_ = LOOP_HEADER; if (!predecessors_.append(pred)) return AbortReason_Alloc; return AbortReason_NoAbort; }
bool MBasicBlock::inheritPhisFromBackedge(MBasicBlock* backedge, bool* hadTypeChange) { // We must be a pending loop header MOZ_ASSERT(kind_ == PENDING_LOOP_HEADER); size_t stackDepth = entryResumePoint()->stackDepth(); for (size_t slot = 0; slot < stackDepth; slot++) { // Get the value stack-slot of the back edge. MDefinition* exitDef = backedge->getSlot(slot); // Get the value of the loop header. MDefinition* loopDef = entryResumePoint()->getOperand(slot); if (loopDef->block() != this) { // If we are finishing a pending loop header, then we need to ensure // that all operands are phis. This is usualy the case, except for // object/arrays build with generators, in which case we share the // same allocations across all blocks. MOZ_ASSERT(loopDef->block()->id() < id()); MOZ_ASSERT(loopDef == exitDef); continue; } // Phis are allocated by NewPendingLoopHeader. MPhi* entryDef = loopDef->toPhi(); MOZ_ASSERT(entryDef->block() == this); if (entryDef == exitDef) { // If the exit def is the same as the entry def, make a redundant // phi. Since loop headers have exactly two incoming edges, we // know that that's just the first input. // // Note that we eliminate later rather than now, to avoid any // weirdness around pending continue edges which might still hold // onto phis. exitDef = entryDef->getOperand(0); } bool typeChange = false; if (!entryDef->addInputSlow(exitDef)) return false; if (!entryDef->checkForTypeChange(exitDef, &typeChange)) return false; *hadTypeChange |= typeChange; setSlot(slot, entryDef); } return true; }
// A loop is about to be made reachable only through an OSR entry into one of // its nested loops. Fix everything up. bool ValueNumberer::fixupOSROnlyLoop(MBasicBlock* block, MBasicBlock* backedge) { // Create an empty and unreachable(!) block which jumps to |block|. This // allows |block| to remain marked as a loop header, so we don't have to // worry about moving a different block into place as the new loop header, // which is hard, especially if the OSR is into a nested loop. Doing all // that would produce slightly more optimal code, but this is so // extraordinarily rare that it isn't worth the complexity. MBasicBlock* fake = MBasicBlock::New(graph_, block->info(), nullptr, MBasicBlock::NORMAL); if (fake == nullptr) return false; graph_.insertBlockBefore(block, fake); fake->setImmediateDominator(fake); fake->addNumDominated(1); fake->setDomIndex(fake->id()); fake->setUnreachable(); // Create zero-input phis to use as inputs for any phis in |block|. // Again, this is a little odd, but it's the least-odd thing we can do // without significant complexity. for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd()); iter != end; ++iter) { MPhi* phi = *iter; MPhi* fakePhi = MPhi::New(graph_.alloc(), phi->type()); fake->addPhi(fakePhi); if (!phi->addInputSlow(fakePhi)) return false; } fake->end(MGoto::New(graph_.alloc(), block)); if (!block->addPredecessorWithoutPhis(fake)) return false; // Restore |backedge| as |block|'s loop backedge. block->clearLoopHeader(); block->setLoopHeader(backedge); JitSpew(JitSpew_GVN, " Created fake block%u", fake->id()); hasOSRFixups_ = true; return true; }
bool MBasicBlock::inherit(TempAllocator &alloc, BytecodeAnalysis *analysis, MBasicBlock *pred, uint32_t popped, unsigned stackPhiCount) { if (pred) { stackPosition_ = pred->stackPosition_; JS_ASSERT(stackPosition_ >= popped); stackPosition_ -= popped; if (kind_ != PENDING_LOOP_HEADER) copySlots(pred); } else { uint32_t stackDepth = analysis->info(pc()).stackDepth; stackPosition_ = info().firstStackSlot() + stackDepth; JS_ASSERT(stackPosition_ >= popped); stackPosition_ -= popped; } JS_ASSERT(info_.nslots() >= stackPosition_); JS_ASSERT(!entryResumePoint_); // Propagate the caller resume point from the inherited block. MResumePoint *callerResumePoint = pred ? pred->callerResumePoint() : nullptr; // Create a resume point using our initial stack state. entryResumePoint_ = new(alloc) MResumePoint(this, pc(), callerResumePoint, MResumePoint::ResumeAt); if (!entryResumePoint_->init(alloc)) return false; if (pred) { if (!predecessors_.append(pred)) return false; if (kind_ == PENDING_LOOP_HEADER) { size_t i = 0; for (i = 0; i < info().firstStackSlot(); i++) { MPhi *phi = MPhi::New(alloc, i); if (!phi->addInputSlow(pred->getSlot(i))) return false; addPhi(phi); setSlot(i, phi); entryResumePoint()->setOperand(i, phi); } JS_ASSERT(stackPhiCount <= stackDepth()); JS_ASSERT(info().firstStackSlot() <= stackDepth() - stackPhiCount); // Avoid creating new phis for stack values that aren't part of the // loop. Note that for loop headers that can OSR, all values on the // stack are part of the loop. for (; i < stackDepth() - stackPhiCount; i++) { MDefinition *val = pred->getSlot(i); setSlot(i, val); entryResumePoint()->setOperand(i, val); } for (; i < stackDepth(); i++) { MPhi *phi = MPhi::New(alloc, i); if (!phi->addInputSlow(pred->getSlot(i))) return false; addPhi(phi); setSlot(i, phi); entryResumePoint()->setOperand(i, phi); } } else { for (size_t i = 0; i < stackDepth(); i++) entryResumePoint()->setOperand(i, getSlot(i)); } } else { /* * Don't leave the operands uninitialized for the caller, as it may not * initialize them later on. */ for (size_t i = 0; i < stackDepth(); i++) entryResumePoint()->clearOperand(i); } return true; }