MDefinition * MIRGraph::forkJoinContext() { // Search the entry block to find a ForkJoinContext instruction. If we do // not find one, add one after the Start instruction. // // Note: the original design used a field in MIRGraph to cache the // forkJoinContext rather than searching for it again. However, this // could become out of date due to DCE. Given that we do not generally // have to search very far to find the ForkJoinContext instruction if it // exists, and that we don't look for it that often, I opted to simply // eliminate the cache and search anew each time, so that it is that much // easier to keep the IR coherent. - nmatsakis MBasicBlock *entry = entryBlock(); JS_ASSERT(entry->info().executionMode() == ParallelExecution); MInstruction *start = nullptr; for (MInstructionIterator ins(entry->begin()); ins != entry->end(); ins++) { if (ins->isForkJoinContext()) return *ins; else if (ins->isStart()) start = *ins; } JS_ASSERT(start); MForkJoinContext *cx = MForkJoinContext::New(alloc()); entry->insertAfter(start, cx); return cx; }
MDefinition * MIRGraph::parSlice() { // Search the entry block to find a par slice instruction. If we do not // find one, add one after the Start instruction. // // Note: the original design used a field in MIRGraph to cache the // parSlice rather than searching for it again. However, this // could become out of date due to DCE. Given that we do not // generally have to search very far to find the par slice // instruction if it exists, and that we don't look for it that // often, I opted to simply eliminate the cache and search anew // each time, so that it is that much easier to keep the IR // coherent. - nmatsakis MBasicBlock *entry = entryBlock(); JS_ASSERT(entry->info().executionMode() == ParallelExecution); MInstruction *start = NULL; for (MInstructionIterator ins(entry->begin()); ins != entry->end(); ins++) { if (ins->isParSlice()) return *ins; else if (ins->isStart()) start = *ins; } JS_ASSERT(start); MParSlice *parSlice = new MParSlice(); entry->insertAfter(start, parSlice); return parSlice; }
// Discard |def| and mine its operands for any subsequently dead defs. bool ValueNumberer::discardDef(MDefinition* def) { #ifdef JS_JITSPEW JitSpew(JitSpew_GVN, " Discarding %s %s%u", def->block()->isMarked() ? "unreachable" : "dead", def->opName(), def->id()); #endif #ifdef DEBUG MOZ_ASSERT(def != nextDef_, "Invalidating the MDefinition iterator"); if (def->block()->isMarked()) { MOZ_ASSERT(!def->hasUses(), "Discarding def that still has uses"); } else { MOZ_ASSERT(IsDiscardable(def), "Discarding non-discardable definition"); MOZ_ASSERT(!values_.has(def), "Discarding a definition still in the set"); } #endif MBasicBlock* block = def->block(); if (def->isPhi()) { MPhi* phi = def->toPhi(); if (!releaseAndRemovePhiOperands(phi)) return false; block->discardPhi(phi); } else { MInstruction* ins = def->toInstruction(); if (MResumePoint* resume = ins->resumePoint()) { if (!releaseResumePointOperands(resume)) return false; } if (!releaseOperands(ins)) return false; block->discardIgnoreOperands(ins); } // If that was the last definition in the block, it can be safely removed // from the graph. if (block->phisEmpty() && block->begin() == block->end()) { MOZ_ASSERT(block->isMarked(), "Reachable block lacks at least a control instruction"); // As a special case, don't remove a block which is a dominator tree // root so that we don't invalidate the iterator in visitGraph. We'll // check for this and remove it later. if (block->immediateDominator() != block) { JitSpew(JitSpew_GVN, " Block block%u is now empty; discarding", block->id()); graph_.removeBlock(block); blocksRemoved_ = true; } else { JitSpew(JitSpew_GVN, " Dominator root block%u is now empty; will discard later", block->id()); } } return true; }
bool UnreachableCodeElimination::removeUnmarkedBlocksAndClearDominators() { // Removes blocks that are not marked from the graph. For blocks // that *are* marked, clears the mark and adjusts the id to its // new value. Also adds blocks that are immediately reachable // from an unmarked block to the frontier. size_t id = marked_; for (PostorderIterator iter(graph_.poBegin()); iter != graph_.poEnd();) { if (mir_->shouldCancel("Eliminate Unreachable Code")) return false; MBasicBlock *block = *iter; iter++; // Unconditionally clear the dominators. It's somewhat complex to // adjust the values and relatively fast to just recompute. block->clearDominatorInfo(); if (block->isMarked()) { block->setId(--id); for (MPhiIterator iter(block->phisBegin()); iter != block->phisEnd(); iter++) checkDependencyAndRemoveUsesFromUnmarkedBlocks(*iter); for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) checkDependencyAndRemoveUsesFromUnmarkedBlocks(*iter); } else { for (size_t i = 0, c = block->numSuccessors(); i < c; i++) { MBasicBlock *succ = block->getSuccessor(i); if (succ->isMarked()) { // succ is on the frontier of blocks to be removed: succ->removePredecessor(block); if (!redundantPhis_) { for (MPhiIterator iter(succ->phisBegin()); iter != succ->phisEnd(); iter++) { if (iter->operandIfRedundant()) { redundantPhis_ = true; break; } } } } } graph_.removeBlock(block); } } JS_ASSERT(id == 0); return true; }
// Visit all the blocks in the graph. bool ValueNumberer::visitGraph() { // Due to OSR blocks, the set of blocks dominated by a blocks may not be // contiguous in the RPO. Do a separate traversal for each dominator tree // root. There's always the main entry, and sometimes there's an OSR entry, // and then there are the roots formed where the OSR paths merge with the // main entry paths. for (ReversePostorderIterator iter(graph_.rpoBegin()); ; ) { MOZ_ASSERT(iter != graph_.rpoEnd(), "Inconsistent dominator information"); MBasicBlock* block = *iter; if (block->immediateDominator() == block) { if (!visitDominatorTree(block)) return false; // Normally unreachable blocks would be removed by now, but if this // block is a dominator tree root, it has been special-cased and left // in place in order to avoid invalidating our iterator. Now that // we've finished the tree, increment the iterator, and then if it's // marked for removal, remove it. ++iter; if (block->isMarked()) { JitSpew(JitSpew_GVN, " Discarding dominator root block%u", block->id()); MOZ_ASSERT(block->begin() == block->end(), "Unreachable dominator tree root has instructions after tree walk"); MOZ_ASSERT(block->phisEmpty(), "Unreachable dominator tree root has phis after tree walk"); graph_.removeBlock(block); blocksRemoved_ = true; } MOZ_ASSERT(totalNumVisited_ <= graph_.numBlocks(), "Visited blocks too many times"); if (totalNumVisited_ >= graph_.numBlocks()) break; } else { // This block a dominator tree root. Proceed to the next one. ++iter; } } totalNumVisited_ = 0; return true; }
// Test whether any instruction in the loop possiblyCalls(). static bool LoopContainsPossibleCall(MIRGraph &graph, MBasicBlock *header, MBasicBlock *backedge) { for (auto i(graph.rpoBegin(header)); ; ++i) { MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop"); MBasicBlock *block = *i; if (!block->isMarked()) continue; for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ++insIter) { MInstruction *ins = *insIter; if (ins->possiblyCalls()) { JitSpew(JitSpew_LICM, " Possile call found at %s%u", ins->opName(), ins->id()); return true; } } if (block == backedge) break; } return false; }
void TypeAnalyzer::replaceRedundantPhi(MPhi *phi) { MBasicBlock *block = phi->block(); js::Value v; switch (phi->type()) { case MIRType_Undefined: v = UndefinedValue(); break; case MIRType_Null: v = NullValue(); break; case MIRType_Magic: v = MagicValue(JS_OPTIMIZED_ARGUMENTS); break; default: JS_NOT_REACHED("unexpected type"); return; } MConstant *c = MConstant::New(v); // The instruction pass will insert the box block->insertBefore(*(block->begin()), c); phi->replaceAllUsesWith(c); }
void LoopUnroller::go(LoopIterationBound *bound) { // For now we always unroll loops the same number of times. static const size_t UnrollCount = 10; JitSpew(JitSpew_Unrolling, "Attempting to unroll loop"); header = bound->header; // UCE might have determined this isn't actually a loop. if (!header->isLoopHeader()) return; backedge = header->backedge(); oldPreheader = header->loopPredecessor(); JS_ASSERT(oldPreheader->numSuccessors() == 1); // Only unroll loops with two blocks: an initial one ending with the // bound's test, and the body ending with the backedge. MTest *test = bound->test; if (header->lastIns() != test) return; if (test->ifTrue() == backedge) { if (test->ifFalse()->id() <= backedge->id()) return; } else if (test->ifFalse() == backedge) { if (test->ifTrue()->id() <= backedge->id()) return; } else { return; } if (backedge->numPredecessors() != 1 || backedge->numSuccessors() != 1) return; JS_ASSERT(backedge->phisEmpty()); MBasicBlock *bodyBlocks[] = { header, backedge }; // All instructions in the header and body must be clonable. for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) { MBasicBlock *block = bodyBlocks[i]; for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) { MInstruction *ins = *iter; if (ins->canClone()) continue; if (ins->isTest() || ins->isGoto() || ins->isInterruptCheck()) continue; #ifdef DEBUG JitSpew(JitSpew_Unrolling, "Aborting: can't clone instruction %s", ins->opName()); #endif return; } } // Compute the linear inequality we will use for exiting the unrolled loop: // // iterationBound - iterationCount - UnrollCount >= 0 // LinearSum remainingIterationsInequality(bound->boundSum); if (!remainingIterationsInequality.add(bound->currentSum, -1)) return; if (!remainingIterationsInequality.add(-int32_t(UnrollCount))) return; // Terms in the inequality need to be either loop invariant or phis from // the original header. for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) { MDefinition *def = remainingIterationsInequality.term(i).term; if (def->block()->id() < header->id()) continue; if (def->block() == header && def->isPhi()) continue; return; } // OK, we've checked everything, now unroll the loop. JitSpew(JitSpew_Unrolling, "Unrolling loop"); // The old preheader will go before the unrolled loop, and the old loop // will need a new empty preheader. CompileInfo &info = oldPreheader->info(); if (header->trackedSite().pc()) { unrolledHeader = MBasicBlock::New(graph, nullptr, info, oldPreheader, header->trackedSite(), MBasicBlock::LOOP_HEADER); unrolledBackedge = MBasicBlock::New(graph, nullptr, info, unrolledHeader, backedge->trackedSite(), MBasicBlock::NORMAL); newPreheader = MBasicBlock::New(graph, nullptr, info, unrolledHeader, oldPreheader->trackedSite(), MBasicBlock::NORMAL); } else { unrolledHeader = MBasicBlock::NewAsmJS(graph, info, oldPreheader, MBasicBlock::LOOP_HEADER); unrolledBackedge = MBasicBlock::NewAsmJS(graph, info, unrolledHeader, MBasicBlock::NORMAL); newPreheader = MBasicBlock::NewAsmJS(graph, info, unrolledHeader, MBasicBlock::NORMAL); } unrolledHeader->discardAllResumePoints(); unrolledBackedge->discardAllResumePoints(); newPreheader->discardAllResumePoints(); // Insert new blocks at their RPO position, and update block ids. graph.insertBlockAfter(oldPreheader, unrolledHeader); graph.insertBlockAfter(unrolledHeader, unrolledBackedge); graph.insertBlockAfter(unrolledBackedge, newPreheader); graph.renumberBlocksAfter(oldPreheader); if (!unrolledDefinitions.init()) CrashAtUnhandlableOOM("LoopUnroller::go"); // Add phis to the unrolled loop header which correspond to the phis in the // original loop header. JS_ASSERT(header->getPredecessor(0) == oldPreheader); for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) { MPhi *old = *iter; JS_ASSERT(old->numOperands() == 2); MPhi *phi = MPhi::New(alloc); phi->setResultType(old->type()); phi->setResultTypeSet(old->resultTypeSet()); phi->setRange(old->range()); unrolledHeader->addPhi(phi); if (!phi->reserveLength(2)) CrashAtUnhandlableOOM("LoopUnroller::go"); // Set the first input for the phi for now. We'll set the second after // finishing the unroll. phi->addInput(old->getOperand(0)); // The old phi will now take the value produced by the unrolled loop. old->replaceOperand(0, phi); if (!unrolledDefinitions.putNew(old, phi)) CrashAtUnhandlableOOM("LoopUnroller::go"); } // The loop condition can bail out on e.g. integer overflow, so make a // resume point based on the initial resume point of the original header. MResumePoint *headerResumePoint = header->entryResumePoint(); if (headerResumePoint) { MResumePoint *rp = makeReplacementResumePoint(unrolledHeader, headerResumePoint); unrolledHeader->setEntryResumePoint(rp); // Perform an interrupt check at the start of the unrolled loop. unrolledHeader->add(MInterruptCheck::New(alloc)); } // Generate code for the test in the unrolled loop. for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) { MDefinition *def = remainingIterationsInequality.term(i).term; MDefinition *replacement = getReplacementDefinition(def); remainingIterationsInequality.replaceTerm(i, replacement); } MCompare *compare = ConvertLinearInequality(alloc, unrolledHeader, remainingIterationsInequality); MTest *unrolledTest = MTest::New(alloc, compare, unrolledBackedge, newPreheader); unrolledHeader->end(unrolledTest); // Make an entry resume point for the unrolled body. The unrolled header // does not have side effects on stack values, even if the original loop // header does, so use the same resume point as for the unrolled header. if (headerResumePoint) { MResumePoint *rp = makeReplacementResumePoint(unrolledBackedge, headerResumePoint); unrolledBackedge->setEntryResumePoint(rp); } // Make an entry resume point for the new preheader. There are no // instructions which use this but some other stuff wants one to be here. if (headerResumePoint) { MResumePoint *rp = makeReplacementResumePoint(newPreheader, headerResumePoint); newPreheader->setEntryResumePoint(rp); } // Generate the unrolled code. JS_ASSERT(UnrollCount > 1); size_t unrollIndex = 0; while (true) { // Clone the contents of the original loop into the unrolled loop body. for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) { MBasicBlock *block = bodyBlocks[i]; for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) { MInstruction *ins = *iter; if (ins->canClone()) { makeReplacementInstruction(*iter); } else { // Control instructions are handled separately. JS_ASSERT(ins->isTest() || ins->isGoto() || ins->isInterruptCheck()); } } } // Compute the value of each loop header phi after the execution of // this unrolled iteration. MDefinitionVector phiValues(alloc); JS_ASSERT(header->getPredecessor(1) == backedge); for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) { MPhi *old = *iter; MDefinition *oldInput = old->getOperand(1); if (!phiValues.append(getReplacementDefinition(oldInput))) CrashAtUnhandlableOOM("LoopUnroller::go"); } unrolledDefinitions.clear(); if (unrollIndex == UnrollCount - 1) { // We're at the end of the last unrolled iteration, set the // backedge input for the unrolled loop phis. size_t phiIndex = 0; for (MPhiIterator iter(unrolledHeader->phisBegin()); iter != unrolledHeader->phisEnd(); iter++) { MPhi *phi = *iter; phi->addInput(phiValues[phiIndex++]); } JS_ASSERT(phiIndex == phiValues.length()); break; } // Update the map for the phis in the next iteration. size_t phiIndex = 0; for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd(); iter++) { MPhi *old = *iter; if (!unrolledDefinitions.putNew(old, phiValues[phiIndex++])) CrashAtUnhandlableOOM("LoopUnroller::go"); } JS_ASSERT(phiIndex == phiValues.length()); unrollIndex++; } MGoto *backedgeJump = MGoto::New(alloc, unrolledHeader); unrolledBackedge->end(backedgeJump); // Place the old preheader before the unrolled loop. JS_ASSERT(oldPreheader->lastIns()->isGoto()); oldPreheader->discardLastIns(); oldPreheader->end(MGoto::New(alloc, unrolledHeader)); // Place the new preheader before the original loop. newPreheader->end(MGoto::New(alloc, header)); // Cleanup the MIR graph. if (!unrolledHeader->addPredecessorWithoutPhis(unrolledBackedge)) CrashAtUnhandlableOOM("LoopUnroller::go"); header->replacePredecessor(oldPreheader, newPreheader); oldPreheader->setSuccessorWithPhis(unrolledHeader, 0); newPreheader->setSuccessorWithPhis(header, 0); unrolledBackedge->setSuccessorWithPhis(unrolledHeader, 1); }
Loop::LoopReturn Loop::init() { IonSpew(IonSpew_LICM, "Loop identified, headed by block %d", header_->id()); IonSpew(IonSpew_LICM, "footer is block %d", header_->backedge()->id()); // The first predecessor of the loop header must dominate the header. JS_ASSERT(header_->id() > header_->getPredecessor(0)->id()); // Loops from backedge to header and marks all visited blocks // as part of the loop. At the same time add all hoistable instructions // (in RPO order) to the instruction worklist. Vector<MBasicBlock *, 1, IonAllocPolicy> inlooplist; if (!inlooplist.append(header_->backedge())) return LoopReturn_Error; header_->backedge()->mark(); while (!inlooplist.empty()) { MBasicBlock *block = inlooplist.back(); // Hoisting requires more finesse if the loop contains a block that // self-dominates: there exists control flow that may enter the loop // without passing through the loop preheader. // // Rather than perform a complicated analysis of the dominance graph, // just return a soft error to ignore this loop. if (block->immediateDominator() == block) { while (!worklist_.empty()) popFromWorklist(); return LoopReturn_Skip; } // Add not yet visited predecessors to the inlooplist. if (block != header_) { for (size_t i = 0; i < block->numPredecessors(); i++) { MBasicBlock *pred = block->getPredecessor(i); if (pred->isMarked()) continue; if (!inlooplist.append(pred)) return LoopReturn_Error; pred->mark(); } } // If any block was added, process them first. if (block != inlooplist.back()) continue; // Add all instructions in this block (but the control instruction) to the worklist for (MInstructionIterator i = block->begin(); i != block->end(); i++) { MInstruction *ins = *i; if (isHoistable(ins)) { if (!insertInWorklist(ins)) return LoopReturn_Error; } } // All successors of this block are visited. inlooplist.popBack(); } return LoopReturn_Success; }
MInstruction* firstInstruction() const { return *loopHeader_->begin(); }
bool RangeAnalysis::addBetaNobes() { IonSpew(IonSpew_Range, "Adding beta nobes"); for (PostorderIterator i(graph_.poBegin()); i != graph_.poEnd(); i++) { MBasicBlock *block = *i; IonSpew(IonSpew_Range, "Looking at block %d", block->id()); BranchDirection branch_dir; MTest *test = block->immediateDominatorBranch(&branch_dir); if (!test || !test->getOperand(0)->isCompare()) continue; MCompare *compare = test->getOperand(0)->toCompare(); MDefinition *left = compare->getOperand(0); MDefinition *right = compare->getOperand(1); int32 bound; MDefinition *val = NULL; JSOp jsop = compare->jsop(); if (branch_dir == FALSE_BRANCH) jsop = analyze::NegateCompareOp(jsop); if (left->isConstant() && left->toConstant()->value().isInt32()) { bound = left->toConstant()->value().toInt32(); val = right; jsop = analyze::ReverseCompareOp(jsop); } else if (right->isConstant() && right->toConstant()->value().isInt32()) { bound = right->toConstant()->value().toInt32(); val = left; } else { MDefinition *smaller = NULL; MDefinition *greater = NULL; if (jsop == JSOP_LT) { smaller = left; greater = right; } else if (jsop == JSOP_GT) { smaller = right; greater = left; } if (smaller && greater) { MBeta *beta; beta = MBeta::New(smaller, Range(JSVAL_INT_MIN, JSVAL_INT_MAX-1)); block->insertBefore(*block->begin(), beta); replaceDominatedUsesWith(smaller, beta, block); beta = MBeta::New(greater, Range(JSVAL_INT_MIN+1, JSVAL_INT_MAX)); block->insertBefore(*block->begin(), beta); replaceDominatedUsesWith(greater, beta, block); } continue; } JS_ASSERT(val); Range comp; switch (jsop) { case JSOP_LE: comp.setUpper(bound); break; case JSOP_LT: if (!SafeSub(bound, 1, &bound)) break; comp.setUpper(bound); break; case JSOP_GE: comp.setLower(bound); break; case JSOP_GT: if (!SafeAdd(bound, 1, &bound)) break; comp.setLower(bound); break; case JSOP_EQ: comp.setLower(bound); comp.setUpper(bound); default: break; // well, for neq we could have // [-\inf, bound-1] U [bound+1, \inf] but we only use contiguous ranges. } IonSpew(IonSpew_Range, "Adding beta node for %d", val->id()); MBeta *beta = MBeta::New(val, comp); block->insertBefore(*block->begin(), beta); replaceDominatedUsesWith(val, beta, block); } return true; }
bool MIRGraph::removeSuccessorBlocks(MBasicBlock* start) { if (!start->hasLastIns()) return true; start->mark(); // Mark all successors. Vector<MBasicBlock*, 4, SystemAllocPolicy> blocks; for (size_t i = 0; i < start->numSuccessors(); i++) { if (start->getSuccessor(i)->isMarked()) continue; if (!blocks.append(start->getSuccessor(i))) return false; start->getSuccessor(i)->mark(); } for (size_t i = 0; i < blocks.length(); i++) { MBasicBlock* block = blocks[i]; if (!block->hasLastIns()) continue; for (size_t j = 0; j < block->numSuccessors(); j++) { if (block->getSuccessor(j)->isMarked()) continue; if (!blocks.append(block->getSuccessor(j))) return false; block->getSuccessor(j)->mark(); } } if (osrBlock()) { if (osrBlock()->getSuccessor(0)->isMarked()) osrBlock()->mark(); } // Remove blocks. // If they don't have any predecessor for (size_t i = 0; i < blocks.length(); i++) { MBasicBlock* block = blocks[i]; bool allMarked = true; for (size_t i = 0; i < block->numPredecessors(); i++) { if (block->getPredecessor(i)->isMarked()) continue; allMarked = false; break; } if (allMarked) { removeBlock(block); } else { MOZ_ASSERT(block != osrBlock()); for (size_t j = 0; j < block->numPredecessors(); ) { if (!block->getPredecessor(j)->isMarked()) { j++; continue; } block->removePredecessor(block->getPredecessor(j)); } // This shouldn't have any instructions yet. MOZ_ASSERT(block->begin() == block->end()); } } if (osrBlock()) { if (osrBlock()->getSuccessor(0)->isDead()) removeBlock(osrBlock()); } for (size_t i = 0; i < blocks.length(); i++) blocks[i]->unmark(); start->unmark(); return true; }
bool UnreachableCodeElimination::removeUnmarkedBlocksAndClearDominators() { // Removes blocks that are not marked from the graph. For blocks // that *are* marked, clears the mark and adjusts the id to its // new value. Also adds blocks that are immediately reachable // from an unmarked block to the frontier. size_t id = marked_; for (PostorderIterator iter(graph_.poBegin()); iter != graph_.poEnd();) { if (mir_->shouldCancel("Eliminate Unreachable Code")) return false; MBasicBlock *block = *iter; iter++; // Unconditionally clear the dominators. It's somewhat complex to // adjust the values and relatively fast to just recompute. block->clearDominatorInfo(); if (block->isMarked()) { block->setId(--id); for (MPhiIterator iter(block->phisBegin()); iter != block->phisEnd(); iter++) checkDependencyAndRemoveUsesFromUnmarkedBlocks(*iter); for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) checkDependencyAndRemoveUsesFromUnmarkedBlocks(*iter); } else { if (block->numPredecessors() > 1) { // If this block had phis, then any reachable // predecessors need to have the successorWithPhis // flag cleared. for (size_t i = 0; i < block->numPredecessors(); i++) block->getPredecessor(i)->setSuccessorWithPhis(nullptr, 0); } if (block->isLoopBackedge()) { // NB. We have to update the loop header if we // eliminate the backedge. At first I thought this // check would be insufficient, because it would be // possible to have code like this: // // while (true) { // ...; // if (1 == 1) break; // } // // in which the backedge is removed as part of // rewriting the condition, but no actual blocks are // removed. However, in all such cases, the backedge // would be a critical edge and hence the critical // edge block is being removed. block->loopHeaderOfBackedge()->clearLoopHeader(); } for (size_t i = 0, c = block->numSuccessors(); i < c; i++) { MBasicBlock *succ = block->getSuccessor(i); if (succ->isMarked()) { // succ is on the frontier of blocks to be removed: succ->removePredecessor(block); if (!redundantPhis_) { for (MPhiIterator iter(succ->phisBegin()); iter != succ->phisEnd(); iter++) { if (iter->operandIfRedundant()) { redundantPhis_ = true; break; } } } } } // When we remove a call, we can't leave the corresponding MPassArg // in the graph. Since lowering will fail. Replace it with the // argument for the exceptional case when it is kept alive in a // ResumePoint. DCE will remove the unused MPassArg instruction. for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) { if (iter->isCall()) { MCall *call = iter->toCall(); for (size_t i = 0; i < call->numStackArgs(); i++) { JS_ASSERT(call->getArg(i)->isPassArg()); JS_ASSERT(call->getArg(i)->hasOneDefUse()); MPassArg *arg = call->getArg(i)->toPassArg(); arg->replaceAllUsesWith(arg->getArgument()); } } } graph_.removeBlock(block); } } JS_ASSERT(id == 0); return true; }