DFGObjectAllocationSinkingPhase.cpp [plain text]
#include "config.h"
#include "DFGObjectAllocationSinkingPhase.h"
#if ENABLE(DFG_JIT)
#include "DFGBlockMapInlines.h"
#include "DFGClobbersExitState.h"
#include "DFGCombinedLiveness.h"
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGLazyNode.h"
#include "DFGLivenessAnalysisPhase.h"
#include "DFGOSRAvailabilityAnalysisPhase.h"
#include "DFGPhase.h"
#include "DFGPromotedHeapLocation.h"
#include "DFGSSACalculator.h"
#include "DFGValidate.h"
#include "JSCInlines.h"
#include <wtf/StdList.h>
namespace JSC { namespace DFG {
namespace {
namespace DFGObjectAllocationSinkingPhaseInternal {
static const bool verbose = false;
}
class Allocation {
public:
enum class Kind { Escaped, Object, Activation, Function, GeneratorFunction, AsyncFunction, AsyncGeneratorFunction, RegExpObject };
using Fields = HashMap<PromotedLocationDescriptor, Node*>;
explicit Allocation(Node* identifier = nullptr, Kind kind = Kind::Escaped)
: m_identifier(identifier)
, m_kind(kind)
{
}
const Fields& fields() const
{
return m_fields;
}
Fields& fields()
{
return m_fields;
}
Node* get(PromotedLocationDescriptor descriptor)
{
return m_fields.get(descriptor);
}
Allocation& set(PromotedLocationDescriptor descriptor, Node* value)
{
if (value)
m_fields.set(descriptor, value);
else
m_fields.remove(descriptor);
return *this;
}
void remove(PromotedLocationDescriptor descriptor)
{
set(descriptor, nullptr);
}
bool hasStructures() const
{
switch (kind()) {
case Kind::Object:
return true;
default:
return false;
}
}
Allocation& setStructures(const RegisteredStructureSet& structures)
{
ASSERT(hasStructures() && !structures.isEmpty());
m_structures = structures;
return *this;
}
Allocation& mergeStructures(const RegisteredStructureSet& structures)
{
ASSERT(hasStructures() || structures.isEmpty());
m_structures.merge(structures);
return *this;
}
Allocation& filterStructures(const RegisteredStructureSet& structures)
{
ASSERT(hasStructures());
m_structures.filter(structures);
RELEASE_ASSERT(!m_structures.isEmpty());
return *this;
}
const RegisteredStructureSet& structures() const
{
return m_structures;
}
Node* identifier() const { return m_identifier; }
Kind kind() const { return m_kind; }
bool isEscapedAllocation() const
{
return kind() == Kind::Escaped;
}
bool isObjectAllocation() const
{
return m_kind == Kind::Object;
}
bool isActivationAllocation() const
{
return m_kind == Kind::Activation;
}
bool isFunctionAllocation() const
{
return m_kind == Kind::Function || m_kind == Kind::GeneratorFunction || m_kind == Kind::AsyncFunction;
}
bool isRegExpObjectAllocation() const
{
return m_kind == Kind::RegExpObject;
}
bool operator==(const Allocation& other) const
{
return m_identifier == other.m_identifier
&& m_kind == other.m_kind
&& m_fields == other.m_fields
&& m_structures == other.m_structures;
}
bool operator!=(const Allocation& other) const
{
return !(*this == other);
}
void dump(PrintStream& out) const
{
dumpInContext(out, nullptr);
}
void dumpInContext(PrintStream& out, DumpContext* context) const
{
switch (m_kind) {
case Kind::Escaped:
out.print("Escaped");
break;
case Kind::Object:
out.print("Object");
break;
case Kind::Function:
out.print("Function");
break;
case Kind::GeneratorFunction:
out.print("GeneratorFunction");
break;
case Kind::AsyncFunction:
out.print("AsyncFunction");
break;
case Kind::AsyncGeneratorFunction:
out.print("AsyncGeneratorFunction");
break;
case Kind::Activation:
out.print("Activation");
break;
case Kind::RegExpObject:
out.print("RegExpObject");
break;
}
out.print("Allocation(");
if (!m_structures.isEmpty())
out.print(inContext(m_structures.toStructureSet(), context));
if (!m_fields.isEmpty()) {
if (!m_structures.isEmpty())
out.print(", ");
out.print(mapDump(m_fields, " => #", ", "));
}
out.print(")");
}
private:
Node* m_identifier; Kind m_kind;
Fields m_fields;
RegisteredStructureSet m_structures;
};
class LocalHeap {
public:
Allocation& newAllocation(Node* node, Allocation::Kind kind)
{
ASSERT(!m_pointers.contains(node) && !isAllocation(node));
m_pointers.add(node, node);
return m_allocations.set(node, Allocation(node, kind)).iterator->value;
}
bool isAllocation(Node* identifier) const
{
return m_allocations.contains(identifier);
}
Allocation& getAllocation(Node* identifier)
{
auto iter = m_allocations.find(identifier);
ASSERT(iter != m_allocations.end());
return iter->value;
}
void newPointer(Node* node, Node* identifier)
{
ASSERT(!m_allocations.contains(node) && !m_pointers.contains(node));
ASSERT(isAllocation(identifier));
m_pointers.add(node, identifier);
}
Node* follow(Node* node) const
{
auto iter = m_pointers.find(node);
ASSERT(iter == m_pointers.end() || m_allocations.contains(iter->value));
return iter == m_pointers.end() ? nullptr : iter->value;
}
Node* follow(PromotedHeapLocation location) const
{
const Allocation& base = m_allocations.find(location.base())->value;
auto iter = base.fields().find(location.descriptor());
if (iter == base.fields().end())
return nullptr;
return iter->value;
}
Allocation* onlyLocalAllocation(Node* node)
{
Node* identifier = follow(node);
if (!identifier)
return nullptr;
return &getAllocation(identifier);
}
Allocation* onlyLocalAllocation(PromotedHeapLocation location)
{
Node* identifier = follow(location);
if (!identifier)
return nullptr;
return &getAllocation(identifier);
}
void setWantEscapees()
{
m_wantEscapees = true;
}
HashMap<Node*, Allocation> takeEscapees()
{
return WTFMove(m_escapees);
}
void escape(Node* node)
{
Node* identifier = follow(node);
if (!identifier)
return;
escapeAllocation(identifier);
}
void merge(const LocalHeap& other)
{
assertIsValid();
other.assertIsValid();
ASSERT(!m_wantEscapees);
if (!reached()) {
ASSERT(other.reached());
*this = other;
return;
}
NodeSet toEscape;
for (auto& allocationEntry : other.m_allocations)
m_allocations.add(allocationEntry.key, allocationEntry.value);
for (auto& allocationEntry : m_allocations) {
auto allocationIter = other.m_allocations.find(allocationEntry.key);
if (allocationIter == other.m_allocations.end())
continue;
if (allocationEntry.value.kind() != allocationIter->value.kind()) {
toEscape.addVoid(allocationEntry.key);
for (const auto& fieldEntry : allocationIter->value.fields())
toEscape.addVoid(fieldEntry.value);
} else {
mergePointerSets(allocationEntry.value.fields(), allocationIter->value.fields(), toEscape);
allocationEntry.value.mergeStructures(allocationIter->value.structures());
}
}
mergePointerSets(m_pointers, other.m_pointers, toEscape);
for (Node* identifier : toEscape)
escapeAllocation(identifier);
if (!ASSERT_DISABLED) {
for (const auto& entry : m_allocations)
ASSERT_UNUSED(entry, entry.value.isEscapedAllocation() || other.m_allocations.contains(entry.key));
}
prune();
assertIsValid();
}
void pruneByLiveness(const NodeSet& live)
{
m_pointers.removeIf(
[&] (const auto& entry) {
return !live.contains(entry.key);
});
prune();
}
void assertIsValid() const
{
if (ASSERT_DISABLED)
return;
for (const auto& entry : m_pointers) {
ASSERT_UNUSED(entry, entry.value);
ASSERT(m_allocations.contains(entry.value));
}
for (const auto& allocationEntry : m_allocations) {
for (const auto& fieldEntry : allocationEntry.value.fields()) {
ASSERT_UNUSED(fieldEntry, fieldEntry.value);
ASSERT(m_allocations.contains(fieldEntry.value));
}
}
}
bool operator==(const LocalHeap& other) const
{
assertIsValid();
other.assertIsValid();
return m_allocations == other.m_allocations
&& m_pointers == other.m_pointers;
}
bool operator!=(const LocalHeap& other) const
{
return !(*this == other);
}
const HashMap<Node*, Allocation>& allocations() const
{
return m_allocations;
}
const HashMap<Node*, Node*>& pointers() const
{
return m_pointers;
}
void dump(PrintStream& out) const
{
out.print(" Allocations:\n");
for (const auto& entry : m_allocations)
out.print(" #", entry.key, ": ", entry.value, "\n");
out.print(" Pointers:\n");
for (const auto& entry : m_pointers)
out.print(" ", entry.key, " => #", entry.value, "\n");
}
bool reached() const
{
return m_reached;
}
void setReached()
{
m_reached = true;
}
private:
template<typename Key>
static void mergePointerSets(HashMap<Key, Node*>& my, const HashMap<Key, Node*>& their, NodeSet& toEscape)
{
auto escape = [&] (Node* identifier) {
toEscape.addVoid(identifier);
};
for (const auto& entry : their) {
if (!my.contains(entry.key))
escape(entry.value);
}
my.removeIf([&] (const auto& entry) {
auto iter = their.find(entry.key);
if (iter == their.end()) {
escape(entry.value);
return true;
}
if (iter->value != entry.value) {
escape(entry.value);
escape(iter->value);
return true;
}
return false;
});
}
void escapeAllocation(Node* identifier)
{
Allocation& allocation = getAllocation(identifier);
if (allocation.isEscapedAllocation())
return;
Allocation unescaped = WTFMove(allocation);
allocation = Allocation(unescaped.identifier(), Allocation::Kind::Escaped);
for (const auto& entry : unescaped.fields())
escapeAllocation(entry.value);
if (m_wantEscapees)
m_escapees.add(unescaped.identifier(), WTFMove(unescaped));
}
void prune()
{
NodeSet reachable;
for (const auto& entry : m_pointers)
reachable.addVoid(entry.value);
{
Vector<Node*> worklist;
worklist.appendRange(reachable.begin(), reachable.end());
while (!worklist.isEmpty()) {
Node* identifier = worklist.takeLast();
Allocation& allocation = m_allocations.find(identifier)->value;
for (const auto& entry : allocation.fields()) {
if (reachable.add(entry.value).isNewEntry)
worklist.append(entry.value);
}
}
}
m_allocations.removeIf(
[&] (const auto& entry) {
return !reachable.contains(entry.key);
});
}
bool m_reached = false;
HashMap<Node*, Node*> m_pointers;
HashMap<Node*, Allocation> m_allocations;
bool m_wantEscapees = false;
HashMap<Node*, Allocation> m_escapees;
};
class ObjectAllocationSinkingPhase : public Phase {
public:
ObjectAllocationSinkingPhase(Graph& graph)
: Phase(graph, "object allocation elimination")
, m_pointerSSA(graph)
, m_allocationSSA(graph)
, m_insertionSet(graph)
{
}
bool run()
{
ASSERT(m_graph.m_form == SSA);
ASSERT(m_graph.m_fixpointState == FixpointNotConverged);
if (!performSinking())
return false;
if (DFGObjectAllocationSinkingPhaseInternal::verbose) {
dataLog("Graph after elimination:\n");
m_graph.dump();
}
return true;
}
private:
bool performSinking()
{
m_graph.computeRefCounts();
m_graph.initializeNodeOwners();
m_graph.ensureSSADominators();
performLivenessAnalysis(m_graph);
performOSRAvailabilityAnalysis(m_graph);
m_combinedLiveness = CombinedLiveness(m_graph);
CString graphBeforeSinking;
if (Options::verboseValidationFailure() && Options::validateGraphAtEachPhase()) {
StringPrintStream out;
m_graph.dump(out);
graphBeforeSinking = out.toCString();
}
if (DFGObjectAllocationSinkingPhaseInternal::verbose) {
dataLog("Graph before elimination:\n");
m_graph.dump();
}
performAnalysis();
if (!determineSinkCandidates())
return false;
if (DFGObjectAllocationSinkingPhaseInternal::verbose) {
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
dataLog("Heap at head of ", *block, ": \n", m_heapAtHead[block]);
dataLog("Heap at tail of ", *block, ": \n", m_heapAtTail[block]);
}
}
promoteLocalHeap();
removeICStatusFilters();
if (Options::validateGraphAtEachPhase())
DFG::validate(m_graph, DumpGraph, graphBeforeSinking);
return true;
}
void performAnalysis()
{
m_heapAtHead = BlockMap<LocalHeap>(m_graph);
m_heapAtTail = BlockMap<LocalHeap>(m_graph);
bool changed;
do {
if (DFGObjectAllocationSinkingPhaseInternal::verbose)
dataLog("Doing iteration of escape analysis.\n");
changed = false;
for (BasicBlock* block : m_graph.blocksInPreOrder()) {
m_heapAtHead[block].setReached();
m_heap = m_heapAtHead[block];
for (Node* node : *block) {
handleNode(
node,
[] (PromotedHeapLocation, LazyNode) { },
[&] (PromotedHeapLocation) -> Node* {
return nullptr;
});
}
if (m_heap == m_heapAtTail[block])
continue;
m_heapAtTail[block] = m_heap;
changed = true;
m_heap.assertIsValid();
m_heap.pruneByLiveness(m_combinedLiveness.liveAtTail[block]);
for (BasicBlock* successorBlock : block->successors())
m_heapAtHead[successorBlock].merge(m_heap);
}
} while (changed);
}
template<typename WriteFunctor, typename ResolveFunctor>
void handleNode(
Node* node,
const WriteFunctor& heapWrite,
const ResolveFunctor& heapResolve)
{
m_heap.assertIsValid();
ASSERT(m_heap.takeEscapees().isEmpty());
Allocation* target = nullptr;
HashMap<PromotedLocationDescriptor, LazyNode> writes;
PromotedLocationDescriptor exactRead;
switch (node->op()) {
case NewObject:
target = &m_heap.newAllocation(node, Allocation::Kind::Object);
target->setStructures(node->structure());
writes.add(
StructurePLoc, LazyNode(m_graph.freeze(node->structure().get())));
break;
case NewFunction:
case NewGeneratorFunction:
case NewAsyncGeneratorFunction:
case NewAsyncFunction: {
if (isStillValid(node->castOperand<FunctionExecutable*>()->singletonFunction())) {
m_heap.escape(node->child1().node());
break;
}
if (node->op() == NewGeneratorFunction)
target = &m_heap.newAllocation(node, Allocation::Kind::GeneratorFunction);
else if (node->op() == NewAsyncFunction)
target = &m_heap.newAllocation(node, Allocation::Kind::AsyncFunction);
else if (node->op() == NewAsyncGeneratorFunction)
target = &m_heap.newAllocation(node, Allocation::Kind::AsyncGeneratorFunction);
else
target = &m_heap.newAllocation(node, Allocation::Kind::Function);
writes.add(FunctionExecutablePLoc, LazyNode(node->cellOperand()));
writes.add(FunctionActivationPLoc, LazyNode(node->child1().node()));
break;
}
case NewRegexp: {
target = &m_heap.newAllocation(node, Allocation::Kind::RegExpObject);
writes.add(RegExpObjectRegExpPLoc, LazyNode(node->cellOperand()));
writes.add(RegExpObjectLastIndexPLoc, LazyNode(node->child1().node()));
break;
}
case CreateActivation: {
if (isStillValid(node->castOperand<SymbolTable*>()->singletonScope())) {
m_heap.escape(node->child1().node());
break;
}
target = &m_heap.newAllocation(node, Allocation::Kind::Activation);
writes.add(ActivationSymbolTablePLoc, LazyNode(node->cellOperand()));
writes.add(ActivationScopePLoc, LazyNode(node->child1().node()));
{
SymbolTable* symbolTable = node->castOperand<SymbolTable*>();
LazyNode initialValue(m_graph.freeze(node->initializationValueForActivation()));
for (unsigned offset = 0; offset < symbolTable->scopeSize(); ++offset) {
writes.add(
PromotedLocationDescriptor(ClosureVarPLoc, offset),
initialValue);
}
}
break;
}
case PutStructure:
target = m_heap.onlyLocalAllocation(node->child1().node());
if (target && target->isObjectAllocation()) {
writes.add(StructurePLoc, LazyNode(m_graph.freeze(JSValue(node->transition()->next.get()))));
target->setStructures(node->transition()->next);
} else
m_heap.escape(node->child1().node());
break;
case CheckStructureOrEmpty:
case CheckStructure: {
Allocation* allocation = m_heap.onlyLocalAllocation(node->child1().node());
if (allocation && allocation->isObjectAllocation()) {
RegisteredStructureSet filteredStructures = allocation->structures();
filteredStructures.filter(node->structureSet());
if (filteredStructures.isEmpty()) {
m_heap.escape(node->child1().node());
break;
}
allocation->setStructures(filteredStructures);
if (Node* value = heapResolve(PromotedHeapLocation(allocation->identifier(), StructurePLoc)))
node->convertToCheckStructureImmediate(value);
} else
m_heap.escape(node->child1().node());
break;
}
case GetByOffset:
case GetGetterSetterByOffset:
target = m_heap.onlyLocalAllocation(node->child2().node());
if (target && target->isObjectAllocation()) {
unsigned identifierNumber = node->storageAccessData().identifierNumber;
exactRead = PromotedLocationDescriptor(NamedPropertyPLoc, identifierNumber);
} else {
m_heap.escape(node->child1().node());
m_heap.escape(node->child2().node());
}
break;
case MultiGetByOffset: {
Allocation* allocation = m_heap.onlyLocalAllocation(node->child1().node());
if (allocation && allocation->isObjectAllocation()) {
MultiGetByOffsetData& data = node->multiGetByOffsetData();
RegisteredStructureSet validStructures;
bool hasInvalidStructures = false;
for (const auto& multiGetByOffsetCase : data.cases) {
if (!allocation->structures().overlaps(multiGetByOffsetCase.set()))
continue;
switch (multiGetByOffsetCase.method().kind()) {
case GetByOffsetMethod::LoadFromPrototype: case GetByOffsetMethod::Constant: hasInvalidStructures = true;
break;
case GetByOffsetMethod::Load: validStructures.merge(multiGetByOffsetCase.set());
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
if (hasInvalidStructures || validStructures.isEmpty()) {
m_heap.escape(node->child1().node());
break;
}
unsigned identifierNumber = data.identifierNumber;
PromotedHeapLocation location(NamedPropertyPLoc, allocation->identifier(), identifierNumber);
if (Node* value = heapResolve(location)) {
if (allocation->structures().isSubsetOf(validStructures))
node->replaceWithWithoutChecks(value);
else {
Node* structure = heapResolve(PromotedHeapLocation(allocation->identifier(), StructurePLoc));
ASSERT(structure);
allocation->filterStructures(validStructures);
node->convertToCheckStructure(m_graph.addStructureSet(allocation->structures()));
node->convertToCheckStructureImmediate(structure);
node->setReplacement(value);
}
} else if (!allocation->structures().isSubsetOf(validStructures)) {
heapResolve(PromotedHeapLocation(allocation->identifier(), StructurePLoc));
allocation->filterStructures(validStructures);
}
Node* identifier = allocation->get(location.descriptor());
if (identifier)
m_heap.newPointer(node, identifier);
} else
m_heap.escape(node->child1().node());
break;
}
case PutByOffset:
target = m_heap.onlyLocalAllocation(node->child2().node());
if (target && target->isObjectAllocation()) {
unsigned identifierNumber = node->storageAccessData().identifierNumber;
writes.add(
PromotedLocationDescriptor(NamedPropertyPLoc, identifierNumber),
LazyNode(node->child3().node()));
} else {
m_heap.escape(node->child1().node());
m_heap.escape(node->child2().node());
m_heap.escape(node->child3().node());
}
break;
case GetClosureVar:
target = m_heap.onlyLocalAllocation(node->child1().node());
if (target && target->isActivationAllocation()) {
exactRead =
PromotedLocationDescriptor(ClosureVarPLoc, node->scopeOffset().offset());
} else
m_heap.escape(node->child1().node());
break;
case PutClosureVar:
target = m_heap.onlyLocalAllocation(node->child1().node());
if (target && target->isActivationAllocation()) {
writes.add(
PromotedLocationDescriptor(ClosureVarPLoc, node->scopeOffset().offset()),
LazyNode(node->child2().node()));
} else {
m_heap.escape(node->child1().node());
m_heap.escape(node->child2().node());
}
break;
case SkipScope:
target = m_heap.onlyLocalAllocation(node->child1().node());
if (target && target->isActivationAllocation())
exactRead = ActivationScopePLoc;
else
m_heap.escape(node->child1().node());
break;
case GetExecutable:
target = m_heap.onlyLocalAllocation(node->child1().node());
if (target && target->isFunctionAllocation())
exactRead = FunctionExecutablePLoc;
else
m_heap.escape(node->child1().node());
break;
case GetScope:
target = m_heap.onlyLocalAllocation(node->child1().node());
if (target && target->isFunctionAllocation())
exactRead = FunctionActivationPLoc;
else
m_heap.escape(node->child1().node());
break;
case GetRegExpObjectLastIndex:
target = m_heap.onlyLocalAllocation(node->child1().node());
if (target && target->isRegExpObjectAllocation())
exactRead = RegExpObjectLastIndexPLoc;
else
m_heap.escape(node->child1().node());
break;
case SetRegExpObjectLastIndex:
target = m_heap.onlyLocalAllocation(node->child1().node());
if (target && target->isRegExpObjectAllocation()) {
writes.add(
PromotedLocationDescriptor(RegExpObjectLastIndexPLoc),
LazyNode(node->child2().node()));
} else {
m_heap.escape(node->child1().node());
m_heap.escape(node->child2().node());
}
break;
case Check:
case CheckVarargs:
m_graph.doToChildren(
node,
[&] (Edge edge) {
if (edge.willNotHaveCheck())
return;
if (alreadyChecked(edge.useKind(), SpecObject))
return;
m_heap.escape(edge.node());
});
break;
case MovHint:
case PutHint:
break;
case FilterCallLinkStatus:
case FilterGetByIdStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
break;
default:
m_graph.doToChildren(
node,
[&] (Edge edge) {
m_heap.escape(edge.node());
});
break;
}
if (exactRead) {
ASSERT(target);
ASSERT(writes.isEmpty());
if (Node* value = heapResolve(PromotedHeapLocation(target->identifier(), exactRead))) {
ASSERT(!value->replacement());
node->replaceWith(m_graph, value);
}
Node* identifier = target->get(exactRead);
if (identifier)
m_heap.newPointer(node, identifier);
}
for (auto entry : writes) {
ASSERT(target);
if (entry.value.isNode())
target->set(entry.key, m_heap.follow(entry.value.asNode()));
else
target->remove(entry.key);
heapWrite(PromotedHeapLocation(target->identifier(), entry.key), entry.value);
}
m_heap.assertIsValid();
}
bool determineSinkCandidates()
{
m_sinkCandidates.clear();
m_materializationToEscapee.clear();
m_materializationSiteToMaterializations.clear();
m_materializationSiteToRecoveries.clear();
m_materializationSiteToHints.clear();
HashMap<Node*, Vector<Node*>> dependencies;
bool hasUnescapedReads = false;
for (BasicBlock* block : m_graph.blocksInPreOrder()) {
m_heap = m_heapAtHead[block];
for (Node* node : *block) {
handleNode(
node,
[&] (PromotedHeapLocation location, LazyNode value) {
if (!value.isNode())
return;
Allocation* allocation = m_heap.onlyLocalAllocation(value.asNode());
if (allocation && !allocation->isEscapedAllocation())
dependencies.add(allocation->identifier(), Vector<Node*>()).iterator->value.append(location.base());
},
[&] (PromotedHeapLocation) -> Node* {
hasUnescapedReads = true;
return nullptr;
});
}
NodeSet allocations;
for (const auto& entry : m_heap.allocations()) {
if (!entry.value.isEscapedAllocation())
allocations.addVoid(entry.key);
}
m_heap.pruneByLiveness(m_combinedLiveness.liveAtTail[block]);
for (Node* identifier : allocations) {
if (!m_heap.isAllocation(identifier))
m_sinkCandidates.addVoid(identifier);
}
}
auto forEachEscapee = [&] (auto callback) {
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
m_heap = m_heapAtHead[block];
m_heap.setWantEscapees();
for (Node* node : *block) {
handleNode(
node,
[] (PromotedHeapLocation, LazyNode) { },
[] (PromotedHeapLocation) -> Node* {
return nullptr;
});
auto escapees = m_heap.takeEscapees();
escapees.removeIf([&] (const auto& entry) { return !m_sinkCandidates.contains(entry.key); });
callback(escapees, node);
}
m_heap.pruneByLiveness(m_combinedLiveness.liveAtTail[block]);
{
HashMap<Node*, Allocation> escapingOnEdge;
for (const auto& entry : m_heap.allocations()) {
if (entry.value.isEscapedAllocation())
continue;
bool mustEscape = false;
for (BasicBlock* successorBlock : block->successors()) {
if (!m_heapAtHead[successorBlock].isAllocation(entry.key)
|| m_heapAtHead[successorBlock].getAllocation(entry.key).isEscapedAllocation())
mustEscape = true;
}
if (mustEscape && m_sinkCandidates.contains(entry.key))
escapingOnEdge.add(entry.key, entry.value);
}
callback(escapingOnEdge, block->terminal());
}
}
};
if (m_sinkCandidates.size()) {
forEachEscapee([&] (HashMap<Node*, Allocation>& escapees, Node* where) {
for (Node* allocation : escapees.keys()) {
InlineCallFrame* inlineCallFrame = allocation->origin.semantic.inlineCallFrame;
if (!inlineCallFrame)
continue;
if ((inlineCallFrame->isClosureCall || inlineCallFrame->isVarargs()) && inlineCallFrame != where->origin.semantic.inlineCallFrame)
m_sinkCandidates.remove(allocation);
}
});
}
Vector<Node*> worklist;
worklist.appendRange(m_sinkCandidates.begin(), m_sinkCandidates.end());
while (!worklist.isEmpty()) {
for (Node* identifier : dependencies.get(worklist.takeLast())) {
if (m_sinkCandidates.add(identifier).isNewEntry)
worklist.append(identifier);
}
}
if (m_sinkCandidates.isEmpty())
return hasUnescapedReads;
if (DFGObjectAllocationSinkingPhaseInternal::verbose)
dataLog("Candidates: ", listDump(m_sinkCandidates), "\n");
forEachEscapee([&] (HashMap<Node*, Allocation>& escapees, Node* where) {
placeMaterializations(WTFMove(escapees), where);
});
return hasUnescapedReads || !m_sinkCandidates.isEmpty();
}
void placeMaterializations(HashMap<Node*, Allocation> escapees, Node* where)
{
Vector<std::pair<PromotedHeapLocation, Node*>> hints;
for (const auto& entry : m_heap.allocations()) {
if (escapees.contains(entry.key))
continue;
for (const auto& field : entry.value.fields()) {
ASSERT(m_sinkCandidates.contains(entry.key) || !escapees.contains(field.value));
auto iter = escapees.find(field.value);
if (iter != escapees.end()) {
ASSERT(m_sinkCandidates.contains(field.value));
hints.append(std::make_pair(PromotedHeapLocation(entry.key, field.key), field.value));
}
}
}
HashMap<Node*, NodeSet> dependencies;
HashMap<Node*, NodeSet> reverseDependencies;
HashMap<Node*, NodeSet> forMaterialization;
for (const auto& entry : escapees) {
auto& myDependencies = dependencies.add(entry.key, NodeSet()).iterator->value;
auto& myDependenciesForMaterialization = forMaterialization.add(entry.key, NodeSet()).iterator->value;
reverseDependencies.add(entry.key, NodeSet());
for (const auto& field : entry.value.fields()) {
if (escapees.contains(field.value) && field.value != entry.key) {
myDependencies.addVoid(field.value);
reverseDependencies.add(field.value, NodeSet()).iterator->value.addVoid(entry.key);
if (field.key.neededForMaterialization())
myDependenciesForMaterialization.addVoid(field.value);
}
}
}
NodeSet materialized;
auto materialize = [&] (Node* identifier) {
materialized.addVoid(identifier);
for (Node* dep : dependencies.get(identifier))
reverseDependencies.find(dep)->value.remove(identifier);
for (Node* rdep : reverseDependencies.get(identifier)) {
dependencies.find(rdep)->value.remove(identifier);
forMaterialization.find(rdep)->value.remove(identifier);
}
dependencies.remove(identifier);
reverseDependencies.remove(identifier);
forMaterialization.remove(identifier);
};
StdList<Allocation> toMaterialize;
auto firstPos = toMaterialize.begin();
auto materializeFirst = [&] (Allocation&& allocation) {
materialize(allocation.identifier());
if (firstPos != toMaterialize.end())
++firstPos;
firstPos = toMaterialize.insert(firstPos, WTFMove(allocation));
};
auto lastPos = toMaterialize.end();
auto materializeLast = [&] (Allocation&& allocation) {
materialize(allocation.identifier());
lastPos = toMaterialize.insert(lastPos, WTFMove(allocation));
};
Vector<PromotedHeapLocation> toRecover;
while (!escapees.isEmpty()) {
materialized.clear();
for (auto& entry : escapees) {
if (!forMaterialization.find(entry.key)->value.isEmpty())
continue;
if (dependencies.find(entry.key)->value.isEmpty()) {
materializeFirst(WTFMove(entry.value));
continue;
}
if (reverseDependencies.find(entry.key)->value.isEmpty()) {
materializeLast(WTFMove(entry.value));
continue;
}
}
if (materialized.isEmpty()) {
uint64_t maxEvaluation = 0;
Allocation* bestAllocation = nullptr;
for (auto& entry : escapees) {
if (!forMaterialization.find(entry.key)->value.isEmpty())
continue;
uint64_t evaluation =
static_cast<uint64_t>(dependencies.get(entry.key).size()) * reverseDependencies.get(entry.key).size();
if (evaluation > maxEvaluation) {
maxEvaluation = evaluation;
bestAllocation = &entry.value;
}
}
RELEASE_ASSERT(maxEvaluation > 0);
materializeFirst(WTFMove(*bestAllocation));
}
RELEASE_ASSERT(!materialized.isEmpty());
for (Node* identifier : materialized)
escapees.remove(identifier);
}
materialized.clear();
NodeSet escaped;
for (const Allocation& allocation : toMaterialize)
escaped.addVoid(allocation.identifier());
for (const Allocation& allocation : toMaterialize) {
for (const auto& field : allocation.fields()) {
if (escaped.contains(field.value) && !materialized.contains(field.value))
toRecover.append(PromotedHeapLocation(allocation.identifier(), field.key));
}
materialized.addVoid(allocation.identifier());
}
Vector<Node*>& materializations = m_materializationSiteToMaterializations.add(
where, Vector<Node*>()).iterator->value;
for (const Allocation& allocation : toMaterialize) {
Node* materialization = createMaterialization(allocation, where);
materializations.append(materialization);
m_materializationToEscapee.add(materialization, allocation.identifier());
}
if (!toRecover.isEmpty()) {
m_materializationSiteToRecoveries.add(
where, Vector<PromotedHeapLocation>()).iterator->value.appendVector(toRecover);
}
m_materializationSiteToHints.add(
where, Vector<std::pair<PromotedHeapLocation, Node*>>()).iterator->value.appendVector(hints);
}
Node* createMaterialization(const Allocation& allocation, Node* where)
{
switch (allocation.kind()) {
case Allocation::Kind::Object: {
ObjectMaterializationData* data = m_graph.m_objectMaterializationData.add();
return m_graph.addNode(
allocation.identifier()->prediction(), Node::VarArg, MaterializeNewObject,
where->origin.withSemantic(allocation.identifier()->origin.semantic),
OpInfo(m_graph.addStructureSet(allocation.structures())), OpInfo(data), 0, 0);
}
case Allocation::Kind::AsyncGeneratorFunction:
case Allocation::Kind::AsyncFunction:
case Allocation::Kind::GeneratorFunction:
case Allocation::Kind::Function: {
FrozenValue* executable = allocation.identifier()->cellOperand();
NodeType nodeType;
switch (allocation.kind()) {
case Allocation::Kind::GeneratorFunction:
nodeType = NewGeneratorFunction;
break;
case Allocation::Kind::AsyncGeneratorFunction:
nodeType = NewAsyncGeneratorFunction;
break;
case Allocation::Kind::AsyncFunction:
nodeType = NewAsyncFunction;
break;
default:
nodeType = NewFunction;
}
return m_graph.addNode(
allocation.identifier()->prediction(), nodeType,
where->origin.withSemantic(
allocation.identifier()->origin.semantic),
OpInfo(executable));
}
case Allocation::Kind::Activation: {
ObjectMaterializationData* data = m_graph.m_objectMaterializationData.add();
FrozenValue* symbolTable = allocation.identifier()->cellOperand();
return m_graph.addNode(
allocation.identifier()->prediction(), Node::VarArg, MaterializeCreateActivation,
where->origin.withSemantic(
allocation.identifier()->origin.semantic),
OpInfo(symbolTable), OpInfo(data), 0, 0);
}
case Allocation::Kind::RegExpObject: {
FrozenValue* regExp = allocation.identifier()->cellOperand();
return m_graph.addNode(
allocation.identifier()->prediction(), NewRegexp,
where->origin.withSemantic(
allocation.identifier()->origin.semantic),
OpInfo(regExp));
}
default:
DFG_CRASH(m_graph, allocation.identifier(), "Bad allocation kind");
}
}
void promoteLocalHeap()
{
HashSet<PromotedHeapLocation> locations;
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
m_heap = m_heapAtHead[block];
for (Node* node : *block) {
handleNode(
node,
[&] (PromotedHeapLocation location, LazyNode) {
if (m_sinkCandidates.contains(location.base()))
locations.addVoid(location);
},
[&] (PromotedHeapLocation location) -> Node* {
locations.addVoid(location);
return nullptr;
});
}
}
m_locationsForAllocation.clear();
for (PromotedHeapLocation location : locations) {
auto result = m_locationsForAllocation.add(
location.base(),
Vector<PromotedHeapLocation>());
ASSERT(!result.iterator->value.contains(location));
result.iterator->value.append(location);
}
m_pointerSSA.reset();
m_allocationSSA.reset();
m_locationToVariable.clear();
m_nodeToVariable.clear();
Vector<Node*> indexToNode;
Vector<PromotedHeapLocation> indexToLocation;
for (Node* index : m_sinkCandidates) {
SSACalculator::Variable* variable = m_allocationSSA.newVariable();
m_nodeToVariable.add(index, variable);
ASSERT(indexToNode.size() == variable->index());
indexToNode.append(index);
}
for (PromotedHeapLocation location : locations) {
SSACalculator::Variable* variable = m_pointerSSA.newVariable();
m_locationToVariable.add(location, variable);
ASSERT(indexToLocation.size() == variable->index());
indexToLocation.append(location);
}
HashMap<FrozenValue*, Node*> lazyMapping;
if (!m_bottom)
m_bottom = m_insertionSet.insertConstant(0, m_graph.block(0)->at(0)->origin, jsNumber(1927));
Vector<HashSet<PromotedHeapLocation>> hintsForPhi(m_sinkCandidates.size());
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
m_heap = m_heapAtHead[block];
for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
Node* node = block->at(nodeIndex);
for (PromotedHeapLocation location : m_locationsForAllocation.get(node)) {
if (location.kind() != NamedPropertyPLoc)
continue;
SSACalculator::Variable* variable = m_locationToVariable.get(location);
m_pointerSSA.newDef(variable, block, m_bottom);
}
for (Node* materialization : m_materializationSiteToMaterializations.get(node)) {
Node* escapee = m_materializationToEscapee.get(materialization);
m_allocationSSA.newDef(m_nodeToVariable.get(escapee), block, materialization);
}
for (std::pair<PromotedHeapLocation, Node*> pair : m_materializationSiteToHints.get(node)) {
PromotedHeapLocation location = pair.first;
Node* identifier = pair.second;
if (UNLIKELY(validationEnabled())) {
RELEASE_ASSERT(m_sinkCandidates.contains(location.base()));
RELEASE_ASSERT(m_sinkCandidates.contains(identifier));
bool found = false;
for (Node* materialization : m_materializationSiteToMaterializations.get(node)) {
if (m_materializationToEscapee.get(materialization) == identifier) {
found = true;
break;
}
}
RELEASE_ASSERT(found);
}
SSACalculator::Variable* variable = m_nodeToVariable.get(identifier);
hintsForPhi[variable->index()].addVoid(location);
}
if (m_sinkCandidates.contains(node))
m_allocationSSA.newDef(m_nodeToVariable.get(node), block, node);
handleNode(
node,
[&] (PromotedHeapLocation location, LazyNode value) {
if (!locations.contains(location))
return;
Node* nodeValue;
if (value.isNode())
nodeValue = value.asNode();
else {
auto iter = lazyMapping.find(value.asValue());
if (iter != lazyMapping.end())
nodeValue = iter->value;
else {
nodeValue = value.ensureIsNode(
m_insertionSet, m_graph.block(0), 0);
lazyMapping.add(value.asValue(), nodeValue);
}
}
SSACalculator::Variable* variable = m_locationToVariable.get(location);
m_pointerSSA.newDef(variable, block, nodeValue);
},
[] (PromotedHeapLocation) -> Node* {
return nullptr;
});
}
}
m_insertionSet.execute(m_graph.block(0));
m_pointerSSA.computePhis(
[&] (SSACalculator::Variable* variable, BasicBlock* block) -> Node* {
PromotedHeapLocation location = indexToLocation[variable->index()];
if (!m_heapAtHead[block].isAllocation(location.base()))
return nullptr;
if (m_heapAtHead[block].getAllocation(location.base()).isEscapedAllocation())
return nullptr;
if (m_heapAtHead[block].follow(location))
return nullptr;
Node* phiNode = m_graph.addNode(SpecHeapTop, Phi, block->at(0)->origin.withInvalidExit());
phiNode->mergeFlags(NodeResultJS);
return phiNode;
});
m_allocationSSA.computePhis(
[&] (SSACalculator::Variable* variable, BasicBlock* block) -> Node* {
Node* identifier = indexToNode[variable->index()];
if (!m_heapAtHead[block].isAllocation(identifier))
return nullptr;
if (!m_heapAtHead[block].getAllocation(identifier).isEscapedAllocation())
return nullptr;
Node* phiNode = m_graph.addNode(SpecHeapTop, Phi, block->at(0)->origin.withInvalidExit());
phiNode->mergeFlags(NodeResultJS);
return phiNode;
});
LocalOSRAvailabilityCalculator availabilityCalculator(m_graph);
m_graph.clearReplacements();
for (BasicBlock* block : m_graph.blocksInPreOrder()) {
m_heap = m_heapAtHead[block];
availabilityCalculator.beginBlock(block);
m_localMapping.clear();
m_escapeeToMaterialization.clear();
for (SSACalculator::Def* phiDef : m_pointerSSA.phisForBlock(block)) {
SSACalculator::Variable* variable = phiDef->variable();
m_insertionSet.insert(0, phiDef->value());
PromotedHeapLocation location = indexToLocation[variable->index()];
m_localMapping.set(location, phiDef->value());
if (m_sinkCandidates.contains(location.base())) {
m_insertionSet.insert(
0,
location.createHint(
m_graph, block->at(0)->origin.withInvalidExit(), phiDef->value()));
}
}
for (SSACalculator::Def* phiDef : m_allocationSSA.phisForBlock(block)) {
SSACalculator::Variable* variable = phiDef->variable();
m_insertionSet.insert(0, phiDef->value());
Node* identifier = indexToNode[variable->index()];
m_escapeeToMaterialization.add(identifier, phiDef->value());
bool canExit = false;
insertOSRHintsForUpdate(
0, block->at(0)->origin, canExit,
availabilityCalculator.m_availability, identifier, phiDef->value());
for (PromotedHeapLocation location : hintsForPhi[variable->index()]) {
m_insertionSet.insert(0,
location.createHint(m_graph, block->at(0)->origin.withInvalidExit(), phiDef->value()));
m_localMapping.set(location, phiDef->value());
}
}
if (DFGObjectAllocationSinkingPhaseInternal::verbose) {
dataLog("Local mapping at ", pointerDump(block), ": ", mapDump(m_localMapping), "\n");
dataLog("Local materializations at ", pointerDump(block), ": ", mapDump(m_escapeeToMaterialization), "\n");
}
for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
Node* node = block->at(nodeIndex);
bool canExit = true;
bool nextCanExit = node->origin.exitOK;
for (PromotedHeapLocation location : m_locationsForAllocation.get(node)) {
if (location.kind() != NamedPropertyPLoc)
continue;
m_localMapping.set(location, m_bottom);
if (m_sinkCandidates.contains(node)) {
if (DFGObjectAllocationSinkingPhaseInternal::verbose)
dataLog("For sink candidate ", node, " found location ", location, "\n");
m_insertionSet.insert(
nodeIndex + 1,
location.createHint(
m_graph, node->origin.takeValidExit(nextCanExit), m_bottom));
}
}
for (Node* materialization : m_materializationSiteToMaterializations.get(node)) {
materialization->origin.exitOK &= canExit;
Node* escapee = m_materializationToEscapee.get(materialization);
populateMaterialization(block, materialization, escapee);
m_escapeeToMaterialization.set(escapee, materialization);
m_insertionSet.insert(nodeIndex, materialization);
if (DFGObjectAllocationSinkingPhaseInternal::verbose)
dataLog("Materializing ", escapee, " => ", materialization, " at ", node, "\n");
}
for (PromotedHeapLocation location : m_materializationSiteToRecoveries.get(node))
m_insertionSet.insert(nodeIndex, createRecovery(block, location, node, canExit));
for (std::pair<PromotedHeapLocation, Node*> pair : m_materializationSiteToHints.get(node))
m_insertionSet.insert(nodeIndex, createRecovery(block, pair.first, node, canExit));
for (Node* materialization : m_materializationSiteToMaterializations.get(node)) {
Node* escapee = m_materializationToEscapee.get(materialization);
insertOSRHintsForUpdate(
nodeIndex, node->origin, canExit,
availabilityCalculator.m_availability, escapee, materialization);
}
if (node->origin.exitOK && !canExit) {
m_insertionSet.insertNode(nodeIndex, SpecNone, ExitOK, node->origin);
}
if (m_sinkCandidates.contains(node))
m_escapeeToMaterialization.set(node, node);
availabilityCalculator.executeNode(node);
bool desiredNextExitOK = node->origin.exitOK && !clobbersExitState(m_graph, node);
bool doLower = false;
handleNode(
node,
[&] (PromotedHeapLocation location, LazyNode value) {
if (!locations.contains(location))
return;
Node* nodeValue;
if (value.isNode())
nodeValue = value.asNode();
else
nodeValue = lazyMapping.get(value.asValue());
nodeValue = resolve(block, nodeValue);
m_localMapping.set(location, nodeValue);
if (!m_sinkCandidates.contains(location.base()))
return;
doLower = true;
if (DFGObjectAllocationSinkingPhaseInternal::verbose)
dataLog("Creating hint with value ", nodeValue, " before ", node, "\n");
m_insertionSet.insert(
nodeIndex + 1,
location.createHint(
m_graph, node->origin.takeValidExit(nextCanExit), nodeValue));
},
[&] (PromotedHeapLocation location) -> Node* {
return resolve(block, location);
});
if (!nextCanExit && desiredNextExitOK) {
m_insertionSet.insertNode(nodeIndex + 1, SpecNone, ExitOK, node->origin);
}
if (m_sinkCandidates.contains(node) || doLower) {
switch (node->op()) {
case NewObject:
node->convertToPhantomNewObject();
break;
case NewFunction:
node->convertToPhantomNewFunction();
break;
case NewGeneratorFunction:
node->convertToPhantomNewGeneratorFunction();
break;
case NewAsyncGeneratorFunction:
node->convertToPhantomNewAsyncGeneratorFunction();
break;
case NewAsyncFunction:
node->convertToPhantomNewAsyncFunction();
break;
case CreateActivation:
node->convertToPhantomCreateActivation();
break;
case NewRegexp:
node->convertToPhantomNewRegexp();
break;
default:
node->remove(m_graph);
break;
}
}
m_graph.doToChildren(
node,
[&] (Edge& edge) {
edge.setNode(resolve(block, edge.node()));
});
}
NodeAndIndex terminal = block->findTerminal();
size_t upsilonInsertionPoint = terminal.index;
NodeOrigin upsilonOrigin = terminal.node->origin;
for (BasicBlock* successorBlock : block->successors()) {
for (SSACalculator::Def* phiDef : m_pointerSSA.phisForBlock(successorBlock)) {
Node* phiNode = phiDef->value();
SSACalculator::Variable* variable = phiDef->variable();
PromotedHeapLocation location = indexToLocation[variable->index()];
Node* incoming = resolve(block, location);
m_insertionSet.insertNode(
upsilonInsertionPoint, SpecNone, Upsilon, upsilonOrigin,
OpInfo(phiNode), incoming->defaultEdge());
}
for (SSACalculator::Def* phiDef : m_allocationSSA.phisForBlock(successorBlock)) {
Node* phiNode = phiDef->value();
SSACalculator::Variable* variable = phiDef->variable();
Node* incoming = getMaterialization(block, indexToNode[variable->index()]);
m_insertionSet.insertNode(
upsilonInsertionPoint, SpecNone, Upsilon, upsilonOrigin,
OpInfo(phiNode), incoming->defaultEdge());
}
}
m_insertionSet.execute(block);
}
}
NEVER_INLINE Node* resolve(BasicBlock* block, PromotedHeapLocation location)
{
if (Node* identifier = m_heap.follow(location))
return getMaterialization(block, identifier);
if (Node* result = m_localMapping.get(location))
return result;
SSACalculator::Def* def = m_pointerSSA.nonLocalReachingDef(
block, m_locationToVariable.get(location));
ASSERT(def);
ASSERT(def->value());
Node* result = def->value();
if (result->replacement())
result = result->replacement();
ASSERT(!result->replacement());
m_localMapping.add(location, result);
return result;
}
NEVER_INLINE Node* resolve(BasicBlock* block, Node* node)
{
if (Node* identifier = m_heap.follow(node))
return getMaterialization(block, identifier);
if (node->replacement())
node = node->replacement();
ASSERT(!node->replacement());
return node;
}
NEVER_INLINE Node* getMaterialization(BasicBlock* block, Node* identifier)
{
ASSERT(m_heap.isAllocation(identifier));
if (!m_sinkCandidates.contains(identifier))
return identifier;
if (Node* materialization = m_escapeeToMaterialization.get(identifier))
return materialization;
SSACalculator::Def* def = m_allocationSSA.nonLocalReachingDef(
block, m_nodeToVariable.get(identifier));
ASSERT(def && def->value());
m_escapeeToMaterialization.add(identifier, def->value());
ASSERT(!def->value()->replacement());
return def->value();
}
void insertOSRHintsForUpdate(unsigned nodeIndex, NodeOrigin origin, bool& canExit, AvailabilityMap& availability, Node* escapee, Node* materialization)
{
if (DFGObjectAllocationSinkingPhaseInternal::verbose) {
dataLog("Inserting OSR hints at ", origin, ":\n");
dataLog(" Escapee: ", escapee, "\n");
dataLog(" Materialization: ", materialization, "\n");
dataLog(" Availability: ", availability, "\n");
}
for (auto entry : availability.m_heap) {
if (!entry.value.hasNode())
continue;
if (m_heap.follow(entry.value.node()) != escapee)
continue;
m_insertionSet.insert(
nodeIndex,
entry.key.createHint(m_graph, origin.takeValidExit(canExit), materialization));
}
for (unsigned i = availability.m_locals.size(); i--;) {
if (!availability.m_locals[i].hasNode())
continue;
if (m_heap.follow(availability.m_locals[i].node()) != escapee)
continue;
int operand = availability.m_locals.operandForIndex(i);
m_insertionSet.insertNode(
nodeIndex, SpecNone, MovHint, origin.takeValidExit(canExit), OpInfo(operand),
materialization->defaultEdge());
}
}
void populateMaterialization(BasicBlock* block, Node* node, Node* escapee)
{
Allocation& allocation = m_heap.getAllocation(escapee);
switch (node->op()) {
case MaterializeNewObject: {
ObjectMaterializationData& data = node->objectMaterializationData();
unsigned firstChild = m_graph.m_varArgChildren.size();
Vector<PromotedHeapLocation> locations = m_locationsForAllocation.get(escapee);
PromotedHeapLocation structure(StructurePLoc, allocation.identifier());
ASSERT(locations.contains(structure));
m_graph.m_varArgChildren.append(Edge(resolve(block, structure), KnownCellUse));
for (PromotedHeapLocation location : locations) {
switch (location.kind()) {
case StructurePLoc:
ASSERT(location == structure);
break;
case NamedPropertyPLoc: {
ASSERT(location.base() == allocation.identifier());
data.m_properties.append(location.descriptor());
Node* value = resolve(block, location);
if (m_sinkCandidates.contains(value))
m_graph.m_varArgChildren.append(m_bottom);
else
m_graph.m_varArgChildren.append(value);
break;
}
default:
DFG_CRASH(m_graph, node, "Bad location kind");
}
}
node->children = AdjacencyList(
AdjacencyList::Variable,
firstChild, m_graph.m_varArgChildren.size() - firstChild);
break;
}
case MaterializeCreateActivation: {
ObjectMaterializationData& data = node->objectMaterializationData();
unsigned firstChild = m_graph.m_varArgChildren.size();
Vector<PromotedHeapLocation> locations = m_locationsForAllocation.get(escapee);
PromotedHeapLocation symbolTable(ActivationSymbolTablePLoc, allocation.identifier());
ASSERT(locations.contains(symbolTable));
ASSERT(node->cellOperand() == resolve(block, symbolTable)->constant());
m_graph.m_varArgChildren.append(Edge(resolve(block, symbolTable), KnownCellUse));
PromotedHeapLocation scope(ActivationScopePLoc, allocation.identifier());
ASSERT(locations.contains(scope));
m_graph.m_varArgChildren.append(Edge(resolve(block, scope), KnownCellUse));
for (PromotedHeapLocation location : locations) {
switch (location.kind()) {
case ActivationScopePLoc: {
ASSERT(location == scope);
break;
}
case ActivationSymbolTablePLoc: {
ASSERT(location == symbolTable);
break;
}
case ClosureVarPLoc: {
ASSERT(location.base() == allocation.identifier());
data.m_properties.append(location.descriptor());
Node* value = resolve(block, location);
if (m_sinkCandidates.contains(value))
m_graph.m_varArgChildren.append(m_bottom);
else
m_graph.m_varArgChildren.append(value);
break;
}
default:
DFG_CRASH(m_graph, node, "Bad location kind");
}
}
node->children = AdjacencyList(
AdjacencyList::Variable,
firstChild, m_graph.m_varArgChildren.size() - firstChild);
break;
}
case NewFunction:
case NewGeneratorFunction:
case NewAsyncGeneratorFunction:
case NewAsyncFunction: {
Vector<PromotedHeapLocation> locations = m_locationsForAllocation.get(escapee);
ASSERT(locations.size() == 2);
PromotedHeapLocation executable(FunctionExecutablePLoc, allocation.identifier());
ASSERT_UNUSED(executable, locations.contains(executable));
PromotedHeapLocation activation(FunctionActivationPLoc, allocation.identifier());
ASSERT(locations.contains(activation));
node->child1() = Edge(resolve(block, activation), KnownCellUse);
break;
}
case NewRegexp: {
Vector<PromotedHeapLocation> locations = m_locationsForAllocation.get(escapee);
ASSERT(locations.size() == 2);
PromotedHeapLocation regExp(RegExpObjectRegExpPLoc, allocation.identifier());
ASSERT_UNUSED(regExp, locations.contains(regExp));
PromotedHeapLocation lastIndex(RegExpObjectLastIndexPLoc, allocation.identifier());
ASSERT(locations.contains(lastIndex));
Node* value = resolve(block, lastIndex);
if (m_sinkCandidates.contains(value))
node->child1() = Edge(m_bottom);
else
node->child1() = Edge(value);
break;
}
default:
DFG_CRASH(m_graph, node, "Bad materialize op");
}
}
Node* createRecovery(BasicBlock* block, PromotedHeapLocation location, Node* where, bool& canExit)
{
if (DFGObjectAllocationSinkingPhaseInternal::verbose)
dataLog("Recovering ", location, " at ", where, "\n");
ASSERT(location.base()->isPhantomAllocation());
Node* base = getMaterialization(block, location.base());
Node* value = resolve(block, location);
NodeOrigin origin = where->origin.withSemantic(base->origin.semantic);
if (DFGObjectAllocationSinkingPhaseInternal::verbose)
dataLog("Base is ", base, " and value is ", value, "\n");
if (base->isPhantomAllocation()) {
return PromotedHeapLocation(base, location.descriptor()).createHint(
m_graph, origin.takeValidExit(canExit), value);
}
switch (location.kind()) {
case NamedPropertyPLoc: {
Allocation& allocation = m_heap.getAllocation(location.base());
Vector<RegisteredStructure> structures;
structures.appendRange(allocation.structures().begin(), allocation.structures().end());
unsigned identifierNumber = location.info();
UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber];
std::sort(
structures.begin(),
structures.end(),
[uid] (RegisteredStructure a, RegisteredStructure b) -> bool {
return a->getConcurrently(uid) < b->getConcurrently(uid);
});
RELEASE_ASSERT(structures.size());
PropertyOffset firstOffset = structures[0]->getConcurrently(uid);
if (firstOffset == structures.last()->getConcurrently(uid)) {
Node* storage = base;
RELEASE_ASSERT(isInlineOffset(firstOffset));
StorageAccessData* data = m_graph.m_storageAccessData.add();
data->offset = firstOffset;
data->identifierNumber = identifierNumber;
return m_graph.addNode(
PutByOffset,
origin.takeValidExit(canExit),
OpInfo(data),
Edge(storage, KnownCellUse),
Edge(base, KnownCellUse),
value->defaultEdge());
}
MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add();
data->identifierNumber = identifierNumber;
{
PropertyOffset currentOffset = firstOffset;
StructureSet currentSet;
for (RegisteredStructure structure : structures) {
PropertyOffset offset = structure->getConcurrently(uid);
if (offset != currentOffset) {
data->variants.append(
PutByIdVariant::replace(currentSet, currentOffset));
currentOffset = offset;
currentSet.clear();
}
currentSet.add(structure.get());
}
data->variants.append(
PutByIdVariant::replace(currentSet, currentOffset));
}
return m_graph.addNode(
MultiPutByOffset,
origin.takeValidExit(canExit),
OpInfo(data),
Edge(base, KnownCellUse),
value->defaultEdge());
}
case ClosureVarPLoc: {
return m_graph.addNode(
PutClosureVar,
origin.takeValidExit(canExit),
OpInfo(location.info()),
Edge(base, KnownCellUse),
value->defaultEdge());
}
case RegExpObjectLastIndexPLoc: {
return m_graph.addNode(
SetRegExpObjectLastIndex,
origin.takeValidExit(canExit),
OpInfo(true),
Edge(base, KnownCellUse),
value->defaultEdge());
}
default:
DFG_CRASH(m_graph, base, "Bad location kind");
break;
}
RELEASE_ASSERT_NOT_REACHED();
}
void removeICStatusFilters()
{
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
for (Node* node : *block) {
switch (node->op()) {
case FilterCallLinkStatus:
case FilterGetByIdStatus:
case FilterPutByIdStatus:
case FilterInByIdStatus:
if (node->child1()->isPhantomAllocation())
node->removeWithoutChecks();
break;
default:
break;
}
}
}
}
bool isStillValid(InferredValue* value)
{
return m_validInferredValues.add(value, value->isStillValid()).iterator->value;
}
SSACalculator m_pointerSSA;
SSACalculator m_allocationSSA;
NodeSet m_sinkCandidates;
HashMap<PromotedHeapLocation, SSACalculator::Variable*> m_locationToVariable;
HashMap<Node*, SSACalculator::Variable*> m_nodeToVariable;
HashMap<PromotedHeapLocation, Node*> m_localMapping;
HashMap<Node*, Node*> m_escapeeToMaterialization;
InsertionSet m_insertionSet;
CombinedLiveness m_combinedLiveness;
HashMap<InferredValue*, bool> m_validInferredValues;
HashMap<Node*, Node*> m_materializationToEscapee;
HashMap<Node*, Vector<Node*>> m_materializationSiteToMaterializations;
HashMap<Node*, Vector<PromotedHeapLocation>> m_materializationSiteToRecoveries;
HashMap<Node*, Vector<std::pair<PromotedHeapLocation, Node*>>> m_materializationSiteToHints;
HashMap<Node*, Vector<PromotedHeapLocation>> m_locationsForAllocation;
BlockMap<LocalHeap> m_heapAtHead;
BlockMap<LocalHeap> m_heapAtTail;
LocalHeap m_heap;
Node* m_bottom = nullptr;
};
}
bool performObjectAllocationSinking(Graph& graph)
{
return runPhase<ObjectAllocationSinkingPhase>(graph);
}
} }
#endif // ENABLE(DFG_JIT)