| Index: test/unittests/compiler/escape-analysis-unittest.cc
|
| diff --git a/test/unittests/compiler/escape-analysis-unittest.cc b/test/unittests/compiler/escape-analysis-unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..92b8a7482bfd318862bb23deb436021e7c5b73c3
|
| --- /dev/null
|
| +++ b/test/unittests/compiler/escape-analysis-unittest.cc
|
| @@ -0,0 +1,311 @@
|
| +// 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-inl.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_objects_(graph(), common(), zone()),
|
| + escape_status_(&escape_objects_, graph(), zone()),
|
| + effect_(graph()->start()),
|
| + control_(graph()->start()) {}
|
| +
|
| + ~EscapeAnalysisTest() {}
|
| +
|
| + EscapeStatusAnalysis* escape_status() { return &escape_status_; }
|
| + EscapeObjectAnalysis* escape_objects() { return &escape_objects_; }
|
| +
|
| + protected:
|
| + void Analysis() {
|
| + escape_objects_.Run();
|
| + escape_status_.Run();
|
| + }
|
| +
|
| + void Transformation() {
|
| + GraphReducer graph_reducer(zone(), graph());
|
| + EscapeAnalysisReducer escape_reducer(
|
| + &graph_reducer, &jsgraph_, &escape_status_, &escape_objects_, 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(), 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* 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 AccessAtIndex(int offset) {
|
| + FieldAccess access = {kTaggedBase, offset, MaybeHandle<Name>(), Type::Any(),
|
| + kMachAnyTagged};
|
| + return access;
|
| + }
|
| +
|
| + // ---------------------------------Assertion Helper--------------------------
|
| +
|
| + void ExpectReplacement(Node* node, Node* rep) {
|
| + EXPECT_EQ(rep, escape_objects()->GetReplacement(node, node->id()));
|
| + }
|
| +
|
| + void ExpectReplacementPhi(Node* node, Node* left, Node* right) {
|
| + Node* rep = escape_objects()->GetReplacement(node, node->id());
|
| + 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_status()->IsVirtual(node));
|
| + }
|
| +
|
| + void ExpectEscaped(Node* node) {
|
| + EXPECT_TRUE(node->opcode() == IrOpcode::kAllocate ||
|
| + node->opcode() == IrOpcode::kFinishRegion);
|
| + EXPECT_TRUE(escape_status()->IsEscaped(node));
|
| + }
|
| +
|
| + SimplifiedOperatorBuilder* simplified() { return &simplified_; }
|
| +
|
| + Node* effect() { return effect_; }
|
| +
|
| + private:
|
| + SimplifiedOperatorBuilder simplified_;
|
| + JSGraph jsgraph_;
|
| + EscapeObjectAnalysis escape_objects_;
|
| + EscapeStatusAnalysis escape_status_;
|
| +
|
| + Node* effect_;
|
| + Node* control_;
|
| +};
|
| +
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +// Test cases.
|
| +
|
| +
|
| +TEST_F(EscapeAnalysisTest, StraightNonEscape) {
|
| + Node* object1 = Constant(1);
|
| + BeginRegion();
|
| + Node* allocation = Allocate(Constant(kPointerSize));
|
| + Store(AccessAtIndex(0), allocation, object1);
|
| + Node* finish = FinishRegion(allocation);
|
| + Node* load = Load(AccessAtIndex(0), finish);
|
| + Node* result = Return(load);
|
| + EndGraph();
|
| + Analysis();
|
| +
|
| + ExpectVirtual(allocation);
|
| + ExpectReplacement(load, object1);
|
| +
|
| + Transformation();
|
| +
|
| + ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
|
| +}
|
| +
|
| +
|
| +TEST_F(EscapeAnalysisTest, StraightEscape) {
|
| + Node* object1 = Constant(1);
|
| + BeginRegion();
|
| + Node* allocation = Allocate(Constant(kPointerSize));
|
| + Store(AccessAtIndex(0), allocation, object1);
|
| + Node* finish = FinishRegion(allocation);
|
| + Node* load = Load(AccessAtIndex(0), finish);
|
| + Node* result = Return(allocation);
|
| + EndGraph();
|
| + 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(AccessAtIndex(0), allocation1, object1);
|
| + Node* finish1 = FinishRegion(allocation1);
|
| +
|
| + BeginRegion();
|
| + Node* allocation2 = Allocate(Constant(kPointerSize));
|
| + Store(AccessAtIndex(0), allocation2, finish1);
|
| + Node* finish2 = FinishRegion(allocation2);
|
| +
|
| + Node* load = Load(AccessAtIndex(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(AccessAtIndex(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* merge = Merge2(ifFalse, ifTrue);
|
| + Node* phi = graph()->NewNode(common()->EffectPhi(2), effect1, effect2, merge);
|
| + Node* load = Load(AccessAtIndex(0), finish, phi, merge);
|
| + Node* result = Return(load, phi);
|
| + EndGraph();
|
| + Analysis();
|
| +
|
| + ExpectVirtual(allocation);
|
| + ExpectReplacementPhi(load, object1, object2);
|
| + Node* replacement_phi = escape_objects()->GetReplacement(load, load->id());
|
| +
|
| + Transformation();
|
| +
|
| + ASSERT_EQ(replacement_phi, NodeProperties::GetValueInput(result, 0));
|
| +}
|
| +
|
| +
|
| +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);
|
| + Store(AccessAtIndex(0), allocation, object2);
|
| + Node* load2 = Load(AccessAtIndex(0), allocation, store1);
|
| + Node* result = Return(load2);
|
| + EndGraph();
|
| +
|
| + Analysis();
|
| +
|
| + ExpectVirtual(allocation);
|
| + ExpectReplacement(load1, object1);
|
| + ExpectReplacement(load2, object1);
|
| +
|
| + Transformation();
|
| +
|
| + ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0));
|
| +}
|
| +
|
| +} // namespace compiler
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|