// A critical edge is an edge which is neither its successor's only predecessor // nor its predecessor's only successor. Critical edges must be split to // prevent copy-insertion and code motion from affecting other edges. bool ion::SplitCriticalEdges(MIRGraph &graph) { for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) { if (block->numSuccessors() < 2) continue; for (size_t i = 0; i < block->numSuccessors(); i++) { MBasicBlock *target = block->getSuccessor(i); if (target->numPredecessors() < 2) continue; // Create a new block inheriting from the predecessor. MBasicBlock *split = MBasicBlock::NewSplitEdge(graph, block->info(), *block); split->setLoopDepth(block->loopDepth()); graph.insertBlockAfter(*block, split); split->end(MGoto::New(target)); block->replaceSuccessor(i, split); target->replacePredecessor(*block, split); } } return true; }
MBasicBlock* MBasicBlock::NewSplitEdge(MIRGraph& graph, MBasicBlock* pred, size_t predEdgeIdx, MBasicBlock* succ) { MBasicBlock* split = nullptr; if (!succ->pc()) { // The predecessor does not have a PC, this is a Wasm compilation. split = MBasicBlock::New(graph, succ->info(), pred, SPLIT_EDGE); if (!split) return nullptr; } else { // The predecessor has a PC, this is an IonBuilder compilation. MResumePoint* succEntry = succ->entryResumePoint(); BytecodeSite* site = new(graph.alloc()) BytecodeSite(succ->trackedTree(), succEntry->pc()); split = new(graph.alloc()) MBasicBlock(graph, succ->info(), site, SPLIT_EDGE); if (!split->init()) return nullptr; // A split edge is used to simplify the graph to avoid having a // predecessor with multiple successors as well as a successor with // multiple predecessors. As instructions can be moved in this // split-edge block, we need to give this block a resume point. To do // so, we copy the entry resume points of the successor and filter the // phis to keep inputs from the current edge. // Propagate the caller resume point from the inherited block. split->callerResumePoint_ = succ->callerResumePoint(); // Split-edge are created after the interpreter stack emulation. Thus, // there is no need for creating slots. split->stackPosition_ = succEntry->stackDepth(); // Create a resume point using our initial stack position. MResumePoint* splitEntry = new(graph.alloc()) MResumePoint(split, succEntry->pc(), MResumePoint::ResumeAt); if (!splitEntry->init(graph.alloc())) return nullptr; split->entryResumePoint_ = splitEntry; // The target entry resume point might have phi operands, keep the // operands of the phi coming from our edge. size_t succEdgeIdx = succ->indexForPredecessor(pred); for (size_t i = 0, e = splitEntry->numOperands(); i < e; i++) { MDefinition* def = succEntry->getOperand(i); // This early in the pipeline, we have no recover instructions in // any entry resume point. MOZ_ASSERT_IF(def->block() == succ, def->isPhi()); if (def->block() == succ) def = def->toPhi()->getOperand(succEdgeIdx); splitEntry->initOperand(i, def); } // This is done in the New variant for wasm, so we cannot keep this // line below, where the rest of the graph is modified. if (!split->predecessors_.append(pred)) return nullptr; } split->setLoopDepth(succ->loopDepth()); // Insert the split edge block in-between. split->end(MGoto::New(graph.alloc(), succ)); graph.insertBlockAfter(pred, split); pred->replaceSuccessor(predEdgeIdx, split); succ->replacePredecessor(pred, split); return split; }