2b93b8aa41
This simplifies the handling of the End node. Based on this CL we will finally fix terminating every loop from the beginning (via Terminate nodes) and fix inlining of Throw, Deoptimize and Terminate. R=mstarzinger@chromium.org Review URL: https://codereview.chromium.org/1157023002 Cr-Commit-Position: refs/heads/master@{#28620}
256 lines
6.1 KiB
C++
256 lines
6.1 KiB
C++
// Copyright 2014 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/control-equivalence.h"
|
|
#include "src/compiler/graph-visualizer.h"
|
|
#include "src/compiler/node-properties.h"
|
|
#include "src/zone-containers.h"
|
|
#include "test/unittests/compiler/graph-unittest.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace compiler {
|
|
|
|
#define ASSERT_EQUIVALENCE(...) \
|
|
do { \
|
|
Node* __n[] = {__VA_ARGS__}; \
|
|
ASSERT_TRUE(IsEquivalenceClass(arraysize(__n), __n)); \
|
|
} while (false)
|
|
|
|
class ControlEquivalenceTest : public GraphTest {
|
|
public:
|
|
ControlEquivalenceTest() : all_nodes_(zone()), classes_(zone()) {
|
|
Store(graph()->start());
|
|
}
|
|
|
|
protected:
|
|
void ComputeEquivalence(Node* node) {
|
|
graph()->SetEnd(graph()->NewNode(common()->End(1), node));
|
|
if (FLAG_trace_turbo) {
|
|
OFStream os(stdout);
|
|
os << AsDOT(*graph());
|
|
}
|
|
ControlEquivalence equivalence(zone(), graph());
|
|
equivalence.Run(node);
|
|
classes_.resize(graph()->NodeCount());
|
|
for (Node* node : all_nodes_) {
|
|
classes_[node->id()] = equivalence.ClassOf(node);
|
|
}
|
|
}
|
|
|
|
bool IsEquivalenceClass(size_t length, Node** nodes) {
|
|
BitVector in_class(graph()->NodeCount(), zone());
|
|
size_t expected_class = classes_[nodes[0]->id()];
|
|
for (size_t i = 0; i < length; ++i) {
|
|
in_class.Add(nodes[i]->id());
|
|
}
|
|
for (Node* node : all_nodes_) {
|
|
if (in_class.Contains(node->id())) {
|
|
if (classes_[node->id()] != expected_class) return false;
|
|
} else {
|
|
if (classes_[node->id()] == expected_class) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Node* Value() { return NumberConstant(0.0); }
|
|
|
|
Node* Branch(Node* control) {
|
|
return Store(graph()->NewNode(common()->Branch(), Value(), control));
|
|
}
|
|
|
|
Node* IfTrue(Node* control) {
|
|
return Store(graph()->NewNode(common()->IfTrue(), control));
|
|
}
|
|
|
|
Node* IfFalse(Node* control) {
|
|
return Store(graph()->NewNode(common()->IfFalse(), control));
|
|
}
|
|
|
|
Node* Merge2(Node* control1, Node* control2) {
|
|
return Store(graph()->NewNode(common()->Merge(2), control1, control2));
|
|
}
|
|
|
|
Node* Loop2(Node* control) {
|
|
return Store(graph()->NewNode(common()->Loop(2), control, control));
|
|
}
|
|
|
|
Node* End(Node* control) {
|
|
return Store(graph()->NewNode(common()->End(1), control));
|
|
}
|
|
|
|
private:
|
|
Node* Store(Node* node) {
|
|
all_nodes_.push_back(node);
|
|
return node;
|
|
}
|
|
|
|
ZoneVector<Node*> all_nodes_;
|
|
ZoneVector<size_t> classes_;
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Test cases.
|
|
|
|
|
|
TEST_F(ControlEquivalenceTest, Empty1) {
|
|
Node* start = graph()->start();
|
|
ComputeEquivalence(start);
|
|
|
|
ASSERT_EQUIVALENCE(start);
|
|
}
|
|
|
|
|
|
TEST_F(ControlEquivalenceTest, Empty2) {
|
|
Node* start = graph()->start();
|
|
Node* end = End(start);
|
|
ComputeEquivalence(end);
|
|
|
|
ASSERT_EQUIVALENCE(start, end);
|
|
}
|
|
|
|
|
|
TEST_F(ControlEquivalenceTest, Diamond1) {
|
|
Node* start = graph()->start();
|
|
Node* b = Branch(start);
|
|
Node* t = IfTrue(b);
|
|
Node* f = IfFalse(b);
|
|
Node* m = Merge2(t, f);
|
|
ComputeEquivalence(m);
|
|
|
|
ASSERT_EQUIVALENCE(b, m, start);
|
|
ASSERT_EQUIVALENCE(f);
|
|
ASSERT_EQUIVALENCE(t);
|
|
}
|
|
|
|
|
|
TEST_F(ControlEquivalenceTest, Diamond2) {
|
|
Node* start = graph()->start();
|
|
Node* b1 = Branch(start);
|
|
Node* t1 = IfTrue(b1);
|
|
Node* f1 = IfFalse(b1);
|
|
Node* b2 = Branch(f1);
|
|
Node* t2 = IfTrue(b2);
|
|
Node* f2 = IfFalse(b2);
|
|
Node* m2 = Merge2(t2, f2);
|
|
Node* m1 = Merge2(t1, m2);
|
|
ComputeEquivalence(m1);
|
|
|
|
ASSERT_EQUIVALENCE(b1, m1, start);
|
|
ASSERT_EQUIVALENCE(t1);
|
|
ASSERT_EQUIVALENCE(f1, b2, m2);
|
|
ASSERT_EQUIVALENCE(t2);
|
|
ASSERT_EQUIVALENCE(f2);
|
|
}
|
|
|
|
|
|
TEST_F(ControlEquivalenceTest, Diamond3) {
|
|
Node* start = graph()->start();
|
|
Node* b1 = Branch(start);
|
|
Node* t1 = IfTrue(b1);
|
|
Node* f1 = IfFalse(b1);
|
|
Node* m1 = Merge2(t1, f1);
|
|
Node* b2 = Branch(m1);
|
|
Node* t2 = IfTrue(b2);
|
|
Node* f2 = IfFalse(b2);
|
|
Node* m2 = Merge2(t2, f2);
|
|
ComputeEquivalence(m2);
|
|
|
|
ASSERT_EQUIVALENCE(b1, m1, b2, m2, start);
|
|
ASSERT_EQUIVALENCE(t1);
|
|
ASSERT_EQUIVALENCE(f1);
|
|
ASSERT_EQUIVALENCE(t2);
|
|
ASSERT_EQUIVALENCE(f2);
|
|
}
|
|
|
|
|
|
TEST_F(ControlEquivalenceTest, Switch1) {
|
|
Node* start = graph()->start();
|
|
Node* b1 = Branch(start);
|
|
Node* t1 = IfTrue(b1);
|
|
Node* f1 = IfFalse(b1);
|
|
Node* b2 = Branch(f1);
|
|
Node* t2 = IfTrue(b2);
|
|
Node* f2 = IfFalse(b2);
|
|
Node* b3 = Branch(f2);
|
|
Node* t3 = IfTrue(b3);
|
|
Node* f3 = IfFalse(b3);
|
|
Node* m1 = Merge2(t1, t2);
|
|
Node* m2 = Merge2(m1, t3);
|
|
Node* m3 = Merge2(m2, f3);
|
|
ComputeEquivalence(m3);
|
|
|
|
ASSERT_EQUIVALENCE(b1, m3, start);
|
|
ASSERT_EQUIVALENCE(t1);
|
|
ASSERT_EQUIVALENCE(f1, b2);
|
|
ASSERT_EQUIVALENCE(t2);
|
|
ASSERT_EQUIVALENCE(f2, b3);
|
|
ASSERT_EQUIVALENCE(t3);
|
|
ASSERT_EQUIVALENCE(f3);
|
|
ASSERT_EQUIVALENCE(m1);
|
|
ASSERT_EQUIVALENCE(m2);
|
|
}
|
|
|
|
|
|
TEST_F(ControlEquivalenceTest, Loop1) {
|
|
Node* start = graph()->start();
|
|
Node* l = Loop2(start);
|
|
l->ReplaceInput(1, l);
|
|
ComputeEquivalence(l);
|
|
|
|
ASSERT_EQUIVALENCE(start);
|
|
ASSERT_EQUIVALENCE(l);
|
|
}
|
|
|
|
|
|
TEST_F(ControlEquivalenceTest, Loop2) {
|
|
Node* start = graph()->start();
|
|
Node* l = Loop2(start);
|
|
Node* b = Branch(l);
|
|
Node* t = IfTrue(b);
|
|
Node* f = IfFalse(b);
|
|
l->ReplaceInput(1, t);
|
|
ComputeEquivalence(f);
|
|
|
|
ASSERT_EQUIVALENCE(f, start);
|
|
ASSERT_EQUIVALENCE(t);
|
|
ASSERT_EQUIVALENCE(l, b);
|
|
}
|
|
|
|
|
|
TEST_F(ControlEquivalenceTest, Irreducible) {
|
|
Node* start = graph()->start();
|
|
Node* b1 = Branch(start);
|
|
Node* t1 = IfTrue(b1);
|
|
Node* f1 = IfFalse(b1);
|
|
Node* lp = Loop2(f1);
|
|
Node* m1 = Merge2(t1, lp);
|
|
Node* b2 = Branch(m1);
|
|
Node* t2 = IfTrue(b2);
|
|
Node* f2 = IfFalse(b2);
|
|
Node* m2 = Merge2(t2, f2);
|
|
Node* b3 = Branch(m2);
|
|
Node* t3 = IfTrue(b3);
|
|
Node* f3 = IfFalse(b3);
|
|
lp->ReplaceInput(1, f3);
|
|
ComputeEquivalence(t3);
|
|
|
|
ASSERT_EQUIVALENCE(b1, t3, start);
|
|
ASSERT_EQUIVALENCE(t1);
|
|
ASSERT_EQUIVALENCE(f1);
|
|
ASSERT_EQUIVALENCE(m1, b2, m2, b3);
|
|
ASSERT_EQUIVALENCE(t2);
|
|
ASSERT_EQUIVALENCE(f2);
|
|
ASSERT_EQUIVALENCE(f3);
|
|
ASSERT_EQUIVALENCE(lp);
|
|
}
|
|
|
|
|
|
} // namespace compiler
|
|
} // namespace internal
|
|
} // namespace v8
|