Commit fc9a73e8 authored by sigurds's avatar sigurds Committed by Commit bot

[turbofan] Various performance enhancements for escape analysis

This bug improves performance of escape analysis.

* A allocation discovery phase  (EscapeAnalysis::AssignAliases)
  ensures compact representation of virtual state
* Node revisiting in EscapeStatusAnalysis has been improved
* Escape analysis no longer requires a trimmed graph

BUG=v8:4586
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#33267}
parent 0830ac7c
...@@ -108,13 +108,17 @@ class Flags final { ...@@ -108,13 +108,17 @@ class Flags final {
ALLOW_UNUSED_TYPE WARN_UNUSED_RESULT; \ ALLOW_UNUSED_TYPE WARN_UNUSED_RESULT; \
inline Type operator^(Type::flag_type lhs, Type::flag_type rhs) { \ inline Type operator^(Type::flag_type lhs, Type::flag_type rhs) { \
return Type(lhs) ^ rhs; \ return Type(lhs) ^ rhs; \
} inline Type operator^(Type::flag_type lhs, const Type& rhs) \ } inline Type \
operator^(Type::flag_type lhs, const Type& rhs) \
ALLOW_UNUSED_TYPE WARN_UNUSED_RESULT; \ ALLOW_UNUSED_TYPE WARN_UNUSED_RESULT; \
inline Type operator^(Type::flag_type lhs, const Type& rhs) { \ inline Type operator^(Type::flag_type lhs, const Type& rhs) { \
return rhs ^ lhs; \ return rhs ^ lhs; \
} inline void operator^(Type::flag_type lhs, Type::mask_type rhs) \ } inline void \
ALLOW_UNUSED_TYPE; \ operator^(Type::flag_type lhs, Type::mask_type rhs) ALLOW_UNUSED_TYPE; \
inline void operator^(Type::flag_type lhs, Type::mask_type rhs) {} inline void operator^(Type::flag_type lhs, Type::mask_type rhs) { \
} inline Type \
operator~(Type::flag_type val)ALLOW_UNUSED_TYPE; \
inline Type operator~(Type::flag_type val) { return ~Type(val); }
} // namespace base } // namespace base
} // namespace v8 } // namespace v8
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "src/compiler/escape-analysis.h" #include "src/compiler/escape-analysis.h"
#include <limits>
#include "src/base/flags.h" #include "src/base/flags.h"
#include "src/bootstrapper.h" #include "src/bootstrapper.h"
#include "src/compilation-dependencies.h" #include "src/compilation-dependencies.h"
...@@ -13,6 +15,7 @@ ...@@ -13,6 +15,7 @@
#include "src/compiler/node.h" #include "src/compiler/node.h"
#include "src/compiler/node-matchers.h" #include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h" #include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.h" #include "src/compiler/simplified-operator.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
#include "src/type-cache.h" #include "src/type-cache.h"
...@@ -21,6 +24,12 @@ namespace v8 { ...@@ -21,6 +24,12 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
const EscapeAnalysis::Alias EscapeAnalysis::kNotReachable =
std::numeric_limits<Alias>::max();
const EscapeAnalysis::Alias EscapeAnalysis::kUntrackable =
std::numeric_limits<Alias>::max() - 1;
class VirtualObject : public ZoneObject { class VirtualObject : public ZoneObject {
public: public:
enum Status { kUntracked = 0, kTracked = 1 }; enum Status { kUntracked = 0, kTracked = 1 };
...@@ -98,9 +107,9 @@ class VirtualObject : public ZoneObject { ...@@ -98,9 +107,9 @@ class VirtualObject : public ZoneObject {
} }
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() { return object_state_; } Node* GetObjectState() const { return object_state_; }
NodeId id() { return id_; } NodeId id() const { return id_; }
void id(NodeId id) { id_ = id; } void id(NodeId id) { id_ = id; }
private: private:
...@@ -134,18 +143,16 @@ class VirtualState : public ZoneObject { ...@@ -134,18 +143,16 @@ class VirtualState : public ZoneObject {
VirtualState(Zone* zone, size_t size); VirtualState(Zone* zone, size_t size);
VirtualState(const VirtualState& states); VirtualState(const VirtualState& states);
VirtualObject* GetVirtualObject(Node* node); VirtualObject* VirtualObjectFromAlias(size_t alias);
VirtualObject* GetVirtualObject(size_t id); VirtualObject* GetOrCreateTrackedVirtualObject(EscapeAnalysis::Alias alias,
VirtualObject* GetOrCreateTrackedVirtualObject(NodeId id, Zone* zone); NodeId id, Zone* zone);
void SetVirtualObject(NodeId id, VirtualObject* state); void SetVirtualObject(EscapeAnalysis::Alias alias, VirtualObject* state);
void LastChangedAt(Node* node) { last_changed_ = node; } void LastChangedAt(Node* node) { last_changed_ = node; }
Node* GetLastChanged() { return last_changed_; } Node* GetLastChanged() { return last_changed_; }
bool UpdateFrom(NodeId id, VirtualObject* state, Zone* zone);
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);
size_t size() const { return info_.size(); }
size_t size() { return info_.size(); }
private: private:
ZoneVector<VirtualObject*> info_; ZoneVector<VirtualObject*> info_;
...@@ -156,7 +163,7 @@ class VirtualState : public ZoneObject { ...@@ -156,7 +163,7 @@ class VirtualState : public ZoneObject {
class MergeCache : public ZoneObject { class MergeCache : public ZoneObject {
public: public:
explicit MergeCache(Zone* zone) explicit MergeCache(Zone* zone)
: states_(zone), objects_(zone), fields_(zone), min_size_(SIZE_MAX) { : states_(zone), objects_(zone), fields_(zone) {
states_.reserve(4); states_.reserve(4);
objects_.reserve(4); objects_.reserve(4);
fields_.reserve(4); fields_.reserve(4);
...@@ -168,32 +175,26 @@ class MergeCache : public ZoneObject { ...@@ -168,32 +175,26 @@ class MergeCache : public ZoneObject {
states_.clear(); states_.clear();
objects_.clear(); objects_.clear();
fields_.clear(); fields_.clear();
min_size_ = SIZE_MAX;
} }
void push_back(VirtualState* state) { size_t LoadVirtualObjectsFromStatesFor(EscapeAnalysis::Alias alias);
states_.push_back(state); void LoadVirtualObjectsForFieldsFrom(
min_size_ = std::min(state->size(), min_size_); VirtualState* state, const ZoneVector<EscapeAnalysis::Alias>& aliases);
}
size_t min_size() { return min_size_; }
size_t LoadVirtualObjectsFromStatesFor(NodeId id);
void LoadVirtualObjectsForFieldsFrom(VirtualState* state);
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_;
size_t min_size_;
}; };
size_t MergeCache::LoadVirtualObjectsFromStatesFor(NodeId id) { size_t MergeCache::LoadVirtualObjectsFromStatesFor(
EscapeAnalysis::Alias alias) {
objects_.clear(); objects_.clear();
DCHECK_GT(states_.size(), 0u); DCHECK_GT(states_.size(), 0u);
size_t min = SIZE_MAX; size_t min = std::numeric_limits<size_t>::max();
for (VirtualState* state : states_) { for (VirtualState* state : states_) {
if (VirtualObject* obj = state->GetVirtualObject(id)) { if (VirtualObject* obj = state->VirtualObjectFromAlias(alias)) {
objects_.push_back(obj); objects_.push_back(obj);
min = std::min(obj->field_count(), min); min = std::min(obj->field_count(), min);
} }
...@@ -202,10 +203,14 @@ size_t MergeCache::LoadVirtualObjectsFromStatesFor(NodeId id) { ...@@ -202,10 +203,14 @@ size_t MergeCache::LoadVirtualObjectsFromStatesFor(NodeId id) {
} }
void MergeCache::LoadVirtualObjectsForFieldsFrom(VirtualState* state) { void MergeCache::LoadVirtualObjectsForFieldsFrom(
VirtualState* state, const ZoneVector<EscapeAnalysis::Alias>& aliases) {
objects_.clear(); objects_.clear();
size_t max_alias = state->size();
for (Node* field : fields_) { for (Node* field : fields_) {
if (VirtualObject* obj = state->GetVirtualObject(field)) { EscapeAnalysis::Alias alias = aliases[field->id()];
if (alias >= max_alias) continue;
if (VirtualObject* obj = state->VirtualObjectFromAlias(alias)) {
objects_.push_back(obj); objects_.push_back(obj);
} }
} }
...@@ -236,75 +241,41 @@ VirtualState::VirtualState(const VirtualState& state) ...@@ -236,75 +241,41 @@ VirtualState::VirtualState(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_) { last_changed_(state.last_changed_) {
for (size_t i = 0; i < state.info_.size(); ++i) { for (size_t i = 0; i < state.info_.size(); ++i) {
if (state.info_[i] && state.info_[i]->id() == i) { if (state.info_[i]) {
info_[i] = new (state.info_.get_allocator().zone()) info_[i] =
VirtualObject(*state.info_[i]); new (info_.get_allocator().zone()) VirtualObject(*state.info_[i]);
} }
} }
for (size_t i = 0; i < state.info_.size(); ++i) {
if (state.info_[i] && state.info_[i]->id() != i) {
info_[i] = info_[state.info_[i]->id()];
}
}
}
VirtualObject* VirtualState::GetVirtualObject(size_t id) {
if (id >= info_.size()) return nullptr;
return info_[id];
} }
VirtualObject* VirtualState::GetVirtualObject(Node* node) { VirtualObject* VirtualState::VirtualObjectFromAlias(size_t alias) {
return GetVirtualObject(node->id()); return info_[alias];
} }
VirtualObject* VirtualState::GetOrCreateTrackedVirtualObject(NodeId id, VirtualObject* VirtualState::GetOrCreateTrackedVirtualObject(
Zone* zone) { EscapeAnalysis::Alias alias, NodeId id, Zone* zone) {
if (VirtualObject* obj = GetVirtualObject(id)) { if (VirtualObject* obj = VirtualObjectFromAlias(alias)) {
return obj; return obj;
} }
VirtualObject* obj = new (zone) VirtualObject(id, zone, 0); VirtualObject* obj = new (zone) VirtualObject(id, zone, 0);
SetVirtualObject(id, obj); SetVirtualObject(alias, obj);
return obj; return obj;
} }
void VirtualState::SetVirtualObject(NodeId id, VirtualObject* obj) { void VirtualState::SetVirtualObject(EscapeAnalysis::Alias alias,
info_[id] = obj; VirtualObject* obj) {
} info_[alias] = obj;
bool VirtualState::UpdateFrom(NodeId id, VirtualObject* fromObj, Zone* zone) {
VirtualObject* obj = GetVirtualObject(id);
if (!obj) {
obj = new (zone) VirtualObject(*fromObj);
SetVirtualObject(id, obj);
if (FLAG_trace_turbo_escape) {
PrintF(" Taking field for #%d from %p\n", id,
static_cast<void*>(fromObj));
}
return true;
}
if (obj->UpdateFrom(*fromObj)) {
if (FLAG_trace_turbo_escape) {
PrintF(" Updating field for #%d from %p\n", id,
static_cast<void*>(fromObj));
}
return true;
}
return false;
} }
bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) { bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) {
bool changed = false; bool changed = false;
for (NodeId id = 0; id < size(); ++id) { for (EscapeAnalysis::Alias alias = 0; alias < size(); ++alias) {
VirtualObject* ls = GetVirtualObject(id); VirtualObject* ls = VirtualObjectFromAlias(alias);
VirtualObject* rs = from->GetVirtualObject(id); VirtualObject* rs = from->VirtualObjectFromAlias(alias);
if (rs == nullptr) { if (rs == nullptr) {
continue; continue;
...@@ -312,13 +283,13 @@ bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) { ...@@ -312,13 +283,13 @@ bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) {
if (ls == nullptr) { if (ls == nullptr) {
ls = new (zone) VirtualObject(*rs); ls = new (zone) VirtualObject(*rs);
SetVirtualObject(id, ls); SetVirtualObject(alias, ls);
changed = true; changed = true;
continue; continue;
} }
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
PrintF(" Updating fields of #%d\n", id); PrintF(" Updating fields of @%d\n", alias);
} }
changed = ls->UpdateFrom(*rs) || changed; changed = ls->UpdateFrom(*rs) || changed;
...@@ -378,15 +349,14 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, ...@@ -378,15 +349,14 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
CommonOperatorBuilder* common, Node* control) { CommonOperatorBuilder* common, Node* control) {
DCHECK_GT(cache->states().size(), 0u); DCHECK_GT(cache->states().size(), 0u);
bool changed = false; bool changed = false;
for (NodeId id = 0; id < cache->min_size(); ++id) { for (EscapeAnalysis::Alias alias = 0; alias < size(); ++alias) {
size_t fields = cache->LoadVirtualObjectsFromStatesFor(id); size_t fields = cache->LoadVirtualObjectsFromStatesFor(alias);
if (cache->objects().size() == cache->states().size()) { if (cache->objects().size() == cache->states().size()) {
// Don't process linked objects.
if (cache->objects()[0]->id() != id) continue;
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
PrintF(" Merging virtual objects of #%d\n", id); PrintF(" Merging virtual objects of @%d\n", alias);
} }
VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject(id, zone); VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject(
alias, cache->objects().front()->id(), zone);
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)) {
...@@ -445,15 +415,7 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, ...@@ -445,15 +415,7 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
} }
} }
} else { } else {
SetVirtualObject(id, nullptr); SetVirtualObject(alias, nullptr);
}
}
// Update linked objects.
for (NodeId id = 0; id < cache->min_size(); ++id) {
if (VirtualObject* obj = cache->states().front()->GetVirtualObject(id)) {
if (obj->id() != id) {
SetVirtualObject(id, GetVirtualObject(obj->id()));
}
} }
} }
return changed; return changed;
...@@ -465,7 +427,7 @@ EscapeStatusAnalysis::EscapeStatusAnalysis(EscapeAnalysis* object_analysis, ...@@ -465,7 +427,7 @@ EscapeStatusAnalysis::EscapeStatusAnalysis(EscapeAnalysis* object_analysis,
: object_analysis_(object_analysis), : object_analysis_(object_analysis),
graph_(graph), graph_(graph),
zone_(zone), zone_(zone),
info_(graph->NodeCount(), kUnknown, zone), status_(graph->NodeCount(), kUnknown, zone),
queue_(zone) {} queue_(zone) {}
...@@ -473,17 +435,17 @@ EscapeStatusAnalysis::~EscapeStatusAnalysis() {} ...@@ -473,17 +435,17 @@ EscapeStatusAnalysis::~EscapeStatusAnalysis() {}
bool EscapeStatusAnalysis::HasEntry(Node* node) { bool EscapeStatusAnalysis::HasEntry(Node* node) {
return info_[node->id()] != kUnknown; return status_[node->id()] & (kTracked | kEscaped);
} }
bool EscapeStatusAnalysis::IsVirtual(Node* node) { bool EscapeStatusAnalysis::IsVirtual(Node* node) {
return info_[node->id()] == kVirtual; return (status_[node->id()] & kTracked) && !(status_[node->id()] & kEscaped);
} }
bool EscapeStatusAnalysis::IsEscaped(Node* node) { bool EscapeStatusAnalysis::IsEscaped(Node* node) {
return info_[node->id()] == kEscaped; return status_[node->id()] & kEscaped;
} }
...@@ -494,36 +456,37 @@ bool EscapeStatusAnalysis::IsAllocation(Node* node) { ...@@ -494,36 +456,37 @@ bool EscapeStatusAnalysis::IsAllocation(Node* node) {
bool EscapeStatusAnalysis::SetEscaped(Node* node) { bool EscapeStatusAnalysis::SetEscaped(Node* node) {
bool changed = info_[node->id()] != kEscaped; bool changed = !(status_[node->id()] & kEscaped);
info_[node->id()] = kEscaped; status_[node->id()] |= kEscaped | kTracked;
return changed; return changed;
} }
void EscapeStatusAnalysis::Resize() { void EscapeStatusAnalysis::Resize() {
info_.resize(graph()->NodeCount(), kUnknown); status_.resize(graph()->NodeCount(), kUnknown);
} }
size_t EscapeStatusAnalysis::size() { return info_.size(); } size_t EscapeStatusAnalysis::size() { return status_.size(); }
void EscapeStatusAnalysis::Run() { void EscapeStatusAnalysis::Run() {
Resize(); Resize();
ZoneVector<bool> visited(zone());
visited.resize(graph()->NodeCount());
queue_.push_back(graph()->end()); queue_.push_back(graph()->end());
status_[graph()->end()->id()] |= kOnStack;
while (!queue_.empty()) { while (!queue_.empty()) {
Node* node = queue_.front(); Node* node = queue_.front();
queue_.pop_front(); queue_.pop_front();
status_[node->id()] &= ~kOnStack;
Process(node); Process(node);
if (!visited[node->id()]) { status_[node->id()] |= kVisited;
RevisitInputs(node); for (Edge edge : node->input_edges()) {
Node* input = edge.to();
if (!(status_[input->id()] & (kVisited | kOnStack))) {
queue_.push_back(input);
status_[input->id()] |= kOnStack;
}
} }
visited[node->id()] = true;
}
if (FLAG_trace_turbo_escape) {
DebugPrint();
} }
} }
...@@ -531,7 +494,10 @@ void EscapeStatusAnalysis::Run() { ...@@ -531,7 +494,10 @@ void EscapeStatusAnalysis::Run() {
void EscapeStatusAnalysis::RevisitInputs(Node* node) { 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();
queue_.push_back(input); if (!(status_[input->id()] & kOnStack)) {
queue_.push_back(input);
status_[input->id()] |= kOnStack;
}
} }
} }
...@@ -539,7 +505,10 @@ void EscapeStatusAnalysis::RevisitInputs(Node* node) { ...@@ -539,7 +505,10 @@ 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();
queue_.push_back(use); if (!(status_[use->id()] & kOnStack)) {
queue_.push_back(use);
status_[use->id()] |= kOnStack;
}
} }
} }
...@@ -570,7 +539,7 @@ void EscapeStatusAnalysis::Process(Node* node) { ...@@ -570,7 +539,7 @@ void EscapeStatusAnalysis::Process(Node* node) {
} }
case IrOpcode::kPhi: case IrOpcode::kPhi:
if (!HasEntry(node)) { if (!HasEntry(node)) {
info_[node->id()] = kVirtual; status_[node->id()] |= kTracked;
if (!IsAllocationPhi(node)) { if (!IsAllocationPhi(node)) {
SetEscaped(node); SetEscaped(node);
RevisitUses(node); RevisitUses(node);
...@@ -627,7 +596,7 @@ void EscapeStatusAnalysis::ProcessStoreElement(Node* node) { ...@@ -627,7 +596,7 @@ void EscapeStatusAnalysis::ProcessStoreElement(Node* node) {
void EscapeStatusAnalysis::ProcessAllocate(Node* node) { void EscapeStatusAnalysis::ProcessAllocate(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kAllocate); DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
if (!HasEntry(node)) { if (!HasEntry(node)) {
info_[node->id()] = kVirtual; status_[node->id()] |= kTracked;
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
PrintF("Created status entry for node #%d (%s)\n", node->id(), PrintF("Created status entry for node #%d (%s)\n", node->id(),
node->op()->mnemonic()); node->op()->mnemonic());
...@@ -657,34 +626,34 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep, ...@@ -657,34 +626,34 @@ 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 (!NodeProperties::IsValueEdge(edge) && if (edge.index() >= use->op()->ValueInputCount() +
!NodeProperties::IsContextEdge(edge)) OperatorProperties::GetContextInputCount(use->op()))
continue; continue;
switch (use->opcode()) { switch (use->opcode()) {
case IrOpcode::kStoreField:
case IrOpcode::kLoadField:
case IrOpcode::kStoreElement:
case IrOpcode::kLoadElement:
case IrOpcode::kFrameState:
case IrOpcode::kStateValues:
case IrOpcode::kReferenceEqual:
case IrOpcode::kFinishRegion:
case IrOpcode::kPhi: case IrOpcode::kPhi:
if (HasEntry(use) && IsEscaped(use) && SetEscaped(rep)) { if (phi_escaping && SetEscaped(rep)) {
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
PrintF( PrintF(
"Setting #%d (%s) to escaped because of use by escaping node " "Setting #%d (%s) to escaped because of use by phi node "
"#%d (%s)\n", "#%d (%s)\n",
rep->id(), rep->op()->mnemonic(), use->id(), rep->id(), rep->op()->mnemonic(), use->id(),
use->op()->mnemonic()); use->op()->mnemonic());
} }
return true; return true;
} }
if (phi_escaping && use->opcode() == IrOpcode::kPhi && // Fallthrough.
SetEscaped(rep)) { case IrOpcode::kStoreField:
case IrOpcode::kLoadField:
case IrOpcode::kStoreElement:
case IrOpcode::kLoadElement:
case IrOpcode::kFrameState:
case IrOpcode::kStateValues:
case IrOpcode::kReferenceEqual:
case IrOpcode::kFinishRegion:
if (IsEscaped(use) && SetEscaped(rep)) {
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
PrintF( PrintF(
"Setting #%d (%s) to escaped because of use by phi node " "Setting #%d (%s) to escaped because of use by escaping node "
"#%d (%s)\n", "#%d (%s)\n",
rep->id(), rep->op()->mnemonic(), use->id(), rep->id(), rep->op()->mnemonic(), use->id(),
use->op()->mnemonic()); use->op()->mnemonic());
...@@ -724,7 +693,7 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep, ...@@ -724,7 +693,7 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep,
void EscapeStatusAnalysis::ProcessFinishRegion(Node* node) { void EscapeStatusAnalysis::ProcessFinishRegion(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion); DCHECK_EQ(node->opcode(), IrOpcode::kFinishRegion);
if (!HasEntry(node)) { if (!HasEntry(node)) {
info_[node->id()] = kVirtual; status_[node->id()] |= kTracked;
RevisitUses(node); RevisitUses(node);
} }
if (CheckUsesForEscape(node, true)) { if (CheckUsesForEscape(node, true)) {
...@@ -734,10 +703,10 @@ void EscapeStatusAnalysis::ProcessFinishRegion(Node* node) { ...@@ -734,10 +703,10 @@ void EscapeStatusAnalysis::ProcessFinishRegion(Node* node) {
void EscapeStatusAnalysis::DebugPrint() { void EscapeStatusAnalysis::DebugPrint() {
for (NodeId id = 0; id < info_.size(); id++) { for (NodeId id = 0; id < status_.size(); id++) {
if (info_[id] != kUnknown) { if (status_[id] & kTracked) {
PrintF("Node #%d is %s\n", id, PrintF("Node #%d is %s\n", id,
info_[id] == kEscaped ? "escaping" : "virtual"); (status_[id] & kEscaped) ? "escaping" : "virtual");
} }
} }
} }
...@@ -751,7 +720,9 @@ EscapeAnalysis::EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common, ...@@ -751,7 +720,9 @@ EscapeAnalysis::EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common,
virtual_states_(zone), virtual_states_(zone),
replacements_(zone), replacements_(zone),
escape_status_(this, graph, 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() {}
...@@ -759,11 +730,69 @@ EscapeAnalysis::~EscapeAnalysis() {} ...@@ -759,11 +730,69 @@ EscapeAnalysis::~EscapeAnalysis() {}
void EscapeAnalysis::Run() { void EscapeAnalysis::Run() {
replacements_.resize(graph()->NodeCount()); replacements_.resize(graph()->NodeCount());
AssignAliases();
RunObjectAnalysis(); RunObjectAnalysis();
escape_status_.Run(); escape_status_.Run();
} }
void EscapeAnalysis::AssignAliases() {
ZoneVector<Node*> stack(zone());
stack.push_back(graph()->end());
CHECK_LT(graph()->NodeCount(), kUntrackable);
aliases_.resize(graph()->NodeCount(), kNotReachable);
aliases_[graph()->end()->id()] = kUntrackable;
while (!stack.empty()) {
Node* node = stack.back();
stack.pop_back();
switch (node->opcode()) {
case IrOpcode::kAllocate:
if (aliases_[node->id()] >= kUntrackable) {
aliases_[node->id()] = NextAlias();
}
break;
case IrOpcode::kFinishRegion: {
Node* allocate = NodeProperties::GetValueInput(node, 0);
if (allocate->opcode() == IrOpcode::kAllocate) {
if (aliases_[allocate->id()] >= kUntrackable) {
if (aliases_[allocate->id()] == kNotReachable) {
stack.push_back(allocate);
}
aliases_[allocate->id()] = NextAlias();
}
aliases_[node->id()] = aliases_[allocate->id()];
} else {
aliases_[node->id()] = NextAlias();
}
break;
}
default:
DCHECK_EQ(aliases_[node->id()], kUntrackable);
break;
}
for (Edge edge : node->input_edges()) {
Node* input = edge.to();
if (aliases_[input->id()] == kNotReachable) {
stack.push_back(input);
aliases_[input->id()] = kUntrackable;
}
}
}
if (FLAG_trace_turbo_escape) {
PrintF("Discovered trackable nodes");
for (EscapeAnalysis::Alias id = 0; id < graph()->NodeCount(); ++id) {
if (aliases_[id] < kUntrackable) {
if (FLAG_trace_turbo_escape) {
PrintF(" #%u", id);
}
}
}
PrintF("\n");
}
}
void EscapeAnalysis::RunObjectAnalysis() { void EscapeAnalysis::RunObjectAnalysis() {
virtual_states_.resize(graph()->NodeCount()); virtual_states_.resize(graph()->NodeCount());
ZoneVector<Node*> stack(zone()); ZoneVector<Node*> stack(zone());
...@@ -771,7 +800,7 @@ void EscapeAnalysis::RunObjectAnalysis() { ...@@ -771,7 +800,7 @@ void EscapeAnalysis::RunObjectAnalysis() {
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()) {
if (NodeProperties::IsEffectEdge(edge)) { if (NodeProperties::IsEffectEdge(edge)) {
Node* use = edge.from(); Node* use = edge.from();
...@@ -788,8 +817,6 @@ void EscapeAnalysis::RunObjectAnalysis() { ...@@ -788,8 +817,6 @@ void EscapeAnalysis::RunObjectAnalysis() {
Node* use = edge.from(); 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);
} }
...@@ -919,7 +946,7 @@ void EscapeAnalysis::ForwardVirtualState(Node* node) { ...@@ -919,7 +946,7 @@ void EscapeAnalysis::ForwardVirtualState(Node* node) {
if (effect->opcode() == IrOpcode::kEffectPhi) { if (effect->opcode() == IrOpcode::kEffectPhi) {
if (virtual_states_[effect->id()] == nullptr) { if (virtual_states_[effect->id()] == nullptr) {
virtual_states_[effect->id()] = virtual_states_[effect->id()] =
new (zone()) VirtualState(zone(), graph()->NodeCount()); new (zone()) VirtualState(zone(), AliasCount());
} }
} }
DCHECK_NOT_NULL(virtual_states_[effect->id()]); DCHECK_NOT_NULL(virtual_states_[effect->id()]);
...@@ -949,8 +976,7 @@ void EscapeAnalysis::ForwardVirtualState(Node* node) { ...@@ -949,8 +976,7 @@ void EscapeAnalysis::ForwardVirtualState(Node* node) {
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()] = virtual_states_[node->id()] = new (zone()) VirtualState(zone(), AliasCount());
new (zone()) VirtualState(zone(), graph()->NodeCount());
} }
...@@ -960,7 +986,7 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) { ...@@ -960,7 +986,7 @@ 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(), graph()->NodeCount()); mergeState = new (zone()) VirtualState(zone(), AliasCount());
virtual_states_[node->id()] = mergeState; virtual_states_[node->id()] = mergeState;
changed = true; changed = true;
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
...@@ -982,7 +1008,7 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) { ...@@ -982,7 +1008,7 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) {
Node* input = NodeProperties::GetEffectInput(node, i); Node* input = NodeProperties::GetEffectInput(node, i);
VirtualState* state = virtual_states_[input->id()]; VirtualState* state = virtual_states_[input->id()];
if (state) { if (state) {
cache_->push_back(state); cache_->states().push_back(state);
} }
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
PrintF(" %p (from %d %s)", static_cast<void*>(state), input->id(), PrintF(" %p (from %d %s)", static_cast<void*>(state), input->id(),
...@@ -1018,7 +1044,10 @@ void EscapeAnalysis::ProcessAllocation(Node* node) { ...@@ -1018,7 +1044,10 @@ void EscapeAnalysis::ProcessAllocation(Node* node) {
ForwardVirtualState(node); ForwardVirtualState(node);
// Check if we have already processed this node. // Check if we have already processed this node.
if (virtual_states_[node->id()]->GetVirtualObject(node)) return; if (virtual_states_[node->id()]->VirtualObjectFromAlias(
aliases_[node->id()])) {
return;
}
NumberMatcher size(node->InputAt(0)); NumberMatcher size(node->InputAt(0));
DCHECK(node->InputAt(0)->opcode() != IrOpcode::kInt32Constant && DCHECK(node->InputAt(0)->opcode() != IrOpcode::kInt32Constant &&
...@@ -1027,11 +1056,12 @@ void EscapeAnalysis::ProcessAllocation(Node* node) { ...@@ -1027,11 +1056,12 @@ void EscapeAnalysis::ProcessAllocation(Node* node) {
node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant); node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant);
if (size.HasValue()) { if (size.HasValue()) {
virtual_states_[node->id()]->SetVirtualObject( virtual_states_[node->id()]->SetVirtualObject(
node->id(), new (zone()) VirtualObject(node->id(), zone(), aliases_[node->id()],
size.Value() / kPointerSize)); new (zone())
VirtualObject(node->id(), zone(), size.Value() / kPointerSize));
} else { } else {
virtual_states_[node->id()]->SetVirtualObject( virtual_states_[node->id()]->SetVirtualObject(
node->id(), new (zone()) VirtualObject(node->id(), zone())); aliases_[node->id()], new (zone()) VirtualObject(node->id(), zone()));
} }
virtual_states_[node->id()]->LastChangedAt(node); virtual_states_[node->id()]->LastChangedAt(node);
} }
...@@ -1042,16 +1072,17 @@ void EscapeAnalysis::ProcessFinishRegion(Node* node) { ...@@ -1042,16 +1072,17 @@ void EscapeAnalysis::ProcessFinishRegion(Node* node) {
ForwardVirtualState(node); ForwardVirtualState(node);
Node* allocation = NodeProperties::GetValueInput(node, 0); Node* allocation = NodeProperties::GetValueInput(node, 0);
if (allocation->opcode() == IrOpcode::kAllocate) { if (allocation->opcode() == IrOpcode::kAllocate) {
VirtualState* states = virtual_states_[node->id()]; VirtualState* state = virtual_states_[node->id()];
DCHECK_NOT_NULL(states->GetVirtualObject(allocation)); if (!state->VirtualObjectFromAlias(aliases_[node->id()])) {
if (!states->GetVirtualObject(node->id())) { VirtualObject* vobj_alloc =
states->SetVirtualObject(node->id(), state->VirtualObjectFromAlias(aliases_[allocation->id()]);
states->GetVirtualObject(allocation)); DCHECK_NOT_NULL(vobj_alloc);
state->SetVirtualObject(aliases_[node->id()], vobj_alloc);
if (FLAG_trace_turbo_escape) { if (FLAG_trace_turbo_escape) {
PrintF("Linked finish region node #%d to node #%d\n", node->id(), PrintF("Linked finish region node #%d to node #%d\n", node->id(),
allocation->id()); allocation->id());
} }
states->LastChangedAt(node); state->LastChangedAt(node);
} }
} }
} }
...@@ -1139,7 +1170,7 @@ bool EscapeAnalysis::SetEscaped(Node* node) { ...@@ -1139,7 +1170,7 @@ bool EscapeAnalysis::SetEscaped(Node* 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->GetVirtualObject(id); return states->VirtualObjectFromAlias(aliases_[id]);
} }
return nullptr; return nullptr;
} }
...@@ -1147,10 +1178,13 @@ VirtualObject* EscapeAnalysis::GetVirtualObject(Node* at, NodeId id) { ...@@ -1147,10 +1178,13 @@ VirtualObject* EscapeAnalysis::GetVirtualObject(Node* at, NodeId id) {
VirtualObject* EscapeAnalysis::ResolveVirtualObject(VirtualState* state, VirtualObject* EscapeAnalysis::ResolveVirtualObject(VirtualState* state,
Node* node) { Node* node) {
VirtualObject* obj = state->GetVirtualObject(ResolveReplacement(node)); VirtualObject* obj = GetVirtualObject(state, ResolveReplacement(node));
while (obj && replacement(obj->id()) && while (obj && replacement(obj->id())) {
state->GetVirtualObject(replacement(obj->id()))) { if (VirtualObject* next = GetVirtualObject(state, replacement(obj->id()))) {
obj = state->GetVirtualObject(replacement(obj->id())); obj = next;
} else {
break;
}
} }
return obj; return obj;
} }
...@@ -1185,7 +1219,7 @@ void EscapeAnalysis::ProcessLoadFromPhi(int offset, Node* from, Node* node, ...@@ -1185,7 +1219,7 @@ void EscapeAnalysis::ProcessLoadFromPhi(int offset, Node* from, Node* node,
cache_->fields().push_back(input); cache_->fields().push_back(input);
} }
cache_->LoadVirtualObjectsForFieldsFrom(state); cache_->LoadVirtualObjectsForFieldsFrom(state, aliases_);
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()) {
...@@ -1387,8 +1421,9 @@ Node* EscapeAnalysis::GetOrCreateObjectState(Node* effect, Node* node) { ...@@ -1387,8 +1421,9 @@ Node* EscapeAnalysis::GetOrCreateObjectState(Node* effect, Node* node) {
} }
void EscapeAnalysis::DebugPrintObject(VirtualObject* object, NodeId id) { void EscapeAnalysis::DebugPrintObject(VirtualObject* object, Alias alias) {
PrintF(" Object #%d with %zu fields\n", id, object->field_count()); PrintF(" Alias @%d: Object #%d with %zu fields\n", alias, object->id(),
object->field_count());
for (size_t i = 0; i < object->field_count(); ++i) { for (size_t i = 0; i < object->field_count(); ++i) {
if (Node* f = object->GetField(i)) { if (Node* f = object->GetField(i)) {
PrintF(" Field %zu = #%d (%s)\n", i, f->id(), f->op()->mnemonic()); PrintF(" Field %zu = #%d (%s)\n", i, f->id(), f->op()->mnemonic());
...@@ -1399,13 +1434,9 @@ void EscapeAnalysis::DebugPrintObject(VirtualObject* object, NodeId id) { ...@@ -1399,13 +1434,9 @@ void EscapeAnalysis::DebugPrintObject(VirtualObject* object, NodeId id) {
void EscapeAnalysis::DebugPrintState(VirtualState* state) { void EscapeAnalysis::DebugPrintState(VirtualState* state) {
PrintF("Dumping object state %p\n", static_cast<void*>(state)); PrintF("Dumping object state %p\n", static_cast<void*>(state));
for (size_t id = 0; id < state->size(); id++) { for (Alias alias = 0; alias < AliasCount(); ++alias) {
if (VirtualObject* object = state->GetVirtualObject(id)) { if (VirtualObject* object = state->VirtualObjectFromAlias(alias)) {
if (object->id() == id) { DebugPrintObject(object, alias);
DebugPrintObject(object, static_cast<int>(id));
} else {
PrintF(" Object #%zu links to object #%d\n", id, object->id());
}
} }
} }
} }
...@@ -1426,6 +1457,15 @@ void EscapeAnalysis::DebugPrint() { ...@@ -1426,6 +1457,15 @@ void EscapeAnalysis::DebugPrint() {
} }
} }
VirtualObject* EscapeAnalysis::GetVirtualObject(VirtualState* state,
Node* node) {
if (node->id() >= aliases_.size()) return nullptr;
Alias alias = aliases_[node->id()];
if (alias >= state->size()) return nullptr;
return state->VirtualObjectFromAlias(alias);
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -26,10 +26,12 @@ class EscapeStatusAnalysis { ...@@ -26,10 +26,12 @@ class EscapeStatusAnalysis {
enum EscapeStatusFlag { enum EscapeStatusFlag {
kUnknown = 0u, kUnknown = 0u,
kVirtual = 1u << 0, kTracked = 1u << 0,
kEscaped = 1u << 1, kEscaped = 1u << 1,
kOnStack = 1u << 2,
kVisited = 1u << 3,
}; };
typedef base::Flags<EscapeStatusFlag> EscapeStatusFlags; typedef base::Flags<EscapeStatusFlag, unsigned char> EscapeStatusFlags;
void Run(); void Run();
...@@ -67,7 +69,7 @@ class EscapeStatusAnalysis { ...@@ -67,7 +69,7 @@ class EscapeStatusAnalysis {
EscapeAnalysis* object_analysis_; EscapeAnalysis* object_analysis_;
Graph* const graph_; Graph* const graph_;
Zone* const zone_; Zone* const zone_;
ZoneVector<EscapeStatusFlags> info_; ZoneVector<EscapeStatusFlags> status_;
ZoneDeque<Node*> queue_; ZoneDeque<Node*> queue_;
DISALLOW_COPY_AND_ASSIGN(EscapeStatusAnalysis); DISALLOW_COPY_AND_ASSIGN(EscapeStatusAnalysis);
...@@ -85,6 +87,8 @@ class MergeCache; ...@@ -85,6 +87,8 @@ class MergeCache;
// an object is virtual and eliminated. // an object is virtual and eliminated.
class EscapeAnalysis { class EscapeAnalysis {
public: public:
typedef NodeId Alias;
EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common, Zone* zone); EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common, Zone* zone);
~EscapeAnalysis(); ~EscapeAnalysis();
...@@ -98,6 +102,7 @@ class EscapeAnalysis { ...@@ -98,6 +102,7 @@ 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);
...@@ -129,14 +134,21 @@ class EscapeAnalysis { ...@@ -129,14 +134,21 @@ class EscapeAnalysis {
bool SetReplacement(Node* node, Node* rep); bool SetReplacement(Node* node, Node* rep);
bool UpdateReplacement(VirtualState* state, Node* node, Node* rep); bool UpdateReplacement(VirtualState* state, Node* node, Node* rep);
VirtualObject* GetVirtualObject(VirtualState* state, Node* node);
void DebugPrint(); void DebugPrint();
void DebugPrintState(VirtualState* state); void DebugPrintState(VirtualState* state);
void DebugPrintObject(VirtualObject* state, NodeId id); 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 graph_; }
CommonOperatorBuilder* common() const { return common_; } CommonOperatorBuilder* common() const { return common_; }
Zone* zone() const { return zone_; } Zone* zone() const { return zone_; }
static const Alias kNotReachable;
static const Alias kUntrackable;
Graph* const graph_; Graph* const graph_;
CommonOperatorBuilder* const common_; CommonOperatorBuilder* const common_;
Zone* const zone_; Zone* const zone_;
...@@ -144,6 +156,8 @@ class EscapeAnalysis { ...@@ -144,6 +156,8 @@ class EscapeAnalysis {
ZoneVector<Node*> replacements_; ZoneVector<Node*> replacements_;
EscapeStatusAnalysis escape_status_; EscapeStatusAnalysis escape_status_;
MergeCache* cache_; MergeCache* cache_;
ZoneVector<Alias> aliases_;
Alias next_free_alias_;
DISALLOW_COPY_AND_ASSIGN(EscapeAnalysis); DISALLOW_COPY_AND_ASSIGN(EscapeAnalysis);
}; };
......
...@@ -1165,9 +1165,6 @@ Handle<Code> Pipeline::GenerateCode() { ...@@ -1165,9 +1165,6 @@ Handle<Code> Pipeline::GenerateCode() {
} }
if (FLAG_turbo_escape) { if (FLAG_turbo_escape) {
// TODO(sigurds): EscapeAnalysis needs a trimmed graph at the moment,
// because it does a forwards traversal of the effect edges.
Run<EarlyGraphTrimmingPhase>();
Run<EscapeAnalysisPhase>(); Run<EscapeAnalysisPhase>();
RunPrintAndVerify("Escape Analysed"); RunPrintAndVerify("Escape Analysed");
} }
......
...@@ -190,6 +190,7 @@ TEST_F(EscapeAnalysisTest, StraightNonEscape) { ...@@ -190,6 +190,7 @@ TEST_F(EscapeAnalysisTest, StraightNonEscape) {
Node* load = Load(AccessAtIndex(0), finish); Node* load = Load(AccessAtIndex(0), finish);
Node* result = Return(load); Node* result = Return(load);
EndGraph(); EndGraph();
Analysis(); Analysis();
ExpectVirtual(allocation); ExpectVirtual(allocation);
...@@ -210,6 +211,8 @@ TEST_F(EscapeAnalysisTest, StraightEscape) { ...@@ -210,6 +211,8 @@ TEST_F(EscapeAnalysisTest, StraightEscape) {
Node* load = Load(AccessAtIndex(0), finish); Node* load = Load(AccessAtIndex(0), finish);
Node* result = Return(allocation); Node* result = Return(allocation);
EndGraph(); EndGraph();
graph()->end()->AppendInput(zone(), load);
Analysis(); Analysis();
ExpectEscaped(allocation); ExpectEscaped(allocation);
...@@ -266,6 +269,8 @@ TEST_F(EscapeAnalysisTest, BranchNonEscape) { ...@@ -266,6 +269,8 @@ TEST_F(EscapeAnalysisTest, BranchNonEscape) {
Node* load = Load(AccessAtIndex(0), finish, phi, merge); Node* load = Load(AccessAtIndex(0), finish, phi, merge);
Node* result = Return(load, phi); Node* result = Return(load, phi);
EndGraph(); EndGraph();
graph()->end()->AppendInput(zone(), result);
Analysis(); Analysis();
ExpectVirtual(allocation); ExpectVirtual(allocation);
...@@ -284,10 +289,12 @@ TEST_F(EscapeAnalysisTest, DanglingLoadOrder) { ...@@ -284,10 +289,12 @@ TEST_F(EscapeAnalysisTest, DanglingLoadOrder) {
Node* allocation = Allocate(Constant(kPointerSize)); Node* allocation = Allocate(Constant(kPointerSize));
Node* store1 = Store(AccessAtIndex(0), allocation, object1); Node* store1 = Store(AccessAtIndex(0), allocation, object1);
Node* load1 = Load(AccessAtIndex(0), allocation); Node* load1 = Load(AccessAtIndex(0), allocation);
Store(AccessAtIndex(0), allocation, object2); Node* store2 = Store(AccessAtIndex(0), allocation, object2);
Node* load2 = Load(AccessAtIndex(0), allocation, store1); Node* load2 = Load(AccessAtIndex(0), allocation, store1);
Node* result = Return(load2); Node* result = Return(load2);
EndGraph(); EndGraph();
graph()->end()->AppendInput(zone(), store2);
graph()->end()->AppendInput(zone(), load1);
Analysis(); Analysis();
......
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