void ion::AssertGraphCoherency(MIRGraph &graph) { #ifdef DEBUG // Assert successor and predecessor list coherency. uint32_t count = 0; for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) { count++; for (size_t i = 0; i < block->numSuccessors(); i++) JS_ASSERT(CheckSuccessorImpliesPredecessor(*block, block->getSuccessor(i))); for (size_t i = 0; i < block->numPredecessors(); i++) JS_ASSERT(CheckPredecessorImpliesSuccessor(*block, block->getPredecessor(i))); // Assert that use chains are valid for this instruction. for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) { for (uint32_t i = 0; i < ins->numOperands(); i++) JS_ASSERT(CheckOperandImpliesUse(*ins, ins->getOperand(i))); } for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) { for (MUseIterator i(ins->usesBegin()); i != ins->usesEnd(); i++) JS_ASSERT(CheckUseImpliesOperand(*ins, *i)); } } JS_ASSERT(graph.numBlocks() == count); AssertReversePostOrder(graph); #endif }
// Operands to a resume point which are dead at the point of the resume can be // replaced with undefined values. This analysis supports limited detection of // dead operands, pruning those which are defined in the resume point's basic // block and have no uses outside the block or at points later than the resume // point. // // This is intended to ensure that extra resume points within a basic block // will not artificially extend the lifetimes of any SSA values. This could // otherwise occur if the new resume point captured a value which is created // between the old and new resume point and is dead at the new resume point. bool ion::EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph) { for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) { if (mir->shouldCancel("Eliminate Dead Resume Point Operands (main loop)")) return false; // The logic below can get confused on infinite loops. if (block->isLoopHeader() && block->backedge() == *block) continue; for (MInstructionIterator ins = block->begin(); ins != block->end(); ins++) { // No benefit to replacing constant operands with other constants. if (ins->isConstant()) continue; // Scanning uses does not give us sufficient information to tell // where instructions that are involved in box/unbox operations or // parameter passing might be live. Rewriting uses of these terms // in resume points may affect the interpreter's behavior. Rather // than doing a more sophisticated analysis, just ignore these. if (ins->isUnbox() || ins->isParameter()) continue; // If the instruction's behavior has been constant folded into a // separate instruction, we can't determine precisely where the // instruction becomes dead and can't eliminate its uses. if (ins->isFolded()) continue; // Check if this instruction's result is only used within the // current block, and keep track of its last use in a definition // (not resume point). This requires the instructions in the block // to be numbered, ensured by running this immediately after alias // analysis. uint32_t maxDefinition = 0; for (MUseDefIterator uses(*ins); uses; uses++) { if (uses.def()->block() != *block || uses.def()->isBox() || uses.def()->isPassArg() || uses.def()->isPhi()) { maxDefinition = UINT32_MAX; break; } maxDefinition = Max(maxDefinition, uses.def()->id()); } if (maxDefinition == UINT32_MAX) continue; // Walk the uses a second time, removing any in resume points after // the last use in a definition. for (MUseIterator uses(ins->usesBegin()); uses != ins->usesEnd(); ) { if (uses->node()->isDefinition()) { uses++; continue; } MResumePoint *mrp = uses->node()->toResumePoint(); if (mrp->block() != *block || !mrp->instruction() || mrp->instruction() == *ins || mrp->instruction()->id() <= maxDefinition) { uses++; continue; } // Store an undefined value in place of all dead resume point // operands. Making any such substitution can in general alter // the interpreter's behavior, even though the code is dead, as // the interpreter will still execute opcodes whose effects // cannot be observed. If the undefined value were to flow to, // say, a dead property access the interpreter could throw an // exception; we avoid this problem by removing dead operands // before removing dead code. MConstant *constant = MConstant::New(UndefinedValue()); block->insertBefore(*(block->begin()), constant); uses = mrp->replaceOperand(uses, constant); } } } return true; }