| Index: src/compiler/escape-analysis.cc
|
| diff --git a/src/compiler/escape-analysis.cc b/src/compiler/escape-analysis.cc
|
| index a16d4c38619ace32232cf054e3ae305762062dcd..cc7e184efad20a3e10073ce6f3a5655ed867f906 100644
|
| --- a/src/compiler/escape-analysis.cc
|
| +++ b/src/compiler/escape-analysis.cc
|
| @@ -24,6 +24,8 @@ namespace v8 {
|
| namespace internal {
|
| namespace compiler {
|
|
|
| +using Alias = EscapeStatusAnalysis::Alias;
|
| +
|
| #ifdef DEBUG
|
| #define TRACE(...) \
|
| do { \
|
| @@ -33,65 +35,62 @@ namespace compiler {
|
| #define TRACE(...)
|
| #endif
|
|
|
| -const EscapeAnalysis::Alias EscapeAnalysis::kNotReachable =
|
| +const Alias EscapeStatusAnalysis::kNotReachable =
|
| std::numeric_limits<Alias>::max();
|
| -const EscapeAnalysis::Alias EscapeAnalysis::kUntrackable =
|
| +const Alias EscapeStatusAnalysis::kUntrackable =
|
| std::numeric_limits<Alias>::max() - 1;
|
|
|
|
|
| class VirtualObject : public ZoneObject {
|
| public:
|
| - enum Status { kUntracked = 0, kTracked = 1 };
|
| - VirtualObject(NodeId id, Zone* zone)
|
| + enum Status {
|
| + 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),
|
| - status_(kUntracked),
|
| + status_(kInitial),
|
| fields_(zone),
|
| phi_(zone),
|
| - object_state_(nullptr) {}
|
| + object_state_(nullptr),
|
| + owner_(owner) {}
|
|
|
| - VirtualObject(const VirtualObject& other)
|
| + VirtualObject(VirtualState* owner, const VirtualObject& other)
|
| : id_(other.id_),
|
| - status_(other.status_),
|
| + status_(other.status_ & ~kCopyRequired),
|
| fields_(other.fields_),
|
| 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),
|
| - status_(kTracked),
|
| + status_(kTracked | (initialized ? kInitialized : kInitial)),
|
| fields_(zone),
|
| phi_(zone),
|
| - object_state_(nullptr) {
|
| + object_state_(nullptr),
|
| + owner_(owner) {
|
| fields_.resize(field_number);
|
| phi_.resize(field_number, false);
|
| }
|
|
|
| - Node* GetField(size_t offset) {
|
| - if (offset < fields_.size()) {
|
| - return fields_[offset];
|
| - }
|
| - return nullptr;
|
| - }
|
| + Node* GetField(size_t offset) { return fields_[offset]; }
|
|
|
| - bool IsCreatedPhi(size_t offset) {
|
| - if (offset < phi_.size()) {
|
| - return phi_[offset];
|
| - }
|
| - return false;
|
| - }
|
| + bool IsCreatedPhi(size_t offset) { return phi_[offset]; }
|
|
|
| - bool SetField(size_t offset, Node* node, bool created_phi = false) {
|
| - bool changed = fields_[offset] != node || phi_[offset] != created_phi;
|
| + void SetField(size_t offset, Node* node, bool created_phi = false) {
|
| fields_[offset] = node;
|
| 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_ != kUntracked; }
|
| + bool IsTracked() const { return status_ & kTracked; }
|
| + bool IsInitialized() const { return status_ & kInitialized; }
|
| + bool SetInitialized() { return status_ |= kInitialized; }
|
| + VirtualState* owner() const { return owner_; }
|
|
|
| Node** fields_array() { return &fields_.front(); }
|
| size_t field_count() { return fields_.size(); }
|
| @@ -103,33 +102,50 @@ class VirtualObject : public ZoneObject {
|
| }
|
| return false;
|
| }
|
| - bool ClearAllFields() {
|
| - bool changed = false;
|
| + void ClearAllFields() {
|
| + 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) {
|
| if (fields_[i] != nullptr) {
|
| - fields_[i] = nullptr;
|
| - changed = true;
|
| + return false;
|
| }
|
| - phi_[i] = false;
|
| }
|
| - return changed;
|
| + return true;
|
| }
|
| bool UpdateFrom(const VirtualObject& other);
|
| void SetObjectState(Node* node) { object_state_ = node; }
|
| 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_; }
|
| void id(NodeId id) { id_ = id; }
|
|
|
| private:
|
| NodeId id_;
|
| - Status status_;
|
| + StatusFlags status_;
|
| ZoneVector<Node*> fields_;
|
| ZoneVector<bool> phi_;
|
| Node* object_state_;
|
| + VirtualState* owner_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(VirtualObject);
|
| };
|
|
|
|
|
| +DEFINE_OPERATORS_FOR_FLAGS(VirtualObject::StatusFlags)
|
| +
|
| +
|
| bool VirtualObject::UpdateFrom(const VirtualObject& other) {
|
| bool changed = status_ != other.status_;
|
| status_ = other.status_;
|
| @@ -150,24 +166,32 @@ bool VirtualObject::UpdateFrom(const VirtualObject& other) {
|
|
|
| class VirtualState : public ZoneObject {
|
| public:
|
| - VirtualState(Zone* zone, size_t size);
|
| - VirtualState(const VirtualState& states);
|
| + VirtualState(Node* owner, Zone* zone, size_t size);
|
| + VirtualState(Node* owner, const VirtualState& states);
|
|
|
| VirtualObject* VirtualObjectFromAlias(size_t alias);
|
| - VirtualObject* GetOrCreateTrackedVirtualObject(EscapeAnalysis::Alias alias,
|
| - NodeId id, size_t fields,
|
| - Zone* zone);
|
| - void SetVirtualObject(EscapeAnalysis::Alias alias, VirtualObject* state);
|
| - void LastChangedAt(Node* node) { last_changed_ = node; }
|
| - Node* GetLastChanged() { return last_changed_; }
|
| + VirtualObject* GetOrCreateTrackedVirtualObject(Alias alias, NodeId id,
|
| + size_t fields,
|
| + bool initialized, Zone* zone,
|
| + bool force_copy);
|
| + void SetVirtualObject(Alias alias, VirtualObject* state);
|
| bool UpdateFrom(VirtualState* state, Zone* zone);
|
| 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(); }
|
| + Node* owner() const { return owner_; }
|
| + VirtualObject* Copy(VirtualObject* obj, Alias alias);
|
| + void SetCopyRequired() {
|
| + for (VirtualObject* obj : info_) {
|
| + if (obj) obj->SetCopyRequired();
|
| + }
|
| + }
|
|
|
| private:
|
| ZoneVector<VirtualObject*> info_;
|
| - Node* last_changed_;
|
| + Node* owner_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(VirtualState);
|
| };
|
|
|
|
|
| @@ -175,9 +199,9 @@ class MergeCache : public ZoneObject {
|
| public:
|
| explicit MergeCache(Zone* zone)
|
| : states_(zone), objects_(zone), fields_(zone) {
|
| - states_.reserve(4);
|
| - objects_.reserve(4);
|
| - fields_.reserve(4);
|
| + states_.reserve(5);
|
| + objects_.reserve(5);
|
| + fields_.reserve(5);
|
| }
|
| ZoneVector<VirtualState*>& states() { return states_; }
|
| ZoneVector<VirtualObject*>& objects() { return objects_; }
|
| @@ -187,20 +211,21 @@ class MergeCache : public ZoneObject {
|
| objects_.clear();
|
| fields_.clear();
|
| }
|
| - size_t LoadVirtualObjectsFromStatesFor(EscapeAnalysis::Alias alias);
|
| - void LoadVirtualObjectsForFieldsFrom(
|
| - VirtualState* state, const ZoneVector<EscapeAnalysis::Alias>& aliases);
|
| + size_t LoadVirtualObjectsFromStatesFor(Alias alias);
|
| + void LoadVirtualObjectsForFieldsFrom(VirtualState* state,
|
| + const ZoneVector<Alias>& aliases);
|
| Node* GetFields(size_t pos);
|
|
|
| private:
|
| ZoneVector<VirtualState*> states_;
|
| ZoneVector<VirtualObject*> objects_;
|
| ZoneVector<Node*> fields_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(MergeCache);
|
| };
|
|
|
|
|
| -size_t MergeCache::LoadVirtualObjectsFromStatesFor(
|
| - EscapeAnalysis::Alias alias) {
|
| +size_t MergeCache::LoadVirtualObjectsFromStatesFor(Alias alias) {
|
| objects_.clear();
|
| DCHECK_GT(states_.size(), 0u);
|
| size_t min = std::numeric_limits<size_t>::max();
|
| @@ -215,11 +240,11 @@ size_t MergeCache::LoadVirtualObjectsFromStatesFor(
|
|
|
|
|
| void MergeCache::LoadVirtualObjectsForFieldsFrom(
|
| - VirtualState* state, const ZoneVector<EscapeAnalysis::Alias>& aliases) {
|
| + VirtualState* state, const ZoneVector<Alias>& aliases) {
|
| objects_.clear();
|
| size_t max_alias = state->size();
|
| for (Node* field : fields_) {
|
| - EscapeAnalysis::Alias alias = aliases[field->id()];
|
| + Alias alias = aliases[field->id()];
|
| if (alias >= max_alias) continue;
|
| if (VirtualObject* obj = state->VirtualObjectFromAlias(alias)) {
|
| objects_.push_back(obj);
|
| @@ -230,8 +255,11 @@ void MergeCache::LoadVirtualObjectsForFieldsFrom(
|
|
|
| Node* MergeCache::GetFields(size_t pos) {
|
| fields_.clear();
|
| - Node* rep = objects_.front()->GetField(pos);
|
| + Node* rep = pos >= objects_.front()->field_count()
|
| + ? nullptr
|
| + : objects_.front()->GetField(pos);
|
| for (VirtualObject* obj : objects_) {
|
| + if (pos >= obj->field_count()) continue;
|
| Node* field = obj->GetField(pos);
|
| if (field) {
|
| fields_.push_back(field);
|
| @@ -244,56 +272,68 @@ Node* MergeCache::GetFields(size_t pos) {
|
| }
|
|
|
|
|
| -VirtualState::VirtualState(Zone* zone, size_t size)
|
| - : info_(size, nullptr, zone), last_changed_(nullptr) {}
|
| +VirtualState::VirtualState(Node* owner, Zone* zone, size_t size)
|
| + : 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()),
|
| - last_changed_(state.last_changed_) {
|
| - for (size_t i = 0; i < state.info_.size(); ++i) {
|
| + owner_(owner) {
|
| + for (size_t i = 0; i < info_.size(); ++i) {
|
| if (state.info_[i]) {
|
| - info_[i] =
|
| - new (info_.get_allocator().zone()) VirtualObject(*state.info_[i]);
|
| + info_[i] = 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) {
|
| return info_[alias];
|
| }
|
|
|
|
|
| VirtualObject* VirtualState::GetOrCreateTrackedVirtualObject(
|
| - EscapeAnalysis::Alias alias, NodeId id, size_t field_number, Zone* zone) {
|
| - if (VirtualObject* obj = VirtualObjectFromAlias(alias)) {
|
| - return obj;
|
| + Alias alias, NodeId id, size_t field_number, bool initialized, Zone* zone,
|
| + bool force_copy) {
|
| + 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);
|
| return obj;
|
| }
|
|
|
|
|
| -void VirtualState::SetVirtualObject(EscapeAnalysis::Alias alias,
|
| - VirtualObject* obj) {
|
| +void VirtualState::SetVirtualObject(Alias alias, VirtualObject* obj) {
|
| info_[alias] = obj;
|
| }
|
|
|
|
|
| bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) {
|
| + if (from == this) return false;
|
| bool changed = false;
|
| - for (EscapeAnalysis::Alias alias = 0; alias < size(); ++alias) {
|
| + for (Alias alias = 0; alias < size(); ++alias) {
|
| VirtualObject* ls = VirtualObjectFromAlias(alias);
|
| VirtualObject* rs = from->VirtualObjectFromAlias(alias);
|
|
|
| - if (rs == nullptr) {
|
| - continue;
|
| - }
|
| + if (ls == rs || rs == nullptr) continue;
|
|
|
| if (ls == nullptr) {
|
| - ls = new (zone) VirtualObject(*rs);
|
| + ls = new (zone) VirtualObject(this, *rs);
|
| SetVirtualObject(alias, ls);
|
| changed = true;
|
| continue;
|
| @@ -355,14 +395,30 @@ Node* EscapeAnalysis::GetReplacementIfSame(ZoneVector<VirtualObject*>& objs) {
|
|
|
|
|
| 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);
|
| bool changed = false;
|
| - for (EscapeAnalysis::Alias alias = 0; alias < size(); ++alias) {
|
| - size_t fields = cache->LoadVirtualObjectsFromStatesFor(alias);
|
| + for (Alias alias = 0; alias < size(); ++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()) {
|
| - VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject(
|
| - alias, cache->objects().front()->id(), fields, zone);
|
| + mergeObject = GetOrCreateTrackedVirtualObject(
|
| + alias, cache->objects().front()->id(),
|
| + cache->objects().front()->IsInitialized(), fields, zone,
|
| + copy_merge_object);
|
| #ifdef DEBUG
|
| if (FLAG_trace_turbo_escape) {
|
| PrintF(" Alias @%d, merging into %p virtual objects", alias,
|
| @@ -376,11 +432,12 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
|
| changed = mergeObject->ResizeFields(fields) || changed;
|
| for (size_t i = 0; i < fields; ++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());
|
| } else {
|
| 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);
|
| if (!rep || !mergeObject->IsCreatedPhi(i)) {
|
| cache->fields().push_back(control);
|
| @@ -403,36 +460,27 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
|
| } else {
|
| DCHECK(rep->opcode() == IrOpcode::kPhi);
|
| for (int n = 0; n < value_input_count; ++n) {
|
| - if (n < rep->op()->ValueInputCount()) {
|
| - Node* old = NodeProperties::GetValueInput(rep, n);
|
| - if (old != cache->fields()[n]) {
|
| - changed = true;
|
| - NodeProperties::ReplaceValueInput(rep, cache->fields()[n],
|
| - n);
|
| - }
|
| - } else {
|
| + Node* old = NodeProperties::GetValueInput(rep, n);
|
| + if (old != cache->fields()[n]) {
|
| 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 {
|
| if (mergeObject->GetField(i) != nullptr) {
|
| TRACE(" Field %zu cleared\n", i);
|
| changed = true;
|
| }
|
| - changed = mergeObject->SetField(i, nullptr) || changed;
|
| + mergeObject->SetField(i, nullptr);
|
| }
|
| }
|
| }
|
| } else {
|
| + if (mergeObject) {
|
| + TRACE(" Alias %d, virtual object removed\n", alias);
|
| + changed = true;
|
| + }
|
| SetVirtualObject(alias, nullptr);
|
| }
|
| }
|
| @@ -442,11 +490,14 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
|
|
|
| EscapeStatusAnalysis::EscapeStatusAnalysis(EscapeAnalysis* object_analysis,
|
| Graph* graph, Zone* zone)
|
| - : object_analysis_(object_analysis),
|
| + : stack_(zone),
|
| + object_analysis_(object_analysis),
|
| graph_(graph),
|
| zone_(zone),
|
| status_(graph->NodeCount(), kUnknown, zone),
|
| - queue_(zone) {}
|
| + next_free_alias_(0),
|
| + status_stack_(zone),
|
| + aliases_(zone) {}
|
|
|
|
|
| EscapeStatusAnalysis::~EscapeStatusAnalysis() {}
|
| @@ -485,31 +536,33 @@ bool EscapeStatusAnalysis::SetEscaped(Node* node) {
|
| }
|
|
|
|
|
| -void EscapeStatusAnalysis::Resize() {
|
| - status_.resize(graph()->NodeCount(), kUnknown);
|
| +void EscapeStatusAnalysis::ResizeStatusVector() {
|
| + 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() {
|
| - Resize();
|
| - queue_.push_back(graph()->end());
|
| - status_[graph()->end()->id()] |= kOnStack;
|
| - while (!queue_.empty()) {
|
| - Node* node = queue_.front();
|
| - queue_.pop_front();
|
| +void EscapeStatusAnalysis::RunStatusAnalysis() {
|
| + ResizeStatusVector();
|
| + while (!status_stack_.empty()) {
|
| + Node* node = status_stack_.back();
|
| + status_stack_.pop_back();
|
| status_[node->id()] &= ~kOnStack;
|
| Process(node);
|
| 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) {
|
| for (Edge edge : node->input_edges()) {
|
| Node* input = edge.to();
|
| if (!(status_[input->id()] & kOnStack)) {
|
| - queue_.push_back(input);
|
| + status_stack_.push_back(input);
|
| status_[input->id()] |= kOnStack;
|
| }
|
| }
|
| @@ -528,8 +581,8 @@ void EscapeStatusAnalysis::RevisitInputs(Node* node) {
|
| void EscapeStatusAnalysis::RevisitUses(Node* node) {
|
| for (Edge edge : node->use_edges()) {
|
| Node* use = edge.from();
|
| - if (!(status_[use->id()] & kOnStack)) {
|
| - queue_.push_back(use);
|
| + if (!(status_[use->id()] & kOnStack) && !IsNotReachable(use)) {
|
| + status_stack_.push_back(use);
|
| status_[use->id()] |= kOnStack;
|
| }
|
| }
|
| @@ -558,15 +611,17 @@ void EscapeStatusAnalysis::Process(Node* node) {
|
| RevisitUses(rep);
|
| }
|
| }
|
| + RevisitUses(node);
|
| break;
|
| }
|
| case IrOpcode::kPhi:
|
| if (!HasEntry(node)) {
|
| status_[node->id()] |= kTracked;
|
| - if (!IsAllocationPhi(node)) {
|
| - SetEscaped(node);
|
| - RevisitUses(node);
|
| - }
|
| + RevisitUses(node);
|
| + }
|
| + if (!IsAllocationPhi(node) && SetEscaped(node)) {
|
| + RevisitInputs(node);
|
| + RevisitUses(node);
|
| }
|
| CheckUsesForEscape(node);
|
| default:
|
| @@ -623,10 +678,11 @@ void EscapeStatusAnalysis::ProcessAllocate(Node* node) {
|
| node->InputAt(0)->opcode() != IrOpcode::kInt64Constant &&
|
| node->InputAt(0)->opcode() != IrOpcode::kFloat32Constant &&
|
| node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant);
|
| + RevisitUses(node);
|
| if (!size.HasValue() && SetEscaped(node)) {
|
| - RevisitUses(node);
|
| 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;
|
| }
|
| }
|
| @@ -640,6 +696,7 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep,
|
| bool phi_escaping) {
|
| for (Edge edge : uses->use_edges()) {
|
| Node* use = edge.from();
|
| + if (IsNotReachable(use)) continue;
|
| if (edge.index() >= use->op()->ValueInputCount() +
|
| OperatorProperties::GetContextInputCount(use->op()))
|
| continue;
|
| @@ -730,15 +787,11 @@ void EscapeStatusAnalysis::DebugPrint() {
|
|
|
| EscapeAnalysis::EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common,
|
| Zone* zone)
|
| - : graph_(graph),
|
| + : status_analysis_(this, graph, zone),
|
| common_(common),
|
| - zone_(zone),
|
| virtual_states_(zone),
|
| replacements_(zone),
|
| - escape_status_(this, graph, zone),
|
| - cache_(new (zone) MergeCache(zone)),
|
| - aliases_(zone),
|
| - next_free_alias_(0) {}
|
| + cache_(new (zone) MergeCache(zone)) {}
|
|
|
|
|
| EscapeAnalysis::~EscapeAnalysis() {}
|
| @@ -746,42 +799,47 @@ EscapeAnalysis::~EscapeAnalysis() {}
|
|
|
| void EscapeAnalysis::Run() {
|
| replacements_.resize(graph()->NodeCount());
|
| - AssignAliases();
|
| - if (AliasCount() == 0) return;
|
| - escape_status_.Resize();
|
| + status_analysis_.AssignAliases();
|
| + if (status_analysis_.AliasCount() == 0) return;
|
| + status_analysis_.ResizeStatusVector();
|
| RunObjectAnalysis();
|
| - escape_status_.Run();
|
| + status_analysis_.RunStatusAnalysis();
|
| }
|
|
|
|
|
| -void EscapeAnalysis::AssignAliases() {
|
| - ZoneVector<Node*> stack(zone());
|
| - stack.push_back(graph()->end());
|
| +void EscapeStatusAnalysis::AssignAliases() {
|
| + stack_.reserve(graph()->NodeCount() * 0.2);
|
| + ResizeStatusVector();
|
| + stack_.push_back(graph()->end());
|
| CHECK_LT(graph()->NodeCount(), kUntrackable);
|
| aliases_.resize(graph()->NodeCount(), kNotReachable);
|
| aliases_[graph()->end()->id()] = kUntrackable;
|
| + status_stack_.reserve(8);
|
| TRACE("Discovering trackable nodes");
|
| - while (!stack.empty()) {
|
| - Node* node = stack.back();
|
| - stack.pop_back();
|
| + 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();
|
| TRACE(" @%d:%s#%u", aliases_[node->id()], node->op()->mnemonic(),
|
| node->id());
|
| + EnqueueForStatusAnalysis(node);
|
| }
|
| break;
|
| case IrOpcode::kFinishRegion: {
|
| Node* allocate = NodeProperties::GetValueInput(node, 0);
|
| + DCHECK_NOT_NULL(allocate);
|
| if (allocate->opcode() == IrOpcode::kAllocate) {
|
| if (aliases_[allocate->id()] >= kUntrackable) {
|
| if (aliases_[allocate->id()] == kNotReachable) {
|
| - stack.push_back(allocate);
|
| + stack_.push_back(allocate);
|
| }
|
| aliases_[allocate->id()] = NextAlias();
|
| TRACE(" @%d:%s#%u", aliases_[allocate->id()],
|
| allocate->op()->mnemonic(), allocate->id());
|
| + EnqueueForStatusAnalysis(allocate);
|
| }
|
| aliases_[node->id()] = aliases_[allocate->id()];
|
| TRACE(" @%d:%s#%u", aliases_[node->id()], node->op()->mnemonic(),
|
| @@ -801,7 +859,7 @@ void EscapeAnalysis::AssignAliases() {
|
| for (Edge edge : node->input_edges()) {
|
| Node* input = edge.to();
|
| if (aliases_[input->id()] == kNotReachable) {
|
| - stack.push_back(input);
|
| + stack_.push_back(input);
|
| aliases_[input->id()] = kUntrackable;
|
| }
|
| }
|
| @@ -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() {
|
| virtual_states_.resize(graph()->NodeCount());
|
| - ZoneVector<Node*> stack(zone());
|
| - stack.push_back(graph()->start());
|
| - while (!stack.empty()) {
|
| - Node* node = stack.back();
|
| - stack.pop_back();
|
| - if (aliases_[node->id()] != kNotReachable && Process(node)) {
|
| + stack().push_back(graph()->start());
|
| + while (!stack().empty()) {
|
| + Node* node = stack().back();
|
| + stack().pop_back();
|
| + if (Process(node)) {
|
| for (Edge edge : node->use_edges()) {
|
| + Node* use = edge.from();
|
| + if (IsNotReachable(use)) {
|
| + continue;
|
| + }
|
| if (NodeProperties::IsEffectEdge(edge)) {
|
| - Node* use = edge.from();
|
| if ((use->opcode() != IrOpcode::kLoadField &&
|
| use->opcode() != IrOpcode::kLoadElement) ||
|
| !IsDanglingEffectNode(use)) {
|
| - stack.push_back(use);
|
| + stack().push_back(use);
|
| }
|
| }
|
| }
|
| // First process loads: dangling loads are a problem otherwise.
|
| for (Edge edge : node->use_edges()) {
|
| + Node* use = edge.from();
|
| + if (IsNotReachable(use)) {
|
| + continue;
|
| + }
|
| if (NodeProperties::IsEffectEdge(edge)) {
|
| - Node* use = edge.from();
|
| if ((use->opcode() == IrOpcode::kLoadField ||
|
| use->opcode() == IrOpcode::kLoadElement) &&
|
| IsDanglingEffectNode(use)) {
|
| - stack.push_back(use);
|
| + stack().push_back(use);
|
| }
|
| }
|
| }
|
| @@ -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()->EffectOutputCount() == 0) return false;
|
| if (node->op()->EffectInputCount() == 1 &&
|
| @@ -857,17 +931,47 @@ bool EscapeAnalysis::IsDanglingEffectNode(Node* node) {
|
| // 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
|
| // produce effects in this instance. We don't consider these nodes dangling.
|
| + status_[node->id()] |= kDanglingComputed;
|
| return false;
|
| }
|
| for (Edge edge : node->use_edges()) {
|
| + Node* use = edge.from();
|
| + if (aliases_[use->id()] == kNotReachable) continue;
|
| if (NodeProperties::IsEffectEdge(edge)) {
|
| + status_[node->id()] |= kDanglingComputed;
|
| return false;
|
| }
|
| }
|
| + status_[node->id()] |= kDanglingComputed | kDangling;
|
| 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) {
|
| switch (node->opcode()) {
|
| case IrOpcode::kAllocate:
|
| @@ -911,8 +1015,9 @@ bool EscapeAnalysis::Process(Node* node) {
|
| void EscapeAnalysis::ProcessAllocationUsers(Node* node) {
|
| for (Edge edge : node->input_edges()) {
|
| Node* input = edge.to();
|
| - if (!NodeProperties::IsValueEdge(edge) &&
|
| - !NodeProperties::IsContextEdge(edge))
|
| + Node* use = edge.from();
|
| + if (edge.index() >= use->op()->ValueInputCount() +
|
| + OperatorProperties::GetContextInputCount(use->op()))
|
| continue;
|
| switch (node->opcode()) {
|
| case IrOpcode::kStoreField:
|
| @@ -928,8 +1033,11 @@ void EscapeAnalysis::ProcessAllocationUsers(Node* node) {
|
| default:
|
| VirtualState* state = virtual_states_[node->id()];
|
| if (VirtualObject* obj = ResolveVirtualObject(state, input)) {
|
| - if (obj->ClearAllFields()) {
|
| - state->LastChangedAt(node);
|
| + if (!obj->AllFieldsClear()) {
|
| + obj = CopyForModificationAt(obj, state, node);
|
| + obj->ClearAllFields();
|
| + TRACE("Cleared all fields of @%d:#%d\n", GetAlias(obj->id()),
|
| + obj->id());
|
| }
|
| }
|
| break;
|
| @@ -938,21 +1046,34 @@ void EscapeAnalysis::ProcessAllocationUsers(Node* node) {
|
| }
|
|
|
|
|
| -bool EscapeAnalysis::IsEffectBranchPoint(Node* node) {
|
| - int count = 0;
|
| - for (Edge edge : node->use_edges()) {
|
| - if (NodeProperties::IsEffectEdge(edge)) {
|
| - if (++count > 1) {
|
| - return true;
|
| - }
|
| - }
|
| +VirtualState* EscapeAnalysis::CopyForModificationAt(VirtualState* state,
|
| + Node* node) {
|
| + if (state->owner() != node) {
|
| + VirtualState* new_state = new (zone()) VirtualState(node, *state);
|
| + virtual_states_[node->id()] = new_state;
|
| + 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) {
|
| DCHECK_EQ(node->op()->EffectInputCount(), 1);
|
| +#ifdef DEBUG
|
| if (node->opcode() != IrOpcode::kLoadField &&
|
| node->opcode() != IrOpcode::kLoadElement &&
|
| node->opcode() != IrOpcode::kLoad && IsDanglingEffectNode(node)) {
|
| @@ -960,38 +1081,42 @@ void EscapeAnalysis::ForwardVirtualState(Node* node) {
|
| node->op()->mnemonic());
|
| UNREACHABLE();
|
| }
|
| +#endif // DEBUG
|
| Node* effect = NodeProperties::GetEffectInput(node);
|
| // Break the cycle for effect phis.
|
| - if (effect->opcode() == IrOpcode::kEffectPhi) {
|
| - if (virtual_states_[effect->id()] == nullptr) {
|
| - virtual_states_[effect->id()] =
|
| - new (zone()) VirtualState(zone(), AliasCount());
|
| - }
|
| + if (effect->opcode() == IrOpcode::kEffectPhi &&
|
| + virtual_states_[effect->id()] == nullptr) {
|
| + VirtualState* state =
|
| + 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()]);
|
| - if (IsEffectBranchPoint(effect)) {
|
| - TRACE("Copying virtual state %p from #%d (%s) to #%d (%s)\n",
|
| - static_cast<void*>(virtual_states_[effect->id()]), effect->id(),
|
| - 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());
|
| - }
|
| + if (virtual_states_[node->id()]) {
|
| + virtual_states_[node->id()]->UpdateFrom(virtual_states_[effect->id()],
|
| + zone());
|
| } else {
|
| virtual_states_[node->id()] = virtual_states_[effect->id()];
|
| - TRACE("Forwarding virtual state %p from #%d (%s) to #%d (%s)\n",
|
| - static_cast<void*>(virtual_states_[effect->id()]), effect->id(),
|
| - effect->op()->mnemonic(), node->id(), node->op()->mnemonic());
|
| + TRACE("Forwarding object state %p from %s#%d to %s#%d",
|
| + static_cast<void*>(virtual_states_[effect->id()]),
|
| + 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) {
|
| 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) {
|
|
|
| VirtualState* mergeState = virtual_states_[node->id()];
|
| if (!mergeState) {
|
| - mergeState = new (zone()) VirtualState(zone(), AliasCount());
|
| + mergeState = new (zone()) VirtualState(node, zone(), AliasCount());
|
| virtual_states_[node->id()] = mergeState;
|
| changed = true;
|
| TRACE("Effect Phi #%d got new virtual state %p.\n", node->id(),
|
| static_cast<void*>(mergeState));
|
| - } else if (mergeState->GetLastChanged() != node) {
|
| - changed = true;
|
| }
|
|
|
| cache_->Clear();
|
| @@ -1020,6 +1143,11 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) {
|
| VirtualState* state = virtual_states_[input->id()];
|
| if (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(),
|
| input->op()->mnemonic());
|
| @@ -1031,14 +1159,14 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) {
|
| }
|
|
|
| changed = mergeState->MergeFrom(cache_, zone(), graph(), common(),
|
| - NodeProperties::GetControlInput(node)) ||
|
| + NodeProperties::GetControlInput(node),
|
| + node->op()->EffectInputCount()) ||
|
| changed;
|
|
|
| TRACE("Merge %s the node.\n", changed ? "changed" : "did not change");
|
|
|
| if (changed) {
|
| - mergeState->LastChangedAt(node);
|
| - escape_status_.Resize();
|
| + status_analysis_.ResizeStatusVector();
|
| }
|
| return changed;
|
| }
|
| @@ -1048,13 +1176,15 @@ void EscapeAnalysis::ProcessAllocation(Node* node) {
|
| DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
|
| ForwardVirtualState(node);
|
| VirtualState* state = virtual_states_[node->id()];
|
| - Alias alias = aliases_[node->id()];
|
| + Alias alias = GetAlias(node->id());
|
|
|
| // Check if we have already processed this node.
|
| if (state->VirtualObjectFromAlias(alias)) {
|
| return;
|
| }
|
|
|
| + state = CopyForModificationAt(state, node);
|
| +
|
| NumberMatcher size(node->InputAt(0));
|
| DCHECK(node->InputAt(0)->opcode() != IrOpcode::kInt32Constant &&
|
| node->InputAt(0)->opcode() != IrOpcode::kInt64Constant &&
|
| @@ -1062,13 +1192,12 @@ void EscapeAnalysis::ProcessAllocation(Node* node) {
|
| node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant);
|
| if (size.HasValue()) {
|
| state->SetVirtualObject(
|
| - alias, new (zone()) VirtualObject(node->id(), zone(),
|
| - size.Value() / kPointerSize));
|
| + alias, new (zone()) VirtualObject(node->id(), state, zone(),
|
| + size.Value() / kPointerSize, false));
|
| } else {
|
| - state->SetVirtualObject(alias,
|
| - new (zone()) VirtualObject(node->id(), zone()));
|
| + state->SetVirtualObject(
|
| + alias, new (zone()) VirtualObject(node->id(), state, zone()));
|
| }
|
| - state->LastChangedAt(node);
|
| }
|
|
|
|
|
| @@ -1078,15 +1207,9 @@ void EscapeAnalysis::ProcessFinishRegion(Node* node) {
|
| Node* allocation = NodeProperties::GetValueInput(node, 0);
|
| if (allocation->opcode() == IrOpcode::kAllocate) {
|
| VirtualState* state = virtual_states_[node->id()];
|
| - if (!state->VirtualObjectFromAlias(aliases_[node->id()])) {
|
| - VirtualObject* vobj_alloc =
|
| - state->VirtualObjectFromAlias(aliases_[allocation->id()]);
|
| - 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);
|
| - }
|
| + VirtualObject* obj = state->VirtualObjectFromAlias(GetAlias(node->id()));
|
| + DCHECK_NOT_NULL(obj);
|
| + obj->SetInitialized();
|
| }
|
| }
|
|
|
| @@ -1112,7 +1235,6 @@ bool EscapeAnalysis::SetReplacement(Node* node, Node* rep) {
|
| bool EscapeAnalysis::UpdateReplacement(VirtualState* state, Node* node,
|
| Node* rep) {
|
| if (SetReplacement(node, rep)) {
|
| - state->LastChangedAt(node);
|
| if (rep) {
|
| TRACE("Replacement of #%d is #%d (%s)\n", node->id(), rep->id(),
|
| rep->op()->mnemonic());
|
| @@ -1149,29 +1271,29 @@ Node* EscapeAnalysis::GetReplacement(NodeId id) {
|
|
|
|
|
| bool EscapeAnalysis::IsVirtual(Node* node) {
|
| - if (node->id() >= escape_status_.size()) {
|
| + if (node->id() >= status_analysis_.GetStatusVectorSize()) {
|
| return false;
|
| }
|
| - return escape_status_.IsVirtual(node);
|
| + return status_analysis_.IsVirtual(node);
|
| }
|
|
|
|
|
| bool EscapeAnalysis::IsEscaped(Node* node) {
|
| - if (node->id() >= escape_status_.size()) {
|
| + if (node->id() >= status_analysis_.GetStatusVectorSize()) {
|
| return false;
|
| }
|
| - return escape_status_.IsEscaped(node);
|
| + return status_analysis_.IsEscaped(node);
|
| }
|
|
|
|
|
| bool EscapeAnalysis::SetEscaped(Node* node) {
|
| - return escape_status_.SetEscaped(node);
|
| + return status_analysis_.SetEscaped(node);
|
| }
|
|
|
|
|
| VirtualObject* EscapeAnalysis::GetVirtualObject(Node* at, NodeId id) {
|
| if (VirtualState* states = virtual_states_[at->id()]) {
|
| - return states->VirtualObjectFromAlias(aliases_[id]);
|
| + return states->VirtualObjectFromAlias(GetAlias(id));
|
| }
|
| return nullptr;
|
| }
|
| @@ -1210,7 +1332,8 @@ void EscapeAnalysis::ProcessLoadFromPhi(int offset, Node* from, Node* node,
|
| cache_->fields().push_back(input);
|
| }
|
|
|
| - cache_->LoadVirtualObjectsForFieldsFrom(state, aliases_);
|
| + cache_->LoadVirtualObjectsForFieldsFrom(state,
|
| + status_analysis_.GetAliasMap());
|
| if (cache_->objects().size() == cache_->fields().size()) {
|
| cache_->GetFields(offset);
|
| if (cache_->fields().size() == cache_->objects().size()) {
|
| @@ -1221,9 +1344,8 @@ void EscapeAnalysis::ProcessLoadFromPhi(int offset, Node* from, Node* node,
|
| Node* phi = graph()->NewNode(
|
| common()->Phi(MachineRepresentation::kTagged, value_input_count),
|
| value_input_count + 1, &cache_->fields().front());
|
| - escape_status_.Resize();
|
| + status_analysis_.ResizeStatusVector();
|
| SetReplacement(node, phi);
|
| - state->LastChangedAt(node);
|
| TRACE(" got phi created.\n");
|
| } else {
|
| TRACE(" has already phi #%d.\n", rep->id());
|
| @@ -1244,7 +1366,10 @@ void EscapeAnalysis::ProcessLoadField(Node* node) {
|
| VirtualState* state = virtual_states_[node->id()];
|
| if (VirtualObject* object = GetVirtualObject(state, from)) {
|
| 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);
|
| if (value) {
|
| value = ResolveReplacement(value);
|
| @@ -1253,9 +1378,9 @@ void EscapeAnalysis::ProcessLoadField(Node* node) {
|
| UpdateReplacement(state, node, value);
|
| } else if (from->opcode() == IrOpcode::kPhi &&
|
| OpParameter<FieldAccess>(node).offset % kPointerSize == 0) {
|
| - int offset = OffsetFromAccess(node);
|
| - // Only binary phis are supported for now.
|
| - ProcessLoadFromPhi(offset, from, node, state);
|
| + int offset = OffsetFromAccess(node);
|
| + // Only binary phis are supported for now.
|
| + ProcessLoadFromPhi(offset, from, node, state);
|
| } else {
|
| UpdateReplacement(state, node, nullptr);
|
| }
|
| @@ -1281,7 +1406,11 @@ void EscapeAnalysis::ProcessLoadElement(Node* node) {
|
| kPointerSizeLog2);
|
| 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);
|
| if (value) {
|
| value = ResolveReplacement(value);
|
| @@ -1314,11 +1443,13 @@ void EscapeAnalysis::ProcessStoreField(Node* node) {
|
| Node* to = ResolveReplacement(NodeProperties::GetValueInput(node, 0));
|
| VirtualState* state = virtual_states_[node->id()];
|
| 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));
|
| - if (obj->SetField(offset, val)) {
|
| - state->LastChangedAt(node);
|
| + if (obj->GetField(offset) != val) {
|
| + obj = CopyForModificationAt(obj, state, node);
|
| + obj->SetField(offset, val);
|
| }
|
| }
|
| }
|
| @@ -1339,13 +1470,15 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) {
|
| VirtualObject* obj = GetVirtualObject(state, to);
|
| if (index.HasValue()) {
|
| 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()),
|
| kPointerSizeLog2);
|
| CHECK_EQ(access.header_size % kPointerSize, 0);
|
| Node* val = ResolveReplacement(NodeProperties::GetValueInput(node, 2));
|
| - if (obj->SetField(offset, val)) {
|
| - state->LastChangedAt(node);
|
| + if (obj->GetField(offset) != val) {
|
| + obj = CopyForModificationAt(obj, state, node);
|
| + obj->SetField(offset, val);
|
| }
|
| }
|
| } else {
|
| @@ -1357,9 +1490,13 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) {
|
| to->id(), to->op()->mnemonic(), node->id(), index_node->id(),
|
| index_node->op()->mnemonic());
|
| }
|
| - if (obj && obj->IsTracked() && obj->ClearAllFields()) {
|
| - state->LastChangedAt(node);
|
| - TRACE("Cleared all fields of @%d:#%d\n", aliases_[obj->id()], obj->id());
|
| + if (obj && obj->IsTracked()) {
|
| + if (!obj->AllFieldsClear()) {
|
| + 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() {
|
|
|
| VirtualObject* EscapeAnalysis::GetVirtualObject(VirtualState* state,
|
| Node* node) {
|
| - if (node->id() >= aliases_.size()) return nullptr;
|
| - Alias alias = aliases_[node->id()];
|
| + if (node->id() >= status_analysis_.GetAliasMap().size()) return nullptr;
|
| + Alias alias = GetAlias(node->id());
|
| if (alias >= state->size()) return nullptr;
|
| return state->VirtualObjectFromAlias(alias);
|
| }
|
|
|
|
|
| bool EscapeAnalysis::ExistsVirtualAllocate() {
|
| - for (size_t id = 0; id < aliases_.size(); ++id) {
|
| - Alias alias = aliases_[id];
|
| - if (alias < kUntrackable) {
|
| - if (escape_status_.IsVirtual(static_cast<int>(id))) {
|
| + for (size_t id = 0; id < status_analysis_.GetAliasMap().size(); ++id) {
|
| + Alias alias = GetAlias(static_cast<NodeId>(id));
|
| + if (alias < EscapeStatusAnalysis::kUntrackable) {
|
| + if (status_analysis_.IsVirtual(static_cast<int>(id))) {
|
| return true;
|
| }
|
| }
|
|
|