DFGOSRAvailabilityAnalysisPhase.cpp [plain text]
#include "config.h"
#include "DFGOSRAvailabilityAnalysisPhase.h"
#if ENABLE(DFG_JIT)
#include "DFGBlockMapInlines.h"
#include "DFGMayExit.h"
#include "DFGPhase.h"
#include "JSCJSValueInlines.h"
#include "OperandsInlines.h"
namespace JSC { namespace DFG {
template<typename HeadFunctor, typename TailFunctor>
class OSRAvailabilityAnalysisPhase : public Phase {
static constexpr bool verbose = false;
public:
OSRAvailabilityAnalysisPhase(Graph& graph, HeadFunctor& availabilityAtHead, TailFunctor& availabilityAtTail)
: Phase(graph, "OSR availability analysis")
, availabilityAtHead(availabilityAtHead)
, availabilityAtTail(availabilityAtTail)
{
}
bool run()
{
ASSERT(m_graph.m_form == SSA);
for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
BasicBlock* block = m_graph.block(blockIndex);
if (!block)
continue;
availabilityAtHead(block).clear();
availabilityAtTail(block).clear();
}
BasicBlock* root = m_graph.block(0);
availabilityAtHead(root).m_locals.fill(Availability::unavailable());
for (unsigned argument = 0; argument < m_graph.block(0)->valuesAtHead.numberOfArguments(); ++argument)
availabilityAtHead(root).m_locals.argument(argument) = Availability(FlushedAt(FlushedJSValue, virtualRegisterForArgumentIncludingThis(argument)));
auto dumpAvailability = [&] (BasicBlock* block) {
dataLogLn("Head: ", availabilityAtHead(block));
dataLogLn("Tail: ", availabilityAtTail(block));
};
auto dumpBytecodeLivenessAtHead = [&] (BasicBlock* block) {
dataLog("Live: ");
m_graph.forAllLiveInBytecode(
block->at(0)->origin.forExit,
[&] (Operand operand) {
dataLog(operand, " ");
});
dataLogLn("");
};
LocalOSRAvailabilityCalculator calculator(m_graph);
bool changed;
do {
changed = false;
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
if (verbose) {
dataLogLn("Before changing Block #", block->index);
dumpAvailability(block);
}
calculator.m_availability = availabilityAtHead(block);
for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex)
calculator.executeNode(block->at(nodeIndex));
if (calculator.m_availability == availabilityAtTail(block))
continue;
availabilityAtTail(block) = calculator.m_availability;
changed = true;
if (verbose) {
dataLogLn("After changing Block #", block->index);
dumpAvailability(block);
}
for (unsigned successorIndex = block->numSuccessors(); successorIndex--;) {
BasicBlock* successor = block->successor(successorIndex);
availabilityAtHead(successor).merge(calculator.m_availability);
}
for (unsigned successorIndex = block->numSuccessors(); successorIndex--;) {
BasicBlock* successor = block->successor(successorIndex);
availabilityAtHead(successor).pruneByLiveness(
m_graph, successor->at(0)->origin.forExit);
if (verbose) {
dataLogLn("After pruning Block #", successor->index);
dumpAvailability(successor);
dumpBytecodeLivenessAtHead(successor);
}
}
}
} while (changed);
if (validationEnabled()) {
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
calculator.m_availability = availabilityAtHead(block);
for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
Node* node = block->at(nodeIndex);
if (node->origin.exitOK) {
CodeOrigin exitOrigin = node->origin.forExit;
AvailabilityMap availabilityMap = calculator.m_availability;
availabilityMap.pruneByLiveness(m_graph, exitOrigin);
for (auto heapPair : availabilityMap.m_heap) {
switch (heapPair.key.kind()) {
case ActivationScopePLoc:
case ActivationSymbolTablePLoc:
case FunctionActivationPLoc:
case FunctionExecutablePLoc:
case StructurePLoc:
if (heapPair.value.isDead()) {
dataLogLn("PromotedHeapLocation is dead, but should not be: ", heapPair.key);
availabilityMap.dump(WTF::dataFile());
CRASH();
}
break;
default:
break;
}
}
if (m_graph.m_plan.mode() != FTLForOSREntryMode) {
for (size_t i = 0; i < availabilityMap.m_locals.size(); ++i) {
Operand operand = availabilityMap.m_locals.operandForIndex(i);
Availability availability = availabilityMap.m_locals[i];
if (availability.isDead() && m_graph.isLiveInBytecode(operand, exitOrigin))
DFG_CRASH(m_graph, node, toCString("Live bytecode local not available: operand = ", operand, ", availabilityMap = ", availabilityMap, ", origin = ", exitOrigin).data());
}
}
}
calculator.executeNode(block->at(nodeIndex));
}
}
}
return true;
}
HeadFunctor& availabilityAtHead;
TailFunctor& availabilityAtTail;
};
bool performOSRAvailabilityAnalysis(Graph& graph)
{
auto availabilityAtHead = [&] (BasicBlock* block) -> AvailabilityMap& { return block->ssa->availabilityAtHead; };
auto availabilityAtTail = [&] (BasicBlock* block) -> AvailabilityMap& { return block->ssa->availabilityAtTail; };
return runPhase<OSRAvailabilityAnalysisPhase<decltype(availabilityAtHead), decltype(availabilityAtTail)>>(graph, availabilityAtHead, availabilityAtTail);
}
void validateOSRExitAvailability(Graph& graph)
{
BlockMap<AvailabilityMap> availabilityMapAtHead(graph);
BlockMap<AvailabilityMap> availabilityMapAtTail(graph);
for (BasicBlock* block : graph.blocksInNaturalOrder()) {
availabilityMapAtHead[block] = AvailabilityMap(block->ssa->availabilityAtHead);
availabilityMapAtTail[block] = AvailabilityMap(block->ssa->availabilityAtTail);
}
auto availabilityAtHead = [&] (BasicBlock* block) -> AvailabilityMap& { return availabilityMapAtHead[block]; };
auto availabilityAtTail = [&] (BasicBlock* block) -> AvailabilityMap& { return availabilityMapAtTail[block]; };
OSRAvailabilityAnalysisPhase phase(graph, availabilityAtHead, availabilityAtTail);
phase.run();
}
LocalOSRAvailabilityCalculator::LocalOSRAvailabilityCalculator(Graph& graph)
: m_graph(graph)
{
}
LocalOSRAvailabilityCalculator::~LocalOSRAvailabilityCalculator()
{
}
void LocalOSRAvailabilityCalculator::beginBlock(BasicBlock* block)
{
m_availability = block->ssa->availabilityAtHead;
}
void LocalOSRAvailabilityCalculator::endBlock(BasicBlock* block)
{
m_availability = block->ssa->availabilityAtTail;
}
void LocalOSRAvailabilityCalculator::executeNode(Node* node)
{
switch (node->op()) {
case GetStack:
case PutStack: {
StackAccessData* data = node->stackAccessData();
m_availability.m_locals.operand(data->operand).setFlush(data->flushedAt());
break;
}
case KillStack: {
m_availability.m_locals.operand(node->unlinkedOperand()).setFlush(FlushedAt(ConflictingFlush));
break;
}
case MovHint: {
m_availability.m_locals.operand(node->unlinkedOperand()).setNode(node->child1().node());
break;
}
case InitializeEntrypointArguments: {
unsigned entrypointIndex = node->entrypointIndex();
const Vector<FlushFormat>& argumentFormats = m_graph.m_argumentFormats[entrypointIndex];
for (unsigned argument = argumentFormats.size(); argument--; ) {
FlushedAt flushedAt = FlushedAt(argumentFormats[argument], virtualRegisterForArgumentIncludingThis(argument));
m_availability.m_locals.argument(argument) = Availability(flushedAt);
}
break;
}
case VarargsLength: {
break;
}
case LoadVarargs:
case ForwardVarargs: {
LoadVarargsData* data = node->loadVarargsData();
m_availability.m_locals.operand(data->count) = Availability(FlushedAt(FlushedInt32, data->machineCount));
for (unsigned i = data->limit; i--;) {
m_availability.m_locals.operand(data->start + i) =
Availability(FlushedAt(FlushedJSValue, data->machineStart.isValid() ? (data->machineStart + i) : VirtualRegister()));
}
break;
}
case PhantomCreateRest:
case PhantomDirectArguments:
case PhantomClonedArguments: {
InlineCallFrame* inlineCallFrame = node->origin.semantic.inlineCallFrame();
if (!inlineCallFrame) {
break;
}
unsigned numberOfArgumentsToSkip = 0;
if (node->op() == PhantomCreateRest)
numberOfArgumentsToSkip = node->numberOfArgumentsToSkip();
if (inlineCallFrame->isVarargs()) {
Availability argumentCount =
m_availability.m_locals.operand(VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::argumentCountIncludingThis));
m_availability.m_heap.set(PromotedHeapLocation(ArgumentCountPLoc, node), argumentCount);
}
if (inlineCallFrame->isClosureCall) {
Availability callee = m_availability.m_locals.operand(
VirtualRegister(inlineCallFrame->stackOffset + CallFrameSlot::callee));
m_availability.m_heap.set(PromotedHeapLocation(ArgumentsCalleePLoc, node), callee);
}
for (unsigned i = numberOfArgumentsToSkip; i < static_cast<unsigned>(inlineCallFrame->argumentCountIncludingThis - 1); ++i) {
Availability argument = m_availability.m_locals.operand(
VirtualRegister(inlineCallFrame->stackOffset + CallFrame::argumentOffset(i)));
m_availability.m_heap.set(PromotedHeapLocation(ArgumentPLoc, node, i), argument);
}
break;
}
case PutHint: {
m_availability.m_heap.set(
PromotedHeapLocation(node->child1().node(), node->promotedLocationDescriptor()),
Availability(node->child2().node()));
break;
}
case PhantomSpread:
m_availability.m_heap.set(PromotedHeapLocation(SpreadPLoc, node), Availability(node->child1().node()));
break;
case PhantomNewArrayWithSpread:
for (unsigned i = 0; i < node->numChildren(); i++) {
Node* child = m_graph.varArgChild(node, i).node();
m_availability.m_heap.set(PromotedHeapLocation(NewArrayWithSpreadArgumentPLoc, node, i), Availability(child));
}
break;
case PhantomNewArrayBuffer:
m_availability.m_heap.set(PromotedHeapLocation(NewArrayBufferPLoc, node), Availability(node->child1().node()));
break;
default:
break;
}
}
} }
#endif // ENABLE(DFG_JIT)