bool attemptHoist(BasicBlock* fromBlock, Node*& nodeRef, const NaturalLoop* loop) { Node* node = nodeRef; LoopData& data = m_data[loop->index()]; if (!data.preHeader->cfaDidFinish) { if (verbose) dataLog(" Not hoisting ", node, " because CFA is invalid.\n"); return false; } if (!edgesDominate(m_graph, node, data.preHeader)) { if (verbose) { dataLog( " Not hoisting ", node, " because it isn't loop invariant.\n"); } return false; } if (readsOverlap(m_graph, node, data.writes)) { if (verbose) { dataLog( " Not hoisting ", node, " because it reads things that the loop writes.\n"); } return false; } m_state.initializeTo(data.preHeader); if (!safeToExecute(m_state, m_graph, node)) { if (verbose) { dataLog( " Not hoisting ", node, " because it isn't safe to execute.\n"); } return false; } if (verbose) { dataLog( " Hoisting ", node, " from ", *fromBlock, " to ", *data.preHeader, "\n"); } data.preHeader->insertBeforeLast(node); node->misc.owner = data.preHeader; NodeFlags didExitForward = node->flags() & NodeExitsForward; node->clearFlags(NodeExitsForward); node->codeOriginForExitTarget = data.preHeader->last()->codeOriginForExitTarget; // Modify the states at the end of the preHeader of the loop we hoisted to, // and all pre-headers inside the loop. // FIXME: This could become a scalability bottleneck. Fortunately, most loops // are small and anyway we rapidly skip over basic blocks here. for (unsigned bodyIndex = loop->size(); bodyIndex--;) { BasicBlock* subBlock = loop->at(bodyIndex); const NaturalLoop* subLoop = m_graph.m_naturalLoops.headerOf(subBlock); if (!subLoop) continue; BasicBlock* subPreHeader = m_data[subLoop->index()].preHeader; if (!subPreHeader->cfaDidFinish) continue; m_state.initializeTo(subPreHeader); m_interpreter.execute(node); } // It just so happens that all of the nodes we currently know how to hoist // don't have var-arg children. That may change and then we can fix this // code. But for now we just assert that's the case. RELEASE_ASSERT(!(node->flags() & NodeHasVarArgs)); nodeRef = m_graph.addNode(SpecNone, Phantom, node->codeOrigin, node->children); nodeRef->mergeFlags(didExitForward); return true; }
bool attemptHoist(BasicBlock* fromBlock, Node*& nodeRef, const NaturalLoop* loop) { Node* node = nodeRef; LoopData& data = m_data[loop->index()]; if (!data.preHeader) { if (verbose) dataLog(" Not hoisting ", node, " because the pre-header is invalid.\n"); return false; } if (!data.preHeader->cfaDidFinish) { if (verbose) dataLog(" Not hoisting ", node, " because CFA is invalid.\n"); return false; } if (!edgesDominate(m_graph, node, data.preHeader)) { if (verbose) { dataLog( " Not hoisting ", node, " because it isn't loop invariant.\n"); } return false; } // FIXME: At this point if the hoisting of the full node fails but the node has type checks, // we could still hoist just the checks. // https://bugs.webkit.org/show_bug.cgi?id=144525 if (readsOverlap(m_graph, node, data.writes)) { if (verbose) { dataLog( " Not hoisting ", node, " because it reads things that the loop writes.\n"); } return false; } m_state.initializeTo(data.preHeader); if (!safeToExecute(m_state, m_graph, node)) { if (verbose) { dataLog( " Not hoisting ", node, " because it isn't safe to execute.\n"); } return false; } NodeOrigin originalOrigin = node->origin; // NOTE: We could just use BackwardsDominators here directly, since we already know that the // preHeader dominates fromBlock. But we wouldn't get anything from being so clever, since // dominance checks are O(1) and only a few integer compares. bool addsBlindSpeculation = mayExit(m_graph, node, m_state) && !m_graph.m_controlEquivalenceAnalysis->dominatesEquivalently(data.preHeader, fromBlock); if (addsBlindSpeculation && m_graph.baselineCodeBlockFor(originalOrigin.semantic)->hasExitSite(FrequentExitSite(HoistingFailed))) { if (verbose) { dataLog( " Not hoisting ", node, " because it may exit and the pre-header (", *data.preHeader, ") is not control equivalent to the node's original block (", *fromBlock, ") and hoisting had previously failed.\n"); } return false; } if (verbose) { dataLog( " Hoisting ", node, " from ", *fromBlock, " to ", *data.preHeader, "\n"); } // FIXME: We should adjust the Check: flags on the edges of node. There are phases that assume // that those flags are correct even if AI is stale. // https://bugs.webkit.org/show_bug.cgi?id=148544 data.preHeader->insertBeforeTerminal(node); node->owner = data.preHeader; NodeOrigin terminalOrigin = data.preHeader->terminal()->origin; node->origin = terminalOrigin.withSemantic(node->origin.semantic); node->origin.wasHoisted |= addsBlindSpeculation; // Modify the states at the end of the preHeader of the loop we hoisted to, // and all pre-headers inside the loop. This isn't a stability bottleneck right now // because most loops are small and most blocks belong to few loops. for (unsigned bodyIndex = loop->size(); bodyIndex--;) { BasicBlock* subBlock = loop->at(bodyIndex); const NaturalLoop* subLoop = m_graph.m_naturalLoops->headerOf(subBlock); if (!subLoop) continue; BasicBlock* subPreHeader = m_data[subLoop->index()].preHeader; // We may not have given this loop a pre-header because either it didn't have exitOK // or the header had multiple predecessors that it did not dominate. In that case the // loop wouldn't be a hoisting candidate anyway, so we don't have to do anything. if (!subPreHeader) continue; // The pre-header's tail may be unreachable, in which case we have nothing to do. if (!subPreHeader->cfaDidFinish) continue; m_state.initializeTo(subPreHeader); m_interpreter.execute(node); } // It just so happens that all of the nodes we currently know how to hoist // don't have var-arg children. That may change and then we can fix this // code. But for now we just assert that's the case. DFG_ASSERT(m_graph, node, !(node->flags() & NodeHasVarArgs)); nodeRef = m_graph.addNode(Check, originalOrigin, node->children); return true; }
bool attemptHoist(BasicBlock* fromBlock, Node*& nodeRef, const NaturalLoop* loop) { Node* node = nodeRef; LoopData& data = m_data[loop->index()]; if (!data.preHeader) { if (verbose) dataLog(" Not hoisting ", node, " because the pre-header is invalid.\n"); return false; } if (!data.preHeader->cfaDidFinish) { if (verbose) dataLog(" Not hoisting ", node, " because CFA is invalid.\n"); return false; } if (!edgesDominate(m_graph, node, data.preHeader)) { if (verbose) { dataLog( " Not hoisting ", node, " because it isn't loop invariant.\n"); } return false; } // FIXME: At this point if the hoisting of the full node fails but the node has type checks, // we could still hoist just the checks. // https://bugs.webkit.org/show_bug.cgi?id=144525 // FIXME: If a node has a type check - even something like a CheckStructure - then we should // only hoist the node if we know that it will execute on every loop iteration or if we know // that the type check will always succeed at the loop pre-header through some other means // (like looking at prediction propagation results). Otherwise, we might make a mistake like // this: // // var o = ...; // sometimes null and sometimes an object with structure S1. // for (...) { // if (o) // ... = o.f; // CheckStructure and GetByOffset, which we will currently hoist. // } // // When we encounter such code, we'll hoist the CheckStructure and GetByOffset and then we // will have a recompile. We'll then end up thinking that the get_by_id needs to be // polymorphic, which is false. // // We can counter this by either having a control flow equivalence check, or by consulting // prediction propagation to see if the check would always succeed. Prediction propagation // would not be enough for things like: // // var p = ...; // some boolean predicate // var o = {}; // if (p) // o.f = 42; // for (...) { // if (p) // ... = o.f; // } // // Prediction propagation can't tell us anything about the structure, and the CheckStructure // will appear to be hoistable because the loop doesn't clobber structures. The cell check // in the CheckStructure will be hoistable though, since prediction propagation can tell us // that o is always SpecFinalObject. In cases like this, control flow equivalence is the // only effective guard. // // https://bugs.webkit.org/show_bug.cgi?id=144527 if (readsOverlap(m_graph, node, data.writes)) { if (verbose) { dataLog( " Not hoisting ", node, " because it reads things that the loop writes.\n"); } return false; } m_state.initializeTo(data.preHeader); if (!safeToExecute(m_state, m_graph, node)) { if (verbose) { dataLog( " Not hoisting ", node, " because it isn't safe to execute.\n"); } return false; } if (verbose) { dataLog( " Hoisting ", node, " from ", *fromBlock, " to ", *data.preHeader, "\n"); } // FIXME: We should adjust the Check: flags on the edges of node. There are phases that assume // that those flags are correct even if AI is stale. // https://bugs.webkit.org/show_bug.cgi?id=148544 data.preHeader->insertBeforeTerminal(node); node->owner = data.preHeader; NodeOrigin originalOrigin = node->origin; node->origin = data.preHeader->terminal()->origin.withSemantic(node->origin.semantic); // Modify the states at the end of the preHeader of the loop we hoisted to, // and all pre-headers inside the loop. This isn't a stability bottleneck right now // because most loops are small and most blocks belong to few loops. for (unsigned bodyIndex = loop->size(); bodyIndex--;) { BasicBlock* subBlock = loop->at(bodyIndex); const NaturalLoop* subLoop = m_graph.m_naturalLoops.headerOf(subBlock); if (!subLoop) continue; BasicBlock* subPreHeader = m_data[subLoop->index()].preHeader; if (!subPreHeader->cfaDidFinish) continue; m_state.initializeTo(subPreHeader); m_interpreter.execute(node); } // It just so happens that all of the nodes we currently know how to hoist // don't have var-arg children. That may change and then we can fix this // code. But for now we just assert that's the case. DFG_ASSERT(m_graph, node, !(node->flags() & NodeHasVarArgs)); nodeRef = m_graph.addNode(SpecNone, Check, originalOrigin, node->children); return true; }