[turbofan] Improve escape analysis.
* Treat Select nodes as escaping * Correctly void virtual field information after a store to a non-const index * Add a shortcut if all allocates escape * Add a shortcut if no allocates are discovered * Only reduce FrameState/StateValues nodes if they have virtual allocates as input (transitively) * Fix bug in FrameState/StateValues duplication * Add check to verifier: First 3 inputs of FrameState must be StateValues R=mstarzinger@chromium.org BUG=v8:4586 LOG=n Review URL: https://codereview.chromium.org/1583213003 Cr-Commit-Position: refs/heads/master@{#33406}
This commit is contained in:
parent
ed24dfe80d
commit
4efbeac115
@ -18,7 +18,8 @@ EscapeAnalysisReducer::EscapeAnalysisReducer(Editor* editor, JSGraph* jsgraph,
|
||||
jsgraph_(jsgraph),
|
||||
escape_analysis_(escape_analysis),
|
||||
zone_(zone),
|
||||
visited_(static_cast<int>(jsgraph->graph()->NodeCount()), zone) {}
|
||||
visited_(static_cast<int>(jsgraph->graph()->NodeCount() * 2), zone),
|
||||
exists_virtual_allocate_(true) {}
|
||||
|
||||
|
||||
Reduction EscapeAnalysisReducer::Reduce(Node* node) {
|
||||
@ -37,11 +38,41 @@ Reduction EscapeAnalysisReducer::Reduce(Node* node) {
|
||||
return ReduceReferenceEqual(node);
|
||||
case IrOpcode::kObjectIsSmi:
|
||||
return ReduceObjectIsSmi(node);
|
||||
case IrOpcode::kFrameState:
|
||||
case IrOpcode::kStateValues: {
|
||||
if (node->id() >= static_cast<NodeId>(visited_.length()) ||
|
||||
visited_.Contains(node->id())) {
|
||||
break;
|
||||
}
|
||||
bool needs_visit = false;
|
||||
for (int i = 0; i < node->InputCount(); i++) {
|
||||
Node* input = node->InputAt(i);
|
||||
switch (input->opcode()) {
|
||||
case IrOpcode::kAllocate:
|
||||
case IrOpcode::kFinishRegion:
|
||||
needs_visit = needs_visit || escape_analysis()->IsVirtual(input);
|
||||
break;
|
||||
case IrOpcode::kFrameState:
|
||||
case IrOpcode::kStateValues:
|
||||
needs_visit =
|
||||
needs_visit ||
|
||||
input->id() >= static_cast<NodeId>(visited_.length()) ||
|
||||
!visited_.Contains(input->id());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!needs_visit) {
|
||||
visited_.Add(node->id());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO(sigurds): Change this to GetFrameStateInputCount once
|
||||
// it is working. For now we use EffectInputCount > 0 to determine
|
||||
// whether a node might have a frame state input.
|
||||
if (node->op()->EffectInputCount() > 0) {
|
||||
if (exists_virtual_allocate_ && node->op()->EffectInputCount() > 0) {
|
||||
return ReduceFrameStateUses(node);
|
||||
}
|
||||
break;
|
||||
@ -174,7 +205,7 @@ Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) {
|
||||
for (int i = 0; i < node->InputCount(); ++i) {
|
||||
Node* input = node->InputAt(i);
|
||||
if (input->opcode() == IrOpcode::kFrameState) {
|
||||
if (Node* ret = ReduceFrameState(input, node, false)) {
|
||||
if (Node* ret = ReduceDeoptState(input, node, false)) {
|
||||
node->ReplaceInput(i, ret);
|
||||
changed = true;
|
||||
}
|
||||
@ -188,76 +219,61 @@ Reduction EscapeAnalysisReducer::ReduceFrameStateUses(Node* node) {
|
||||
|
||||
|
||||
// Returns the clone if it duplicated the node, and null otherwise.
|
||||
Node* EscapeAnalysisReducer::ReduceFrameState(Node* node, Node* effect,
|
||||
Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect,
|
||||
bool multiple_users) {
|
||||
DCHECK(node->opcode() == IrOpcode::kFrameState);
|
||||
DCHECK(node->opcode() == IrOpcode::kFrameState ||
|
||||
node->opcode() == IrOpcode::kStateValues);
|
||||
if (node->id() < static_cast<NodeId>(visited_.length()) &&
|
||||
visited_.Contains(node->id())) {
|
||||
return nullptr;
|
||||
}
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF("Reducing FrameState %d\n", node->id());
|
||||
PrintF("Reducing %s %d\n", node->op()->mnemonic(), node->id());
|
||||
}
|
||||
Node* clone = nullptr;
|
||||
bool node_multiused = node->UseCount() > 1;
|
||||
bool multiple_users_rec = multiple_users || node_multiused;
|
||||
for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
|
||||
Node* input = NodeProperties::GetValueInput(node, i);
|
||||
Node* ret =
|
||||
input->opcode() == IrOpcode::kStateValues
|
||||
? ReduceStateValueInputs(input, effect, node->UseCount() > 1)
|
||||
: ReduceStateValueInput(node, i, effect, node->UseCount() > 1);
|
||||
if (ret) {
|
||||
if (node->UseCount() > 1 || multiple_users) {
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" Cloning #%d", node->id());
|
||||
}
|
||||
node = clone = jsgraph()->graph()->CloneNode(node);
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" to #%d\n", node->id());
|
||||
}
|
||||
multiple_users = false; // Don't clone anymore.
|
||||
}
|
||||
NodeProperties::ReplaceValueInput(node, ret, i);
|
||||
}
|
||||
}
|
||||
Node* outer_frame_state = NodeProperties::GetFrameStateInput(node, 0);
|
||||
if (outer_frame_state->opcode() == IrOpcode::kFrameState) {
|
||||
if (Node* ret =
|
||||
ReduceFrameState(outer_frame_state, effect, node->UseCount() > 1)) {
|
||||
if (node->UseCount() > 1 || multiple_users) {
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" Cloning #%d", node->id());
|
||||
}
|
||||
node = clone = jsgraph()->graph()->CloneNode(node);
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" to #%d\n", node->id());
|
||||
}
|
||||
multiple_users = false;
|
||||
}
|
||||
NodeProperties::ReplaceFrameStateInput(node, 0, ret);
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
// Returns the clone if it duplicated the node, and null otherwise.
|
||||
Node* EscapeAnalysisReducer::ReduceStateValueInputs(Node* node, Node* effect,
|
||||
bool multiple_users) {
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF("Reducing StateValue #%d\n", node->id());
|
||||
}
|
||||
DCHECK(node->opcode() == IrOpcode::kStateValues);
|
||||
DCHECK_NOT_NULL(effect);
|
||||
Node* clone = nullptr;
|
||||
for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
|
||||
Node* input = NodeProperties::GetValueInput(node, i);
|
||||
Node* ret = nullptr;
|
||||
if (input->opcode() == IrOpcode::kStateValues) {
|
||||
ret = ReduceStateValueInputs(input, effect, multiple_users);
|
||||
if (Node* ret = ReduceDeoptState(input, effect, multiple_users_rec)) {
|
||||
if (node_multiused || (multiple_users && !clone)) {
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" Cloning #%d", node->id());
|
||||
}
|
||||
node = clone = jsgraph()->graph()->CloneNode(node);
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" to #%d\n", node->id());
|
||||
}
|
||||
node_multiused = false;
|
||||
}
|
||||
NodeProperties::ReplaceValueInput(node, ret, i);
|
||||
}
|
||||
} else {
|
||||
ret = ReduceStateValueInput(node, i, effect, multiple_users);
|
||||
if (Node* ret = ReduceStateValueInput(node, i, effect, node_multiused,
|
||||
clone, multiple_users)) {
|
||||
DCHECK_NULL(clone);
|
||||
node_multiused = false; // Don't clone anymore.
|
||||
node = clone = ret;
|
||||
}
|
||||
}
|
||||
if (ret) {
|
||||
node = ret;
|
||||
DCHECK_NULL(clone);
|
||||
clone = ret;
|
||||
multiple_users = false;
|
||||
}
|
||||
if (node->opcode() == IrOpcode::kFrameState) {
|
||||
Node* outer_frame_state = NodeProperties::GetFrameStateInput(node, 0);
|
||||
if (outer_frame_state->opcode() == IrOpcode::kFrameState) {
|
||||
if (Node* ret =
|
||||
ReduceDeoptState(outer_frame_state, effect, multiple_users_rec)) {
|
||||
if (node_multiused || (multiple_users && !clone)) {
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" Cloning #%d", node->id());
|
||||
}
|
||||
node = clone = jsgraph()->graph()->CloneNode(node);
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" to #%d\n", node->id());
|
||||
}
|
||||
}
|
||||
NodeProperties::ReplaceFrameStateInput(node, 0, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
@ -267,6 +283,8 @@ Node* EscapeAnalysisReducer::ReduceStateValueInputs(Node* node, Node* effect,
|
||||
// Returns the clone if it duplicated the node, and null otherwise.
|
||||
Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index,
|
||||
Node* effect,
|
||||
bool node_multiused,
|
||||
bool already_cloned,
|
||||
bool multiple_users) {
|
||||
Node* input = NodeProperties::GetValueInput(node, node_index);
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
@ -279,7 +297,7 @@ Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index,
|
||||
if (escape_analysis()->IsVirtual(input)) {
|
||||
if (Node* object_state =
|
||||
escape_analysis()->GetOrCreateObjectState(effect, input)) {
|
||||
if (node->UseCount() > 1 || multiple_users) {
|
||||
if (node_multiused || (multiple_users && !already_cloned)) {
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF("Cloning #%d", node->id());
|
||||
}
|
||||
@ -287,6 +305,8 @@ Node* EscapeAnalysisReducer::ReduceStateValueInput(Node* node, int node_index,
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" to #%d\n", node->id());
|
||||
}
|
||||
node_multiused = false;
|
||||
already_cloned = true;
|
||||
}
|
||||
NodeProperties::ReplaceValueInput(node, object_state, node_index);
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
|
@ -29,6 +29,9 @@ class EscapeAnalysisReducer final : public AdvancedReducer {
|
||||
EscapeAnalysis* escape_analysis, Zone* zone);
|
||||
|
||||
Reduction Reduce(Node* node) final;
|
||||
void SetExistsVirtualAllocate(bool exists) {
|
||||
exists_virtual_allocate_ = exists;
|
||||
}
|
||||
|
||||
private:
|
||||
Reduction ReduceLoad(Node* node);
|
||||
@ -38,9 +41,9 @@ class EscapeAnalysisReducer final : public AdvancedReducer {
|
||||
Reduction ReduceReferenceEqual(Node* node);
|
||||
Reduction ReduceObjectIsSmi(Node* node);
|
||||
Reduction ReduceFrameStateUses(Node* node);
|
||||
Node* ReduceFrameState(Node* node, Node* effect, bool multiple_users);
|
||||
Node* ReduceStateValueInputs(Node* node, Node* effect, bool multiple_users);
|
||||
Node* ReduceDeoptState(Node* node, Node* effect, bool multiple_users);
|
||||
Node* ReduceStateValueInput(Node* node, int node_index, Node* effect,
|
||||
bool node_multiused, bool already_cloned,
|
||||
bool multiple_users);
|
||||
|
||||
JSGraph* jsgraph() const { return jsgraph_; }
|
||||
@ -51,7 +54,10 @@ class EscapeAnalysisReducer final : public AdvancedReducer {
|
||||
JSGraph* const jsgraph_;
|
||||
EscapeAnalysis* escape_analysis_;
|
||||
Zone* const zone_;
|
||||
// _visited marks nodes we already processed (allocs, loads, stores)
|
||||
// and nodes that do not need a visit from ReduceDeoptState etc.
|
||||
BitVector visited_;
|
||||
bool exists_virtual_allocate_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(EscapeAnalysisReducer);
|
||||
};
|
||||
|
@ -124,6 +124,7 @@ class VirtualObject : public ZoneObject {
|
||||
bool VirtualObject::UpdateFrom(const VirtualObject& other) {
|
||||
bool changed = status_ != other.status_;
|
||||
status_ = other.status_;
|
||||
phi_ = other.phi_;
|
||||
if (fields_.size() != other.fields_.size()) {
|
||||
fields_ = other.fields_;
|
||||
return true;
|
||||
@ -145,7 +146,8 @@ class VirtualState : public ZoneObject {
|
||||
|
||||
VirtualObject* VirtualObjectFromAlias(size_t alias);
|
||||
VirtualObject* GetOrCreateTrackedVirtualObject(EscapeAnalysis::Alias alias,
|
||||
NodeId id, Zone* zone);
|
||||
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_; }
|
||||
@ -255,7 +257,7 @@ VirtualObject* VirtualState::VirtualObjectFromAlias(size_t alias) {
|
||||
|
||||
|
||||
VirtualObject* VirtualState::GetOrCreateTrackedVirtualObject(
|
||||
EscapeAnalysis::Alias alias, NodeId id, Zone* zone) {
|
||||
EscapeAnalysis::Alias alias, NodeId id, size_t field_number, Zone* zone) {
|
||||
if (VirtualObject* obj = VirtualObjectFromAlias(alias)) {
|
||||
return obj;
|
||||
}
|
||||
@ -356,7 +358,7 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
|
||||
PrintF(" Merging virtual objects of @%d\n", alias);
|
||||
}
|
||||
VirtualObject* mergeObject = GetOrCreateTrackedVirtualObject(
|
||||
alias, cache->objects().front()->id(), zone);
|
||||
alias, cache->objects().front()->id(), fields, zone);
|
||||
changed = mergeObject->ResizeFields(fields) || changed;
|
||||
for (size_t i = 0; i < fields; ++i) {
|
||||
if (Node* field = cache->GetFields(i)) {
|
||||
@ -410,6 +412,9 @@ bool VirtualState::MergeFrom(MergeCache* cache, Zone* zone, Graph* graph,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (FLAG_trace_turbo_escape) {
|
||||
PrintF(" Field %zu cleared\n", i);
|
||||
}
|
||||
changed = mergeObject->SetField(i, nullptr) || changed;
|
||||
}
|
||||
}
|
||||
@ -440,7 +445,12 @@ bool EscapeStatusAnalysis::HasEntry(Node* node) {
|
||||
|
||||
|
||||
bool EscapeStatusAnalysis::IsVirtual(Node* node) {
|
||||
return (status_[node->id()] & kTracked) && !(status_[node->id()] & kEscaped);
|
||||
return IsVirtual(node->id());
|
||||
}
|
||||
|
||||
|
||||
bool EscapeStatusAnalysis::IsVirtual(NodeId id) {
|
||||
return (status_[id] & kTracked) && !(status_[id] & kEscaped);
|
||||
}
|
||||
|
||||
|
||||
@ -669,6 +679,14 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep,
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case IrOpcode::kSelect:
|
||||
if (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) {
|
||||
@ -731,6 +749,8 @@ EscapeAnalysis::~EscapeAnalysis() {}
|
||||
void EscapeAnalysis::Run() {
|
||||
replacements_.resize(graph()->NodeCount());
|
||||
AssignAliases();
|
||||
if (AliasCount() == 0) return;
|
||||
escape_status_.Resize();
|
||||
RunObjectAnalysis();
|
||||
escape_status_.Run();
|
||||
}
|
||||
@ -1042,10 +1062,11 @@ bool EscapeAnalysis::ProcessEffectPhi(Node* node) {
|
||||
void EscapeAnalysis::ProcessAllocation(Node* node) {
|
||||
DCHECK_EQ(node->opcode(), IrOpcode::kAllocate);
|
||||
ForwardVirtualState(node);
|
||||
VirtualState* state = virtual_states_[node->id()];
|
||||
Alias alias = aliases_[node->id()];
|
||||
|
||||
// Check if we have already processed this node.
|
||||
if (virtual_states_[node->id()]->VirtualObjectFromAlias(
|
||||
aliases_[node->id()])) {
|
||||
if (state->VirtualObjectFromAlias(alias)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1055,15 +1076,14 @@ void EscapeAnalysis::ProcessAllocation(Node* node) {
|
||||
node->InputAt(0)->opcode() != IrOpcode::kFloat32Constant &&
|
||||
node->InputAt(0)->opcode() != IrOpcode::kFloat64Constant);
|
||||
if (size.HasValue()) {
|
||||
virtual_states_[node->id()]->SetVirtualObject(
|
||||
aliases_[node->id()],
|
||||
new (zone())
|
||||
VirtualObject(node->id(), zone(), size.Value() / kPointerSize));
|
||||
state->SetVirtualObject(
|
||||
alias, new (zone()) VirtualObject(node->id(), zone(),
|
||||
size.Value() / kPointerSize));
|
||||
} else {
|
||||
virtual_states_[node->id()]->SetVirtualObject(
|
||||
aliases_[node->id()], new (zone()) VirtualObject(node->id(), zone()));
|
||||
state->SetVirtualObject(alias,
|
||||
new (zone()) VirtualObject(node->id(), zone()));
|
||||
}
|
||||
virtual_states_[node->id()]->LastChangedAt(node);
|
||||
state->LastChangedAt(node);
|
||||
}
|
||||
|
||||
|
||||
@ -1178,15 +1198,7 @@ VirtualObject* EscapeAnalysis::GetVirtualObject(Node* at, NodeId id) {
|
||||
|
||||
VirtualObject* EscapeAnalysis::ResolveVirtualObject(VirtualState* state,
|
||||
Node* node) {
|
||||
VirtualObject* obj = GetVirtualObject(state, ResolveReplacement(node));
|
||||
while (obj && replacement(obj->id())) {
|
||||
if (VirtualObject* next = GetVirtualObject(state, replacement(obj->id()))) {
|
||||
obj = next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
return GetVirtualObject(state, ResolveReplacement(node));
|
||||
}
|
||||
|
||||
|
||||
@ -1251,9 +1263,9 @@ void EscapeAnalysis::ProcessLoadFromPhi(int offset, Node* from, Node* node,
|
||||
void EscapeAnalysis::ProcessLoadField(Node* node) {
|
||||
DCHECK_EQ(node->opcode(), IrOpcode::kLoadField);
|
||||
ForwardVirtualState(node);
|
||||
Node* from = NodeProperties::GetValueInput(node, 0);
|
||||
Node* from = ResolveReplacement(NodeProperties::GetValueInput(node, 0));
|
||||
VirtualState* state = virtual_states_[node->id()];
|
||||
if (VirtualObject* object = ResolveVirtualObject(state, from)) {
|
||||
if (VirtualObject* object = GetVirtualObject(state, from)) {
|
||||
int offset = OffsetFromAccess(node);
|
||||
if (!object->IsTracked()) return;
|
||||
Node* value = object->GetField(offset);
|
||||
@ -1262,13 +1274,13 @@ void EscapeAnalysis::ProcessLoadField(Node* node) {
|
||||
}
|
||||
// Record that the load has this alias.
|
||||
UpdateReplacement(state, node, value);
|
||||
} else {
|
||||
if (from->opcode() == IrOpcode::kPhi &&
|
||||
OpParameter<FieldAccess>(node).offset % kPointerSize == 0) {
|
||||
} 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);
|
||||
}
|
||||
} else {
|
||||
UpdateReplacement(state, node, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1276,7 +1288,7 @@ void EscapeAnalysis::ProcessLoadField(Node* node) {
|
||||
void EscapeAnalysis::ProcessLoadElement(Node* node) {
|
||||
DCHECK_EQ(node->opcode(), IrOpcode::kLoadElement);
|
||||
ForwardVirtualState(node);
|
||||
Node* from = NodeProperties::GetValueInput(node, 0);
|
||||
Node* from = ResolveReplacement(NodeProperties::GetValueInput(node, 0));
|
||||
VirtualState* state = virtual_states_[node->id()];
|
||||
Node* index_node = node->InputAt(1);
|
||||
NumberMatcher index(index_node);
|
||||
@ -1287,7 +1299,7 @@ void EscapeAnalysis::ProcessLoadElement(Node* node) {
|
||||
ElementAccess access = OpParameter<ElementAccess>(node);
|
||||
if (index.HasValue()) {
|
||||
int offset = index.Value() + access.header_size / kPointerSize;
|
||||
if (VirtualObject* object = ResolveVirtualObject(state, from)) {
|
||||
if (VirtualObject* object = GetVirtualObject(state, from)) {
|
||||
CHECK_GE(ElementSizeLog2Of(access.machine_type.representation()),
|
||||
kPointerSizeLog2);
|
||||
CHECK_EQ(access.header_size % kPointerSize, 0);
|
||||
@ -1303,13 +1315,15 @@ void EscapeAnalysis::ProcessLoadElement(Node* node) {
|
||||
ElementAccess access = OpParameter<ElementAccess>(node);
|
||||
int offset = index.Value() + access.header_size / kPointerSize;
|
||||
ProcessLoadFromPhi(offset, from, node, state);
|
||||
} else {
|
||||
UpdateReplacement(state, node, nullptr);
|
||||
}
|
||||
} 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 "
|
||||
"Setting #%d (%s) to escaped because load element #%d from "
|
||||
"non-const "
|
||||
"index #%d (%s)\n",
|
||||
from->id(), from->op()->mnemonic(), node->id(), index_node->id(),
|
||||
@ -1323,13 +1337,13 @@ void EscapeAnalysis::ProcessLoadElement(Node* node) {
|
||||
void EscapeAnalysis::ProcessStoreField(Node* node) {
|
||||
DCHECK_EQ(node->opcode(), IrOpcode::kStoreField);
|
||||
ForwardVirtualState(node);
|
||||
Node* to = NodeProperties::GetValueInput(node, 0);
|
||||
Node* val = NodeProperties::GetValueInput(node, 1);
|
||||
Node* to = ResolveReplacement(NodeProperties::GetValueInput(node, 0));
|
||||
VirtualState* state = virtual_states_[node->id()];
|
||||
if (VirtualObject* obj = ResolveVirtualObject(state, to)) {
|
||||
if (!obj->IsTracked()) return;
|
||||
VirtualObject* obj = GetVirtualObject(state, to);
|
||||
if (obj && obj->IsTracked()) {
|
||||
int offset = OffsetFromAccess(node);
|
||||
if (obj->SetField(offset, ResolveReplacement(val))) {
|
||||
Node* val = ResolveReplacement(NodeProperties::GetValueInput(node, 1));
|
||||
if (obj->SetField(offset, val)) {
|
||||
state->LastChangedAt(node);
|
||||
}
|
||||
}
|
||||
@ -1339,7 +1353,7 @@ void EscapeAnalysis::ProcessStoreField(Node* node) {
|
||||
void EscapeAnalysis::ProcessStoreElement(Node* node) {
|
||||
DCHECK_EQ(node->opcode(), IrOpcode::kStoreElement);
|
||||
ForwardVirtualState(node);
|
||||
Node* to = NodeProperties::GetValueInput(node, 0);
|
||||
Node* to = ResolveReplacement(NodeProperties::GetValueInput(node, 0));
|
||||
Node* index_node = node->InputAt(1);
|
||||
NumberMatcher index(index_node);
|
||||
DCHECK(index_node->opcode() != IrOpcode::kInt32Constant &&
|
||||
@ -1347,17 +1361,17 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) {
|
||||
index_node->opcode() != IrOpcode::kFloat32Constant &&
|
||||
index_node->opcode() != IrOpcode::kFloat64Constant);
|
||||
ElementAccess access = OpParameter<ElementAccess>(node);
|
||||
Node* val = NodeProperties::GetValueInput(node, 2);
|
||||
VirtualState* state = virtual_states_[node->id()];
|
||||
VirtualObject* obj = GetVirtualObject(state, to);
|
||||
if (index.HasValue()) {
|
||||
int offset = index.Value() + access.header_size / kPointerSize;
|
||||
VirtualState* states = virtual_states_[node->id()];
|
||||
if (VirtualObject* obj = ResolveVirtualObject(states, to)) {
|
||||
if (!obj->IsTracked()) return;
|
||||
if (obj && obj->IsTracked()) {
|
||||
CHECK_GE(ElementSizeLog2Of(access.machine_type.representation()),
|
||||
kPointerSizeLog2);
|
||||
CHECK_EQ(access.header_size % kPointerSize, 0);
|
||||
if (obj->SetField(offset, ResolveReplacement(val))) {
|
||||
states->LastChangedAt(node);
|
||||
Node* val = ResolveReplacement(NodeProperties::GetValueInput(node, 2));
|
||||
if (obj->SetField(offset, val)) {
|
||||
state->LastChangedAt(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1372,6 +1386,9 @@ void EscapeAnalysis::ProcessStoreElement(Node* node) {
|
||||
index_node->op()->mnemonic());
|
||||
}
|
||||
}
|
||||
if (obj && obj->IsTracked() && obj->ClearAllFields()) {
|
||||
state->LastChangedAt(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1466,6 +1483,19 @@ VirtualObject* EscapeAnalysis::GetVirtualObject(VirtualState* state,
|
||||
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))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -58,6 +58,7 @@ class EscapeStatusAnalysis {
|
||||
void RevisitUses(Node* node);
|
||||
void RevisitInputs(Node* node);
|
||||
bool SetEscaped(Node* node);
|
||||
bool IsVirtual(NodeId id);
|
||||
bool HasEntry(Node* node);
|
||||
void Resize();
|
||||
size_t size();
|
||||
@ -99,6 +100,7 @@ class EscapeAnalysis {
|
||||
bool IsEscaped(Node* node);
|
||||
bool CompareVirtualObjects(Node* left, Node* right);
|
||||
Node* GetOrCreateObjectState(Node* effect, Node* node);
|
||||
bool ExistsVirtualAllocate();
|
||||
|
||||
private:
|
||||
void RunObjectAnalysis();
|
||||
|
@ -664,6 +664,8 @@ struct EscapeAnalysisPhase {
|
||||
JSGraphReducer graph_reducer(data->jsgraph(), temp_zone);
|
||||
EscapeAnalysisReducer escape_reducer(&graph_reducer, data->jsgraph(),
|
||||
&escape_analysis, temp_zone);
|
||||
escape_reducer.SetExistsVirtualAllocate(
|
||||
escape_analysis.ExistsVirtualAllocate());
|
||||
AddReducer(data, &graph_reducer, &escape_reducer);
|
||||
graph_reducer.ReduceGraph();
|
||||
}
|
||||
|
@ -428,13 +428,20 @@ void Verifier::Visitor::Check(Node* node) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kFrameState:
|
||||
case IrOpcode::kFrameState: {
|
||||
// TODO(jarin): what are the constraints on these?
|
||||
CHECK_EQ(5, value_count);
|
||||
CHECK_EQ(0, control_count);
|
||||
CHECK_EQ(0, effect_count);
|
||||
CHECK_EQ(6, input_count);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
CHECK(NodeProperties::GetValueInput(node, i)->opcode() ==
|
||||
IrOpcode::kStateValues ||
|
||||
NodeProperties::GetValueInput(node, i)->opcode() ==
|
||||
IrOpcode::kTypedStateValues);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kStateValues:
|
||||
case IrOpcode::kObjectState:
|
||||
case IrOpcode::kTypedStateValues:
|
||||
|
@ -85,6 +85,20 @@ class EscapeAnalysisTest : public GraphTest {
|
||||
allocation, value, effect, control);
|
||||
}
|
||||
|
||||
Node* StoreElement(const ElementAccess& access, Node* allocation, Node* index,
|
||||
Node* value, Node* effect = nullptr,
|
||||
Node* control = nullptr) {
|
||||
if (!effect) {
|
||||
effect = effect_;
|
||||
}
|
||||
if (!control) {
|
||||
control = control_;
|
||||
}
|
||||
return effect_ =
|
||||
graph()->NewNode(simplified()->StoreElement(access), allocation,
|
||||
index, value, effect, control);
|
||||
}
|
||||
|
||||
Node* Load(const FieldAccess& access, Node* from, Node* effect = nullptr,
|
||||
Node* control = nullptr) {
|
||||
if (!effect) {
|
||||
@ -131,12 +145,18 @@ class EscapeAnalysisTest : public GraphTest {
|
||||
return control_ = graph()->NewNode(common()->Merge(2), control1, control2);
|
||||
}
|
||||
|
||||
FieldAccess AccessAtIndex(int offset) {
|
||||
FieldAccess FieldAccessAtIndex(int offset) {
|
||||
FieldAccess access = {kTaggedBase, offset, MaybeHandle<Name>(), Type::Any(),
|
||||
MachineType::AnyTagged()};
|
||||
return access;
|
||||
}
|
||||
|
||||
ElementAccess MakeElementAccess(int header_size) {
|
||||
ElementAccess access = {kTaggedBase, header_size, Type::Any(),
|
||||
MachineType::AnyTagged()};
|
||||
return access;
|
||||
}
|
||||
|
||||
// ---------------------------------Assertion Helper--------------------------
|
||||
|
||||
void ExpectReplacement(Node* node, Node* rep) {
|
||||
@ -166,6 +186,7 @@ class EscapeAnalysisTest : public GraphTest {
|
||||
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
|
||||
|
||||
Node* effect() { return effect_; }
|
||||
Node* control() { return control_; }
|
||||
|
||||
private:
|
||||
SimplifiedOperatorBuilder simplified_;
|
||||
@ -185,9 +206,9 @@ TEST_F(EscapeAnalysisTest, StraightNonEscape) {
|
||||
Node* object1 = Constant(1);
|
||||
BeginRegion();
|
||||
Node* allocation = Allocate(Constant(kPointerSize));
|
||||
Store(AccessAtIndex(0), allocation, object1);
|
||||
Store(FieldAccessAtIndex(0), allocation, object1);
|
||||
Node* finish = FinishRegion(allocation);
|
||||
Node* load = Load(AccessAtIndex(0), finish);
|
||||
Node* load = Load(FieldAccessAtIndex(0), finish);
|
||||
Node* result = Return(load);
|
||||
EndGraph();
|
||||
|
||||
@ -202,13 +223,39 @@ TEST_F(EscapeAnalysisTest, StraightNonEscape) {
|
||||
}
|
||||
|
||||
|
||||
TEST_F(EscapeAnalysisTest, StraightNonEscapeNonConstStore) {
|
||||
Node* object1 = Constant(1);
|
||||
Node* object2 = Constant(2);
|
||||
BeginRegion();
|
||||
Node* allocation = Allocate(Constant(kPointerSize));
|
||||
Store(FieldAccessAtIndex(0), allocation, object1);
|
||||
Node* index =
|
||||
graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
|
||||
object1, object2, control());
|
||||
StoreElement(MakeElementAccess(0), allocation, index, object1);
|
||||
Node* finish = FinishRegion(allocation);
|
||||
Node* load = Load(FieldAccessAtIndex(0), finish);
|
||||
Node* result = Return(load);
|
||||
EndGraph();
|
||||
|
||||
Analysis();
|
||||
|
||||
ExpectEscaped(allocation);
|
||||
ExpectReplacement(load, nullptr);
|
||||
|
||||
Transformation();
|
||||
|
||||
ASSERT_EQ(load, NodeProperties::GetValueInput(result, 0));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(EscapeAnalysisTest, StraightEscape) {
|
||||
Node* object1 = Constant(1);
|
||||
BeginRegion();
|
||||
Node* allocation = Allocate(Constant(kPointerSize));
|
||||
Store(AccessAtIndex(0), allocation, object1);
|
||||
Store(FieldAccessAtIndex(0), allocation, object1);
|
||||
Node* finish = FinishRegion(allocation);
|
||||
Node* load = Load(AccessAtIndex(0), finish);
|
||||
Node* load = Load(FieldAccessAtIndex(0), finish);
|
||||
Node* result = Return(allocation);
|
||||
EndGraph();
|
||||
graph()->end()->AppendInput(zone(), load);
|
||||
@ -229,15 +276,15 @@ TEST_F(EscapeAnalysisTest, StoreLoadEscape) {
|
||||
|
||||
BeginRegion();
|
||||
Node* allocation1 = Allocate(Constant(kPointerSize));
|
||||
Store(AccessAtIndex(0), allocation1, object1);
|
||||
Store(FieldAccessAtIndex(0), allocation1, object1);
|
||||
Node* finish1 = FinishRegion(allocation1);
|
||||
|
||||
BeginRegion();
|
||||
Node* allocation2 = Allocate(Constant(kPointerSize));
|
||||
Store(AccessAtIndex(0), allocation2, finish1);
|
||||
Store(FieldAccessAtIndex(0), allocation2, finish1);
|
||||
Node* finish2 = FinishRegion(allocation2);
|
||||
|
||||
Node* load = Load(AccessAtIndex(0), finish2);
|
||||
Node* load = Load(FieldAccessAtIndex(0), finish2);
|
||||
Node* result = Return(load);
|
||||
EndGraph();
|
||||
Analysis();
|
||||
@ -257,16 +304,18 @@ TEST_F(EscapeAnalysisTest, BranchNonEscape) {
|
||||
Node* object2 = Constant(2);
|
||||
BeginRegion();
|
||||
Node* allocation = Allocate(Constant(kPointerSize));
|
||||
Store(AccessAtIndex(0), allocation, object1);
|
||||
Store(FieldAccessAtIndex(0), allocation, object1);
|
||||
Node* finish = FinishRegion(allocation);
|
||||
Branch();
|
||||
Node* ifFalse = IfFalse();
|
||||
Node* ifTrue = IfTrue();
|
||||
Node* effect1 = Store(AccessAtIndex(0), allocation, object1, finish, ifFalse);
|
||||
Node* effect2 = Store(AccessAtIndex(0), allocation, object2, finish, ifTrue);
|
||||
Node* effect1 =
|
||||
Store(FieldAccessAtIndex(0), allocation, object1, finish, ifFalse);
|
||||
Node* effect2 =
|
||||
Store(FieldAccessAtIndex(0), allocation, object2, finish, ifTrue);
|
||||
Node* merge = Merge2(ifFalse, ifTrue);
|
||||
Node* phi = graph()->NewNode(common()->EffectPhi(2), effect1, effect2, merge);
|
||||
Node* load = Load(AccessAtIndex(0), finish, phi, merge);
|
||||
Node* load = Load(FieldAccessAtIndex(0), finish, phi, merge);
|
||||
Node* result = Return(load, phi);
|
||||
EndGraph();
|
||||
graph()->end()->AppendInput(zone(), result);
|
||||
@ -287,10 +336,10 @@ TEST_F(EscapeAnalysisTest, DanglingLoadOrder) {
|
||||
Node* object1 = Constant(1);
|
||||
Node* object2 = Constant(2);
|
||||
Node* allocation = Allocate(Constant(kPointerSize));
|
||||
Node* store1 = Store(AccessAtIndex(0), allocation, object1);
|
||||
Node* load1 = Load(AccessAtIndex(0), allocation);
|
||||
Node* store2 = Store(AccessAtIndex(0), allocation, object2);
|
||||
Node* load2 = Load(AccessAtIndex(0), allocation, store1);
|
||||
Node* store1 = Store(FieldAccessAtIndex(0), allocation, object1);
|
||||
Node* load1 = Load(FieldAccessAtIndex(0), allocation);
|
||||
Node* store2 = Store(FieldAccessAtIndex(0), allocation, object2);
|
||||
Node* load2 = Load(FieldAccessAtIndex(0), allocation, store1);
|
||||
Node* result = Return(load2);
|
||||
EndGraph();
|
||||
graph()->end()->AppendInput(zone(), store2);
|
||||
@ -312,9 +361,9 @@ TEST_F(EscapeAnalysisTest, DeoptReplacement) {
|
||||
Node* object1 = Constant(1);
|
||||
BeginRegion();
|
||||
Node* allocation = Allocate(Constant(kPointerSize));
|
||||
Store(AccessAtIndex(0), allocation, object1);
|
||||
Store(FieldAccessAtIndex(0), allocation, object1);
|
||||
Node* finish = FinishRegion(allocation);
|
||||
Node* effect1 = Store(AccessAtIndex(0), allocation, object1, finish);
|
||||
Node* effect1 = Store(FieldAccessAtIndex(0), allocation, object1, finish);
|
||||
Branch();
|
||||
Node* ifFalse = IfFalse();
|
||||
Node* state_values1 = graph()->NewNode(common()->StateValues(1), finish);
|
||||
@ -328,7 +377,7 @@ TEST_F(EscapeAnalysisTest, DeoptReplacement) {
|
||||
Node* deopt = graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
|
||||
frame_state, effect1, ifFalse);
|
||||
Node* ifTrue = IfTrue();
|
||||
Node* load = Load(AccessAtIndex(0), finish, effect1, ifTrue);
|
||||
Node* load = Load(FieldAccessAtIndex(0), finish, effect1, ifTrue);
|
||||
Node* result = Return(load, effect1, ifTrue);
|
||||
EndGraph();
|
||||
graph()->end()->AppendInput(zone(), deopt);
|
||||
@ -351,10 +400,10 @@ TEST_F(EscapeAnalysisTest, DeoptReplacementIdentity) {
|
||||
Node* object1 = Constant(1);
|
||||
BeginRegion();
|
||||
Node* allocation = Allocate(Constant(kPointerSize * 2));
|
||||
Store(AccessAtIndex(0), allocation, object1);
|
||||
Store(AccessAtIndex(kPointerSize), allocation, allocation);
|
||||
Store(FieldAccessAtIndex(0), allocation, object1);
|
||||
Store(FieldAccessAtIndex(kPointerSize), allocation, allocation);
|
||||
Node* finish = FinishRegion(allocation);
|
||||
Node* effect1 = Store(AccessAtIndex(0), allocation, object1, finish);
|
||||
Node* effect1 = Store(FieldAccessAtIndex(0), allocation, object1, finish);
|
||||
Branch();
|
||||
Node* ifFalse = IfFalse();
|
||||
Node* state_values1 = graph()->NewNode(common()->StateValues(1), finish);
|
||||
@ -368,7 +417,7 @@ TEST_F(EscapeAnalysisTest, DeoptReplacementIdentity) {
|
||||
Node* deopt = graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
|
||||
frame_state, effect1, ifFalse);
|
||||
Node* ifTrue = IfTrue();
|
||||
Node* load = Load(AccessAtIndex(0), finish, effect1, ifTrue);
|
||||
Node* load = Load(FieldAccessAtIndex(0), finish, effect1, ifTrue);
|
||||
Node* result = Return(load, effect1, ifTrue);
|
||||
EndGraph();
|
||||
graph()->end()->AppendInput(zone(), deopt);
|
||||
|
Loading…
Reference in New Issue
Block a user