blob: ce9a6e89b72d5de18ab51ffe96dc38afc452d27f [file] [log] [blame]
/*
* Copyright (C) 2015-2019 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 "DFGForAllKills.h"
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGMayExit.h"
#include "DFGPhase.h"
#include "JSCJSValueInlines.h"
#include "OperandsInlines.h"
namespace JSC { namespace DFG {
namespace {
class PhantomInsertionPhase : public Phase {
static constexpr bool verbose = false;
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->unlinkedOperand()) = node->child1().node();
break;
case ZombieHint:
m_values.operand(node->unlinkedOperand()) = nullptr;
break;
case GetLocal:
case SetArgumentDefinitely:
case SetArgumentMaybe:
m_values.operand(node->operand()) = nullptr;
break;
default:
break;
}
bool nodeMayExit = mayExit(m_graph, node) != DoesNotExit;
if (nodeMayExit) {
currentEpoch.bump();
lastExitingIndex = nodeIndex;
}
m_graph.doToChildren(
node,
[&] (Edge edge) {
dataLogLnIf(verbose, "Updating epoch for ", edge, " to ", currentEpoch);
edge->setEpoch(currentEpoch);
});
node->setEpoch(currentEpoch);
Operand alreadyKilled;
auto processKilledOperand = [&] (Operand operand) {
dataLogLnIf(verbose, " Killed operand: ", operand);
// Already handled from SetLocal.
if (operand == alreadyKilled) {
dataLogLnIf(verbose, " Operand ", operand, " already killed by set local");
return;
}
Node* killedNode = m_values.operand(operand);
if (!killedNode) {
dataLogLnIf(verbose, " Operand ", operand, " was not defined in this block.");
return;
}
m_values.operand(operand) = nullptr;
// 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) {
dataLogLnIf(verbose, " Operand ", operand, " has current epoch ", currentEpoch);
return;
}
dataLogLnIf(verbose,
" Inserting Phantom on ", killedNode, " after ",
block->at(lastExitingIndex));
// 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());
};
if (node->op() == SetLocal) {
Operand operand = node->operand();
if (nodeMayExit) {
// If the SetLocal does exit, we need the MovHint of its local
// to be live until the SetLocal is done.
processKilledOperand(operand);
alreadyKilled = operand;
}
m_values.operand(operand) = nullptr;
}
forAllKilledOperands(m_graph, node, block->tryAt(nodeIndex + 1), processKilledOperand);
}
m_insertionSet.execute(block);
}
InsertionSet m_insertionSet;
Operands<Node*> m_values;
};
} // anonymous namespace
bool performPhantomInsertion(Graph& graph)
{
return runPhase<PhantomInsertionPhase>(graph);
}
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)