/* * Copyright (C) 2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "DFGStoreBarrierClusteringPhase.h" #if ENABLE(DFG_JIT) #include "DFGDoesGC.h" #include "DFGGraph.h" #include "DFGInsertionSet.h" #include "DFGMayExit.h" #include "DFGPhase.h" #include "JSCJSValueInlines.h" #include namespace JSC { namespace DFG { namespace { class StoreBarrierClusteringPhase : public Phase { public: StoreBarrierClusteringPhase(Graph& graph) : Phase(graph, "store barrier fencing") , m_insertionSet(graph) { } bool run() { size_t maxSize = 0; for (BasicBlock* block : m_graph.blocksInNaturalOrder()) maxSize = std::max(maxSize, block->size()); m_barrierPoints.resize(maxSize); for (BasicBlock* block : m_graph.blocksInNaturalOrder()) { size_t blockSize = block->size(); doBlock(block); m_barrierPoints.clearRange(0, blockSize); } return true; } private: // This summarizes everything we need to remember about a barrier. struct ChildAndOrigin { ChildAndOrigin() { } ChildAndOrigin(Node* child, CodeOrigin semanticOrigin) : child(child) , semanticOrigin(semanticOrigin) { } Node* child { nullptr }; CodeOrigin semanticOrigin; }; void doBlock(BasicBlock* block) { ASSERT(m_barrierPoints.isEmpty()); // First identify the places where we would want to place all of the barriers based on a // backwards analysis. We use the futureGC flag to tell us if we had seen a GC. Since this // is a backwards analysis, when we get to a node, futureGC tells us if a GC will happen // in the future after that node. bool futureGC = true; for (unsigned nodeIndex = block->size(); nodeIndex--;) { Node* node = block->at(nodeIndex); // This is a backwards analysis, so exits require conservatism. If we exit, then there // probably will be a GC in the future! If we needed to then we could lift that // requirement by either (1) having a StoreBarrierHint that tells OSR exit to barrier that // value or (2) automatically barriering any DFG-live Node on OSR exit. Either way, it // would be weird because it would create a new root for OSR availability analysis. I // don't have evidence that it would be worth it. if (doesGC(m_graph, node) || mayExit(m_graph, node) != DoesNotExit) { futureGC = true; continue; } if (node->isStoreBarrier() && futureGC) { m_barrierPoints[nodeIndex] = true; futureGC = false; } } // Now we run forward and collect the barriers. When we hit a barrier point, insert all of // them with a fence. for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) { Node* node = block->at(nodeIndex); if (!node->isStoreBarrier()) continue; DFG_ASSERT(m_graph, node, !node->origin.wasHoisted); DFG_ASSERT(m_graph, node, node->child1().useKind() == KnownCellUse, node->op(), node->child1().useKind()); NodeOrigin origin = node->origin; m_neededBarriers.append(ChildAndOrigin(node->child1().node(), origin.semantic)); node->remove(m_graph); if (!m_barrierPoints[nodeIndex]) continue; std::sort( m_neededBarriers.begin(), m_neededBarriers.end(), [&] (const ChildAndOrigin& a, const ChildAndOrigin& b) -> bool { return a.child < b.child; }); removeRepeatedElements( m_neededBarriers, [&] (const ChildAndOrigin& a, const ChildAndOrigin& b) -> bool{ return a.child == b.child; }); for (auto iter = m_neededBarriers.begin(); iter != m_neededBarriers.end(); ++iter) { Node* child = iter->child; CodeOrigin semanticOrigin = iter->semanticOrigin; NodeType type; if (iter == m_neededBarriers.begin()) type = FencedStoreBarrier; else type = StoreBarrier; m_insertionSet.insertNode( nodeIndex, SpecNone, type, origin.withSemantic(semanticOrigin), Edge(child, KnownCellUse)); } m_neededBarriers.shrink(0); } m_insertionSet.execute(block); } InsertionSet m_insertionSet; FastBitVector m_barrierPoints; Vector m_neededBarriers; }; } // anonymous namespace bool performStoreBarrierClustering(Graph& graph) { return runPhase(graph); } } } // namespace JSC::DFG #endif // ENABLE(DFG_JIT)