| Index: src/compiler/escape-analysis.cc
|
| diff --git a/src/compiler/escape-analysis.cc b/src/compiler/escape-analysis.cc
|
| index c7c451ce06464f1411f97eb5774ef1cefe9ac06e..6ac9e63ddf237c14d448a3ac2291197bb8dfbf39 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,16 @@ bool EscapeAnalysis::IsEscaped(Node* node) {
|
| }
|
|
|
|
|
| +bool EscapeAnalysis::IsAllocation(Node* node) {
|
| + return escape_status_.IsAllocation(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 +1122,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 +1174,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 +1192,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 +1250,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());
|
| + }
|
| + }
|
| }
|
| }
|
|
|
|
|