[turbofan] Memory improvements for escape analysis

This CL reduces the memory overhead of escape analysis
by introducing a "copy on demand" strategy for virtual states
and virtual objects.

BUG=v8:4586
LOG=n

Review URL: https://codereview.chromium.org/1606613002

Cr-Commit-Position: refs/heads/master@{#33491}
This commit is contained in:
sigurds 2016-01-25 04:46:25 -08:00 committed by Commit bot
parent 5eff542054
commit 5259af606b
5 changed files with 517 additions and 305 deletions

View File

@ -27,11 +27,16 @@ EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph,
jsgraph_(jsgraph),
escape_analysis_(escape_analysis),
zone_(zone),
visited_(static_cast<int>(jsgraph->graph()->NodeCount() * 2), zone),
fully_reduced_(static_cast<int>(jsgraph->graph()->NodeCount() * 2), zone),
exists_virtual_allocate_(true) {}
Reduction EscapeAnalysisReducer::Reduce(Node* node) {
if (node->id() < static_cast<NodeId>(fully_reduced_.length()) &&
fully_reduced_.Contains(node->id())) {
return NoChange();
}
switch (node->opcode()) {
case IrOpcode::kLoadField:
case IrOpcode::kLoadElement:
@ -47,35 +52,38 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) {
return ReduceReferenceEqual(node);
case IrOpcode::kObjectIsSmi:
return ReduceObjectIsSmi(node);
// FrameStates and Value nodes are preprocessed here,
// and visited via ReduceFrameStateUses from their user nodes.
case IrOpcode::kFrameState:
case IrOpcode::kStateValues: {
if (node->id() >= static_cast<NodeId>(visited_.length()) ||
visited_.Contains(node->id())) {
if (node->id() >= static_cast<NodeId>(fully_reduced_.length()) ||
fully_reduced_.Contains(node->id())) {
break;
}
bool needs_visit = false;
bool depends_on_object_state = false;
for (int i = 0; i < node->InputCount(); i++) {
Node* input = node->InputAt(i);
switch (input->opcode()) {
case IrOpcode::kAllocate:
case IrOpcode::kFinishRegion:
needs_visit = needs_visit || escape_analysis()->IsVirtual(input);
depends_on_object_state =
depends_on_object_state || escape_analysis()->IsVirtual(input);
break;
case IrOpcode::kFrameState:
case IrOpcode::kStateValues:
needs_visit =
needs_visit ||
input->id() >= static_cast<NodeId>(visited_.length()) ||
!visited_.Contains(input->id());
depends_on_object_state =
depends_on_object_state ||
input->id() >= static_cast<NodeId>(fully_reduced_.length()) ||
!fully_reduced_.Contains(input->id());
break;
default:
break;
}
}
if (!needs_visit) {
visited_.Add(node->id());
if (!depends_on_object_state) {
fully_reduced_.Add(node->id());
}
break;
return NoChange();
}
default:
// TODO(sigurds): Change this to GetFrameStateInputCount once
@ -93,10 +101,10 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) {
Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) {
DCHECK(node->opcode() == IrOpcode::kLoadField ||
node->opcode() == IrOpcode::kLoadElement);
if (visited_.Contains(node->id())) return NoChange();
visited_.Add(node->id());
if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
fully_reduced_.Add(node->id());
}
if (Node* rep = escape_analysis()->GetReplacement(node)) {
visited_.Add(node->id());
counters()->turbo_escape_loads_replaced()->Increment();
TRACE("Replaced #%d (%s) with #%d (%s)\n", node->id(),
node->op()->mnemonic(), rep->id(), rep->op()->mnemonic());
@ -110,8 +118,9 @@ Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) {
Reduction EscapeAnalysisReducer::ReduceStore(Node* node) {
DCHECK(node->opcode() == IrOpcode::kStoreField ||
node->opcode() == IrOpcode::kStoreElement);
if (visited_.Contains(node->id())) return NoChange();
visited_.Add(node->id());
if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
fully_reduced_.Add(node->id());
}
if (escape_analysis()->IsVirtual(NodeProperties::GetValueInput(node, 0))) {
TRACE("Removed #%d (%s) from effect chain\n", node->id(),
node->op()->mnemonic());
@ -124,8 +133,6 @@ Reduction EscapeAnalysisReducer::ReduceStore(Node* node) {
Reduction EscapeAnalysisReducer::ReduceAllocate(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
if (visited_.Contains(node->id())) return NoChange();
visited_.Add(node->id());
if (escape_analysis()->IsVirtual(node)) {
RelaxEffectsAndControls(node);
counters()->turbo_escape_allocs_replaced()->Increment();
@ -140,6 +147,9 @@ Reduction EscapeAnalysisReducer::ReduceFinishRegion(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion);
Node* effect = NodeProperties::GetEffectInput(node, 0);
if (effect->opcode() == IrOpcode::kBeginRegion) {
if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
fully_reduced_.Add(node->id());
}
RelaxEffectsAndControls(effect);
RelaxEffectsAndControls(node);
#ifdef DEBUG
@ -177,6 +187,7 @@ Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) {
// Left-hand side is not a virtual object.
ReplaceWithValue(node, jsgraph()->FalseConstant());
TRACE("Replaced ref eq #%d with false\n", node->id());
return Replace(node);
}
return NoChange();
}
@ -195,8 +206,6 @@ Reduction EscapeAnalysisReducer::ReduceObjectIsSmi(Node* node) {
Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) {
if (visited_.Contains(node->id())) return NoChange();
visited_.Add(node->id());
DCHECK_GE(node->op()->EffectInputCount(), 1);
bool changed = false;
for (int i = 0; i < node->InputCount(); ++i) {
@ -220,8 +229,8 @@ Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect,
bool multiple_users) {
DCHECK(node->opcode() == IrOpcode::kFrameState ||
node->opcode() == IrOpcode::kStateValues);
if (node->id() < static_cast<NodeId>(visited_.length()) &&
visited_.Contains(node->id())) {
if (node->id() < static_cast<NodeId>(fully_reduced_.length()) &&
fully_reduced_.Contains(node->id())) {
return nullptr;
}
TRACE("Reducing %s %d\n", node->op()->mnemonic(), node->id());
@ -263,6 +272,9 @@ Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect,
}
}
}
if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
fully_reduced_.Add(node->id());
}
return clone;
}
@ -274,6 +286,10 @@ Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index,
bool already_cloned,
bool multiple_users) {
Node* input = NodeProperties::GetValueInput(node, node_index);
if (node->id() < static_cast<NodeId>(fully_reduced_.length()) &&
fully_reduced_.Contains(node->id())) {
return nullptr;
}
TRACE("Reducing State Input #%d (%s)\n", input->id(),
input->op()->mnemonic());
Node* clone = nullptr;
@ -307,6 +323,36 @@ Counters* EscapeAnalysisReducer::counters() const {
return jsgraph_->isolate()->counters();
}
class EscapeAnalysisVerifier final : public AdvancedReducer {
public:
EscapeAnalysisVerifier(Editor* editor, EscapeAnalysis* escape_analysis)
: AdvancedReducer(editor), escape_analysis_(escape_analysis) {}
Reduction Reduce(Node* node) final {
switch (node->opcode()) {
case IrOpcode::kAllocate:
CHECK(!escape_analysis_->IsVirtual(node));
break;
default:
break;
}
return NoChange();
}
private:
EscapeAnalysis* escape_analysis_;
};
void EscapeAnalysisReducer::VerifyReplacement() const {
#ifdef DEBUG
GraphReducer graph_reducer(zone(), jsgraph()->graph());
EscapeAnalysisVerifier verifier(&graph_reducer, escape_analysis());
graph_reducer.AddReducer(&verifier);
graph_reducer.ReduceGraph();
#endif // DEBUG
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -32,6 +32,7 @@ class EscapeAnalysisReducer final : public AdvancedReducer {
void SetExistsVirtualAllocate(bool exists) {
exists_virtual_allocate_ = exists;
}
void VerifyReplacement() const;
private:
Reduction ReduceLoad(Node* node);
@ -56,7 +57,7 @@ class EscapeAnalysisReducer final : public AdvancedReducer {
Zone* const zone_;
// _visited marks nodes we already processed (allocs, loads, stores)
// and nodes that do not need a visit from ReduceDeoptState etc.
BitVector visited_;
BitVector fully_reduced_;
bool exists_virtual_allocate_;
DISALLOW_COPY_AND_ASSIGN(EscapeAnalysisReducer);

File diff suppressed because it is too large Load Diff

View File

@ -22,30 +22,56 @@ class VirtualObject;
// EscapeStatusAnalysis determines for each allocation whether it escapes.
class EscapeStatusAnalysis {
public:
typedef NodeId Alias;
~EscapeStatusAnalysis();
enum EscapeStatusFlag {
enum Status {
kUnknown = 0u,
kTracked = 1u << 0,
kEscaped = 1u << 1,
kOnStack = 1u << 2,
kVisited = 1u << 3,
// A node is dangling, if it is a load of some kind, and does not have
// an effect successor.
kDanglingComputed = 1u << 4,
kDangling = 1u << 5,
// A node is is an effect branch point, if it has more than 2 non-dangling
// effect successors.
kBranchPointComputed = 1u << 6,
kBranchPoint = 1u << 7,
};
typedef base::Flags<EscapeStatusFlag, unsigned char> EscapeStatusFlags;
typedef base::Flags<Status, unsigned char> StatusFlags;
void Run();
void RunStatusAnalysis();
bool IsVirtual(Node* node);
bool IsEscaped(Node* node);
bool IsAllocation(Node* node);
void DebugPrint();
friend class EscapeAnalysis;
private:
EscapeStatusAnalysis(EscapeAnalysis* object_analysis, Graph* graph,
Zone* zone);
void EnqueueForStatusAnalysis(Node* node);
bool SetEscaped(Node* node);
bool IsEffectBranchPoint(Node* node);
bool IsDanglingEffectNode(Node* node);
void ResizeStatusVector();
size_t GetStatusVectorSize();
bool IsVirtual(NodeId id);
Graph* graph() const { return graph_; }
Zone* zone() const { return zone_; }
void AssignAliases();
Alias GetAlias(NodeId id) const { return aliases_[id]; }
const ZoneVector<Alias>& GetAliasMap() const { return aliases_; }
Alias AliasCount() const { return next_free_alias_; }
static const Alias kNotReachable;
static const Alias kUntrackable;
bool IsNotReachable(Node* node);
ZoneVector<Node*>& stack() { return stack_; }
private:
void Process(Node* node);
void ProcessAllocate(Node* node);
void ProcessFinishRegion(Node* node);
@ -57,27 +83,27 @@ class EscapeStatusAnalysis {
bool CheckUsesForEscape(Node* node, Node* rep, bool phi_escaping = false);
void RevisitUses(Node* node);
void RevisitInputs(Node* node);
bool SetEscaped(Node* node);
bool IsVirtual(NodeId id);
Alias NextAlias() { return next_free_alias_++; }
bool HasEntry(Node* node);
void Resize();
size_t size();
bool IsAllocationPhi(Node* node);
Graph* graph() const { return graph_; }
Zone* zone() const { return zone_; }
ZoneVector<Node*> stack_;
EscapeAnalysis* object_analysis_;
Graph* const graph_;
Zone* const zone_;
ZoneVector<EscapeStatusFlags> status_;
ZoneDeque<Node*> queue_;
ZoneVector<StatusFlags> status_;
Alias next_free_alias_;
ZoneVector<Node*> status_stack_;
ZoneVector<Alias> aliases_;
DISALLOW_COPY_AND_ASSIGN(EscapeStatusAnalysis);
};
DEFINE_OPERATORS_FOR_FLAGS(EscapeStatusAnalysis::EscapeStatusFlags)
DEFINE_OPERATORS_FOR_FLAGS(EscapeStatusAnalysis::StatusFlags)
// Forward Declaration.
@ -88,8 +114,7 @@ class MergeCache;
// an object is virtual and eliminated.
class EscapeAnalysis {
public:
typedef NodeId Alias;
using Alias = EscapeStatusAnalysis::Alias;
EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common, Zone* zone);
~EscapeAnalysis();
@ -104,7 +129,6 @@ class EscapeAnalysis {
private:
void RunObjectAnalysis();
void AssignAliases();
bool Process(Node* node);
void ProcessLoadField(Node* node);
void ProcessStoreField(Node* node);
@ -120,10 +144,10 @@ class EscapeAnalysis {
VirtualState* states);
void ForwardVirtualState(Node* node);
bool IsEffectBranchPoint(Node* node);
bool IsDanglingEffectNode(Node* node);
int OffsetFromAccess(Node* node);
VirtualState* CopyForModificationAt(VirtualState* state, Node* node);
VirtualObject* CopyForModificationAt(VirtualObject* obj, VirtualState* state,
Node* node);
VirtualObject* GetVirtualObject(Node* at, NodeId id);
VirtualObject* ResolveVirtualObject(VirtualState* state, Node* node);
Node* GetReplacementIfSame(ZoneVector<VirtualObject*>& objs);
@ -142,24 +166,27 @@ class EscapeAnalysis {
void DebugPrintState(VirtualState* state);
void DebugPrintObject(VirtualObject* state, Alias id);
Alias NextAlias() { return next_free_alias_++; }
Alias AliasCount() const { return next_free_alias_; }
Graph* graph() const { return graph_; }
Graph* graph() const { return status_analysis_.graph(); }
Zone* zone() const { return status_analysis_.zone(); }
CommonOperatorBuilder* common() const { return common_; }
Zone* zone() const { return zone_; }
ZoneVector<Node*>& stack() { return status_analysis_.stack(); }
bool IsEffectBranchPoint(Node* node) {
return status_analysis_.IsEffectBranchPoint(node);
}
bool IsDanglingEffectNode(Node* node) {
return status_analysis_.IsDanglingEffectNode(node);
}
bool IsNotReachable(Node* node) {
return status_analysis_.IsNotReachable(node);
}
Alias GetAlias(NodeId id) const { return status_analysis_.GetAlias(id); }
Alias AliasCount() const { return status_analysis_.AliasCount(); }
static const Alias kNotReachable;
static const Alias kUntrackable;
Graph* const graph_;
EscapeStatusAnalysis status_analysis_;
CommonOperatorBuilder* const common_;
Zone* const zone_;
ZoneVector<VirtualState*> virtual_states_;
ZoneVector<Node*> replacements_;
EscapeStatusAnalysis escape_status_;
MergeCache* cache_;
ZoneVector<Alias> aliases_;
Alias next_free_alias_;
DISALLOW_COPY_AND_ASSIGN(EscapeAnalysis);
};

View File

@ -668,6 +668,7 @@ struct EscapeAnalysisPhase {
escape_analysis.ExistsVirtualAllocate());
AddReducer(data, &graph_reducer, &escape_reducer);
graph_reducer.ReduceGraph();
escape_reducer.VerifyReplacement();
}
};