v8/test/unittests/compiler/escape-analysis-unittest.cc
bmeurer db635d5b72 [turbofan] Add support for eager/soft deoptimization reasons.
So far TurboFan wasn't adding the deoptimization reasons for eager/soft
deoptimization exits that can be used by either the DevTools profiler or
the --trace-deopt flag. This adds basic support for deopt reasons on
Deoptimize, DeoptimizeIf and DeoptimizeUnless nodes and threads through
the reasons to the code generation.

Also moves the DeoptReason to it's own file (to resolve include cycles)
and drops unused reasons.

R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2161543002
Cr-Commit-Position: refs/heads/master@{#37823}
2016-07-18 09:25:16 +00:00

520 lines
16 KiB
C++

// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/bit-vector.h"
#include "src/compiler/escape-analysis.h"
#include "src/compiler/escape-analysis-reducer.h"
#include "src/compiler/graph-visualizer.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/types.h"
#include "src/zone-containers.h"
#include "test/unittests/compiler/graph-unittest.h"
namespace v8 {
namespace internal {
namespace compiler {
class EscapeAnalysisTest : public GraphTest {
public:
EscapeAnalysisTest()
: simplified_(zone()),
jsgraph_(isolate(), graph(), common(), nullptr, nullptr, nullptr),
escape_analysis_(graph(), common(), zone()),
effect_(graph()->start()),
control_(graph()->start()) {}
~EscapeAnalysisTest() {}
EscapeAnalysis* escape_analysis() { return &escape_analysis_; }
protected:
void Analysis() { escape_analysis_.Run(); }
void Transformation() {
GraphReducer graph_reducer(zone(), graph());
EscapeAnalysisReducer escape_reducer(&graph_reducer, &jsgraph_,
&escape_analysis_, zone());
graph_reducer.AddReducer(&escape_reducer);
graph_reducer.ReduceGraph();
}
// ---------------------------------Node Creation Helper----------------------
Node* BeginRegion(Node* effect = nullptr) {
if (!effect) {
effect = effect_;
}
return effect_ = graph()->NewNode(
common()->BeginRegion(RegionObservability::kObservable), effect);
}
Node* FinishRegion(Node* value, Node* effect = nullptr) {
if (!effect) {
effect = effect_;
}
return effect_ = graph()->NewNode(common()->FinishRegion(), value, effect);
}
Node* Allocate(Node* size, Node* effect = nullptr, Node* control = nullptr) {
if (!effect) {
effect = effect_;
}
if (!control) {
control = control_;
}
return effect_ = graph()->NewNode(simplified()->Allocate(), size, effect,
control);
}
Node* Constant(int num) {
return graph()->NewNode(common()->NumberConstant(num));
}
Node* Store(const FieldAccess& access, Node* allocation, Node* value,
Node* effect = nullptr, Node* control = nullptr) {
if (!effect) {
effect = effect_;
}
if (!control) {
control = control_;
}
return effect_ = graph()->NewNode(simplified()->StoreField(access),
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) {
effect = effect_;
}
if (!control) {
control = control_;
}
return graph()->NewNode(simplified()->LoadField(access), from, effect,
control);
}
Node* Return(Node* value, Node* effect = nullptr, Node* control = nullptr) {
if (!effect) {
effect = effect_;
}
if (!control) {
control = control_;
}
return control_ =
graph()->NewNode(common()->Return(), value, effect, control);
}
void EndGraph() {
for (Edge edge : graph()->end()->input_edges()) {
if (NodeProperties::IsControlEdge(edge)) {
edge.UpdateTo(control_);
}
}
}
Node* Branch() {
return control_ =
graph()->NewNode(common()->Branch(), Constant(0), control_);
}
Node* IfTrue() {
return control_ = graph()->NewNode(common()->IfTrue(), control_);
}
Node* IfFalse() { return graph()->NewNode(common()->IfFalse(), control_); }
Node* Merge2(Node* control1, Node* control2) {
return control_ = graph()->NewNode(common()->Merge(2), control1, control2);
}
FieldAccess FieldAccessAtIndex(int offset) {
FieldAccess access = {kTaggedBase,
offset,
MaybeHandle<Name>(),
Type::Any(),
MachineType::AnyTagged(),
kFullWriteBarrier};
return access;
}
ElementAccess MakeElementAccess(int header_size) {
ElementAccess access = {kTaggedBase, header_size, Type::Any(),
MachineType::AnyTagged(), kFullWriteBarrier};
return access;
}
// ---------------------------------Assertion Helper--------------------------
void ExpectReplacement(Node* node, Node* rep) {
EXPECT_EQ(rep, escape_analysis()->GetReplacement(node));
}
void ExpectReplacementPhi(Node* node, Node* left, Node* right) {
Node* rep = escape_analysis()->GetReplacement(node);
ASSERT_NE(nullptr, rep);
ASSERT_EQ(IrOpcode::kPhi, rep->opcode());
EXPECT_EQ(left, NodeProperties::GetValueInput(rep, 0));
EXPECT_EQ(right, NodeProperties::GetValueInput(rep, 1));
}
void ExpectVirtual(Node* node) {
EXPECT_TRUE(node->opcode() == IrOpcode::kAllocate ||
node->opcode() == IrOpcode::kFinishRegion);
EXPECT_TRUE(escape_analysis()->IsVirtual(node));
}
void ExpectEscaped(Node* node) {
EXPECT_TRUE(node->opcode() == IrOpcode::kAllocate ||
node->opcode() == IrOpcode::kFinishRegion);
EXPECT_TRUE(escape_analysis()->IsEscaped(node));
}
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
Node* effect() { return effect_; }
Node* control() { return control_; }
private:
SimplifiedOperatorBuilder simplified_;
JSGraph jsgraph_;
EscapeAnalysis escape_analysis_;
Node* effect_;
Node* control_;
};
// -----------------------------------------------------------------------------
// Test cases.
TEST_F(EscapeAnalysisTest, StraightNonEscape) {
Node* object1 = Constant(1);
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize));
Store(FieldAccessAtIndex(0), allocation, object1);
Node* finish = FinishRegion(allocation);
Node* load = Load(FieldAccessAtIndex(0), finish);
Node* result = Return(load);
EndGraph();
Analysis();
ExpectVirtual(allocation);
ExpectReplacement(load, object1);
Transformation();
ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
}
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(FieldAccessAtIndex(0), allocation, object1);
Node* finish = FinishRegion(allocation);
Node* load = Load(FieldAccessAtIndex(0), finish);
Node* result = Return(allocation);
EndGraph();
graph()->end()->AppendInput(zone(), load);
Analysis();
ExpectEscaped(allocation);
ExpectReplacement(load, object1);
Transformation();
ASSERT_EQ(allocation, NodeProperties::GetValueInput(result, 0));
}
TEST_F(EscapeAnalysisTest, StoreLoadEscape) {
Node* object1 = Constant(1);
BeginRegion();
Node* allocation1 = Allocate(Constant(kPointerSize));
Store(FieldAccessAtIndex(0), allocation1, object1);
Node* finish1 = FinishRegion(allocation1);
BeginRegion();
Node* allocation2 = Allocate(Constant(kPointerSize));
Store(FieldAccessAtIndex(0), allocation2, finish1);
Node* finish2 = FinishRegion(allocation2);
Node* load = Load(FieldAccessAtIndex(0), finish2);
Node* result = Return(load);
EndGraph();
Analysis();
ExpectEscaped(allocation1);
ExpectVirtual(allocation2);
ExpectReplacement(load, finish1);
Transformation();
ASSERT_EQ(finish1, NodeProperties::GetValueInput(result, 0));
}
TEST_F(EscapeAnalysisTest, BranchNonEscape) {
Node* object1 = Constant(1);
Node* object2 = Constant(2);
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize));
Store(FieldAccessAtIndex(0), allocation, object1);
Node* finish = FinishRegion(allocation);
Branch();
Node* ifFalse = IfFalse();
Node* ifTrue = 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(FieldAccessAtIndex(0), finish, phi, merge);
Node* result = Return(load, phi);
EndGraph();
graph()->end()->AppendInput(zone(), result);
Analysis();
ExpectVirtual(allocation);
ExpectReplacementPhi(load, object1, object2);
Node* replacement_phi = escape_analysis()->GetReplacement(load);
Transformation();
ASSERT_EQ(replacement_phi, NodeProperties::GetValueInput(result, 0));
}
TEST_F(EscapeAnalysisTest, BranchEscapeOne) {
Node* object1 = Constant(1);
Node* object2 = Constant(2);
Node* index = graph()->NewNode(common()->Parameter(0), start());
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize));
Store(FieldAccessAtIndex(0), allocation, object1);
Node* finish = FinishRegion(allocation);
Branch();
Node* ifFalse = IfFalse();
Node* ifTrue = IfTrue();
Node* effect1 =
Store(FieldAccessAtIndex(0), allocation, object1, finish, ifFalse);
Node* effect2 = StoreElement(MakeElementAccess(0), allocation, index, object2,
finish, ifTrue);
Node* merge = Merge2(ifFalse, ifTrue);
Node* phi = graph()->NewNode(common()->EffectPhi(2), effect1, effect2, merge);
Node* load = Load(FieldAccessAtIndex(0), finish, phi, merge);
Node* result = Return(load, phi);
EndGraph();
Analysis();
ExpectEscaped(allocation);
ExpectReplacement(load, nullptr);
Transformation();
ASSERT_EQ(load, NodeProperties::GetValueInput(result, 0));
}
TEST_F(EscapeAnalysisTest, BranchEscapeThroughStore) {
Node* object1 = Constant(1);
Node* object2 = Constant(2);
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize));
Store(FieldAccessAtIndex(0), allocation, object1);
FinishRegion(allocation);
BeginRegion();
Node* allocation2 = Allocate(Constant(kPointerSize));
Store(FieldAccessAtIndex(0), allocation, object2);
Node* finish2 = FinishRegion(allocation2);
Branch();
Node* ifFalse = IfFalse();
Node* ifTrue = IfTrue();
Node* effect1 =
Store(FieldAccessAtIndex(0), allocation, allocation2, finish2, ifFalse);
Node* merge = Merge2(ifFalse, ifTrue);
Node* phi = graph()->NewNode(common()->EffectPhi(2), effect1, finish2, merge);
Node* load = Load(FieldAccessAtIndex(0), finish2, phi, merge);
Node* result = Return(allocation, phi);
EndGraph();
graph()->end()->AppendInput(zone(), load);
Analysis();
ExpectEscaped(allocation);
ExpectEscaped(allocation2);
ExpectReplacement(load, nullptr);
Transformation();
ASSERT_EQ(allocation, NodeProperties::GetValueInput(result, 0));
}
TEST_F(EscapeAnalysisTest, DanglingLoadOrder) {
Node* object1 = Constant(1);
Node* object2 = Constant(2);
Node* allocation = Allocate(Constant(kPointerSize));
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);
graph()->end()->AppendInput(zone(), load1);
Analysis();
ExpectVirtual(allocation);
ExpectReplacement(load1, object1);
ExpectReplacement(load2, object1);
Transformation();
ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
}
TEST_F(EscapeAnalysisTest, DeoptReplacement) {
Node* object1 = Constant(1);
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize));
Store(FieldAccessAtIndex(0), allocation, object1);
Node* finish = FinishRegion(allocation);
Node* effect1 = Store(FieldAccessAtIndex(0), allocation, object1, finish);
Branch();
Node* ifFalse = IfFalse();
Node* state_values1 = graph()->NewNode(common()->StateValues(1), finish);
Node* state_values2 = graph()->NewNode(common()->StateValues(0));
Node* state_values3 = graph()->NewNode(common()->StateValues(0));
Node* frame_state = graph()->NewNode(
common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
nullptr),
state_values1, state_values2, state_values3, UndefinedConstant(),
graph()->start(), graph()->start());
Node* deopt = graph()->NewNode(
common()->Deoptimize(DeoptimizeKind::kEager, DeoptimizeReason::kNoReason),
frame_state, effect1, ifFalse);
Node* ifTrue = IfTrue();
Node* load = Load(FieldAccessAtIndex(0), finish, effect1, ifTrue);
Node* result = Return(load, effect1, ifTrue);
EndGraph();
graph()->end()->AppendInput(zone(), deopt);
Analysis();
ExpectVirtual(allocation);
ExpectReplacement(load, object1);
Transformation();
ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
Node* object_state = NodeProperties::GetValueInput(state_values1, 0);
ASSERT_EQ(object_state->opcode(), IrOpcode::kObjectState);
ASSERT_EQ(1, object_state->op()->ValueInputCount());
ASSERT_EQ(object1, NodeProperties::GetValueInput(object_state, 0));
}
TEST_F(EscapeAnalysisTest, DeoptReplacementIdentity) {
Node* object1 = Constant(1);
BeginRegion();
Node* allocation = Allocate(Constant(kPointerSize * 2));
Store(FieldAccessAtIndex(0), allocation, object1);
Store(FieldAccessAtIndex(kPointerSize), allocation, allocation);
Node* finish = FinishRegion(allocation);
Node* effect1 = Store(FieldAccessAtIndex(0), allocation, object1, finish);
Branch();
Node* ifFalse = IfFalse();
Node* state_values1 = graph()->NewNode(common()->StateValues(1), finish);
Node* state_values2 = graph()->NewNode(common()->StateValues(1), finish);
Node* state_values3 = graph()->NewNode(common()->StateValues(0));
Node* frame_state = graph()->NewNode(
common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
nullptr),
state_values1, state_values2, state_values3, UndefinedConstant(),
graph()->start(), graph()->start());
Node* deopt = graph()->NewNode(
common()->Deoptimize(DeoptimizeKind::kEager, DeoptimizeReason::kNoReason),
frame_state, effect1, ifFalse);
Node* ifTrue = IfTrue();
Node* load = Load(FieldAccessAtIndex(0), finish, effect1, ifTrue);
Node* result = Return(load, effect1, ifTrue);
EndGraph();
graph()->end()->AppendInput(zone(), deopt);
Analysis();
ExpectVirtual(allocation);
ExpectReplacement(load, object1);
Transformation();
ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
Node* object_state = NodeProperties::GetValueInput(state_values1, 0);
ASSERT_EQ(object_state->opcode(), IrOpcode::kObjectState);
ASSERT_EQ(2, object_state->op()->ValueInputCount());
ASSERT_EQ(object1, NodeProperties::GetValueInput(object_state, 0));
ASSERT_EQ(object_state, NodeProperties::GetValueInput(object_state, 1));
Node* object_state2 = NodeProperties::GetValueInput(state_values1, 0);
ASSERT_EQ(object_state, object_state2);
}
} // namespace compiler
} // namespace internal
} // namespace v8