Index: src/compiler/escape-analysis.cc |
diff --git a/src/compiler/escape-analysis.cc b/src/compiler/escape-analysis.cc |
index c7c451ce06464f1411f97eb5774ef1cefe9ac06e..0103a6f65766489400860f141378df338ab7430b 100644 |
--- a/src/compiler/escape-analysis.cc |
+++ b/src/compiler/escape-analysis.cc |
@@ -29,17 +29,27 @@ class VirtualObject : public ZoneObject { |
public: |
enum Status { kUntracked = 0, kTracked = 1 }; |
VirtualObject(NodeId id, Zone* zone) |
- : id_(id), status_(kUntracked), fields_(zone), replacement_(nullptr) {} |
+ : id_(id), |
+ status_(kUntracked), |
+ fields_(zone), |
+ phi_(zone), |
+ replacement_(nullptr) {} |
VirtualObject(const VirtualObject& other) |
: id_(other.id_), |
status_(other.status_), |
fields_(other.fields_), |
+ phi_(other.phi_), |
replacement_(other.replacement_) {} |
VirtualObject(NodeId id, Zone* zone, size_t field_number) |
- : id_(id), status_(kTracked), fields_(zone), replacement_(nullptr) { |
+ : id_(id), |
+ status_(kTracked), |
+ fields_(zone), |
+ phi_(zone), |
+ replacement_(nullptr) { |
fields_.resize(field_number); |
+ phi_.resize(field_number, false); |
} |
Node* GetField(size_t offset) { |
@@ -49,9 +59,21 @@ class VirtualObject : public ZoneObject { |
return nullptr; |
} |
- bool SetField(size_t offset, Node* node) { |
- bool changed = fields_[offset] != node; |
+ bool IsCreatedPhi(size_t offset) { |
+ if (offset < phi_.size()) { |
+ return phi_[offset]; |
+ } |
+ return false; |
+ } |
+ |
+ bool SetField(size_t offset, Node* node, bool created_phi = false) { |
+ bool changed = fields_[offset] != node || phi_[offset] != created_phi; |
fields_[offset] = node; |
+ phi_[offset] = created_phi; |
+ if (changed && FLAG_trace_turbo_escape && node) { |
+ PrintF("Setting field %zu of #%d to #%d (%s)\n", offset, id(), node->id(), |
+ node->op()->mnemonic()); |
+ } |
return changed; |
} |
bool IsVirtual() const { return status_ == kTracked; } |
@@ -68,6 +90,7 @@ class VirtualObject : public ZoneObject { |
bool ResizeFields(size_t field_count) { |
if (field_count != fields_.size()) { |
fields_.resize(field_count); |
+ phi_.resize(field_count); |
return true; |
} |
return false; |
@@ -79,6 +102,7 @@ class VirtualObject : public ZoneObject { |
fields_[i] = nullptr; |
changed = true; |
} |
+ phi_[i] = false; |
} |
return changed; |
} |
@@ -91,6 +115,7 @@ class VirtualObject : public ZoneObject { |
NodeId id_; |
Status status_; |
ZoneVector<Node*> fields_; |
+ ZoneVector<bool> phi_; |
Node* replacement_; |
}; |
@@ -133,8 +158,8 @@ class VirtualState : public ZoneObject { |
Node* ResolveReplacement(Node* node); |
bool UpdateReplacement(Node* node, Node* rep, Zone* zone); |
bool UpdateFrom(VirtualState* state, Zone* zone); |
- bool MergeFrom(VirtualState* left, VirtualState* right, Zone* zone, |
- Graph* graph, CommonOperatorBuilder* common, Node* control); |
+ bool MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, |
+ CommonOperatorBuilder* common, Node* control); |
size_t size() { return info_.size(); } |
@@ -230,7 +255,6 @@ bool VirtualState::UpdateFrom(NodeId id, VirtualObject* fromObj, Zone* zone) { |
bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) { |
- DCHECK_EQ(size(), from->size()); |
bool changed = false; |
for (NodeId id = 0; id < size(); ++id) { |
VirtualObject* ls = GetVirtualObject(id); |
@@ -257,59 +281,187 @@ bool VirtualState::UpdateFrom(VirtualState* from, Zone* zone) { |
} |
-bool VirtualState::MergeFrom(VirtualState* left, VirtualState* right, |
- Zone* zone, Graph* graph, |
+namespace { |
+ |
+size_t min_size(ZoneVector<VirtualState*>& states) { |
+ size_t min = SIZE_MAX; |
+ for (VirtualState* state : states) { |
+ min = std::min(state->size(), min); |
+ } |
+ return min; |
+} |
+ |
+ |
+size_t min_field_count(ZoneVector<VirtualObject*>& objs) { |
+ size_t min = SIZE_MAX; |
+ for (VirtualObject* obj : objs) { |
+ min = std::min(obj->field_count(), min); |
+ } |
+ return min; |
+} |
+ |
+ |
+void GetVirtualObjects(ZoneVector<VirtualState*> states, |
+ ZoneVector<VirtualObject*>& objs, NodeId id) { |
+ objs.clear(); |
+ for (VirtualState* state : states) { |
+ if (VirtualObject* obj = state->GetVirtualObject(id)) { |
+ objs.push_back(obj); |
+ } |
+ } |
+} |
+ |
+ |
+void GetVirtualObjects(VirtualState* state, ZoneVector<Node*> nodes, |
+ ZoneVector<VirtualObject*>& objs) { |
+ objs.clear(); |
+ for (Node* node : nodes) { |
+ if (VirtualObject* obj = state->GetVirtualObject(node)) { |
+ objs.push_back(obj); |
+ } |
+ } |
+} |
+ |
+ |
+Node* GetFieldIfSame(size_t pos, ZoneVector<VirtualObject*>& objs) { |
+ Node* rep = objs.front()->GetField(pos); |
+ for (VirtualObject* obj : objs) { |
+ if (obj->GetField(pos) != rep) { |
+ return nullptr; |
+ } |
+ } |
+ return rep; |
+} |
+ |
+ |
+Node* GetReplacementIfSame(ZoneVector<VirtualObject*>& objs) { |
+ Node* rep = objs.front()->GetReplacement(); |
+ for (VirtualObject* obj : objs) { |
+ if (obj->GetReplacement() != rep) { |
+ return nullptr; |
+ } |
+ } |
+ return rep; |
+} |
+ |
+ |
+void GetFields(ZoneVector<VirtualObject*>& objs, ZoneVector<Node*>& fields, |
+ size_t pos) { |
+ fields.clear(); |
+ for (VirtualObject* obj : objs) { |
+ if (Node* field = obj->GetField(pos)) { |
+ fields.push_back(field); |
+ } |
+ } |
+} |
+ |
+ |
+bool IsEquivalentPhi(Node* node1, Node* node2) { |
+ if (node1 == node2) return true; |
+ if (node1->opcode() != IrOpcode::kPhi || node2->opcode() != IrOpcode::kPhi || |
+ node1->op()->ValueInputCount() != node2->op()->ValueInputCount()) { |
+ return false; |
+ } |
+ for (int i = 0; i < node1->op()->ValueInputCount(); ++i) { |
+ Node* input1 = NodeProperties::GetValueInput(node1, i); |
+ Node* input2 = NodeProperties::GetValueInput(node2, i); |
+ if (!IsEquivalentPhi(input1, input2)) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+ |
+bool IsEquivalentPhi(Node* phi, ZoneVector<Node*>& inputs) { |
+ if (phi->opcode() != IrOpcode::kPhi) return false; |
+ if (phi->op()->ValueInputCount() != inputs.size()) { |
+ return false; |
+ } |
+ for (size_t i = 0; i < inputs.size(); ++i) { |
+ Node* input = NodeProperties::GetValueInput(phi, static_cast<int>(i)); |
+ if (!IsEquivalentPhi(input, inputs[i])) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+ |
+bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph, |
CommonOperatorBuilder* common, Node* control) { |
+ DCHECK_GT(cache->states().size(), 0u); |
bool changed = false; |
- for (NodeId id = 0; id < std::min(left->size(), right->size()); ++id) { |
- VirtualObject* ls = left->GetVirtualObject(id); |
- VirtualObject* rs = right->GetVirtualObject(id); |
- |
- if (ls != nullptr && rs != nullptr) { |
+ for (NodeId id = 0; id < min_size(cache->states()); ++id) { |
+ GetVirtualObjects(cache->states(), cache->objects(), id); |
+ if (cache->objects().size() == cache->states().size()) { |
+ // Don't process linked objects. |
+ if (cache->objects()[0]->id() != id) continue; |
if (FLAG_trace_turbo_escape) { |
- PrintF(" Merging fields of #%d\n", id); |
+ PrintF(" Merging virtual objects of #%d\n", id); |
} |
VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject(id, zone); |
- size_t fields = std::max(ls->field_count(), rs->field_count()); |
+ mergeObject->SetReplacement(GetReplacementIfSame(cache->objects())); |
+ size_t fields = min_field_count(cache->objects()); |
changed = mergeObject->ResizeFields(fields) || changed; |
for (size_t i = 0; i < fields; ++i) { |
- if (ls->GetField(i) == rs->GetField(i)) { |
- changed = mergeObject->SetField(i, ls->GetField(i)) || changed; |
- if (FLAG_trace_turbo_escape && ls->GetField(i)) { |
- PrintF(" Field %zu agree on rep #%d\n", i, |
- ls->GetField(i)->id()); |
+ if (Node* field = GetFieldIfSame(i, cache->objects())) { |
+ changed = mergeObject->SetField(i, field) || changed; |
+ if (FLAG_trace_turbo_escape) { |
+ PrintF(" Field %zu agree on rep #%d\n", i, field->id()); |
} |
- } else if (ls->GetField(i) != nullptr && rs->GetField(i) != nullptr) { |
- Node* rep = mergeObject->GetField(i); |
- if (!rep || rep->opcode() != IrOpcode::kPhi || |
- NodeProperties::GetValueInput(rep, 0) != ls->GetField(i) || |
- NodeProperties::GetValueInput(rep, 1) != rs->GetField(i)) { |
- Node* phi = |
- graph->NewNode(common->Phi(MachineRepresentation::kTagged, 2), |
- ls->GetField(i), rs->GetField(i), control); |
- if (mergeObject->SetField(i, phi)) { |
- if (FLAG_trace_turbo_escape) { |
- PrintF(" Creating Phi #%d as merge of #%d and #%d\n", |
- phi->id(), ls->GetField(i)->id(), rs->GetField(i)->id()); |
+ } else { |
+ GetFields(cache->objects(), cache->fields(), i); |
+ if (cache->fields().size() == cache->objects().size()) { |
+ Node* rep = mergeObject->GetField(i); |
+ if (!rep || !mergeObject->IsCreatedPhi(i)) { |
+ cache->fields().push_back(control); |
+ Node* phi = |
+ graph->NewNode(common->Phi(MachineRepresentation::kTagged, 2), |
+ static_cast<int>(cache->fields().size()), |
+ &cache->fields().front()); |
+ if (mergeObject->SetField(i, phi, true)) { |
+ if (FLAG_trace_turbo_escape) { |
+ PrintF(" Creating Phi #%d as merge of", phi->id()); |
+ for (size_t i = 0; i + 1 < cache->fields().size(); i++) { |
+ PrintF(" #%d (%s)", cache->fields()[i]->id(), |
+ cache->fields()[i]->op()->mnemonic()); |
+ } |
+ PrintF("\n"); |
+ } |
+ changed = true; |
+ } |
+ } else { |
+ DCHECK(rep->opcode() == IrOpcode::kPhi); |
+ for (size_t n = 0; n < cache->fields().size(); ++n) { |
+ Node* old = |
+ NodeProperties::GetValueInput(rep, static_cast<int>(n)); |
+ if (old != cache->fields()[n]) { |
+ changed = true; |
+ NodeProperties::ReplaceValueInput(rep, cache->fields()[n], |
+ static_cast<int>(n)); |
+ } |
} |
- changed = true; |
} |
} else { |
- if (FLAG_trace_turbo_escape) { |
- PrintF(" Retaining Phi #%d as merge of #%d and #%d\n", |
- rep->id(), ls->GetField(i)->id(), rs->GetField(i)->id()); |
- } |
+ changed = mergeObject->SetField(i, nullptr) || changed; |
} |
- } else { |
- changed = mergeObject->SetField(i, nullptr) || changed; |
} |
} |
- } else if (ls) { |
- VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject(id, zone); |
- changed = mergeObject->UpdateFrom(*ls) || changed; |
- } else if (rs) { |
- VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject(id, zone); |
- changed = mergeObject->UpdateFrom(*rs) || changed; |
+ |
+ } else { |
+ SetVirtualObject(id, nullptr); |
+ } |
+ } |
+ // Update linked objects. |
+ for (NodeId id = 0; id < min_size(cache->states()); ++id) { |
+ GetVirtualObjects(cache->states(), cache->objects(), id); |
+ if (cache->objects().size() == cache->states().size()) { |
+ if (cache->objects()[0]->id() != id) { |
+ SetVirtualObject(id, GetVirtualObject(cache->objects()[0]->id())); |
+ } |
} |
} |
return changed; |
@@ -329,13 +481,21 @@ Node* VirtualState::ResolveReplacement(Node* node) { |
bool VirtualState::UpdateReplacement(Node* node, Node* rep, Zone* zone) { |
if (!GetVirtualObject(node)) { |
- SetVirtualObject(node->id(), new (zone) VirtualObject(node->id(), zone)); |
+ if (rep) { |
+ SetVirtualObject(node->id(), new (zone) VirtualObject(node->id(), zone)); |
+ } else { |
+ return false; |
+ } |
} |
if (GetVirtualObject(node)->SetReplacement(rep)) { |
LastChangedAt(node); |
if (FLAG_trace_turbo_escape) { |
- PrintF("Representation of #%d is #%d (%s)\n", node->id(), rep->id(), |
- rep->op()->mnemonic()); |
+ if (rep) { |
+ PrintF("Replacement of #%d is #%d (%s)\n", node->id(), rep->id(), |
+ rep->op()->mnemonic()); |
+ } else { |
+ PrintF("Replacement of #%d cleared\n", node->id()); |
+ } |
} |
return true; |
} |
@@ -352,7 +512,9 @@ EscapeStatusAnalysis::EscapeStatusAnalysis(EscapeAnalysis* object_analysis, |
graph_(graph), |
zone_(zone), |
info_(zone), |
- queue_(zone) {} |
+ queue_(zone) { |
+ info_.resize(graph->NodeCount()); |
+} |
EscapeStatusAnalysis::~EscapeStatusAnalysis() {} |
@@ -376,6 +538,12 @@ bool EscapeStatusAnalysis::IsEscaped(Node* node) { |
} |
+bool EscapeStatusAnalysis::IsAllocation(Node* node) { |
+ return node->opcode() == IrOpcode::kAllocate || |
+ node->opcode() == IrOpcode::kFinishRegion; |
+} |
+ |
+ |
bool EscapeStatusAnalysis::SetEscaped(Node* node) { |
bool changed = info_[node->id()] != kEscaped; |
info_[node->id()] = kEscaped; |
@@ -436,12 +604,9 @@ void EscapeStatusAnalysis::Process(Node* node) { |
case IrOpcode::kLoadField: |
case IrOpcode::kLoadElement: { |
if (Node* rep = object_analysis_->GetReplacement(node, node->id())) { |
- if (rep->opcode() == IrOpcode::kAllocate || |
- rep->opcode() == IrOpcode::kFinishRegion) { |
- if (CheckUsesForEscape(node, rep)) { |
- RevisitInputs(rep); |
- RevisitUses(rep); |
- } |
+ if (IsAllocation(rep) && CheckUsesForEscape(node, rep)) { |
+ RevisitInputs(rep); |
+ RevisitUses(rep); |
} |
} |
break; |
@@ -461,8 +626,9 @@ void EscapeStatusAnalysis::ProcessStoreField(Node* node) { |
DCHECK_EQ(node->opcode(), IrOpcode::kStoreField); |
Node* to = NodeProperties::GetValueInput(node, 0); |
Node* val = NodeProperties::GetValueInput(node, 1); |
- if (IsEscaped(to) && SetEscaped(val)) { |
+ if ((IsEscaped(to) || !IsAllocation(to)) && SetEscaped(val)) { |
RevisitUses(val); |
+ RevisitInputs(val); |
if (FLAG_trace_turbo_escape) { |
PrintF("Setting #%d (%s) to escaped because of store to field of #%d\n", |
val->id(), val->op()->mnemonic(), to->id()); |
@@ -475,8 +641,9 @@ void EscapeStatusAnalysis::ProcessStoreElement(Node* node) { |
DCHECK_EQ(node->opcode(), IrOpcode::kStoreElement); |
Node* to = NodeProperties::GetValueInput(node, 0); |
Node* val = NodeProperties::GetValueInput(node, 2); |
- if (IsEscaped(to) && SetEscaped(val)) { |
+ if ((IsEscaped(to) || !IsAllocation(to)) && SetEscaped(val)) { |
RevisitUses(val); |
+ RevisitInputs(val); |
if (FLAG_trace_turbo_escape) { |
PrintF("Setting #%d (%s) to escaped because of store to field of #%d\n", |
val->id(), val->op()->mnemonic(), to->id()); |
@@ -549,7 +716,21 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep, |
return true; |
} |
break; |
+ case IrOpcode::kObjectIsSmi: |
+ if (!IsAllocation(rep) && SetEscaped(rep)) { |
+ PrintF("Setting #%d (%s) to escaped because of use by #%d (%s)\n", |
+ rep->id(), rep->op()->mnemonic(), use->id(), |
+ use->op()->mnemonic()); |
+ return true; |
+ } |
+ break; |
default: |
+ if (use->op()->EffectInputCount() == 0 && |
+ uses->op()->EffectInputCount() > 0) { |
+ PrintF("Encountered unaccounted use by #%d (%s)\n", use->id(), |
+ use->op()->mnemonic()); |
+ UNREACHABLE(); |
+ } |
if (SetEscaped(rep)) { |
if (FLAG_trace_turbo_escape) { |
PrintF("Setting #%d (%s) to escaped because of use by #%d (%s)\n", |
@@ -558,15 +739,6 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep, |
} |
return true; |
} |
- if (use->op()->EffectInputCount() == 0 && |
- uses->op()->EffectInputCount() > 0 && |
- uses->opcode() != IrOpcode::kLoadField) { |
- if (FLAG_trace_turbo_escape) { |
- PrintF("Encountered unaccounted use by #%d (%s)\n", use->id(), |
- use->op()->mnemonic()); |
- } |
- UNREACHABLE(); |
- } |
} |
} |
return false; |
@@ -604,7 +776,8 @@ EscapeAnalysis::EscapeAnalysis(Graph* graph, CommonOperatorBuilder* common, |
common_(common), |
zone_(zone), |
virtual_states_(zone), |
- escape_status_(this, graph, zone) {} |
+ escape_status_(this, graph, zone), |
+ cache_(zone) {} |
EscapeAnalysis::~EscapeAnalysis() {} |
@@ -658,6 +831,13 @@ void EscapeAnalysis::RunObjectAnalysis() { |
bool EscapeAnalysis::IsDanglingEffectNode(Node* node) { |
if (node->op()->EffectInputCount() == 0) return false; |
if (node->op()->EffectOutputCount() == 0) return false; |
+ if (node->op()->EffectInputCount() == 1 && |
+ NodeProperties::GetEffectInput(node)->opcode() == IrOpcode::kStart) { |
+ // 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. |
+ return false; |
+ } |
for (Edge edge : node->use_edges()) { |
if (NodeProperties::IsEffectEdge(edge)) { |
return false; |
@@ -740,9 +920,7 @@ void EscapeAnalysis::ProcessAllocationUsers(Node* node) { |
bool EscapeAnalysis::IsEffectBranchPoint(Node* node) { |
int count = 0; |
for (Edge edge : node->use_edges()) { |
- Node* use = edge.from(); |
- if (NodeProperties::IsEffectEdge(edge) && |
- use->opcode() != IrOpcode::kLoadField) { |
+ if (NodeProperties::IsEffectEdge(edge)) { |
if (++count > 1) { |
return true; |
} |
@@ -771,6 +949,11 @@ void EscapeAnalysis::ForwardVirtualState(Node* node) { |
} |
DCHECK_NOT_NULL(virtual_states_[effect->id()]); |
if (IsEffectBranchPoint(effect)) { |
+ if (FLAG_trace_turbo_escape) { |
+ PrintF("Copying object 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()]); |
@@ -778,11 +961,6 @@ void EscapeAnalysis::ForwardVirtualState(Node* node) { |
virtual_states_[node->id()]->UpdateFrom(virtual_states_[effect->id()], |
zone()); |
} |
- if (FLAG_trace_turbo_escape) { |
- PrintF("Copying object 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()); |
- } |
} else { |
virtual_states_[node->id()] = virtual_states_[effect->id()]; |
if (FLAG_trace_turbo_escape) { |
@@ -803,10 +981,6 @@ void EscapeAnalysis::ProcessStart(Node* node) { |
bool EscapeAnalysis::ProcessEffectPhi(Node* node) { |
DCHECK_EQ(node->opcode(), IrOpcode::kEffectPhi); |
- // For now only support binary phis. |
- CHECK_EQ(node->op()->EffectInputCount(), 2); |
- Node* left = NodeProperties::GetEffectInput(node, 0); |
- Node* right = NodeProperties::GetEffectInput(node, 1); |
bool changed = false; |
VirtualState* mergeState = virtual_states_[node->id()]; |
@@ -822,33 +996,40 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) { |
changed = true; |
} |
- VirtualState* l = virtual_states_[left->id()]; |
- VirtualState* r = virtual_states_[right->id()]; |
+ cache_.Clear(); |
- if (l == nullptr && r == nullptr) { |
- return changed; |
+ if (FLAG_trace_turbo_escape) { |
+ PrintF("At Effect Phi #%d, merging states into %p:", node->id(), |
+ static_cast<void*>(mergeState)); |
} |
+ for (int i = 0; i < node->op()->EffectInputCount(); ++i) { |
+ Node* input = NodeProperties::GetEffectInput(node, i); |
+ VirtualState* state = virtual_states_[input->id()]; |
+ if (state) { |
+ cache_.states().push_back(state); |
+ } |
+ if (FLAG_trace_turbo_escape) { |
+ PrintF(" %p (from %d %s)", static_cast<void*>(state), input->id(), |
+ input->op()->mnemonic()); |
+ } |
+ } |
if (FLAG_trace_turbo_escape) { |
- PrintF( |
- "At Effect Phi #%d, merging states %p (from #%d) and %p (from #%d) " |
- "into %p\n", |
- node->id(), static_cast<void*>(l), left->id(), static_cast<void*>(r), |
- right->id(), static_cast<void*>(mergeState)); |
+ PrintF("\n"); |
} |
- if (r && l == nullptr) { |
- changed = mergeState->UpdateFrom(r, zone()) || changed; |
- } else if (l && r == nullptr) { |
- changed = mergeState->UpdateFrom(l, zone()) || changed; |
- } else { |
- changed = mergeState->MergeFrom(l, r, zone(), graph(), common(), |
- NodeProperties::GetControlInput(node)) || |
- changed; |
+ if (cache_.states().size() == 0) { |
+ return changed; |
} |
+ |
+ changed = mergeState->MergeFrom(&cache_, zone(), graph(), common(), |
+ NodeProperties::GetControlInput(node)) || |
+ changed; |
+ |
if (FLAG_trace_turbo_escape) { |
PrintF("Merge %s the node.\n", changed ? "changed" : "did not change"); |
} |
+ |
if (changed) { |
mergeState->LastChangedAt(node); |
} |
@@ -915,6 +1096,11 @@ bool EscapeAnalysis::IsEscaped(Node* node) { |
} |
+bool EscapeAnalysis::SetEscaped(Node* node) { |
+ return escape_status_.SetEscaped(node); |
+} |
+ |
+ |
VirtualObject* EscapeAnalysis::GetVirtualObject(Node* at, NodeId id) { |
if (VirtualState* states = virtual_states_[at->id()]) { |
return states->GetVirtualObject(id); |
@@ -931,45 +1117,43 @@ int EscapeAnalysis::OffsetFromAccess(Node* node) { |
void EscapeAnalysis::ProcessLoadFromPhi(int offset, Node* from, Node* node, |
VirtualState* state) { |
- // Only binary phis are supported for now. |
- CHECK_EQ(from->op()->ValueInputCount(), 2); |
if (FLAG_trace_turbo_escape) { |
PrintF("Load #%d from phi #%d", node->id(), from->id()); |
} |
- Node* left = NodeProperties::GetValueInput(from, 0); |
- Node* right = NodeProperties::GetValueInput(from, 1); |
- VirtualObject* l = state->GetVirtualObject(left); |
- VirtualObject* r = state->GetVirtualObject(right); |
- if (l && r) { |
- Node* lv = l->GetField(offset); |
- Node* rv = r->GetField(offset); |
- if (lv && rv) { |
+ |
+ ZoneVector<Node*> inputs(zone()); |
+ for (int i = 0; i < node->op()->ValueInputCount(); ++i) { |
+ Node* input = NodeProperties::GetValueInput(node, i); |
+ inputs.push_back(input); |
+ } |
+ |
+ GetVirtualObjects(state, inputs, cache_.objects()); |
+ if (cache_.objects().size() == inputs.size()) { |
+ GetFields(cache_.objects(), cache_.fields(), offset); |
+ if (cache_.fields().size() == cache_.objects().size()) { |
if (!state->GetVirtualObject(node)) { |
state->SetVirtualObject(node->id(), |
new (zone()) VirtualObject(node->id(), zone())); |
} |
Node* rep = state->GetVirtualObject(node)->GetReplacement(); |
- if (!rep || rep->opcode() != IrOpcode::kPhi || |
- NodeProperties::GetValueInput(rep, 0) != lv || |
- NodeProperties::GetValueInput(rep, 1) != rv) { |
- Node* phi = |
- graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
- lv, rv, NodeProperties::GetControlInput(from)); |
+ if (!rep || !IsEquivalentPhi(rep, cache_.fields())) { |
+ cache_.fields().push_back(NodeProperties::GetControlInput(from)); |
+ Node* phi = graph()->NewNode( |
+ common()->Phi(MachineRepresentation::kTagged, 2), |
+ static_cast<int>(cache_.fields().size()), &cache_.fields().front()); |
state->GetVirtualObject(node)->SetReplacement(phi); |
state->LastChangedAt(node); |
if (FLAG_trace_turbo_escape) { |
- PrintF(" got phi of #%d is #%d created.\n", lv->id(), rv->id()); |
+ PrintF(" got phi created.\n"); |
} |
} else if (FLAG_trace_turbo_escape) { |
- PrintF(" has already the right phi representation.\n"); |
+ PrintF(" has already phi #%d.\n", rep->id()); |
} |
} else if (FLAG_trace_turbo_escape) { |
- PrintF(" has incomplete field info: %p %p\n", static_cast<void*>(lv), |
- static_cast<void*>(rv)); |
+ PrintF(" has incomplete field info.\n"); |
} |
} else if (FLAG_trace_turbo_escape) { |
- PrintF(" has incomplete virtual object info: %p %p\n", |
- static_cast<void*>(l), static_cast<void*>(r)); |
+ PrintF(" has incomplete virtual object info.\n"); |
} |
} |
@@ -985,11 +1169,9 @@ void EscapeAnalysis::ProcessLoadField(Node* node) { |
Node* value = object->GetField(offset); |
if (value) { |
value = state->ResolveReplacement(value); |
- // Record that the load has this alias. |
- state->UpdateReplacement(node, value, zone()); |
- } else if (FLAG_trace_turbo_escape) { |
- PrintF("No field %d on record for #%d\n", offset, from->id()); |
} |
+ // Record that the load has this alias. |
+ state->UpdateReplacement(node, value, zone()); |
} else { |
if (from->opcode() == IrOpcode::kPhi) { |
int offset = OffsetFromAccess(node); |
@@ -1005,31 +1187,38 @@ void EscapeAnalysis::ProcessLoadElement(Node* node) { |
ForwardVirtualState(node); |
Node* from = NodeProperties::GetValueInput(node, 0); |
VirtualState* state = virtual_states_[node->id()]; |
- if (VirtualObject* object = state->ResolveVirtualObject(from)) { |
- NumberMatcher index(node->InputAt(1)); |
- ElementAccess access = OpParameter<ElementAccess>(node); |
- if (index.HasValue()) { |
- CHECK_EQ(ElementSizeLog2Of(access.machine_type.representation()), |
+ Node* index_node = node->InputAt(1); |
+ NumberMatcher index(index_node); |
+ ElementAccess access = OpParameter<ElementAccess>(node); |
+ if (index.HasValue()) { |
+ int offset = index.Value() + access.header_size / kPointerSize; |
+ if (VirtualObject* object = state->ResolveVirtualObject(from)) { |
+ CHECK_GE(ElementSizeLog2Of(access.machine_type.representation()), |
kPointerSizeLog2); |
CHECK_EQ(access.header_size % kPointerSize, 0); |
- int offset = index.Value() + access.header_size / kPointerSize; |
+ |
if (!object->IsTracked()) return; |
Node* value = object->GetField(offset); |
if (value) { |
value = state->ResolveReplacement(value); |
- // Record that the load has this alias. |
- state->UpdateReplacement(node, value, zone()); |
- } else if (FLAG_trace_turbo_escape) { |
- PrintF("No field %d on record for #%d\n", offset, from->id()); |
} |
- } |
- } else { |
- if (from->opcode() == IrOpcode::kPhi) { |
- NumberMatcher index(node->InputAt(1)); |
+ // Record that the load has this alias. |
+ state->UpdateReplacement(node, value, zone()); |
+ } else if (from->opcode() == IrOpcode::kPhi) { |
ElementAccess access = OpParameter<ElementAccess>(node); |
int offset = index.Value() + access.header_size / kPointerSize; |
- if (index.HasValue()) { |
- ProcessLoadFromPhi(offset, from, node, state); |
+ ProcessLoadFromPhi(offset, from, node, state); |
+ } |
+ } else { |
+ // We have a load from a non-const index, cannot eliminate object. |
+ if (SetEscaped(from)) { |
+ if (FLAG_trace_turbo_escape) { |
+ PrintF( |
+ "Setting #%d (%s) to escaped because store element #%d to " |
+ "non-const " |
+ "index #%d (%s)\n", |
+ from->id(), from->op()->mnemonic(), node->id(), index_node->id(), |
+ index_node->op()->mnemonic()); |
} |
} |
} |
@@ -1056,21 +1245,34 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) { |
DCHECK_EQ(node->opcode(), IrOpcode::kStoreElement); |
ForwardVirtualState(node); |
Node* to = NodeProperties::GetValueInput(node, 0); |
- NumberMatcher index(node->InputAt(1)); |
+ Node* index_node = node->InputAt(1); |
+ NumberMatcher index(index_node); |
ElementAccess access = OpParameter<ElementAccess>(node); |
Node* val = NodeProperties::GetValueInput(node, 2); |
if (index.HasValue()) { |
- CHECK_EQ(ElementSizeLog2Of(access.machine_type.representation()), |
- kPointerSizeLog2); |
- CHECK_EQ(access.header_size % kPointerSize, 0); |
int offset = index.Value() + access.header_size / kPointerSize; |
VirtualState* states = virtual_states_[node->id()]; |
if (VirtualObject* obj = states->ResolveVirtualObject(to)) { |
if (!obj->IsTracked()) return; |
+ CHECK_GE(ElementSizeLog2Of(access.machine_type.representation()), |
+ kPointerSizeLog2); |
+ CHECK_EQ(access.header_size % kPointerSize, 0); |
if (obj->SetField(offset, states->ResolveReplacement(val))) { |
states->LastChangedAt(node); |
} |
} |
+ } else { |
+ // We have a store to a non-const index, cannot eliminate object. |
+ if (SetEscaped(to)) { |
+ if (FLAG_trace_turbo_escape) { |
+ PrintF( |
+ "Setting #%d (%s) to escaped because store element #%d to " |
+ "non-const " |
+ "index #%d (%s)\n", |
+ to->id(), to->op()->mnemonic(), node->id(), index_node->id(), |
+ index_node->op()->mnemonic()); |
+ } |
+ } |
} |
} |