DFGStoreBarrierInsertionPhase.cpp [plain text]
#include "config.h"
#include "DFGStoreBarrierInsertionPhase.h"
#if ENABLE(DFG_JIT)
#include "DFGAbstractInterpreterInlines.h"
#include "DFGBlockMapInlines.h"
#include "DFGDoesGC.h"
#include "DFGGraph.h"
#include "DFGInPlaceAbstractState.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
#include "JSCInlines.h"
#include <wtf/CommaPrinter.h>
#include <wtf/HashSet.h>
namespace JSC { namespace DFG {
namespace {
namespace DFGStoreBarrierInsertionPhaseInternal {
static const bool verbose = false;
}
enum class PhaseMode {
Fast,
Global
};
template<PhaseMode mode>
class StoreBarrierInsertionPhase : public Phase {
public:
StoreBarrierInsertionPhase(Graph& graph)
: Phase(graph, mode == PhaseMode::Fast ? "fast store barrier insertion" : "global store barrier insertion")
, m_insertionSet(graph)
{
}
bool run()
{
if (DFGStoreBarrierInsertionPhaseInternal::verbose) {
dataLog("Starting store barrier insertion:\n");
m_graph.dump();
}
switch (mode) {
case PhaseMode::Fast: {
DFG_ASSERT(m_graph, nullptr, m_graph.m_form != SSA);
m_graph.clearEpochs();
for (BasicBlock* block : m_graph.blocksInNaturalOrder())
handleBlock(block);
return true;
}
case PhaseMode::Global: {
DFG_ASSERT(m_graph, nullptr, m_graph.m_form == SSA);
m_state = std::make_unique<InPlaceAbstractState>(m_graph);
m_interpreter = std::make_unique<AbstractInterpreter<InPlaceAbstractState>>(m_graph, *m_state);
m_isConverged = false;
m_stateAtHead = std::make_unique<BlockMap<HashSet<Node*>>>(m_graph);
m_stateAtTail = std::make_unique<BlockMap<HashSet<Node*>>>(m_graph);
BlockList postOrder = m_graph.blocksInPostOrder();
bool changed = true;
while (changed) {
changed = false;
for (unsigned blockIndex = postOrder.size(); blockIndex--;) {
BasicBlock* block = postOrder[blockIndex];
if (!handleBlock(block)) {
continue;
}
bool thisBlockChanged = false;
for (NodeFlowProjection node : block->ssa->liveAtTail) {
if (node.kind() == NodeFlowProjection::Shadow)
continue;
if (node->epoch() != m_currentEpoch) {
thisBlockChanged |= m_stateAtTail->at(block).add(node.node()).isNewEntry;
}
}
if (!thisBlockChanged) {
continue;
}
changed = true;
for (BasicBlock* successor : block->successors()) {
for (Node* node : m_stateAtTail->at(block))
m_stateAtHead->at(successor).add(node);
}
}
}
m_isConverged = true;
for (BasicBlock* block : m_graph.blocksInNaturalOrder())
handleBlock(block);
return true;
} }
RELEASE_ASSERT_NOT_REACHED();
return false;
}
private:
bool handleBlock(BasicBlock* block)
{
if (DFGStoreBarrierInsertionPhaseInternal::verbose) {
dataLog("Dealing with block ", pointerDump(block), "\n");
if (reallyInsertBarriers())
dataLog(" Really inserting barriers.\n");
}
m_currentEpoch = Epoch::first();
if (mode == PhaseMode::Global) {
if (!block->cfaHasVisited)
return false;
m_state->beginBasicBlock(block);
for (NodeFlowProjection node : block->ssa->liveAtHead) {
if (node.kind() == NodeFlowProjection::Shadow)
continue;
if (m_stateAtHead->at(block).contains(node.node())) {
node->setEpoch(Epoch());
} else {
node->setEpoch(m_currentEpoch);
}
}
}
bool result = true;
for (m_nodeIndex = 0; m_nodeIndex < block->size(); ++m_nodeIndex) {
m_node = block->at(m_nodeIndex);
if (DFGStoreBarrierInsertionPhaseInternal::verbose) {
dataLog(
" ", m_currentEpoch, ": Looking at node ", m_node, " with children: ");
CommaPrinter comma;
m_graph.doToChildren(
m_node,
[&] (Edge edge) {
dataLog(comma, edge, " (", edge->epoch(), ")");
});
dataLog("\n");
}
if (mode == PhaseMode::Global) {
m_interpreter->startExecuting();
m_interpreter->executeEdges(m_node);
}
switch (m_node->op()) {
case PutByValDirect:
case PutByVal:
case PutByValAlias: {
switch (m_node->arrayMode().modeForPut().type()) {
case Array::Contiguous:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage: {
Edge child1 = m_graph.varArgChild(m_node, 0);
Edge child3 = m_graph.varArgChild(m_node, 2);
considerBarrier(child1, child3);
break;
}
default:
break;
}
break;
}
case ArrayPush: {
switch (m_node->arrayMode().type()) {
case Array::Contiguous:
case Array::ArrayStorage: {
unsigned elementOffset = 2;
unsigned elementCount = m_node->numChildren() - elementOffset;
Edge& arrayEdge = m_graph.varArgChild(m_node, 1);
for (unsigned i = 0; i < elementCount; ++i) {
Edge& element = m_graph.varArgChild(m_node, i + elementOffset);
considerBarrier(arrayEdge, element);
}
break;
}
default:
break;
}
break;
}
case PutById:
case PutByIdFlush:
case PutByIdDirect:
case PutStructure: {
considerBarrier(m_node->child1());
break;
}
case RecordRegExpCachedResult: {
considerBarrier(m_graph.varArgChild(m_node, 0));
break;
}
case PutClosureVar:
case PutToArguments:
case SetRegExpObjectLastIndex: {
considerBarrier(m_node->child1(), m_node->child2());
break;
}
case MultiPutByOffset: {
considerBarrier(m_node->child1());
break;
}
case PutByOffset: {
considerBarrier(m_node->child2(), m_node->child3());
break;
}
case PutGlobalVariable: {
considerBarrier(m_node->child1(), m_node->child2());
break;
}
case SetFunctionName: {
considerBarrier(m_node->child1(), m_node->child2());
break;
}
case NukeStructureAndSetButterfly: {
considerBarrier(m_node->child1());
break;
}
default:
break;
}
if (doesGC(m_graph, m_node))
m_currentEpoch.bump();
switch (m_node->op()) {
case NewObject:
case NewArray:
case NewArrayWithSize:
case NewArrayBuffer:
case NewTypedArray:
case NewRegexp:
case MaterializeNewObject:
case MaterializeCreateActivation:
case NewStringObject:
case MakeRope:
case CreateActivation:
case CreateDirectArguments:
case CreateScopedArguments:
case CreateClonedArguments:
case NewFunction:
case NewGeneratorFunction:
case NewAsyncGeneratorFunction:
case NewAsyncFunction:
case AllocatePropertyStorage:
case ReallocatePropertyStorage:
m_node->setEpoch(m_currentEpoch);
break;
case Upsilon:
m_node->phi()->setEpoch(Epoch());
m_node->setEpoch(Epoch());
break;
default:
m_node->setEpoch(Epoch());
break;
}
if (DFGStoreBarrierInsertionPhaseInternal::verbose) {
dataLog(
" ", m_currentEpoch, ": Done with node ", m_node, " (", m_node->epoch(),
") with children: ");
CommaPrinter comma;
m_graph.doToChildren(
m_node,
[&] (Edge edge) {
dataLog(comma, edge, " (", edge->epoch(), ")");
});
dataLog("\n");
}
if (mode == PhaseMode::Global) {
if (!m_interpreter->executeEffects(m_nodeIndex, m_node)) {
result = false;
break;
}
}
}
if (mode == PhaseMode::Global)
m_state->reset();
if (reallyInsertBarriers())
m_insertionSet.execute(block);
return result;
}
void considerBarrier(Edge base, Edge child)
{
if (DFGStoreBarrierInsertionPhaseInternal::verbose)
dataLog(" Considering adding barrier ", base, " => ", child, "\n");
switch (mode) {
case PhaseMode::Fast: {
if (child->hasConstant()) {
if (!child->asJSValue().isCell()) {
if (DFGStoreBarrierInsertionPhaseInternal::verbose)
dataLog(" Rejecting because of constant type.\n");
return;
}
} else {
switch (child->result()) {
case NodeResultNumber:
case NodeResultDouble:
case NodeResultInt32:
case NodeResultInt52:
case NodeResultBoolean:
if (DFGStoreBarrierInsertionPhaseInternal::verbose)
dataLog(" Rejecting because of result type.\n");
return;
default:
break;
}
}
break;
}
case PhaseMode::Global: {
if (!m_interpreter->needsTypeCheck(child, ~SpecCell)) {
if (DFGStoreBarrierInsertionPhaseInternal::verbose)
dataLog(" Rejecting because of AI type.\n");
return;
}
break;
} }
considerBarrier(base);
}
void considerBarrier(Edge base)
{
if (DFGStoreBarrierInsertionPhaseInternal::verbose)
dataLog(" Considering adding barrier on ", base, "\n");
if (base->epoch() == m_currentEpoch) {
if (DFGStoreBarrierInsertionPhaseInternal::verbose)
dataLog(" Rejecting because it's in the current epoch.\n");
return;
}
if (DFGStoreBarrierInsertionPhaseInternal::verbose)
dataLog(" Inserting barrier.\n");
insertBarrier(m_nodeIndex + 1, base);
}
void insertBarrier(unsigned nodeIndex, Edge base)
{
base->setEpoch(Epoch());
if (!reallyInsertBarriers())
return;
DFG_ASSERT(m_graph, m_node, isCell(base.useKind()), m_node->op(), base.useKind());
base.setUseKind(KnownCellUse);
NodeOrigin origin = m_node->origin;
if (clobbersExitState(m_graph, m_node))
origin = origin.withInvalidExit();
NodeType type;
if (Options::useConcurrentBarriers())
type = FencedStoreBarrier;
else
type = StoreBarrier;
m_insertionSet.insertNode(nodeIndex, SpecNone, type, origin, base);
}
bool reallyInsertBarriers()
{
return mode == PhaseMode::Fast || m_isConverged;
}
InsertionSet m_insertionSet;
Epoch m_currentEpoch;
unsigned m_nodeIndex;
Node* m_node;
std::unique_ptr<InPlaceAbstractState> m_state;
std::unique_ptr<AbstractInterpreter<InPlaceAbstractState>> m_interpreter;
std::unique_ptr<BlockMap<HashSet<Node*>>> m_stateAtHead;
std::unique_ptr<BlockMap<HashSet<Node*>>> m_stateAtTail;
bool m_isConverged;
};
}
bool performFastStoreBarrierInsertion(Graph& graph)
{
return runPhase<StoreBarrierInsertionPhase<PhaseMode::Fast>>(graph);
}
bool performGlobalStoreBarrierInsertion(Graph& graph)
{
return runPhase<StoreBarrierInsertionPhase<PhaseMode::Global>>(graph);
}
} }
#endif // ENABLE(DFG_JIT)