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;
 }
Beispiel #2
0
    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;
    }