void MBasicBlock::removePredecessor(MBasicBlock *pred) { // If we're removing the last backedge, this is no longer a loop. if (isLoopHeader() && hasUniqueBackedge() && backedge() == pred) clearLoopHeader(); for (size_t i = 0; i < numPredecessors(); i++) { if (getPredecessor(i) != pred) continue; // Adjust phis. Note that this can leave redundant phis // behind. if (!phisEmpty()) { JS_ASSERT(pred->successorWithPhis()); JS_ASSERT(pred->positionInPhiSuccessor() == i); for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) iter->removeOperand(i); pred->setSuccessorWithPhis(nullptr, 0); for (size_t j = i+1; j < numPredecessors(); j++) getPredecessor(j)->setSuccessorWithPhis(this, j - 1); } // Remove from pred list. MBasicBlock **ptr = predecessors_.begin() + i; predecessors_.erase(ptr); return; } MOZ_ASSUME_UNREACHABLE("predecessor was not found"); }
void MBasicBlock::removePredecessor(MBasicBlock *pred) { JS_ASSERT(numPredecessors() >= 2); for (size_t i = 0; i < numPredecessors(); i++) { if (getPredecessor(i) != pred) continue; // Adjust phis. Note that this can leave redundant phis // behind. if (!phisEmpty()) { JS_ASSERT(pred->successorWithPhis()); JS_ASSERT(pred->positionInPhiSuccessor() == i); for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) iter->removeOperand(i); for (size_t j = i+1; j < numPredecessors(); j++) getPredecessor(j)->setSuccessorWithPhis(this, j - 1); } // Remove from pred list. MBasicBlock **ptr = predecessors_.begin() + i; predecessors_.erase(ptr); return; } JS_NOT_REACHED("predecessor was not found"); }
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. if (!inheritPhisFromBackedge(pred, &hadTypeChange)) return AbortReason_Alloc; 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; }
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; }
void MBasicBlock::discardAllPhiOperands() { for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) iter->removeAllOperands(); for (MBasicBlock **pred = predecessors_.begin(); pred != predecessors_.end(); pred++) (*pred)->setSuccessorWithPhis(nullptr, 0); }
bool MBasicBlock::setBackedgeWasm(MBasicBlock* pred) { // Predecessors must be finished, and at the correct stack depth. MOZ_ASSERT(hasLastIns()); MOZ_ASSERT(pred->hasLastIns()); MOZ_ASSERT(stackDepth() == pred->stackDepth()); // We must be a pending loop header MOZ_ASSERT(kind_ == PENDING_LOOP_HEADER); // Add exit definitions to each corresponding phi at the entry. // Note: Phis are inserted in the same order as the slots. (see // MBasicBlock::New) size_t slot = 0; for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++, slot++) { MPhi* entryDef = *phi; MDefinition* exitDef = pred->getSlot(slot); // Assert that we already placed phis for each slot. MOZ_ASSERT(entryDef->block() == this); // Assert that the phi already has the correct type. MOZ_ASSERT(entryDef->type() == exitDef->type()); MOZ_ASSERT(entryDef->type() != MIRType::Value); 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); } // Phis always have room for 2 operands, so this can't fail. MOZ_ASSERT(phi->numOperands() == 1); entryDef->addInlineInput(exitDef); MOZ_ASSERT(slot < pred->stackDepth()); setSlot(slot, entryDef); } // We are now a loop header proper kind_ = LOOP_HEADER; return predecessors_.append(pred); }
MPhiIterator MBasicBlock::discardPhiAt(MPhiIterator &at) { JS_ASSERT(!phis_.empty()); for (size_t i = 0; i < at->numOperands(); i++) at->discardOperand(i); MPhiIterator result = phis_.removeAt(at); if (phis_.empty()) { for (MBasicBlock **pred = predecessors_.begin(); pred != predecessors_.end(); pred++) (*pred)->setSuccessorWithPhis(NULL, 0); } return result; }
void MBasicBlock::addPredecessorSameInputsAs(MBasicBlock* pred, MBasicBlock* existingPred) { MOZ_ASSERT(pred); MOZ_ASSERT(predecessors_.length() > 0); // Predecessors must be finished, and at the correct stack depth. MOZ_ASSERT(pred->hasLastIns()); MOZ_ASSERT(!pred->successorWithPhis()); if (!phisEmpty()) { size_t existingPosition = indexForPredecessor(existingPred); for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) { if (!iter->addInputSlow(iter->getOperand(existingPosition))) CrashAtUnhandlableOOM("MBasicBlock::addPredecessorAdjustPhis"); } } if (!predecessors_.append(pred)) CrashAtUnhandlableOOM("MBasicBlock::addPredecessorAdjustPhis"); }
MPhiIterator MBasicBlock::discardPhiAt(MPhiIterator &at) { JS_ASSERT(!phis_.empty()); at->removeAllOperands(); MPhiIterator result = phis_.removeAt(at); if (phis_.empty()) { for (MBasicBlock **pred = predecessors_.begin(); pred != predecessors_.end(); pred++) (*pred)->setSuccessorWithPhis(nullptr, 0); } return result; }
bool ion::EliminatePhis(MIRGenerator *mir, MIRGraph &graph) { Vector<MPhi *, 16, SystemAllocPolicy> worklist; // Add all observable phis to a worklist. We use the "in worklist" bit to // mean "this phi is live". for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) { if (mir->shouldCancel("Eliminate Phis (populate loop)")) return false; MPhiIterator iter = block->phisBegin(); while (iter != block->phisEnd()) { // Flag all as unused, only observable phis would be marked as used // when processed by the work list. iter->setUnused(); // If the phi is redundant, remove it here. if (MDefinition *redundant = IsPhiRedundant(*iter)) { iter->replaceAllUsesWith(redundant); iter = block->discardPhiAt(iter); continue; } // Enqueue observable Phis. if (IsPhiObservable(*iter)) { iter->setInWorklist(); if (!worklist.append(*iter)) return false; } iter++; } } // Iteratively mark all phis reachable from live phis. while (!worklist.empty()) { if (mir->shouldCancel("Eliminate Phis (worklist)")) return false; MPhi *phi = worklist.popCopy(); JS_ASSERT(phi->isUnused()); phi->setNotInWorklist(); // The removal of Phis can produce newly redundant phis. if (MDefinition *redundant = IsPhiRedundant(phi)) { // Add to the worklist the used phis which are impacted. for (MUseDefIterator it(phi); it; it++) { if (it.def()->isPhi()) { MPhi *use = it.def()->toPhi(); if (!use->isUnused()) { use->setUnusedUnchecked(); use->setInWorklist(); if (!worklist.append(use)) return false; } } } phi->replaceAllUsesWith(redundant); } else { // Otherwise flag them as used. phi->setNotUnused(); } // The current phi is/was used, so all its operands are used. for (size_t i = 0; i < phi->numOperands(); i++) { MDefinition *in = phi->getOperand(i); if (!in->isPhi() || !in->isUnused() || in->isInWorklist()) continue; in->setInWorklist(); if (!worklist.append(in->toPhi())) return false; } } // Sweep dead phis. for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) { MPhiIterator iter = block->phisBegin(); while (iter != block->phisEnd()) { if (iter->isUnused()) iter = block->discardPhiAt(iter); else iter++; } } return true; }
bool ion::EliminatePhis(MIRGenerator *mir, MIRGraph &graph, Observability observe) { // Eliminates redundant or unobservable phis from the graph. A // redundant phi is something like b = phi(a, a) or b = phi(a, b), // both of which can be replaced with a. An unobservable phi is // one that whose value is never used in the program. // // Note that we must be careful not to eliminate phis representing // values that the interpreter will require later. When the graph // is first constructed, we can be more aggressive, because there // is a greater correspondence between the CFG and the bytecode. // After optimizations such as GVN have been performed, however, // the bytecode and CFG may not correspond as closely to one // another. In that case, we must be more conservative. The flag // |conservativeObservability| is used to indicate that eliminate // phis is being run after some optimizations have been performed, // and thus we should use more conservative rules about // observability. The particular danger is that we can optimize // away uses of a phi because we think they are not executable, // but the foundation for that assumption is false TI information // that will eventually be invalidated. Therefore, if // |conservativeObservability| is set, we will consider any use // from a resume point to be observable. Otherwise, we demand a // use from an actual instruction. Vector<MPhi *, 16, SystemAllocPolicy> worklist; // Add all observable phis to a worklist. We use the "in worklist" bit to // mean "this phi is live". for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) { if (mir->shouldCancel("Eliminate Phis (populate loop)")) return false; MPhiIterator iter = block->phisBegin(); while (iter != block->phisEnd()) { // Flag all as unused, only observable phis would be marked as used // when processed by the work list. iter->setUnused(); // If the phi is redundant, remove it here. if (MDefinition *redundant = IsPhiRedundant(*iter)) { iter->replaceAllUsesWith(redundant); iter = block->discardPhiAt(iter); continue; } // Enqueue observable Phis. if (IsPhiObservable(*iter, observe)) { iter->setInWorklist(); if (!worklist.append(*iter)) return false; } iter++; } } // Iteratively mark all phis reachable from live phis. while (!worklist.empty()) { if (mir->shouldCancel("Eliminate Phis (worklist)")) return false; MPhi *phi = worklist.popCopy(); JS_ASSERT(phi->isUnused()); phi->setNotInWorklist(); // The removal of Phis can produce newly redundant phis. if (MDefinition *redundant = IsPhiRedundant(phi)) { // Add to the worklist the used phis which are impacted. for (MUseDefIterator it(phi); it; it++) { if (it.def()->isPhi()) { MPhi *use = it.def()->toPhi(); if (!use->isUnused()) { use->setUnusedUnchecked(); use->setInWorklist(); if (!worklist.append(use)) return false; } } } phi->replaceAllUsesWith(redundant); } else { // Otherwise flag them as used. phi->setNotUnused(); } // The current phi is/was used, so all its operands are used. for (size_t i = 0; i < phi->numOperands(); i++) { MDefinition *in = phi->getOperand(i); if (!in->isPhi() || !in->isUnused() || in->isInWorklist()) continue; in->setInWorklist(); if (!worklist.append(in->toPhi())) return false; } } // Sweep dead phis. for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) { MPhiIterator iter = block->phisBegin(); while (iter != block->phisEnd()) { if (iter->isUnused()) iter = block->discardPhiAt(iter); else iter++; } } return true; }