Commit 5259af60 authored by sigurds's avatar sigurds Committed by Commit bot

[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}
parent 5eff5420
...@@ -27,11 +27,16 @@ EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph, ...@@ -27,11 +27,16 @@ EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph,
jsgraph_(jsgraph), jsgraph_(jsgraph),
escape_analysis_(escape_analysis), escape_analysis_(escape_analysis),
zone_(zone), 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) {} exists_virtual_allocate_(true) {}
Reduction EscapeAnalysisReducer::Reduce(Node* node) { Reduction EscapeAnalysisReducer::Reduce(Node* node) {
if (node->id() < static_cast<NodeId>(fully_reduced_.length()) &&
fully_reduced_.Contains(node->id())) {
return NoChange();
}
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kLoadField: case IrOpcode::kLoadField:
case IrOpcode::kLoadElement: case IrOpcode::kLoadElement:
...@@ -47,35 +52,38 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) { ...@@ -47,35 +52,38 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) {
return ReduceReferenceEqual(node); return ReduceReferenceEqual(node);
case IrOpcode::kObjectIsSmi: case IrOpcode::kObjectIsSmi:
return ReduceObjectIsSmi(node); return ReduceObjectIsSmi(node);
// FrameStates and Value nodes are preprocessed here,
// and visited via ReduceFrameStateUses from their user nodes.
case IrOpcode::kFrameState: case IrOpcode::kFrameState:
case IrOpcode::kStateValues: { case IrOpcode::kStateValues: {
if (node->id() >= static_cast<NodeId>(visited_.length()) || if (node->id() >= static_cast<NodeId>(fully_reduced_.length()) ||
visited_.Contains(node->id())) { fully_reduced_.Contains(node->id())) {
break; break;
} }
bool needs_visit = false; bool depends_on_object_state = false;
for (int i = 0; i < node->InputCount(); i++) { for (int i = 0; i < node->InputCount(); i++) {
Node* input = node->InputAt(i); Node* input = node->InputAt(i);
switch (input->opcode()) { switch (input->opcode()) {
case IrOpcode::kAllocate: case IrOpcode::kAllocate:
case IrOpcode::kFinishRegion: case IrOpcode::kFinishRegion:
needs_visit = needs_visit || escape_analysis()->IsVirtual(input); depends_on_object_state =
depends_on_object_state || escape_analysis()->IsVirtual(input);
break; break;
case IrOpcode::kFrameState: case IrOpcode::kFrameState:
case IrOpcode::kStateValues: case IrOpcode::kStateValues:
needs_visit = depends_on_object_state =
needs_visit || depends_on_object_state ||
input->id() >= static_cast<NodeId>(visited_.length()) || input->id() >= static_cast<NodeId>(fully_reduced_.length()) ||
!visited_.Contains(input->id()); !fully_reduced_.Contains(input->id());
break; break;
default: default:
break; break;
} }
} }
if (!needs_visit) { if (!depends_on_object_state) {
visited_.Add(node->id()); fully_reduced_.Add(node->id());
} }
break; return NoChange();
} }
default: default:
// TODO(sigurds): Change this to GetFrameStateInputCount once // TODO(sigurds): Change this to GetFrameStateInputCount once
...@@ -93,10 +101,10 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) { ...@@ -93,10 +101,10 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) {
Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) { Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) {
DCHECK(node->opcode() == IrOpcode::kLoadField || DCHECK(node->opcode() == IrOpcode::kLoadField ||
node->opcode() == IrOpcode::kLoadElement); node->opcode() == IrOpcode::kLoadElement);
if (visited_.Contains(node->id())) return NoChange(); if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
visited_.Add(node->id()); fully_reduced_.Add(node->id());
}
if (Node* rep = escape_analysis()->GetReplacement(node)) { if (Node* rep = escape_analysis()->GetReplacement(node)) {
visited_.Add(node->id());
counters()->turbo_escape_loads_replaced()->Increment(); counters()->turbo_escape_loads_replaced()->Increment();
TRACE("Replaced #%d (%s) with #%d (%s)\n", node->id(), TRACE("Replaced #%d (%s) with #%d (%s)\n", node->id(),
node->op()->mnemonic(), rep->id(), rep->op()->mnemonic()); node->op()->mnemonic(), rep->id(), rep->op()->mnemonic());
...@@ -110,8 +118,9 @@ Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) { ...@@ -110,8 +118,9 @@ Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) {
Reduction EscapeAnalysisReducer::ReduceStore(Node* node) { Reduction EscapeAnalysisReducer::ReduceStore(Node* node) {
DCHECK(node->opcode() == IrOpcode::kStoreField || DCHECK(node->opcode() == IrOpcode::kStoreField ||
node->opcode() == IrOpcode::kStoreElement); node->opcode() == IrOpcode::kStoreElement);
if (visited_.Contains(node->id())) return NoChange(); if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
visited_.Add(node->id()); fully_reduced_.Add(node->id());
}
if (escape_analysis()->IsVirtual(NodeProperties::GetValueInput(node, 0))) { if (escape_analysis()->IsVirtual(NodeProperties::GetValueInput(node, 0))) {
TRACE("Removed #%d (%s) from effect chain\n", node->id(), TRACE("Removed #%d (%s) from effect chain\n", node->id(),
node->op()->mnemonic()); node->op()->mnemonic());
...@@ -124,8 +133,6 @@ Reduction EscapeAnalysisReducer::ReduceStore(Node* node) { ...@@ -124,8 +133,6 @@ Reduction EscapeAnalysisReducer::ReduceStore(Node* node) {
Reduction EscapeAnalysisReducer::ReduceAllocate(Node* node) { Reduction EscapeAnalysisReducer::ReduceAllocate(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kAllocate); DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
if (visited_.Contains(node->id())) return NoChange();
visited_.Add(node->id());
if (escape_analysis()->IsVirtual(node)) { if (escape_analysis()->IsVirtual(node)) {
RelaxEffectsAndControls(node); RelaxEffectsAndControls(node);
counters()->turbo_escape_allocs_replaced()->Increment(); counters()->turbo_escape_allocs_replaced()->Increment();
...@@ -140,6 +147,9 @@ Reduction EscapeAnalysisReducer::ReduceFinishRegion(Node* node) { ...@@ -140,6 +147,9 @@ Reduction EscapeAnalysisReducer::ReduceFinishRegion(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion); DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion);
Node* effect = NodeProperties::GetEffectInput(node, 0); Node* effect = NodeProperties::GetEffectInput(node, 0);
if (effect->opcode() == IrOpcode::kBeginRegion) { if (effect->opcode() == IrOpcode::kBeginRegion) {
if (node->id() < static_cast<NodeId>(fully_reduced_.length())) {
fully_reduced_.Add(node->id());
}
RelaxEffectsAndControls(effect); RelaxEffectsAndControls(effect);
RelaxEffectsAndControls(node); RelaxEffectsAndControls(node);
#ifdef DEBUG #ifdef DEBUG
...@@ -177,6 +187,7 @@ Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) { ...@@ -177,6 +187,7 @@ Reduction EscapeAnalysisReducer::ReduceReferenceEqual(Node* node) {
// Left-hand side is not a virtual object. // Left-hand side is not a virtual object.
ReplaceWithValue(node, jsgraph()->FalseConstant()); ReplaceWithValue(node, jsgraph()->FalseConstant());
TRACE("Replaced ref eq #%d with false\n", node->id()); TRACE("Replaced ref eq #%d with false\n", node->id());
return Replace(node);
} }
return NoChange(); return NoChange();
} }
...@@ -195,8 +206,6 @@ Reduction EscapeAnalysisReducer::ReduceObjectIsSmi(Node* node) { ...@@ -195,8 +206,6 @@ Reduction EscapeAnalysisReducer::ReduceObjectIsSmi(Node* node) {
Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) { Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) {
if (visited_.Contains(node->id())) return NoChange();
visited_.Add(node->id());
DCHECK_GE(node->op()->EffectInputCount(), 1); DCHECK_GE(node->op()->EffectInputCount(), 1);
bool changed = false; bool changed = false;
for (int i = 0; i < node->InputCount(); ++i) { for (int i = 0; i < node->InputCount(); ++i) {
...@@ -220,8 +229,8 @@ Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect, ...@@ -220,8 +229,8 @@ Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect,
bool multiple_users) { bool multiple_users) {
DCHECK(node->opcode() == IrOpcode::kFrameState || DCHECK(node->opcode() == IrOpcode::kFrameState ||
node->opcode() == IrOpcode::kStateValues); node->opcode() == IrOpcode::kStateValues);
if (node->id() < static_cast<NodeId>(visited_.length()) && if (node->id() < static_cast<NodeId>(fully_reduced_.length()) &&
visited_.Contains(node->id())) { fully_reduced_.Contains(node->id())) {
return nullptr; return nullptr;
} }
TRACE("Reducing %s %d\n", node->op()->mnemonic(), node->id()); TRACE("Reducing %s %d\n", node->op()->mnemonic(), node->id());
...@@ -263,6 +272,9 @@ Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect, ...@@ -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; return clone;
} }
...@@ -274,6 +286,10 @@ Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index, ...@@ -274,6 +286,10 @@ Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index,
bool already_cloned, bool already_cloned,
bool multiple_users) { bool multiple_users) {
Node* input = NodeProperties::GetValueInput(node, node_index); 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(), TRACE("Reducing State Input #%d (%s)\n", input->id(),
input->op()->mnemonic()); input->op()->mnemonic());
Node* clone = nullptr; Node* clone = nullptr;
...@@ -307,6 +323,36 @@ Counters* EscapeAnalysisReducer::counters() const { ...@@ -307,6 +323,36 @@ Counters* EscapeAnalysisReducer::counters() const {
return jsgraph_->isolate()->counters(); 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 compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -32,6 +32,7 @@ class EscapeAnalysisReducer final : public AdvancedReducer { ...@@ -32,6 +32,7 @@ class EscapeAnalysisReducer final : public AdvancedReducer {
void SetExistsVirtualAllocate(bool exists) { void SetExistsVirtualAllocate(bool exists) {
exists_virtual_allocate_ = exists; exists_virtual_allocate_ = exists;
} }
void VerifyReplacement() const;
private: private:
Reduction ReduceLoad(Node* node); Reduction ReduceLoad(Node* node);
...@@ -56,7 +57,7 @@ class EscapeAnalysisReducer final : public AdvancedReducer { ...@@ -56,7 +57,7 @@ class EscapeAnalysisReducer final : public AdvancedReducer {
Zone* const zone_; Zone* const zone_;
// _visited marks nodes we already processed (allocs, loads, stores) // _visited marks nodes we already processed (allocs, loads, stores)
// and nodes that do not need a visit from ReduceDeoptState etc. // and nodes that do not need a visit from ReduceDeoptState etc.
BitVector visited_; BitVector fully_reduced_;
bool exists_virtual_allocate_; bool exists_virtual_allocate_;
DISALLOW_COPY_AND_ASSIGN(EscapeAnalysisReducer); DISALLOW_COPY_AND_ASSIGN(EscapeAnalysisReducer);
......
...@@ -24,6 +24,8 @@ namespace v8 { ...@@ -24,6 +24,8 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
using Alias = EscapeStatusAnalysis::Alias;
#ifdef DEBUG #ifdef DEBUG
#define TRACE(...) \ #define TRACE(...) \
do { \ do { \
...@@ -33,65 +35,62 @@ namespace compiler { ...@@ -33,65 +35,62 @@ namespace compiler {
#define TRACE(...) #define TRACE(...)
#endif #endif
const EscapeAnalysis::Alias EscapeAnalysis::kNotReachable = const Alias EscapeStatusAnalysis::kNotReachable =
std::numeric_limits<Alias>::max(); std::numeric_limits<Alias>::max();
const EscapeAnalysis::Alias EscapeAnalysis::kUntrackable = const Alias EscapeStatusAnalysis::kUntrackable =
std::numeric_limits<Alias>::max() - 1; std::numeric_limits<Alias>::max() - 1;
class VirtualObject : public ZoneObject { class VirtualObject : public ZoneObject {
public: public:
enum Status { kUntracked = 0, kTracked = 1 }; enum Status {
VirtualObject(NodeId id, Zone* zone) kInitial = 0,
kTracked = 1u << 0,
kInitialized = 1u << 1,
kCopyRequired = 1u << 2,
};
typedef base::Flags<Status, unsigned char> StatusFlags;
VirtualObject(NodeId id, VirtualState* owner, Zone* zone)
: id_(id), : id_(id),
status_(kUntracked), status_(kInitial),
fields_(zone), fields_(zone),
phi_(zone), phi_(zone),
object_state_(nullptr) {} object_state_(nullptr),
owner_(owner) {}
VirtualObject(const VirtualObject& other) VirtualObject(VirtualState* owner, const VirtualObject& other)
: id_(other.id_), : id_(other.id_),
status_(other.status_), status_(other.status_ & ~kCopyRequired),
fields_(other.fields_), fields_(other.fields_),
phi_(other.phi_), phi_(other.phi_),
object_state_(other.object_state_) {} object_state_(other.object_state_),
owner_(owner) {}
VirtualObject(NodeId id, Zone* zone, size_t field_number) VirtualObject(NodeId id, VirtualState* owner, Zone* zone, size_t field_number,
bool initialized)
: id_(id), : id_(id),
status_(kTracked), status_(kTracked | (initialized ? kInitialized : kInitial)),
fields_(zone), fields_(zone),
phi_(zone), phi_(zone),
object_state_(nullptr) { object_state_(nullptr),
owner_(owner) {
fields_.resize(field_number); fields_.resize(field_number);
phi_.resize(field_number, false); phi_.resize(field_number, false);
} }
Node* GetField(size_t offset) { Node* GetField(size_t offset) { return fields_[offset]; }
if (offset < fields_.size()) {
return fields_[offset];
}
return nullptr;
}
bool IsCreatedPhi(size_t offset) { bool IsCreatedPhi(size_t offset) { return phi_[offset]; }
if (offset < phi_.size()) {
return phi_[offset];
}
return false;
}
bool SetField(size_t offset, Node* node, bool created_phi = false) { void SetField(size_t offset, Node* node, bool created_phi = false) {
bool changed = fields_[offset] != node || phi_[offset] != created_phi;
fields_[offset] = node; fields_[offset] = node;
phi_[offset] = created_phi; phi_[offset] = created_phi;
if (changed && node) {
TRACE("Setting field %zu of #%d to #%d (%s)\n", offset, id(), node->id(),
node->op()->mnemonic());
}
return changed;
} }
bool IsVirtual() const { return status_ == kTracked; } bool IsTracked() const { return status_ & kTracked; }
bool IsTracked() const { return status_ != kUntracked; } bool IsInitialized() const { return status_ & kInitialized; }
bool SetInitialized() { return status_ |= kInitialized; }
VirtualState* owner() const { return owner_; }
Node** fields_array() { return &fields_.front(); } Node** fields_array() { return &fields_.front(); }
size_t field_count() { return fields_.size(); } size_t field_count() { return fields_.size(); }
...@@ -103,33 +102,50 @@ class VirtualObject : public ZoneObject { ...@@ -103,33 +102,50 @@ class VirtualObject : public ZoneObject {
} }
return false; return false;
} }
bool ClearAllFields() { void ClearAllFields() {
bool changed = false; for (size_t i = 0; i < fields_.size(); ++i) {
fields_[i] = nullptr;
phi_[i] = false;
}
}
bool AllFieldsClear() {
for (size_t i = 0; i < fields_.size(); ++i) { for (size_t i = 0; i < fields_.size(); ++i) {
if (fields_[i] != nullptr) { if (fields_[i] != nullptr) {
fields_[i] = nullptr; return false;
changed = true;
} }
phi_[i] = false;
} }
return changed; return true;
} }
bool UpdateFrom(const VirtualObject& other); bool UpdateFrom(const VirtualObject& other);
void SetObjectState(Node* node) { object_state_ = node; } void SetObjectState(Node* node) { object_state_ = node; }
Node* GetObjectState() const { return object_state_; } Node* GetObjectState() const { return object_state_; }
bool IsCopyRequired() const { return status_ & kCopyRequired; }
void SetCopyRequired() { status_ |= kCopyRequired; }
bool NeedCopyForModification() {
if (!IsCopyRequired() || !IsInitialized()) {
return false;
}
return true;
}
NodeId id() const { return id_; } NodeId id() const { return id_; }
void id(NodeId id) { id_ = id; } void id(NodeId id) { id_ = id; }
private: private:
NodeId id_; NodeId id_;
Status status_; StatusFlags status_;
ZoneVector<Node*> fields_; ZoneVector<Node*> fields_;
ZoneVector<bool> phi_; ZoneVector<bool> phi_;
Node* object_state_; Node* object_state_;
VirtualState* owner_;
DISALLOW_COPY_AND_ASSIGN(VirtualObject);
}; };
DEFINE_OPERATORS_FOR_FLAGS(VirtualObject::StatusFlags)
bool VirtualObject::UpdateFrom(const VirtualObject& other) { bool VirtualObject::UpdateFrom(const VirtualObject& other) {
bool changed = status_ != other.status_; bool changed = status_ != other.status_;
status_ = other.status_; status_ = other.status_;
...@@ -150,24 +166,32 @@ bool VirtualObject::UpdateFrom(const VirtualObject& other) { ...@@ -150,24 +166,32 @@ bool VirtualObject::UpdateFrom(const VirtualObject& other) {
class VirtualState : public ZoneObject { class VirtualState : public ZoneObject {
public: public:
VirtualState(Zone* zone, size_t size); VirtualState(Node* owner, Zone* zone, size_t size);
VirtualState(const VirtualState& states); VirtualState(Node* owner, const VirtualState& states);
VirtualObject* VirtualObjectFromAlias(size_t alias); VirtualObject* VirtualObjectFromAlias(size_t alias);
VirtualObject* GetOrCreateTrackedVirtualObject(EscapeAnalysis::Alias alias, VirtualObject* GetOrCreateTrackedVirtualObject(Alias alias, NodeId id,
NodeId id, size_t fields, size_t fields,
Zone* zone); bool initialized, Zone* zone,
void SetVirtualObject(EscapeAnalysis::Alias alias, VirtualObject* state); bool force_copy);
void LastChangedAt(Node* node) { last_changed_ = node; } void SetVirtualObject(Alias alias, VirtualObject* state);
Node* GetLastChanged() { return last_changed_; }
bool UpdateFrom(VirtualState* state, Zone* zone); bool UpdateFrom(VirtualState* state, Zone* zone);
bool MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, bool MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
CommonOperatorBuilder* common, Node* control); CommonOperatorBuilder* common, Node* control, int arity);
size_t size() const { return info_.size(); } size_t size() const { return info_.size(); }
Node* owner() const { return owner_; }
VirtualObject* Copy(VirtualObject* obj, Alias alias);
void SetCopyRequired() {
for (VirtualObject* obj : info_) {
if (obj) obj->SetCopyRequired();
}
}
private: private:
ZoneVector<VirtualObject*> info_; ZoneVector<VirtualObject*> info_;
Node* last_changed_; Node* owner_;
DISALLOW_COPY_AND_ASSIGN(VirtualState);
}; };
...@@ -175,9 +199,9 @@ class MergeCache : public ZoneObject { ...@@ -175,9 +199,9 @@ class MergeCache : public ZoneObject {
public: public:
explicit MergeCache(Zone* zone) explicit MergeCache(Zone* zone)
: states_(zone), objects_(zone), fields_(zone) { : states_(zone), objects_(zone), fields_(zone) {
states_.reserve(4); states_.reserve(5);
objects_.reserve(4); objects_.reserve(5);
fields_.reserve(4); fields_.reserve(5);
} }
ZoneVector<VirtualState*>& states() { return states_; } ZoneVector<VirtualState*>& states() { return states_; }
ZoneVector<VirtualObject*>& objects() { return objects_; } ZoneVector<VirtualObject*>& objects() { return objects_; }
...@@ -187,20 +211,21 @@ class MergeCache : public ZoneObject { ...@@ -187,20 +211,21 @@ class MergeCache : public ZoneObject {
objects_.clear(); objects_.clear();
fields_.clear(); fields_.clear();
} }
size_t LoadVirtualObjectsFromStatesFor(EscapeAnalysis::Alias alias); size_t LoadVirtualObjectsFromStatesFor(Alias alias);
void LoadVirtualObjectsForFieldsFrom( void LoadVirtualObjectsForFieldsFrom(VirtualState* state,
VirtualState* state, const ZoneVector<EscapeAnalysis::Alias>& aliases); const ZoneVector<Alias>& aliases);
Node* GetFields(size_t pos); Node* GetFields(size_t pos);
private: private:
ZoneVector<VirtualState*> states_; ZoneVector<VirtualState*> states_;
ZoneVector<VirtualObject*> objects_; ZoneVector<VirtualObject*> objects_;
ZoneVector<Node*> fields_; ZoneVector<Node*> fields_;
DISALLOW_COPY_AND_ASSIGN(MergeCache);
}; };
size_t MergeCache::LoadVirtualObjectsFromStatesFor( size_t MergeCache::LoadVirtualObjectsFromStatesFor(Alias alias) {
EscapeAnalysis::Alias alias) {
objects_.clear(); objects_.clear();
DCHECK_GT(states_.size(), 0u); DCHECK_GT(states_.size(), 0u);
size_t min = std::numeric_limits<size_t>::max(); size_t min = std::numeric_limits<size_t>::max();
...@@ -215,11 +240,11 @@ size_t MergeCache::LoadVirtualObjectsFromStatesFor( ...@@ -215,11 +240,11 @@ size_t MergeCache::LoadVirtualObjectsFromStatesFor(
void MergeCache::LoadVirtualObjectsForFieldsFrom( void MergeCache::LoadVirtualObjectsForFieldsFrom(
VirtualState* state, const ZoneVector<EscapeAnalysis::Alias>& aliases) { VirtualState* state, const ZoneVector<Alias>& aliases) {
objects_.clear(); objects_.clear();
size_t max_alias = state->size(); size_t max_alias = state->size();
for (Node* field : fields_) { for (Node* field : fields_) {
EscapeAnalysis::Alias alias = aliases[field->id()]; Alias alias = aliases[field->id()];
if (alias >= max_alias) continue; if (alias >= max_alias) continue;
if (VirtualObject* obj = state->VirtualObjectFromAlias(alias)) { if (VirtualObject* obj = state->VirtualObjectFromAlias(alias)) {
objects_.push_back(obj); objects_.push_back(obj);
...@@ -230,8 +255,11 @@ void MergeCache::LoadVirtualObjectsForFieldsFrom( ...@@ -230,8 +255,11 @@ void MergeCache::LoadVirtualObjectsForFieldsFrom(
Node* MergeCache::GetFields(size_t pos) { Node* MergeCache::GetFields(size_t pos) {
fields_.clear(); fields_.clear();
Node* rep = objects_.front()->GetField(pos); Node* rep = pos >= objects_.front()->field_count()
? nullptr
: objects_.front()->GetField(pos);
for (VirtualObject* obj : objects_) { for (VirtualObject* obj : objects_) {
if (pos >= obj->field_count()) continue;
Node* field = obj->GetField(pos); Node* field = obj->GetField(pos);
if (field) { if (field) {
fields_.push_back(field); fields_.push_back(field);
...@@ -244,56 +272,68 @@ Node* MergeCache::GetFields(size_t pos) { ...@@ -244,56 +272,68 @@ Node* MergeCache::GetFields(size_t pos) {
} }
VirtualState::VirtualState(Zone* zone, size_t size) VirtualState::VirtualState(Node* owner, Zone* zone, size_t size)
: info_(size, nullptr, zone), last_changed_(nullptr) {} : info_(size, nullptr, zone), owner_(owner) {}
VirtualState::VirtualState(const VirtualState& state) VirtualState::VirtualState(Node* owner, const VirtualState& state)
: info_(state.info_.size(), nullptr, state.info_.get_allocator().zone()), : info_(state.info_.size(), nullptr, state.info_.get_allocator().zone()),
last_changed_(state.last_changed_) { owner_(owner) {
for (size_t i = 0; i < state.info_.size(); ++i) { for (size_t i = 0; i < info_.size(); ++i) {
if (state.info_[i]) { if (state.info_[i]) {
info_[i] = info_[i] = state.info_[i];
new (info_.get_allocator().zone()) VirtualObject(*state.info_[i]);
} }
} }
} }
VirtualObject* VirtualState::Copy(VirtualObject* obj, Alias alias) {
if (obj->owner() == this) return obj;
VirtualObject* new_obj =
new (info_.get_allocator().zone()) VirtualObject(this, *obj);
TRACE("At state %p, alias @%d (#%d), copying virtual object from %p to %p\n",
static_cast<void*>(this), alias, obj->id(), static_cast<void*>(obj),
static_cast<void*>(new_obj));
info_[alias] = new_obj;
return new_obj;
}
VirtualObject* VirtualState::VirtualObjectFromAlias(size_t alias) { VirtualObject* VirtualState::VirtualObjectFromAlias(size_t alias) {
return info_[alias]; return info_[alias];
} }
VirtualObject* VirtualState::GetOrCreateTrackedVirtualObject( VirtualObject* VirtualState::GetOrCreateTrackedVirtualObject(
EscapeAnalysis::Alias alias, NodeId id, size_t field_number, Zone* zone) { Alias alias, NodeId id, size_t field_number, bool initialized, Zone* zone,
if (VirtualObject* obj = VirtualObjectFromAlias(alias)) { bool force_copy) {
return obj; if (!force_copy) {
if (VirtualObject* obj = VirtualObjectFromAlias(alias)) {
return obj;
}
} }
VirtualObject* obj = new (zone) VirtualObject(id, zone, 0); VirtualObject* obj = new (zone) VirtualObject(id, this, zone, 0, initialized);
SetVirtualObject(alias, obj); SetVirtualObject(alias, obj);
return obj; return obj;
} }
void VirtualState::SetVirtualObject(EscapeAnalysis::Alias alias, void VirtualState::SetVirtualObject(Alias alias, VirtualObject* obj) {
VirtualObject* obj) {
info_[alias] = obj; info_[alias] = obj;
} }
bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) { bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) {
if (from == this) return false;
bool changed = false; bool changed = false;
for (EscapeAnalysis::Alias alias = 0; alias < size(); ++alias) { for (Alias alias = 0; alias < size(); ++alias) {
VirtualObject* ls = VirtualObjectFromAlias(alias); VirtualObject* ls = VirtualObjectFromAlias(alias);
VirtualObject* rs = from->VirtualObjectFromAlias(alias); VirtualObject* rs = from->VirtualObjectFromAlias(alias);
if (rs == nullptr) { if (ls == rs || rs == nullptr) continue;
continue;
}
if (ls == nullptr) { if (ls == nullptr) {
ls = new (zone) VirtualObject(*rs); ls = new (zone) VirtualObject(this, *rs);
SetVirtualObject(alias, ls); SetVirtualObject(alias, ls);
changed = true; changed = true;
continue; continue;
...@@ -355,14 +395,30 @@ Node* EscapeAnalysis::GetReplacementIfSame(ZoneVector<VirtualObject*>& objs) { ...@@ -355,14 +395,30 @@ Node* EscapeAnalysis::GetReplacementIfSame(ZoneVector<VirtualObject*>& objs) {
bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
CommonOperatorBuilder* common, Node* control) { CommonOperatorBuilder* common, Node* control,
int arity) {
DCHECK_GT(cache->states().size(), 0u); DCHECK_GT(cache->states().size(), 0u);
bool changed = false; bool changed = false;
for (EscapeAnalysis::Alias alias = 0; alias < size(); ++alias) { for (Alias alias = 0; alias < size(); ++alias) {
size_t fields = cache->LoadVirtualObjectsFromStatesFor(alias); cache->objects().clear();
VirtualObject* mergeObject = VirtualObjectFromAlias(alias);
bool copy_merge_object = false;
size_t fields = std::numeric_limits<size_t>::max();
for (VirtualState* state : cache->states()) {
if (VirtualObject* obj = state->VirtualObjectFromAlias(alias)) {
cache->objects().push_back(obj);
if (mergeObject == obj) {
copy_merge_object = true;
changed = true;
}
fields = std::min(obj->field_count(), fields);
}
}
if (cache->objects().size() == cache->states().size()) { if (cache->objects().size() == cache->states().size()) {
VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject( mergeObject = GetOrCreateTrackedVirtualObject(
alias, cache->objects().front()->id(), fields, zone); alias, cache->objects().front()->id(),
cache->objects().front()->IsInitialized(), fields, zone,
copy_merge_object);
#ifdef DEBUG #ifdef DEBUG
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
PrintF(" Alias @%d, merging into %p virtual objects", alias, PrintF(" Alias @%d, merging into %p virtual objects", alias,
...@@ -376,11 +432,12 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, ...@@ -376,11 +432,12 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
changed = mergeObject->ResizeFields(fields) || changed; changed = mergeObject->ResizeFields(fields) || changed;
for (size_t i = 0; i < fields; ++i) { for (size_t i = 0; i < fields; ++i) {
if (Node* field = cache->GetFields(i)) { if (Node* field = cache->GetFields(i)) {
changed = mergeObject->SetField(i, field) || changed; changed = changed || mergeObject->GetField(i) != field;
mergeObject->SetField(i, field);
TRACE(" Field %zu agree on rep #%d\n", i, field->id()); TRACE(" Field %zu agree on rep #%d\n", i, field->id());
} else { } else {
int value_input_count = static_cast<int>(cache->fields().size()); int value_input_count = static_cast<int>(cache->fields().size());
if (cache->fields().size() == cache->objects().size()) { if (cache->fields().size() == arity) {
Node* rep = mergeObject->GetField(i); Node* rep = mergeObject->GetField(i);
if (!rep || !mergeObject->IsCreatedPhi(i)) { if (!rep || !mergeObject->IsCreatedPhi(i)) {
cache->fields().push_back(control); cache->fields().push_back(control);
...@@ -403,36 +460,27 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, ...@@ -403,36 +460,27 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
} else { } else {
DCHECK(rep->opcode() == IrOpcode::kPhi); DCHECK(rep->opcode() == IrOpcode::kPhi);
for (int n = 0; n < value_input_count; ++n) { for (int n = 0; n < value_input_count; ++n) {
if (n < rep->op()->ValueInputCount()) { Node* old = NodeProperties::GetValueInput(rep, n);
Node* old = NodeProperties::GetValueInput(rep, n); if (old != cache->fields()[n]) {
if (old != cache->fields()[n]) {
changed = true;
NodeProperties::ReplaceValueInput(rep, cache->fields()[n],
n);
}
} else {
changed = true; changed = true;
rep->InsertInput(graph->zone(), n, cache->fields()[n]); NodeProperties::ReplaceValueInput(rep, cache->fields()[n], n);
} }
} }
if (rep->op()->ValueInputCount() != value_input_count) {
TRACE(" Widening Phi #%d of arity %d to %d\n", rep->id(),
rep->op()->ValueInputCount(), value_input_count);
NodeProperties::ChangeOp(
rep, common->Phi(MachineRepresentation::kTagged,
value_input_count));
}
} }
} else { } else {
if (mergeObject->GetField(i) != nullptr) { if (mergeObject->GetField(i) != nullptr) {
TRACE(" Field %zu cleared\n", i); TRACE(" Field %zu cleared\n", i);
changed = true; changed = true;
} }
changed = mergeObject->SetField(i, nullptr) || changed; mergeObject->SetField(i, nullptr);
} }
} }
} }
} else { } else {
if (mergeObject) {
TRACE(" Alias %d, virtual object removed\n", alias);
changed = true;
}
SetVirtualObject(alias, nullptr); SetVirtualObject(alias, nullptr);
} }
} }
...@@ -442,11 +490,14 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, ...@@ -442,11 +490,14 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
EscapeStatusAnalysis::EscapeStatusAnalysis(EscapeAnalysis* object_analysis, EscapeStatusAnalysis::EscapeStatusAnalysis(EscapeAnalysis* object_analysis,
Graph* graph, Zone* zone) Graph* graph, Zone* zone)
: object_analysis_(object_analysis), : stack_(zone),
object_analysis_(object_analysis),
graph_(graph), graph_(graph),
zone_(zone), zone_(zone),
status_(graph->NodeCount(), kUnknown, zone), status_(graph->NodeCount(), kUnknown, zone),
queue_(zone) {} next_free_alias_(0),
status_stack_(zone),
aliases_(zone) {}
EscapeStatusAnalysis::~EscapeStatusAnalysis() {} EscapeStatusAnalysis::~EscapeStatusAnalysis() {}
...@@ -485,31 +536,33 @@ bool EscapeStatusAnalysis::SetEscaped(Node* node) { ...@@ -485,31 +536,33 @@ bool EscapeStatusAnalysis::SetEscaped(Node* node) {
} }
void EscapeStatusAnalysis::Resize() { void EscapeStatusAnalysis::ResizeStatusVector() {
status_.resize(graph()->NodeCount(), kUnknown); if (status_.size() <= graph()->NodeCount()) {
status_.resize(graph()->NodeCount() * 1.1, kUnknown);
}
} }
size_t EscapeStatusAnalysis::size() { return status_.size(); } size_t EscapeStatusAnalysis::GetStatusVectorSize() { return status_.size(); }
void EscapeStatusAnalysis::Run() { void EscapeStatusAnalysis::RunStatusAnalysis() {
Resize(); ResizeStatusVector();
queue_.push_back(graph()->end()); while (!status_stack_.empty()) {
status_[graph()->end()->id()] |= kOnStack; Node* node = status_stack_.back();
while (!queue_.empty()) { status_stack_.pop_back();
Node* node = queue_.front();
queue_.pop_front();
status_[node->id()] &= ~kOnStack; status_[node->id()] &= ~kOnStack;
Process(node); Process(node);
status_[node->id()] |= kVisited; status_[node->id()] |= kVisited;
for (Edge edge : node->input_edges()) { }
Node* input = edge.to(); }
if (!(status_[input->id()] & (kVisited | kOnStack))) {
queue_.push_back(input);
status_[input->id()] |= kOnStack; void EscapeStatusAnalysis::EnqueueForStatusAnalysis(Node* node) {
} DCHECK_NOT_NULL(node);
} if (!(status_[node->id()] & kOnStack)) {
status_stack_.push_back(node);
status_[node->id()] |= kOnStack;
} }
} }
...@@ -518,7 +571,7 @@ void EscapeStatusAnalysis::RevisitInputs(Node* node) { ...@@ -518,7 +571,7 @@ void EscapeStatusAnalysis::RevisitInputs(Node* node) {
for (Edge edge : node->input_edges()) { for (Edge edge : node->input_edges()) {
Node* input = edge.to(); Node* input = edge.to();
if (!(status_[input->id()] & kOnStack)) { if (!(status_[input->id()] & kOnStack)) {
queue_.push_back(input); status_stack_.push_back(input);
status_[input->id()] |= kOnStack; status_[input->id()] |= kOnStack;
} }
} }
...@@ -528,8 +581,8 @@ void EscapeStatusAnalysis::RevisitInputs(Node* node) { ...@@ -528,8 +581,8 @@ void EscapeStatusAnalysis::RevisitInputs(Node* node) {
void EscapeStatusAnalysis::RevisitUses(Node* node) { void EscapeStatusAnalysis::RevisitUses(Node* node) {
for (Edge edge : node->use_edges()) { for (Edge edge : node->use_edges()) {
Node* use = edge.from(); Node* use = edge.from();
if (!(status_[use->id()] & kOnStack)) { if (!(status_[use->id()] & kOnStack) && !IsNotReachable(use)) {
queue_.push_back(use); status_stack_.push_back(use);
status_[use->id()] |= kOnStack; status_[use->id()] |= kOnStack;
} }
} }
...@@ -558,15 +611,17 @@ void EscapeStatusAnalysis::Process(Node* node) { ...@@ -558,15 +611,17 @@ void EscapeStatusAnalysis::Process(Node* node) {
RevisitUses(rep); RevisitUses(rep);
} }
} }
RevisitUses(node);
break; break;
} }
case IrOpcode::kPhi: case IrOpcode::kPhi:
if (!HasEntry(node)) { if (!HasEntry(node)) {
status_[node->id()] |= kTracked; status_[node->id()] |= kTracked;
if (!IsAllocationPhi(node)) { RevisitUses(node);
SetEscaped(node); }
RevisitUses(node); if (!IsAllocationPhi(node) && SetEscaped(node)) {
} RevisitInputs(node);
RevisitUses(node);
} }
CheckUsesForEscape(node); CheckUsesForEscape(node);
default: default:
...@@ -623,10 +678,11 @@ void EscapeStatusAnalysis::ProcessAllocate(Node* node) { ...@@ -623,10 +678,11 @@ void EscapeStatusAnalysis::ProcessAllocate(Node* node) {
node->InputAt(0)->opcode() != IrOpcode::kInt64Constant && node->InputAt(0)->opcode() != IrOpcode::kInt64Constant &&
node->InputAt(0)->opcode() != IrOpcode::kFloat32Constant && node->InputAt(0)->opcode() != IrOpcode::kFloat32Constant &&
node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant); node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant);
RevisitUses(node);
if (!size.HasValue() && SetEscaped(node)) { if (!size.HasValue() && SetEscaped(node)) {
RevisitUses(node);
TRACE("Setting #%d to escaped because of non-const alloc\n", node->id()); TRACE("Setting #%d to escaped because of non-const alloc\n", node->id());
// This node is known to escape, uses do not have to be checked. // This node is already known to escape, uses do not have to be checked
// for escape.
return; return;
} }
} }
...@@ -640,6 +696,7 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep, ...@@ -640,6 +696,7 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep,
bool phi_escaping) { bool phi_escaping) {
for (Edge edge : uses->use_edges()) { for (Edge edge : uses->use_edges()) {
Node* use = edge.from(); Node* use = edge.from();
if (IsNotReachable(use)) continue;
if (edge.index() >= use->op()->ValueInputCount() + if (edge.index() >= use->op()->ValueInputCount() +
OperatorProperties::GetContextInputCount(use->op())) OperatorProperties::GetContextInputCount(use->op()))
continue; continue;
...@@ -730,15 +787,11 @@ void EscapeStatusAnalysis::DebugPrint() { ...@@ -730,15 +787,11 @@ void EscapeStatusAnalysis::DebugPrint() {
EscapeAnalysis::EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common, EscapeAnalysis::EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common,
Zone* zone) Zone* zone)
: graph_(graph), : status_analysis_(this, graph, zone),
common_(common), common_(common),
zone_(zone),
virtual_states_(zone), virtual_states_(zone),
replacements_(zone), replacements_(zone),
escape_status_(this, graph, zone), cache_(new (zone) MergeCache(zone)) {}
cache_(new (zone) MergeCache(zone)),
aliases_(zone),
next_free_alias_(0) {}
EscapeAnalysis::~EscapeAnalysis() {} EscapeAnalysis::~EscapeAnalysis() {}
...@@ -746,42 +799,47 @@ EscapeAnalysis::~EscapeAnalysis() {} ...@@ -746,42 +799,47 @@ EscapeAnalysis::~EscapeAnalysis() {}
void EscapeAnalysis::Run() { void EscapeAnalysis::Run() {
replacements_.resize(graph()->NodeCount()); replacements_.resize(graph()->NodeCount());
AssignAliases(); status_analysis_.AssignAliases();
if (AliasCount() == 0) return; if (status_analysis_.AliasCount() == 0) return;
escape_status_.Resize(); status_analysis_.ResizeStatusVector();
RunObjectAnalysis(); RunObjectAnalysis();
escape_status_.Run(); status_analysis_.RunStatusAnalysis();
} }
void EscapeAnalysis::AssignAliases() { void EscapeStatusAnalysis::AssignAliases() {
ZoneVector<Node*> stack(zone()); stack_.reserve(graph()->NodeCount() * 0.2);
stack.push_back(graph()->end()); ResizeStatusVector();
stack_.push_back(graph()->end());
CHECK_LT(graph()->NodeCount(), kUntrackable); CHECK_LT(graph()->NodeCount(), kUntrackable);
aliases_.resize(graph()->NodeCount(), kNotReachable); aliases_.resize(graph()->NodeCount(), kNotReachable);
aliases_[graph()->end()->id()] = kUntrackable; aliases_[graph()->end()->id()] = kUntrackable;
status_stack_.reserve(8);
TRACE("Discovering trackable nodes"); TRACE("Discovering trackable nodes");
while (!stack.empty()) { while (!stack_.empty()) {
Node* node = stack.back(); Node* node = stack_.back();
stack.pop_back(); stack_.pop_back();
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kAllocate: case IrOpcode::kAllocate:
if (aliases_[node->id()] >= kUntrackable) { if (aliases_[node->id()] >= kUntrackable) {
aliases_[node->id()] = NextAlias(); aliases_[node->id()] = NextAlias();
TRACE(" @%d:%s#%u", aliases_[node->id()], node->op()->mnemonic(), TRACE(" @%d:%s#%u", aliases_[node->id()], node->op()->mnemonic(),
node->id()); node->id());
EnqueueForStatusAnalysis(node);
} }
break; break;
case IrOpcode::kFinishRegion: { case IrOpcode::kFinishRegion: {
Node* allocate = NodeProperties::GetValueInput(node, 0); Node* allocate = NodeProperties::GetValueInput(node, 0);
DCHECK_NOT_NULL(allocate);
if (allocate->opcode() == IrOpcode::kAllocate) { if (allocate->opcode() == IrOpcode::kAllocate) {
if (aliases_[allocate->id()] >= kUntrackable) { if (aliases_[allocate->id()] >= kUntrackable) {
if (aliases_[allocate->id()] == kNotReachable) { if (aliases_[allocate->id()] == kNotReachable) {
stack.push_back(allocate); stack_.push_back(allocate);
} }
aliases_[allocate->id()] = NextAlias(); aliases_[allocate->id()] = NextAlias();
TRACE(" @%d:%s#%u", aliases_[allocate->id()], TRACE(" @%d:%s#%u", aliases_[allocate->id()],
allocate->op()->mnemonic(), allocate->id()); allocate->op()->mnemonic(), allocate->id());
EnqueueForStatusAnalysis(allocate);
} }
aliases_[node->id()] = aliases_[allocate->id()]; aliases_[node->id()] = aliases_[allocate->id()];
TRACE(" @%d:%s#%u", aliases_[node->id()], node->op()->mnemonic(), TRACE(" @%d:%s#%u", aliases_[node->id()], node->op()->mnemonic(),
...@@ -801,7 +859,7 @@ void EscapeAnalysis::AssignAliases() { ...@@ -801,7 +859,7 @@ void EscapeAnalysis::AssignAliases() {
for (Edge edge : node->input_edges()) { for (Edge edge : node->input_edges()) {
Node* input = edge.to(); Node* input = edge.to();
if (aliases_[input->id()] == kNotReachable) { if (aliases_[input->id()] == kNotReachable) {
stack.push_back(input); stack_.push_back(input);
aliases_[input->id()] = kUntrackable; aliases_[input->id()] = kUntrackable;
} }
} }
...@@ -810,32 +868,45 @@ void EscapeAnalysis::AssignAliases() { ...@@ -810,32 +868,45 @@ void EscapeAnalysis::AssignAliases() {
} }
bool EscapeStatusAnalysis::IsNotReachable(Node* node) {
if (node->id() >= aliases_.size()) {
return false;
}
return aliases_[node->id()] == kNotReachable;
}
void EscapeAnalysis::RunObjectAnalysis() { void EscapeAnalysis::RunObjectAnalysis() {
virtual_states_.resize(graph()->NodeCount()); virtual_states_.resize(graph()->NodeCount());
ZoneVector<Node*> stack(zone()); stack().push_back(graph()->start());
stack.push_back(graph()->start()); while (!stack().empty()) {
while (!stack.empty()) { Node* node = stack().back();
Node* node = stack.back(); stack().pop_back();
stack.pop_back(); if (Process(node)) {
if (aliases_[node->id()] != kNotReachable && Process(node)) {
for (Edge edge : node->use_edges()) { for (Edge edge : node->use_edges()) {
Node* use = edge.from();
if (IsNotReachable(use)) {
continue;
}
if (NodeProperties::IsEffectEdge(edge)) { if (NodeProperties::IsEffectEdge(edge)) {
Node* use = edge.from();
if ((use->opcode() != IrOpcode::kLoadField && if ((use->opcode() != IrOpcode::kLoadField &&
use->opcode() != IrOpcode::kLoadElement) || use->opcode() != IrOpcode::kLoadElement) ||
!IsDanglingEffectNode(use)) { !IsDanglingEffectNode(use)) {
stack.push_back(use); stack().push_back(use);
} }
} }
} }
// First process loads: dangling loads are a problem otherwise. // First process loads: dangling loads are a problem otherwise.
for (Edge edge : node->use_edges()) { for (Edge edge : node->use_edges()) {
Node* use = edge.from();
if (IsNotReachable(use)) {
continue;
}
if (NodeProperties::IsEffectEdge(edge)) { if (NodeProperties::IsEffectEdge(edge)) {
Node* use = edge.from();
if ((use->opcode() == IrOpcode::kLoadField || if ((use->opcode() == IrOpcode::kLoadField ||
use->opcode() == IrOpcode::kLoadElement) && use->opcode() == IrOpcode::kLoadElement) &&
IsDanglingEffectNode(use)) { IsDanglingEffectNode(use)) {
stack.push_back(use); stack().push_back(use);
} }
} }
} }
...@@ -849,7 +920,10 @@ void EscapeAnalysis::RunObjectAnalysis() { ...@@ -849,7 +920,10 @@ void EscapeAnalysis::RunObjectAnalysis() {
} }
bool EscapeAnalysis::IsDanglingEffectNode(Node* node) { bool EscapeStatusAnalysis::IsDanglingEffectNode(Node* node) {
if (status_[node->id()] & kDanglingComputed) {
return status_[node->id()] & kDangling;
}
if (node->op()->EffectInputCount() == 0) return false; if (node->op()->EffectInputCount() == 0) return false;
if (node->op()->EffectOutputCount() == 0) return false; if (node->op()->EffectOutputCount() == 0) return false;
if (node->op()->EffectInputCount() == 1 && if (node->op()->EffectInputCount() == 1 &&
...@@ -857,17 +931,47 @@ bool EscapeAnalysis::IsDanglingEffectNode(Node* node) { ...@@ -857,17 +931,47 @@ bool EscapeAnalysis::IsDanglingEffectNode(Node* node) {
// The start node is used as sentinel for nodes that are in general // The start node is used as sentinel for nodes that are in general
// effectful, but of which an analysis has determined that they do not // effectful, but of which an analysis has determined that they do not
// produce effects in this instance. We don't consider these nodes dangling. // produce effects in this instance. We don't consider these nodes dangling.
status_[node->id()] |= kDanglingComputed;
return false; return false;
} }
for (Edge edge : node->use_edges()) { for (Edge edge : node->use_edges()) {
Node* use = edge.from();
if (aliases_[use->id()] == kNotReachable) continue;
if (NodeProperties::IsEffectEdge(edge)) { if (NodeProperties::IsEffectEdge(edge)) {
status_[node->id()] |= kDanglingComputed;
return false; return false;
} }
} }
status_[node->id()] |= kDanglingComputed | kDangling;
return true; return true;
} }
bool EscapeStatusAnalysis::IsEffectBranchPoint(Node* node) {
if (status_[node->id()] & kBranchPointComputed) {
return status_[node->id()] & kBranchPoint;
}
int count = 0;
for (Edge edge : node->use_edges()) {
Node* use = edge.from();
if (aliases_[use->id()] == kNotReachable) continue;
if (NodeProperties::IsEffectEdge(edge)) {
if ((node->opcode() == IrOpcode::kLoadField ||
node->opcode() == IrOpcode::kLoadElement ||
node->opcode() == IrOpcode::kLoad) &&
IsDanglingEffectNode(node))
continue;
if (++count > 1) {
status_[node->id()] |= kBranchPointComputed | kBranchPoint;
return true;
}
}
}
status_[node->id()] |= kBranchPointComputed;
return false;
}
bool EscapeAnalysis::Process(Node* node) { bool EscapeAnalysis::Process(Node* node) {
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kAllocate: case IrOpcode::kAllocate:
...@@ -911,8 +1015,9 @@ bool EscapeAnalysis::Process(Node* node) { ...@@ -911,8 +1015,9 @@ bool EscapeAnalysis::Process(Node* node) {
void EscapeAnalysis::ProcessAllocationUsers(Node* node) { void EscapeAnalysis::ProcessAllocationUsers(Node* node) {
for (Edge edge : node->input_edges()) { for (Edge edge : node->input_edges()) {
Node* input = edge.to(); Node* input = edge.to();
if (!NodeProperties::IsValueEdge(edge) && Node* use = edge.from();
!NodeProperties::IsContextEdge(edge)) if (edge.index() >= use->op()->ValueInputCount() +
OperatorProperties::GetContextInputCount(use->op()))
continue; continue;
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kStoreField: case IrOpcode::kStoreField:
...@@ -928,8 +1033,11 @@ void EscapeAnalysis::ProcessAllocationUsers(Node* node) { ...@@ -928,8 +1033,11 @@ void EscapeAnalysis::ProcessAllocationUsers(Node* node) {
default: default:
VirtualState* state = virtual_states_[node->id()]; VirtualState* state = virtual_states_[node->id()];
if (VirtualObject* obj = ResolveVirtualObject(state, input)) { if (VirtualObject* obj = ResolveVirtualObject(state, input)) {
if (obj->ClearAllFields()) { if (!obj->AllFieldsClear()) {
state->LastChangedAt(node); obj = CopyForModificationAt(obj, state, node);
obj->ClearAllFields();
TRACE("Cleared all fields of @%d:#%d\n", GetAlias(obj->id()),
obj->id());
} }
} }
break; break;
...@@ -938,21 +1046,34 @@ void EscapeAnalysis::ProcessAllocationUsers(Node* node) { ...@@ -938,21 +1046,34 @@ void EscapeAnalysis::ProcessAllocationUsers(Node* node) {
} }
bool EscapeAnalysis::IsEffectBranchPoint(Node* node) { VirtualState* EscapeAnalysis::CopyForModificationAt(VirtualState* state,
int count = 0; Node* node) {
for (Edge edge : node->use_edges()) { if (state->owner() != node) {
if (NodeProperties::IsEffectEdge(edge)) { VirtualState* new_state = new (zone()) VirtualState(node, *state);
if (++count > 1) { virtual_states_[node->id()] = new_state;
return true; TRACE("Copying virtual state %p to new state %p at node %s#%d\n",
} static_cast<void*>(state), static_cast<void*>(new_state),
} node->op()->mnemonic(), node->id());
return new_state;
} }
return false; return state;
}
VirtualObject* EscapeAnalysis::CopyForModificationAt(VirtualObject* obj,
VirtualState* state,
Node* node) {
if (obj->NeedCopyForModification()) {
state = CopyForModificationAt(state, node);
return state->Copy(obj, GetAlias(obj->id()));
}
return obj;
} }
void EscapeAnalysis::ForwardVirtualState(Node* node) { void EscapeAnalysis::ForwardVirtualState(Node* node) {
DCHECK_EQ(node->op()->EffectInputCount(), 1); DCHECK_EQ(node->op()->EffectInputCount(), 1);
#ifdef DEBUG
if (node->opcode() != IrOpcode::kLoadField && if (node->opcode() != IrOpcode::kLoadField &&
node->opcode() != IrOpcode::kLoadElement && node->opcode() != IrOpcode::kLoadElement &&
node->opcode() != IrOpcode::kLoad && IsDanglingEffectNode(node)) { node->opcode() != IrOpcode::kLoad && IsDanglingEffectNode(node)) {
...@@ -960,38 +1081,42 @@ void EscapeAnalysis::ForwardVirtualState(Node* node) { ...@@ -960,38 +1081,42 @@ void EscapeAnalysis::ForwardVirtualState(Node* node) {
node->op()->mnemonic()); node->op()->mnemonic());
UNREACHABLE(); UNREACHABLE();
} }
#endif // DEBUG
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
// Break the cycle for effect phis. // Break the cycle for effect phis.
if (effect->opcode() == IrOpcode::kEffectPhi) { if (effect->opcode() == IrOpcode::kEffectPhi &&
if (virtual_states_[effect->id()] == nullptr) { virtual_states_[effect->id()] == nullptr) {
virtual_states_[effect->id()] = VirtualState* state =
new (zone()) VirtualState(zone(), AliasCount()); new (zone()) VirtualState(effect, zone(), AliasCount());
} virtual_states_[effect->id()] = state;
TRACE("Effect Phi #%d got new virtual state %p.\n", effect->id(),
static_cast<void*>(virtual_states_[effect->id()]));
} }
DCHECK_NOT_NULL(virtual_states_[effect->id()]); DCHECK_NOT_NULL(virtual_states_[effect->id()]);
if (IsEffectBranchPoint(effect)) { if (virtual_states_[node->id()]) {
TRACE("Copying virtual state %p from #%d (%s) to #%d (%s)\n", virtual_states_[node->id()]->UpdateFrom(virtual_states_[effect->id()],
static_cast<void*>(virtual_states_[effect->id()]), effect->id(), zone());
effect->op()->mnemonic(), node->id(), node->op()->mnemonic());
if (!virtual_states_[node->id()]) {
virtual_states_[node->id()] =
new (zone()) VirtualState(*virtual_states_[effect->id()]);
} else {
virtual_states_[node->id()]->UpdateFrom(virtual_states_[effect->id()],
zone());
}
} else { } else {
virtual_states_[node->id()] = virtual_states_[effect->id()]; virtual_states_[node->id()] = virtual_states_[effect->id()];
TRACE("Forwarding virtual state %p from #%d (%s) to #%d (%s)\n", TRACE("Forwarding object state %p from %s#%d to %s#%d",
static_cast<void*>(virtual_states_[effect->id()]), effect->id(), static_cast<void*>(virtual_states_[effect->id()]),
effect->op()->mnemonic(), node->id(), node->op()->mnemonic()); effect->op()->mnemonic(), effect->id(), node->op()->mnemonic(),
node->id());
if (IsEffectBranchPoint(effect) ||
OperatorProperties::GetFrameStateInputCount(node->op()) > 0) {
virtual_states_[node->id()]->SetCopyRequired();
TRACE(", effect input %s#%d is branch point", effect->op()->mnemonic(),
effect->id());
}
TRACE("\n");
} }
} }
void EscapeAnalysis::ProcessStart(Node* node) { void EscapeAnalysis::ProcessStart(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kStart); DCHECK_EQ(node->opcode(), IrOpcode::kStart);
virtual_states_[node->id()] = new (zone()) VirtualState(zone(), AliasCount()); virtual_states_[node->id()] =
new (zone()) VirtualState(node, zone(), AliasCount());
} }
...@@ -1001,13 +1126,11 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) { ...@@ -1001,13 +1126,11 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) {
VirtualState* mergeState = virtual_states_[node->id()]; VirtualState* mergeState = virtual_states_[node->id()];
if (!mergeState) { if (!mergeState) {
mergeState = new (zone()) VirtualState(zone(), AliasCount()); mergeState = new (zone()) VirtualState(node, zone(), AliasCount());
virtual_states_[node->id()] = mergeState; virtual_states_[node->id()] = mergeState;
changed = true; changed = true;
TRACE("Effect Phi #%d got new virtual state %p.\n", node->id(), TRACE("Effect Phi #%d got new virtual state %p.\n", node->id(),
static_cast<void*>(mergeState)); static_cast<void*>(mergeState));
} else if (mergeState->GetLastChanged() != node) {
changed = true;
} }
cache_->Clear(); cache_->Clear();
...@@ -1020,6 +1143,11 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) { ...@@ -1020,6 +1143,11 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) {
VirtualState* state = virtual_states_[input->id()]; VirtualState* state = virtual_states_[input->id()];
if (state) { if (state) {
cache_->states().push_back(state); cache_->states().push_back(state);
if (state == mergeState) {
mergeState = new (zone()) VirtualState(node, zone(), AliasCount());
virtual_states_[node->id()] = mergeState;
changed = true;
}
} }
TRACE(" %p (from %d %s)", static_cast<void*>(state), input->id(), TRACE(" %p (from %d %s)", static_cast<void*>(state), input->id(),
input->op()->mnemonic()); input->op()->mnemonic());
...@@ -1031,14 +1159,14 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) { ...@@ -1031,14 +1159,14 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) {
} }
changed = mergeState->MergeFrom(cache_, zone(), graph(), common(), changed = mergeState->MergeFrom(cache_, zone(), graph(), common(),
NodeProperties::GetControlInput(node)) || NodeProperties::GetControlInput(node),
node->op()->EffectInputCount()) ||
changed; changed;
TRACE("Merge %s the node.\n", changed ? "changed" : "did not change"); TRACE("Merge %s the node.\n", changed ? "changed" : "did not change");
if (changed) { if (changed) {
mergeState->LastChangedAt(node); status_analysis_.ResizeStatusVector();
escape_status_.Resize();
} }
return changed; return changed;
} }
...@@ -1048,13 +1176,15 @@ void EscapeAnalysis::ProcessAllocation(Node* node) { ...@@ -1048,13 +1176,15 @@ void EscapeAnalysis::ProcessAllocation(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kAllocate); DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
ForwardVirtualState(node); ForwardVirtualState(node);
VirtualState* state = virtual_states_[node->id()]; VirtualState* state = virtual_states_[node->id()];
Alias alias = aliases_[node->id()]; Alias alias = GetAlias(node->id());
// Check if we have already processed this node. // Check if we have already processed this node.
if (state->VirtualObjectFromAlias(alias)) { if (state->VirtualObjectFromAlias(alias)) {
return; return;
} }
state = CopyForModificationAt(state, node);
NumberMatcher size(node->InputAt(0)); NumberMatcher size(node->InputAt(0));
DCHECK(node->InputAt(0)->opcode() != IrOpcode::kInt32Constant && DCHECK(node->InputAt(0)->opcode() != IrOpcode::kInt32Constant &&
node->InputAt(0)->opcode() != IrOpcode::kInt64Constant && node->InputAt(0)->opcode() != IrOpcode::kInt64Constant &&
...@@ -1062,13 +1192,12 @@ void EscapeAnalysis::ProcessAllocation(Node* node) { ...@@ -1062,13 +1192,12 @@ void EscapeAnalysis::ProcessAllocation(Node* node) {
node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant); node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant);
if (size.HasValue()) { if (size.HasValue()) {
state->SetVirtualObject( state->SetVirtualObject(
alias, new (zone()) VirtualObject(node->id(), zone(), alias, new (zone()) VirtualObject(node->id(), state, zone(),
size.Value() / kPointerSize)); size.Value() / kPointerSize, false));
} else { } else {
state->SetVirtualObject(alias, state->SetVirtualObject(
new (zone()) VirtualObject(node->id(), zone())); alias, new (zone()) VirtualObject(node->id(), state, zone()));
} }
state->LastChangedAt(node);
} }
...@@ -1078,15 +1207,9 @@ void EscapeAnalysis::ProcessFinishRegion(Node* node) { ...@@ -1078,15 +1207,9 @@ void EscapeAnalysis::ProcessFinishRegion(Node* node) {
Node* allocation = NodeProperties::GetValueInput(node, 0); Node* allocation = NodeProperties::GetValueInput(node, 0);
if (allocation->opcode() == IrOpcode::kAllocate) { if (allocation->opcode() == IrOpcode::kAllocate) {
VirtualState* state = virtual_states_[node->id()]; VirtualState* state = virtual_states_[node->id()];
if (!state->VirtualObjectFromAlias(aliases_[node->id()])) { VirtualObject* obj = state->VirtualObjectFromAlias(GetAlias(node->id()));
VirtualObject* vobj_alloc = DCHECK_NOT_NULL(obj);
state->VirtualObjectFromAlias(aliases_[allocation->id()]); obj->SetInitialized();
DCHECK_NOT_NULL(vobj_alloc);
state->SetVirtualObject(aliases_[node->id()], vobj_alloc);
TRACE("Linked finish region node #%d to node #%d\n", node->id(),
allocation->id());
state->LastChangedAt(node);
}
} }
} }
...@@ -1112,7 +1235,6 @@ bool EscapeAnalysis::SetReplacement(Node* node, Node* rep) { ...@@ -1112,7 +1235,6 @@ bool EscapeAnalysis::SetReplacement(Node* node, Node* rep) {
bool EscapeAnalysis::UpdateReplacement(VirtualState* state, Node* node, bool EscapeAnalysis::UpdateReplacement(VirtualState* state, Node* node,
Node* rep) { Node* rep) {
if (SetReplacement(node, rep)) { if (SetReplacement(node, rep)) {
state->LastChangedAt(node);
if (rep) { if (rep) {
TRACE("Replacement of #%d is #%d (%s)\n", node->id(), rep->id(), TRACE("Replacement of #%d is #%d (%s)\n", node->id(), rep->id(),
rep->op()->mnemonic()); rep->op()->mnemonic());
...@@ -1149,29 +1271,29 @@ Node* EscapeAnalysis::GetReplacement(NodeId id) { ...@@ -1149,29 +1271,29 @@ Node* EscapeAnalysis::GetReplacement(NodeId id) {
bool EscapeAnalysis::IsVirtual(Node* node) { bool EscapeAnalysis::IsVirtual(Node* node) {
if (node->id() >= escape_status_.size()) { if (node->id() >= status_analysis_.GetStatusVectorSize()) {
return false; return false;
} }
return escape_status_.IsVirtual(node); return status_analysis_.IsVirtual(node);
} }
bool EscapeAnalysis::IsEscaped(Node* node) { bool EscapeAnalysis::IsEscaped(Node* node) {
if (node->id() >= escape_status_.size()) { if (node->id() >= status_analysis_.GetStatusVectorSize()) {
return false; return false;
} }
return escape_status_.IsEscaped(node); return status_analysis_.IsEscaped(node);
} }
bool EscapeAnalysis::SetEscaped(Node* node) { bool EscapeAnalysis::SetEscaped(Node* node) {
return escape_status_.SetEscaped(node); return status_analysis_.SetEscaped(node);
} }
VirtualObject* EscapeAnalysis::GetVirtualObject(Node* at, NodeId id) { VirtualObject* EscapeAnalysis::GetVirtualObject(Node* at, NodeId id) {
if (VirtualState* states = virtual_states_[at->id()]) { if (VirtualState* states = virtual_states_[at->id()]) {
return states->VirtualObjectFromAlias(aliases_[id]); return states->VirtualObjectFromAlias(GetAlias(id));
} }
return nullptr; return nullptr;
} }
...@@ -1210,7 +1332,8 @@ void EscapeAnalysis::ProcessLoadFromPhi(int offset, Node* from, Node* node, ...@@ -1210,7 +1332,8 @@ void EscapeAnalysis::ProcessLoadFromPhi(int offset, Node* from, Node* node,
cache_->fields().push_back(input); cache_->fields().push_back(input);
} }
cache_->LoadVirtualObjectsForFieldsFrom(state, aliases_); cache_->LoadVirtualObjectsForFieldsFrom(state,
status_analysis_.GetAliasMap());
if (cache_->objects().size() == cache_->fields().size()) { if (cache_->objects().size() == cache_->fields().size()) {
cache_->GetFields(offset); cache_->GetFields(offset);
if (cache_->fields().size() == cache_->objects().size()) { if (cache_->fields().size() == cache_->objects().size()) {
...@@ -1221,9 +1344,8 @@ void EscapeAnalysis::ProcessLoadFromPhi(int offset, Node* from, Node* node, ...@@ -1221,9 +1344,8 @@ void EscapeAnalysis::ProcessLoadFromPhi(int offset, Node* from, Node* node,
Node* phi = graph()->NewNode( Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, value_input_count), common()->Phi(MachineRepresentation::kTagged, value_input_count),
value_input_count + 1, &cache_->fields().front()); value_input_count + 1, &cache_->fields().front());
escape_status_.Resize(); status_analysis_.ResizeStatusVector();
SetReplacement(node, phi); SetReplacement(node, phi);
state->LastChangedAt(node);
TRACE(" got phi created.\n"); TRACE(" got phi created.\n");
} else { } else {
TRACE(" has already phi #%d.\n", rep->id()); TRACE(" has already phi #%d.\n", rep->id());
...@@ -1244,7 +1366,10 @@ void EscapeAnalysis::ProcessLoadField(Node* node) { ...@@ -1244,7 +1366,10 @@ void EscapeAnalysis::ProcessLoadField(Node* node) {
VirtualState* state = virtual_states_[node->id()]; VirtualState* state = virtual_states_[node->id()];
if (VirtualObject* object = GetVirtualObject(state, from)) { if (VirtualObject* object = GetVirtualObject(state, from)) {
int offset = OffsetFromAccess(node); int offset = OffsetFromAccess(node);
if (!object->IsTracked()) return; if (!object->IsTracked() ||
static_cast<size_t>(offset) >= object->field_count()) {
return;
}
Node* value = object->GetField(offset); Node* value = object->GetField(offset);
if (value) { if (value) {
value = ResolveReplacement(value); value = ResolveReplacement(value);
...@@ -1253,9 +1378,9 @@ void EscapeAnalysis::ProcessLoadField(Node* node) { ...@@ -1253,9 +1378,9 @@ void EscapeAnalysis::ProcessLoadField(Node* node) {
UpdateReplacement(state, node, value); UpdateReplacement(state, node, value);
} else if (from->opcode() == IrOpcode::kPhi && } else if (from->opcode() == IrOpcode::kPhi &&
OpParameter<FieldAccess>(node).offset % kPointerSize == 0) { OpParameter<FieldAccess>(node).offset % kPointerSize == 0) {
int offset = OffsetFromAccess(node); int offset = OffsetFromAccess(node);
// Only binary phis are supported for now. // Only binary phis are supported for now.
ProcessLoadFromPhi(offset, from, node, state); ProcessLoadFromPhi(offset, from, node, state);
} else { } else {
UpdateReplacement(state, node, nullptr); UpdateReplacement(state, node, nullptr);
} }
...@@ -1281,7 +1406,11 @@ void EscapeAnalysis::ProcessLoadElement(Node* node) { ...@@ -1281,7 +1406,11 @@ void EscapeAnalysis::ProcessLoadElement(Node* node) {
kPointerSizeLog2); kPointerSizeLog2);
CHECK_EQ(access.header_size % kPointerSize, 0); CHECK_EQ(access.header_size % kPointerSize, 0);
if (!object->IsTracked()) return; if (!object->IsTracked() ||
static_cast<size_t>(offset) >= object->field_count()) {
return;
}
Node* value = object->GetField(offset); Node* value = object->GetField(offset);
if (value) { if (value) {
value = ResolveReplacement(value); value = ResolveReplacement(value);
...@@ -1314,11 +1443,13 @@ void EscapeAnalysis::ProcessStoreField(Node* node) { ...@@ -1314,11 +1443,13 @@ void EscapeAnalysis::ProcessStoreField(Node* node) {
Node* to = ResolveReplacement(NodeProperties::GetValueInput(node, 0)); Node* to = ResolveReplacement(NodeProperties::GetValueInput(node, 0));
VirtualState* state = virtual_states_[node->id()]; VirtualState* state = virtual_states_[node->id()];
VirtualObject* obj = GetVirtualObject(state, to); VirtualObject* obj = GetVirtualObject(state, to);
if (obj && obj->IsTracked()) { int offset = OffsetFromAccess(node);
int offset = OffsetFromAccess(node); if (obj && obj->IsTracked() &&
static_cast<size_t>(offset) < obj->field_count()) {
Node* val = ResolveReplacement(NodeProperties::GetValueInput(node, 1)); Node* val = ResolveReplacement(NodeProperties::GetValueInput(node, 1));
if (obj->SetField(offset, val)) { if (obj->GetField(offset) != val) {
state->LastChangedAt(node); obj = CopyForModificationAt(obj, state, node);
obj->SetField(offset, val);
} }
} }
} }
...@@ -1339,13 +1470,15 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) { ...@@ -1339,13 +1470,15 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) {
VirtualObject* obj = GetVirtualObject(state, to); VirtualObject* obj = GetVirtualObject(state, to);
if (index.HasValue()) { if (index.HasValue()) {
int offset = index.Value() + access.header_size / kPointerSize; int offset = index.Value() + access.header_size / kPointerSize;
if (obj && obj->IsTracked()) { if (obj && obj->IsTracked() &&
static_cast<size_t>(offset) < obj->field_count()) {
CHECK_GE(ElementSizeLog2Of(access.machine_type.representation()), CHECK_GE(ElementSizeLog2Of(access.machine_type.representation()),
kPointerSizeLog2); kPointerSizeLog2);
CHECK_EQ(access.header_size % kPointerSize, 0); CHECK_EQ(access.header_size % kPointerSize, 0);
Node* val = ResolveReplacement(NodeProperties::GetValueInput(node, 2)); Node* val = ResolveReplacement(NodeProperties::GetValueInput(node, 2));
if (obj->SetField(offset, val)) { if (obj->GetField(offset) != val) {
state->LastChangedAt(node); obj = CopyForModificationAt(obj, state, node);
obj->SetField(offset, val);
} }
} }
} else { } else {
...@@ -1357,9 +1490,13 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) { ...@@ -1357,9 +1490,13 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) {
to->id(), to->op()->mnemonic(), node->id(), index_node->id(), to->id(), to->op()->mnemonic(), node->id(), index_node->id(),
index_node->op()->mnemonic()); index_node->op()->mnemonic());
} }
if (obj && obj->IsTracked() && obj->ClearAllFields()) { if (obj && obj->IsTracked()) {
state->LastChangedAt(node); if (!obj->AllFieldsClear()) {
TRACE("Cleared all fields of @%d:#%d\n", aliases_[obj->id()], obj->id()); obj = CopyForModificationAt(obj, state, node);
obj->ClearAllFields();
TRACE("Cleared all fields of @%d:#%d\n", GetAlias(obj->id()),
obj->id());
}
} }
} }
} }
...@@ -1447,18 +1584,18 @@ void EscapeAnalysis::DebugPrint() { ...@@ -1447,18 +1584,18 @@ void EscapeAnalysis::DebugPrint() {
VirtualObject* EscapeAnalysis::GetVirtualObject(VirtualState* state, VirtualObject* EscapeAnalysis::GetVirtualObject(VirtualState* state,
Node* node) { Node* node) {
if (node->id() >= aliases_.size()) return nullptr; if (node->id() >= status_analysis_.GetAliasMap().size()) return nullptr;
Alias alias = aliases_[node->id()]; Alias alias = GetAlias(node->id());
if (alias >= state->size()) return nullptr; if (alias >= state->size()) return nullptr;
return state->VirtualObjectFromAlias(alias); return state->VirtualObjectFromAlias(alias);
} }
bool EscapeAnalysis::ExistsVirtualAllocate() { bool EscapeAnalysis::ExistsVirtualAllocate() {
for (size_t id = 0; id < aliases_.size(); ++id) { for (size_t id = 0; id < status_analysis_.GetAliasMap().size(); ++id) {
Alias alias = aliases_[id]; Alias alias = GetAlias(static_cast<NodeId>(id));
if (alias < kUntrackable) { if (alias < EscapeStatusAnalysis::kUntrackable) {
if (escape_status_.IsVirtual(static_cast<int>(id))) { if (status_analysis_.IsVirtual(static_cast<int>(id))) {
return true; return true;
} }
} }
......
...@@ -22,30 +22,56 @@ class VirtualObject; ...@@ -22,30 +22,56 @@ class VirtualObject;
// EscapeStatusAnalysis determines for each allocation whether it escapes. // EscapeStatusAnalysis determines for each allocation whether it escapes.
class EscapeStatusAnalysis { class EscapeStatusAnalysis {
public: public:
typedef NodeId Alias;
~EscapeStatusAnalysis(); ~EscapeStatusAnalysis();
enum EscapeStatusFlag { enum Status {
kUnknown = 0u, kUnknown = 0u,
kTracked = 1u << 0, kTracked = 1u << 0,
kEscaped = 1u << 1, kEscaped = 1u << 1,
kOnStack = 1u << 2, kOnStack = 1u << 2,
kVisited = 1u << 3, 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 IsVirtual(Node* node);
bool IsEscaped(Node* node); bool IsEscaped(Node* node);
bool IsAllocation(Node* node); bool IsAllocation(Node* node);
void DebugPrint(); void DebugPrint();
friend class EscapeAnalysis;
private:
EscapeStatusAnalysis(EscapeAnalysis* object_analysis, Graph* graph, EscapeStatusAnalysis(EscapeAnalysis* object_analysis, Graph* graph,
Zone* zone); 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 Process(Node* node);
void ProcessAllocate(Node* node); void ProcessAllocate(Node* node);
void ProcessFinishRegion(Node* node); void ProcessFinishRegion(Node* node);
...@@ -57,27 +83,27 @@ class EscapeStatusAnalysis { ...@@ -57,27 +83,27 @@ class EscapeStatusAnalysis {
bool CheckUsesForEscape(Node* node, Node* rep, bool phi_escaping = false); bool CheckUsesForEscape(Node* node, Node* rep, bool phi_escaping = false);
void RevisitUses(Node* node); void RevisitUses(Node* node);
void RevisitInputs(Node* node); void RevisitInputs(Node* node);
bool SetEscaped(Node* node);
bool IsVirtual(NodeId id); Alias NextAlias() { return next_free_alias_++; }
bool HasEntry(Node* node); bool HasEntry(Node* node);
void Resize();
size_t size();
bool IsAllocationPhi(Node* node);
Graph* graph() const { return graph_; } bool IsAllocationPhi(Node* node);
Zone* zone() const { return zone_; }
ZoneVector<Node*> stack_;
EscapeAnalysis* object_analysis_; EscapeAnalysis* object_analysis_;
Graph* const graph_; Graph* const graph_;
Zone* const zone_; Zone* const zone_;
ZoneVector<EscapeStatusFlags> status_; ZoneVector<StatusFlags> status_;
ZoneDeque<Node*> queue_; Alias next_free_alias_;
ZoneVector<Node*> status_stack_;
ZoneVector<Alias> aliases_;
DISALLOW_COPY_AND_ASSIGN(EscapeStatusAnalysis); DISALLOW_COPY_AND_ASSIGN(EscapeStatusAnalysis);
}; };
DEFINE_OPERATORS_FOR_FLAGS(EscapeStatusAnalysis::EscapeStatusFlags) DEFINE_OPERATORS_FOR_FLAGS(EscapeStatusAnalysis::StatusFlags)
// Forward Declaration. // Forward Declaration.
...@@ -88,8 +114,7 @@ class MergeCache; ...@@ -88,8 +114,7 @@ class MergeCache;
// an object is virtual and eliminated. // an object is virtual and eliminated.
class EscapeAnalysis { class EscapeAnalysis {
public: public:
typedef NodeId Alias; using Alias = EscapeStatusAnalysis::Alias;
EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common, Zone* zone); EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common, Zone* zone);
~EscapeAnalysis(); ~EscapeAnalysis();
...@@ -104,7 +129,6 @@ class EscapeAnalysis { ...@@ -104,7 +129,6 @@ class EscapeAnalysis {
private: private:
void RunObjectAnalysis(); void RunObjectAnalysis();
void AssignAliases();
bool Process(Node* node); bool Process(Node* node);
void ProcessLoadField(Node* node); void ProcessLoadField(Node* node);
void ProcessStoreField(Node* node); void ProcessStoreField(Node* node);
...@@ -120,10 +144,10 @@ class EscapeAnalysis { ...@@ -120,10 +144,10 @@ class EscapeAnalysis {
VirtualState* states); VirtualState* states);
void ForwardVirtualState(Node* node); void ForwardVirtualState(Node* node);
bool IsEffectBranchPoint(Node* node);
bool IsDanglingEffectNode(Node* node);
int OffsetFromAccess(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* GetVirtualObject(Node* at, NodeId id);
VirtualObject* ResolveVirtualObject(VirtualState* state, Node* node); VirtualObject* ResolveVirtualObject(VirtualState* state, Node* node);
Node* GetReplacementIfSame(ZoneVector<VirtualObject*>& objs); Node* GetReplacementIfSame(ZoneVector<VirtualObject*>& objs);
...@@ -142,24 +166,27 @@ class EscapeAnalysis { ...@@ -142,24 +166,27 @@ class EscapeAnalysis {
void DebugPrintState(VirtualState* state); void DebugPrintState(VirtualState* state);
void DebugPrintObject(VirtualObject* state, Alias id); void DebugPrintObject(VirtualObject* state, Alias id);
Alias NextAlias() { return next_free_alias_++; } Graph* graph() const { return status_analysis_.graph(); }
Alias AliasCount() const { return next_free_alias_; } Zone* zone() const { return status_analysis_.zone(); }
Graph* graph() const { return graph_; }
CommonOperatorBuilder* common() const { return common_; } 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; EscapeStatusAnalysis status_analysis_;
static const Alias kUntrackable;
Graph* const graph_;
CommonOperatorBuilder* const common_; CommonOperatorBuilder* const common_;
Zone* const zone_;
ZoneVector<VirtualState*> virtual_states_; ZoneVector<VirtualState*> virtual_states_;
ZoneVector<Node*> replacements_; ZoneVector<Node*> replacements_;
EscapeStatusAnalysis escape_status_;
MergeCache* cache_; MergeCache* cache_;
ZoneVector<Alias> aliases_;
Alias next_free_alias_;
DISALLOW_COPY_AND_ASSIGN(EscapeAnalysis); DISALLOW_COPY_AND_ASSIGN(EscapeAnalysis);
}; };
......
...@@ -668,6 +668,7 @@ struct EscapeAnalysisPhase { ...@@ -668,6 +668,7 @@ struct EscapeAnalysisPhase {
escape_analysis.ExistsVirtualAllocate()); escape_analysis.ExistsVirtualAllocate());
AddReducer(data, &graph_reducer, &escape_reducer); AddReducer(data, &graph_reducer, &escape_reducer);
graph_reducer.ReduceGraph(); graph_reducer.ReduceGraph();
escape_reducer.VerifyReplacement();
} }
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment