diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGPhantomInsertionPhase.cpp')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGPhantomInsertionPhase.cpp | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGPhantomInsertionPhase.cpp b/Source/JavaScriptCore/dfg/DFGPhantomInsertionPhase.cpp new file mode 100644 index 000000000..02b1958ce --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGPhantomInsertionPhase.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2015 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. ``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 + * 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 "DFGPhantomInsertionPhase.h" + +#if ENABLE(DFG_JIT) + +#include "BytecodeLivenessAnalysisInlines.h" +#include "DFGForAllKills.h" +#include "DFGGraph.h" +#include "DFGInsertionSet.h" +#include "DFGMayExit.h" +#include "DFGPhase.h" +#include "DFGPredictionPropagationPhase.h" +#include "DFGVariableAccessDataDump.h" +#include "JSCInlines.h" +#include "OperandsInlines.h" + +namespace JSC { namespace DFG { + +namespace { + +bool verbose = false; + +class PhantomInsertionPhase : public Phase { +public: + PhantomInsertionPhase(Graph& graph) + : Phase(graph, "phantom insertion") + , m_insertionSet(graph) + , m_values(OperandsLike, graph.block(0)->variablesAtHead) + { + } + + bool run() + { + // We assume that DCE has already run. If we run before DCE then we think that all + // SetLocals execute, which is inaccurate. That causes us to insert too few Phantoms. + DFG_ASSERT(m_graph, nullptr, m_graph.m_refCountState == ExactRefCount); + + if (verbose) { + dataLog("Graph before Phantom insertion:\n"); + m_graph.dump(); + } + + m_graph.clearEpochs(); + + for (BasicBlock* block : m_graph.blocksInNaturalOrder()) + handleBlock(block); + + if (verbose) { + dataLog("Graph after Phantom insertion:\n"); + m_graph.dump(); + } + + return true; + } + +private: + void handleBlock(BasicBlock* block) + { + // FIXME: For blocks that have low register pressure, it would make the most sense to + // simply insert Phantoms at the last point possible since that would obviate the need to + // query bytecode liveness: + // + // - If we MovHint @x into loc42 then put a Phantom on the last MovHinted value in loc42. + // - At the end of the block put Phantoms for each MovHinted value. + // + // This will definitely not work if there are any phantom allocations. For those blocks + // where this would be legal, it remains to be seen how profitable it would be even if there + // was high register pressure. After all, a Phantom would cause a spill but it wouldn't + // cause a fill. + // + // https://bugs.webkit.org/show_bug.cgi?id=144524 + + m_values.fill(nullptr); + + Epoch currentEpoch = Epoch::first(); + unsigned lastExitingIndex = 0; + for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) { + Node* node = block->at(nodeIndex); + if (verbose) + dataLog("Considering ", node, "\n"); + + switch (node->op()) { + case MovHint: + m_values.operand(node->unlinkedLocal()) = node->child1().node(); + break; + + case ZombieHint: + m_values.operand(node->unlinkedLocal()) = nullptr; + break; + + case SetLocal: + case GetLocal: + case SetArgument: + m_values.operand(node->local()) = nullptr; + break; + + default: + break; + } + + if (mayExit(m_graph, node) != DoesNotExit) { + currentEpoch.bump(); + lastExitingIndex = nodeIndex; + } + + m_graph.doToChildren( + node, + [&] (Edge edge) { + edge->setEpoch(currentEpoch); + }); + + node->setEpoch(currentEpoch); + + forAllKilledOperands( + m_graph, node, block->tryAt(nodeIndex + 1), + [&] (VirtualRegister reg) { + if (verbose) + dataLog(" Killed operand: ", reg, "\n"); + + Node* killedNode = m_values.operand(reg); + if (!killedNode) + return; + + // We only need to insert a Phantom if the node hasn't been used since the last + // exit, and was born before the last exit. + if (killedNode->epoch() == currentEpoch) + return; + + if (verbose) { + dataLog( + " Inserting Phantom on ", killedNode, " after ", + block->at(lastExitingIndex), "\n"); + } + + // We have exact ref counts, so creating a new use means that we have to + // increment the ref count. + killedNode->postfixRef(); + + Node* lastExitingNode = block->at(lastExitingIndex); + + m_insertionSet.insertNode( + lastExitingIndex + 1, SpecNone, Phantom, + lastExitingNode->origin.forInsertingAfter(m_graph, lastExitingNode), + killedNode->defaultEdge()); + }); + } + + m_insertionSet.execute(block); + } + + InsertionSet m_insertionSet; + Operands<Node*> m_values; +}; + +} // anonymous namespace + +bool performPhantomInsertion(Graph& graph) +{ + SamplingRegion samplingRegion("DFG Phantom Insertion Phase"); + return runPhase<PhantomInsertionPhase>(graph); +} + +} } // namespace JSC::DFG + +#endif // ENABLE(DFG_JIT) + |