[turbofan] Improve escape analysis

This patch improves escape analysis and fixes bugs
triggered by clusterfuzz. Impovements include:
* Handling of LoadElement/StoreElement if index is a
  constant
* Handling of JSStoreProperty: invalidate all information,
  as the store could have altered any field.
* Treat phis that use an allocation as escaping
* Improve resolution of replacements

R=mstarzinger@chromium.org
BUG=v8:4586
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#32656}
This commit is contained in:
sigurds 2015-12-07 05:28:52 -08:00 committed by Commit bot
parent 063920e2e4
commit 5b5821142b
7 changed files with 412 additions and 131 deletions

View File

@ -23,9 +23,11 @@ EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph,
Reduction EscapeAnalysisReducer::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kLoadField:
return ReduceLoadField(node);
case IrOpcode::kLoadElement:
return ReduceLoad(node);
case IrOpcode::kStoreField:
return ReduceStoreField(node);
case IrOpcode::kStoreElement:
return ReduceStore(node);
case IrOpcode::kAllocate:
return ReduceAllocate(node);
case IrOpcode::kFinishRegion:
@ -42,11 +44,13 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) {
}
Reduction EscapeAnalysisReducer::ReduceLoadField(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kLoadField);
Reduction EscapeAnalysisReducer::ReduceLoad(Node* node) {
DCHECK(node->opcode() == IrOpcode::kLoadField ||
node->opcode() == IrOpcode::kLoadElement);
if (Node* rep = escape_analysis()->GetReplacement(node, node->id())) {
if (FLAG_trace_turbo_escape) {
PrintF("Replaced #%d with #%d\n", node->id(), rep->id());
PrintF("Replaced #%d (%s) with #%d (%s)\n", node->id(),
node->op()->mnemonic(), rep->id(), rep->op()->mnemonic());
}
ReplaceWithValue(node, rep);
return Changed(rep);
@ -55,11 +59,13 @@ Reduction EscapeAnalysisReducer::ReduceLoadField(Node* node) {
}
Reduction EscapeAnalysisReducer::ReduceStoreField(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kStoreField);
Reduction EscapeAnalysisReducer::ReduceStore(Node* node) {
DCHECK(node->opcode() == IrOpcode::kStoreField ||
node->opcode() == IrOpcode::kStoreElement);
if (escape_analysis()->IsVirtual(NodeProperties::GetValueInput(node, 0))) {
if (FLAG_trace_turbo_escape) {
PrintF("Removed store field #%d from effect chain\n", node->id());
PrintF("Removed #%d (%s) from effect chain\n", node->id(),
node->op()->mnemonic());
}
RelaxEffectsAndControls(node);
return Changed(node);
@ -90,7 +96,7 @@ Reduction EscapeAnalysisReducer::ReduceFinishRegion(Node* node) {
if (FLAG_trace_turbo_escape) {
PrintF("Removed region #%d / #%d from effect chain,", effect->id(),
node->id());
PrintF("%d user(s) of #%d remain(s):", node->UseCount(), node->id());
PrintF(" %d user(s) of #%d remain(s):", node->UseCount(), node->id());
for (Edge edge : node->use_edges()) {
PrintF(" #%d", edge.from()->id());
}

View File

@ -24,8 +24,8 @@ class EscapeAnalysisReducer final : public AdvancedReducer {
Reduction Reduce(Node* node) final;
private:
Reduction ReduceLoadField(Node* node);
Reduction ReduceStoreField(Node* node);
Reduction ReduceLoad(Node* node);
Reduction ReduceStore(Node* node);
Reduction ReduceAllocate(Node* node);
Reduction ReduceFinishRegion(Node* node);
Reduction ReduceReferenceEqual(Node* node);

View File

@ -63,15 +63,25 @@ class VirtualObject : public ZoneObject {
return changed;
}
size_t fields() { return fields_.size(); }
bool ResizeFields(size_t field_number) {
if (field_number != fields_.size()) {
fields_.resize(field_number);
Node** fields_array() { return &fields_.front(); }
size_t field_count() { return fields_.size(); }
bool ResizeFields(size_t field_count) {
if (field_count != fields_.size()) {
fields_.resize(field_count);
return true;
}
return false;
}
bool ClearAllFields() {
bool changed = false;
for (size_t i = 0; i < fields_.size(); ++i) {
if (fields_[i] != nullptr) {
fields_[i] = nullptr;
changed = true;
}
}
return changed;
}
bool UpdateFrom(const VirtualObject& other);
NodeId id() { return id_; }
@ -112,6 +122,7 @@ class VirtualState : public ZoneObject {
VirtualState(Zone* zone, size_t size);
VirtualState(const VirtualState& states);
VirtualObject* ResolveVirtualObject(Node* node);
VirtualObject* GetVirtualObject(Node* node);
VirtualObject* GetVirtualObject(size_t id);
VirtualObject* GetOrCreateTrackedVirtualObject(NodeId id, Zone* zone);
@ -157,7 +168,10 @@ VirtualState::VirtualState(const VirtualState& state)
}
VirtualObject* VirtualState::GetVirtualObject(size_t id) { return info_[id]; }
VirtualObject* VirtualState::GetVirtualObject(size_t id) {
if (id >= info_.size()) return nullptr;
return info_[id];
}
VirtualObject* VirtualState::GetVirtualObject(Node* node) {
@ -165,6 +179,16 @@ VirtualObject* VirtualState::GetVirtualObject(Node* node) {
}
VirtualObject* VirtualState::ResolveVirtualObject(Node* node) {
VirtualObject* obj = GetVirtualObject(node->id());
while (obj && !obj->IsTracked() && obj->GetReplacement() &&
GetVirtualObject(obj->GetReplacement())) {
obj = GetVirtualObject(obj->GetReplacement());
}
return obj;
}
VirtualObject* VirtualState::GetOrCreateTrackedVirtualObject(NodeId id,
Zone* zone) {
if (VirtualObject* obj = GetVirtualObject(id)) {
@ -246,7 +270,7 @@ bool VirtualState::MergeFrom(VirtualState* left, VirtualState* right,
PrintF(" Merging fields of #%d\n", id);
}
VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject(id, zone);
size_t fields = std::max(ls->fields(), rs->fields());
size_t fields = std::max(ls->field_count(), rs->field_count());
changed = mergeObject->ResizeFields(fields) || changed;
for (size_t i = 0; i < fields; ++i) {
if (ls->GetField(i) == rs->GetField(i)) {
@ -256,19 +280,36 @@ bool VirtualState::MergeFrom(VirtualState* left, VirtualState* right,
ls->GetField(i)->id());
}
} else if (ls->GetField(i) != nullptr && rs->GetField(i) != nullptr) {
Node* phi = graph->NewNode(common->Phi(kMachAnyTagged, 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());
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(kMachAnyTagged, 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());
}
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 = true;
}
} 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;
}
}
return changed;
@ -276,12 +317,13 @@ bool VirtualState::MergeFrom(VirtualState* left, VirtualState* right,
Node* VirtualState::ResolveReplacement(Node* node) {
if (VirtualObject* obj = GetVirtualObject(node)) {
if (Node* rep = obj->GetReplacement()) {
return rep;
}
Node* replacement = node;
VirtualObject* obj = GetVirtualObject(node);
while (obj != nullptr && obj->GetReplacement()) {
replacement = obj->GetReplacement();
obj = GetVirtualObject(replacement);
}
return node;
return replacement;
}
@ -388,7 +430,11 @@ void EscapeStatusAnalysis::Process(Node* node) {
case IrOpcode::kStoreField:
ProcessStoreField(node);
break;
case IrOpcode::kLoadField: {
case IrOpcode::kStoreElement:
ProcessStoreElement(node);
break;
case IrOpcode::kLoadField:
case IrOpcode::kLoadElement: {
if (Node* rep = object_analysis_->GetReplacement(node, node->id())) {
if (rep->opcode() == IrOpcode::kAllocate ||
rep->opcode() == IrOpcode::kFinishRegion) {
@ -425,6 +471,20 @@ void EscapeStatusAnalysis::ProcessStoreField(Node* node) {
}
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)) {
RevisitUses(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());
}
}
}
void EscapeStatusAnalysis::ProcessAllocate(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
if (!HasEntry(node)) {
@ -444,19 +504,24 @@ void EscapeStatusAnalysis::ProcessAllocate(Node* node) {
return;
}
}
if (CheckUsesForEscape(node)) {
if (CheckUsesForEscape(node, true)) {
RevisitUses(node);
}
}
bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep) {
bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep,
bool phi_escaping) {
for (Edge edge : uses->use_edges()) {
Node* use = edge.from();
if (!NodeProperties::IsValueEdge(edge)) continue;
if (!NodeProperties::IsValueEdge(edge) &&
!NodeProperties::IsContextEdge(edge))
continue;
switch (use->opcode()) {
case IrOpcode::kStoreField:
case IrOpcode::kLoadField:
case IrOpcode::kStoreElement:
case IrOpcode::kLoadElement:
case IrOpcode::kFrameState:
case IrOpcode::kStateValues:
case IrOpcode::kReferenceEqual:
@ -472,6 +537,17 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep) {
}
return true;
}
if (phi_escaping && use->opcode() == IrOpcode::kPhi &&
SetEscaped(rep)) {
if (FLAG_trace_turbo_escape) {
PrintF(
"Setting #%d (%s) to escaped because of use by phi node "
"#%d (%s)\n",
rep->id(), rep->op()->mnemonic(), use->id(),
use->op()->mnemonic());
}
return true;
}
break;
default:
if (SetEscaped(rep)) {
@ -503,7 +579,7 @@ void EscapeStatusAnalysis::ProcessFinishRegion(Node* node) {
info_[node->id()] = kVirtual;
RevisitUses(node);
}
if (CheckUsesForEscape(node)) {
if (CheckUsesForEscape(node, true)) {
RevisitInputs(node);
}
}
@ -542,18 +618,19 @@ void EscapeAnalysis::Run() {
void EscapeAnalysis::RunObjectAnalysis() {
virtual_states_.resize(graph()->NodeCount());
ZoneVector<Node*> queue(zone());
queue.push_back(graph()->start());
while (!queue.empty()) {
Node* node = queue.back();
queue.pop_back();
ZoneVector<Node*> stack(zone());
stack.push_back(graph()->start());
while (!stack.empty()) {
Node* node = stack.back();
stack.pop_back();
if (Process(node)) {
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsEffectEdge(edge)) {
Node* use = edge.from();
if (use->opcode() != IrOpcode::kLoadField ||
if ((use->opcode() != IrOpcode::kLoadField &&
use->opcode() != IrOpcode::kLoadElement) ||
!IsDanglingEffectNode(use)) {
queue.push_back(use);
stack.push_back(use);
}
}
}
@ -561,9 +638,12 @@ void EscapeAnalysis::RunObjectAnalysis() {
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsEffectEdge(edge)) {
Node* use = edge.from();
if (use->opcode() == IrOpcode::kLoadField &&
if ((use->opcode() == IrOpcode::kLoadField ||
use->opcode() == IrOpcode::kLoadElement) &&
IsDanglingEffectNode(use)) {
queue.push_back(use);
stack.push_back(use);
}
}
}
@ -604,6 +684,12 @@ bool EscapeAnalysis::Process(Node* node) {
case IrOpcode::kLoadField:
ProcessLoadField(node);
break;
case IrOpcode::kStoreElement:
ProcessStoreElement(node);
break;
case IrOpcode::kLoadElement:
ProcessLoadElement(node);
break;
case IrOpcode::kStart:
ProcessStart(node);
break;
@ -614,12 +700,43 @@ bool EscapeAnalysis::Process(Node* node) {
if (node->op()->EffectInputCount() > 0) {
ForwardVirtualState(node);
}
ProcessAllocationUsers(node);
break;
}
return true;
}
void EscapeAnalysis::ProcessAllocationUsers(Node* node) {
for (Edge edge : node->input_edges()) {
Node* input = edge.to();
if (!NodeProperties::IsValueEdge(edge) &&
!NodeProperties::IsContextEdge(edge))
continue;
switch (node->opcode()) {
case IrOpcode::kStoreField:
case IrOpcode::kLoadField:
case IrOpcode::kStoreElement:
case IrOpcode::kLoadElement:
case IrOpcode::kFrameState:
case IrOpcode::kStateValues:
case IrOpcode::kReferenceEqual:
case IrOpcode::kFinishRegion:
case IrOpcode::kPhi:
break;
default:
VirtualState* states = virtual_states_[node->id()];
if (VirtualObject* obj = states->ResolveVirtualObject(input)) {
if (obj->ClearAllFields()) {
states->LastChangedAt(node);
}
}
break;
}
}
}
bool EscapeAnalysis::IsEffectBranchPoint(Node* node) {
int count = 0;
for (Edge edge : node->use_edges()) {
@ -637,10 +754,12 @@ bool EscapeAnalysis::IsEffectBranchPoint(Node* node) {
void EscapeAnalysis::ForwardVirtualState(Node* node) {
DCHECK_EQ(node->op()->EffectInputCount(), 1);
if (node->opcode() != IrOpcode::kLoadField && IsDanglingEffectNode(node)) {
if (node->opcode() != IrOpcode::kLoadField &&
node->opcode() != IrOpcode::kLoadElement &&
node->opcode() != IrOpcode::kLoad && IsDanglingEffectNode(node)) {
PrintF("Dangeling effect node: #%d (%s)\n", node->id(),
node->op()->mnemonic());
DCHECK(false);
UNREACHABLE();
}
Node* effect = NodeProperties::GetEffectInput(node);
// Break the cycle for effect phis.
@ -652,9 +771,13 @@ void EscapeAnalysis::ForwardVirtualState(Node* node) {
}
DCHECK_NOT_NULL(virtual_states_[effect->id()]);
if (IsEffectBranchPoint(effect)) {
if (virtual_states_[node->id()]) return;
virtual_states_[node->id()] =
new (zone()) VirtualState(*virtual_states_[effect->id()]);
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 (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(),
@ -681,7 +804,7 @@ void EscapeAnalysis::ProcessStart(Node* node) {
bool EscapeAnalysis::ProcessEffectPhi(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kEffectPhi);
// For now only support binary phis.
DCHECK_EQ(node->op()->EffectInputCount(), 2);
CHECK_EQ(node->op()->EffectInputCount(), 2);
Node* left = NodeProperties::GetEffectInput(node, 0);
Node* right = NodeProperties::GetEffectInput(node, 1);
bool changed = false;
@ -692,7 +815,7 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) {
virtual_states_[node->id()] = mergeState;
changed = true;
if (FLAG_trace_turbo_escape) {
PrintF("Phi #%d got new states map %p.\n", node->id(),
PrintF("Effect Phi #%d got new states map %p.\n", node->id(),
static_cast<void*>(mergeState));
}
} else if (mergeState->GetLastChanged() != node) {
@ -707,9 +830,11 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) {
}
if (FLAG_trace_turbo_escape) {
PrintF("At Phi #%d, merging states %p (from #%d) and %p (from #%d)\n",
node->id(), static_cast<void*>(l), left->id(), static_cast<void*>(r),
right->id());
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));
}
if (r && l == nullptr) {
@ -790,68 +915,119 @@ bool EscapeAnalysis::IsEscaped(Node* node) {
}
VirtualObject* EscapeAnalysis::GetVirtualObject(Node* at, NodeId id) {
if (VirtualState* states = virtual_states_[at->id()]) {
return states->GetVirtualObject(id);
}
return nullptr;
}
int EscapeAnalysis::OffsetFromAccess(Node* node) {
DCHECK(OpParameter<FieldAccess>(node).offset % kPointerSize == 0);
return OpParameter<FieldAccess>(node).offset / kPointerSize;
}
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) {
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(kMachAnyTagged, 2), lv, rv,
NodeProperties::GetControlInput(from));
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());
}
} else if (FLAG_trace_turbo_escape) {
PrintF(" has already the right phi representation.\n");
}
} else if (FLAG_trace_turbo_escape) {
PrintF(" has incomplete field info: %p %p\n", static_cast<void*>(lv),
static_cast<void*>(rv));
}
} else if (FLAG_trace_turbo_escape) {
PrintF(" has incomplete virtual object info: %p %p\n",
static_cast<void*>(l), static_cast<void*>(r));
}
}
void EscapeAnalysis::ProcessLoadField(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kLoadField);
ForwardVirtualState(node);
Node* from = NodeProperties::GetValueInput(node, 0);
int offset = OffsetFromAccess(node);
VirtualState* states = virtual_states_[node->id()];
if (VirtualObject* state = states->GetVirtualObject(from)) {
if (!state->IsTracked()) return;
Node* value = state->GetField(offset);
VirtualState* state = virtual_states_[node->id()];
if (VirtualObject* object = state->ResolveVirtualObject(from)) {
int offset = OffsetFromAccess(node);
if (!object->IsTracked()) return;
Node* value = object->GetField(offset);
if (value) {
value = state->ResolveReplacement(value);
// Record that the load has this alias.
states->UpdateReplacement(node, value, zone());
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) {
int offset = OffsetFromAccess(node);
// 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 = states->GetVirtualObject(left);
VirtualObject* r = states->GetVirtualObject(right);
if (l && r) {
Node* lv = l->GetField(offset);
Node* rv = r->GetField(offset);
if (lv && rv) {
if (!states->GetVirtualObject(node)) {
states->SetVirtualObject(
node->id(), new (zone()) VirtualObject(node->id(), zone()));
}
Node* rep = states->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(kMachAnyTagged, 2), lv, rv,
NodeProperties::GetControlInput(from));
states->GetVirtualObject(node)->SetReplacement(phi);
states->LastChangedAt(node);
if (FLAG_trace_turbo_escape) {
PrintF(" got phi of #%d is #%d created.\n", lv->id(), rv->id());
}
} else if (FLAG_trace_turbo_escape) {
PrintF(" has already the right phi representation.\n");
}
} else if (FLAG_trace_turbo_escape) {
PrintF(" has incomplete field info: %p %p\n", static_cast<void*>(lv),
static_cast<void*>(rv));
}
ProcessLoadFromPhi(offset, from, node, state);
}
}
}
void EscapeAnalysis::ProcessLoadElement(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kLoadElement);
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), 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(" has incomplete virtual object info: %p %p\n",
static_cast<void*>(l), static_cast<void*>(r));
PrintF("No field %d on record for #%d\n", offset, from->id());
}
}
} else {
if (from->opcode() == IrOpcode::kPhi) {
NumberMatcher index(node->InputAt(1));
ElementAccess access = OpParameter<ElementAccess>(node);
int offset = index.Value() + access.header_size / kPointerSize;
if (index.HasValue()) {
ProcessLoadFromPhi(offset, from, node, state);
}
}
}
@ -865,7 +1041,7 @@ void EscapeAnalysis::ProcessStoreField(Node* node) {
Node* val = NodeProperties::GetValueInput(node, 1);
int offset = OffsetFromAccess(node);
VirtualState* states = virtual_states_[node->id()];
if (VirtualObject* obj = states->GetVirtualObject(to)) {
if (VirtualObject* obj = states->ResolveVirtualObject(to)) {
if (!obj->IsTracked()) return;
if (obj->SetField(offset, states->ResolveReplacement(val))) {
states->LastChangedAt(node);
@ -874,6 +1050,56 @@ void EscapeAnalysis::ProcessStoreField(Node* node) {
}
void EscapeAnalysis::ProcessStoreElement(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kStoreElement);
ForwardVirtualState(node);
Node* to = NodeProperties::GetValueInput(node, 0);
NumberMatcher index(node->InputAt(1));
ElementAccess access = OpParameter<ElementAccess>(node);
Node* val = NodeProperties::GetValueInput(node, 2);
if (index.HasValue()) {
CHECK_EQ(ElementSizeLog2Of(access.machine_type), 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;
if (obj->SetField(offset, states->ResolveReplacement(val))) {
states->LastChangedAt(node);
}
}
}
}
void EscapeAnalysis::DebugPrintObject(VirtualObject* object, NodeId id) {
PrintF(" Object #%d with %zu fields", id, object->field_count());
if (Node* rep = object->GetReplacement()) {
PrintF(", rep = #%d (%s)", rep->id(), rep->op()->mnemonic());
}
PrintF("\n");
for (size_t i = 0; i < object->field_count(); ++i) {
if (Node* f = object->GetField(i)) {
PrintF(" Field %zu = #%d (%s)\n", i, f->id(), f->op()->mnemonic());
}
}
}
void EscapeAnalysis::DebugPrintState(VirtualState* state) {
PrintF("Dumping object state %p\n", static_cast<void*>(state));
for (size_t id = 0; id < state->size(); id++) {
if (VirtualObject* object = state->GetVirtualObject(id)) {
if (object->id() == id) {
DebugPrintObject(object, static_cast<int>(id));
} else {
PrintF(" Object #%zu links to object #%d\n", id, object->id());
}
}
}
}
void EscapeAnalysis::DebugPrint() {
ZoneVector<VirtualState*> object_states(zone());
for (NodeId id = 0; id < virtual_states_.size(); id++) {
@ -885,26 +1111,7 @@ void EscapeAnalysis::DebugPrint() {
}
}
for (size_t n = 0; n < object_states.size(); n++) {
PrintF("Dumping object state %p\n", static_cast<void*>(object_states[n]));
for (size_t id = 0; id < object_states[n]->size(); id++) {
if (VirtualObject* obj = object_states[n]->GetVirtualObject(id)) {
if (obj->id() == id) {
PrintF(" Object #%zu with %zu fields", id, obj->fields());
if (Node* rep = obj->GetReplacement()) {
PrintF(", rep = #%d (%s)", rep->id(), rep->op()->mnemonic());
}
PrintF("\n");
for (size_t i = 0; i < obj->fields(); ++i) {
if (Node* f = obj->GetField(i)) {
PrintF(" Field %zu = #%d (%s)\n", i, f->id(),
f->op()->mnemonic());
}
}
} else {
PrintF(" Object #%zu links to object #%d\n", id, obj->id());
}
}
}
DebugPrintState(object_states[n]);
}
}

View File

@ -16,6 +16,7 @@ namespace compiler {
class CommonOperatorBuilder;
class EscapeAnalysis;
class VirtualState;
class VirtualObject;
// EscapeStatusAnalysis determines for each allocation whether it escapes.
@ -46,8 +47,11 @@ class EscapeStatusAnalysis {
void ProcessAllocate(Node* node);
void ProcessFinishRegion(Node* node);
void ProcessStoreField(Node* node);
bool CheckUsesForEscape(Node* node) { return CheckUsesForEscape(node, node); }
bool CheckUsesForEscape(Node* node, Node* rep);
void ProcessStoreElement(Node* node);
bool CheckUsesForEscape(Node* node, bool phi_escaping = false) {
return CheckUsesForEscape(node, node, phi_escaping);
}
bool CheckUsesForEscape(Node* node, Node* rep, bool phi_escaping = false);
void RevisitUses(Node* node);
void RevisitInputs(Node* node);
bool SetEscaped(Node* node);
@ -87,17 +91,28 @@ class EscapeAnalysis {
bool Process(Node* node);
void ProcessLoadField(Node* node);
void ProcessStoreField(Node* node);
void ProcessLoadElement(Node* node);
void ProcessStoreElement(Node* node);
void ProcessAllocationUsers(Node* node);
void ProcessAllocation(Node* node);
void ProcessFinishRegion(Node* node);
void ProcessCall(Node* node);
void ProcessStart(Node* node);
bool ProcessEffectPhi(Node* node);
void ProcessLoadFromPhi(int offset, Node* from, Node* node,
VirtualState* states);
void ForwardVirtualState(Node* node);
bool IsEffectBranchPoint(Node* node);
bool IsDanglingEffectNode(Node* node);
int OffsetFromAccess(Node* node);
VirtualObject* GetVirtualObject(Node* at, NodeId id);
void DebugPrint();
void DebugPrintState(VirtualState* state);
void DebugPrintObject(VirtualObject* state, NodeId id);
Graph* graph() const { return graph_; }
CommonOperatorBuilder* common() const { return common_; }

View File

@ -28,24 +28,25 @@
// Flags: --allow-natives-syntax --turbo-escape
//
function f(a) {
"use strict";
return arguments;
function f() {
this.x=0;
}
function g(a) {
"use strict";
var x = f(1,2,3);
var o = new f();
if (a) {
x[1] = 5;
o.x = 5;
} else {
x[1] = 7;
o.x = 7;
}
return x[1];
return o.x;
}
assertEquals(7, g());
assertEquals(7, g());
assertEquals(5, g(true));
assertEquals(7, g(false));
%OptimizeFunctionOnNextCall(g);
assertEquals(5, g(true));
assertEquals(7, g(false));
assertEquals(7, g());

View File

@ -0,0 +1,52 @@
// Copyright 2015 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --turbo-escape
//
function f() {
return arguments;
}
function g(a) {
"use strict";
var o = f(1,2);
if (a) {
o[0] = 5;
} else {
o[0] = 7;
}
return o[0];
}
assertEquals(7, g());
assertEquals(7, g());
%OptimizeFunctionOnNextCall(g);
assertEquals(5, g(true));
assertEquals(7, g(false));
assertEquals(7, g());

View File

@ -172,9 +172,9 @@
'compiler/escape-analysis-3': [PASS, NO_VARIANTS],
'compiler/escape-analysis-4': [PASS, NO_VARIANTS],
'compiler/escape-analysis-5': [PASS, NO_VARIANTS],
'compiler/escape-analysis-7': [PASS, NO_VARIANTS],
# TODO(sigurds): Tests that fail because of incomplete phi handling.
'compiler/escape-analysis-6': [PASS, NO_VARIANTS],
'compiler/escape-analysis-7': [PASS, NO_VARIANTS],
'compiler/escape-analysis-9': [PASS, NO_VARIANTS],
# TODO(sigurds): Tests that fail because of incomplete use handling (i.e. select).
'compiler/escape-analysis-8': [PASS, NO_VARIANTS],