[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:
sigurds 2016-01-20 05:25:18 -08:00 committed by Commit bot
parent ed24dfe80d
commit 4efbeac115
7 changed files with 250 additions and 134 deletions

View File

@ -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) {

View File

@ -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);
};

View File

@ -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

View File

@ -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();

View File

@ -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();
}

View File

@ -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:

View File

@ -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);