v8/test/unittests/compiler/graph-reducer-unittest.cc
danno fe552636be [turbofan] Support variable size argument removal in TF-generated functions
This is preparation for using TF to create builtins that handle variable number of
arguments and have to remove these arguments dynamically from the stack upon
return.

The gist of the changes:
- Added a second argument to the Return node which specifies the number of stack
  slots to pop upon return in addition to those specified by the Linkage of the
  compiled function.
- Removed Tail -> Non-Tail fallback in the instruction selector. Since TF now should
  handles all tail-call cases except where the return value type differs, this fallback
  was not really useful and in fact caused unexpected behavior with variable
  sized argument popping, since it wasn't possible to materialize a Return node
  with the right pop count from the TailCall without additional context.
- Modified existing Return generation to pass a constant zero as the additional
  pop argument since the variable pop functionality

LOG=N

Review-Url: https://codereview.chromium.org/2446543002
Cr-Commit-Position: refs/heads/master@{#40699}
2016-11-02 13:15:57 +00:00

861 lines
25 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/compiler/common-operator.h"
#include "src/compiler/graph.h"
#include "src/compiler/node.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator.h"
#include "test/unittests/compiler/graph-reducer-unittest.h"
#include "test/unittests/test-utils.h"
using testing::_;
using testing::DefaultValue;
using testing::ElementsAre;
using testing::Return;
using testing::Sequence;
using testing::StrictMock;
using testing::UnorderedElementsAre;
namespace v8 {
namespace internal {
namespace compiler {
namespace {
struct TestOperator : public Operator {
TestOperator(Operator::Opcode opcode, Operator::Properties properties,
const char* op_name, size_t value_in, size_t value_out)
: Operator(opcode, properties, op_name, value_in, 0, 0, value_out, 0, 0) {
}
};
const uint8_t kOpcodeA0 = 10;
const uint8_t kOpcodeA1 = 11;
const uint8_t kOpcodeA2 = 12;
const uint8_t kOpcodeB0 = 20;
const uint8_t kOpcodeB1 = 21;
const uint8_t kOpcodeB2 = 22;
const uint8_t kOpcodeC0 = 30;
const uint8_t kOpcodeC1 = 31;
const uint8_t kOpcodeC2 = 32;
static TestOperator kOpA0(kOpcodeA0, Operator::kNoWrite, "opa1", 0, 1);
static TestOperator kOpA1(kOpcodeA1, Operator::kNoProperties, "opa2", 1, 1);
static TestOperator kOpA2(kOpcodeA2, Operator::kNoProperties, "opa3", 2, 1);
static TestOperator kOpB0(kOpcodeB0, Operator::kNoWrite, "opa0", 0, 0);
static TestOperator kOpB1(kOpcodeB1, Operator::kNoWrite, "opa1", 1, 0);
static TestOperator kOpB2(kOpcodeB2, Operator::kNoWrite, "opa2", 2, 0);
static TestOperator kOpC0(kOpcodeC0, Operator::kNoWrite, "opc0", 0, 0);
static TestOperator kOpC1(kOpcodeC1, Operator::kNoWrite, "opc1", 1, 0);
static TestOperator kOpC2(kOpcodeC2, Operator::kNoWrite, "opc2", 2, 0);
struct MockReducer : public Reducer {
MOCK_METHOD1(Reduce, Reduction(Node*));
};
// Replaces all "A" operators with "B" operators without creating new nodes.
class InPlaceABReducer final : public Reducer {
public:
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeA0:
EXPECT_EQ(0, node->InputCount());
NodeProperties::ChangeOp(node, &kOpB0);
return Replace(node);
case kOpcodeA1:
EXPECT_EQ(1, node->InputCount());
NodeProperties::ChangeOp(node, &kOpB1);
return Replace(node);
case kOpcodeA2:
EXPECT_EQ(2, node->InputCount());
NodeProperties::ChangeOp(node, &kOpB2);
return Replace(node);
}
return NoChange();
}
};
// Replaces all "A" operators with "B" operators by allocating new nodes.
class NewABReducer final : public Reducer {
public:
explicit NewABReducer(Graph* graph) : graph_(graph) {}
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeA0:
EXPECT_EQ(0, node->InputCount());
return Replace(graph_->NewNode(&kOpB0));
case kOpcodeA1:
EXPECT_EQ(1, node->InputCount());
return Replace(graph_->NewNode(&kOpB1, node->InputAt(0)));
case kOpcodeA2:
EXPECT_EQ(2, node->InputCount());
return Replace(
graph_->NewNode(&kOpB2, node->InputAt(0), node->InputAt(1)));
}
return NoChange();
}
private:
Graph* const graph_;
};
// Wraps all "kOpA0" nodes in "kOpB1" operators by allocating new nodes.
class A0Wrapper final : public Reducer {
public:
explicit A0Wrapper(Graph* graph) : graph_(graph) {}
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeA0:
EXPECT_EQ(0, node->InputCount());
return Replace(graph_->NewNode(&kOpB1, node));
}
return NoChange();
}
private:
Graph* const graph_;
};
// Wraps all "kOpB0" nodes in two "kOpC1" operators by allocating new nodes.
class B0Wrapper final : public Reducer {
public:
explicit B0Wrapper(Graph* graph) : graph_(graph) {}
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeB0:
EXPECT_EQ(0, node->InputCount());
return Replace(graph_->NewNode(&kOpC1, graph_->NewNode(&kOpC1, node)));
}
return NoChange();
}
private:
Graph* const graph_;
};
// Replaces all "kOpA1" nodes with the first input.
class A1Forwarder final : public Reducer {
public:
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeA1:
EXPECT_EQ(1, node->InputCount());
return Replace(node->InputAt(0));
}
return NoChange();
}
};
// Replaces all "kOpB1" nodes with the first input.
class B1Forwarder final : public Reducer {
public:
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeB1:
EXPECT_EQ(1, node->InputCount());
return Replace(node->InputAt(0));
}
return NoChange();
}
};
// Replaces all "B" operators with "C" operators without creating new nodes.
class InPlaceBCReducer final : public Reducer {
public:
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeB0:
EXPECT_EQ(0, node->InputCount());
NodeProperties::ChangeOp(node, &kOpC0);
return Replace(node);
case kOpcodeB1:
EXPECT_EQ(1, node->InputCount());
NodeProperties::ChangeOp(node, &kOpC1);
return Replace(node);
case kOpcodeB2:
EXPECT_EQ(2, node->InputCount());
NodeProperties::ChangeOp(node, &kOpC2);
return Replace(node);
}
return NoChange();
}
};
// Swaps the inputs to "kOp2A" and "kOp2B" nodes based on ids.
class AB2Sorter final : public Reducer {
public:
Reduction Reduce(Node* node) final {
switch (node->op()->opcode()) {
case kOpcodeA2:
case kOpcodeB2:
EXPECT_EQ(2, node->InputCount());
Node* x = node->InputAt(0);
Node* y = node->InputAt(1);
if (x->id() > y->id()) {
node->ReplaceInput(0, y);
node->ReplaceInput(1, x);
return Replace(node);
}
}
return NoChange();
}
};
} // namespace
class AdvancedReducerTest : public TestWithZone {
public:
AdvancedReducerTest() : graph_(zone()) {}
protected:
Graph* graph() { return &graph_; }
private:
Graph graph_;
};
TEST_F(AdvancedReducerTest, Replace) {
struct DummyReducer final : public AdvancedReducer {
explicit DummyReducer(Editor* editor) : AdvancedReducer(editor) {}
Reduction Reduce(Node* node) final {
Replace(node, node);
return NoChange();
}
};
StrictMock<MockAdvancedReducerEditor> e;
DummyReducer r(&e);
Node* node0 = graph()->NewNode(&kOpA0);
Node* node1 = graph()->NewNode(&kOpA1, node0);
EXPECT_CALL(e, Replace(node0, node0));
EXPECT_CALL(e, Replace(node1, node1));
EXPECT_FALSE(r.Reduce(node0).Changed());
EXPECT_FALSE(r.Reduce(node1).Changed());
}
TEST_F(AdvancedReducerTest, Revisit) {
struct DummyReducer final : public AdvancedReducer {
explicit DummyReducer(Editor* editor) : AdvancedReducer(editor) {}
Reduction Reduce(Node* node) final {
Revisit(node);
return NoChange();
}
};
StrictMock<MockAdvancedReducerEditor> e;
DummyReducer r(&e);
Node* node0 = graph()->NewNode(&kOpA0);
Node* node1 = graph()->NewNode(&kOpA1, node0);
EXPECT_CALL(e, Revisit(node0));
EXPECT_CALL(e, Revisit(node1));
EXPECT_FALSE(r.Reduce(node0).Changed());
EXPECT_FALSE(r.Reduce(node1).Changed());
}
namespace {
struct ReplaceWithValueReducer final : public AdvancedReducer {
explicit ReplaceWithValueReducer(Editor* editor) : AdvancedReducer(editor) {}
Reduction Reduce(Node* node) final { return NoChange(); }
using AdvancedReducer::ReplaceWithValue;
};
const Operator kMockOperator(IrOpcode::kDead, Operator::kNoProperties,
"MockOperator", 0, 0, 0, 1, 0, 0);
const Operator kMockOpEffect(IrOpcode::kDead, Operator::kNoProperties,
"MockOpEffect", 0, 1, 0, 1, 1, 0);
const Operator kMockOpControl(IrOpcode::kDead, Operator::kNoProperties,
"MockOpControl", 0, 0, 1, 1, 0, 1);
} // namespace
TEST_F(AdvancedReducerTest, ReplaceWithValue_ValueUse) {
CommonOperatorBuilder common(zone());
Node* node = graph()->NewNode(&kMockOperator);
Node* start = graph()->NewNode(common.Start(1));
Node* zero = graph()->NewNode(common.Int32Constant(0));
Node* use_value = graph()->NewNode(common.Return(), zero, node, start, start);
Node* replacement = graph()->NewNode(&kMockOperator);
GraphReducer graph_reducer(zone(), graph(), nullptr);
ReplaceWithValueReducer r(&graph_reducer);
r.ReplaceWithValue(node, replacement);
EXPECT_EQ(replacement, use_value->InputAt(1));
EXPECT_EQ(0, node->UseCount());
EXPECT_EQ(1, replacement->UseCount());
EXPECT_THAT(replacement->uses(), ElementsAre(use_value));
}
TEST_F(AdvancedReducerTest, ReplaceWithValue_EffectUse) {
CommonOperatorBuilder common(zone());
Node* start = graph()->NewNode(common.Start(1));
Node* node = graph()->NewNode(&kMockOpEffect, start);
Node* use_control = graph()->NewNode(common.Merge(1), start);
Node* use_effect = graph()->NewNode(common.EffectPhi(1), node, use_control);
Node* replacement = graph()->NewNode(&kMockOperator);
GraphReducer graph_reducer(zone(), graph(), nullptr);
ReplaceWithValueReducer r(&graph_reducer);
r.ReplaceWithValue(node, replacement);
EXPECT_EQ(start, use_effect->InputAt(0));
EXPECT_EQ(0, node->UseCount());
EXPECT_EQ(3, start->UseCount());
EXPECT_EQ(0, replacement->UseCount());
EXPECT_THAT(start->uses(),
UnorderedElementsAre(use_effect, use_control, node));
}
TEST_F(AdvancedReducerTest, ReplaceWithValue_ControlUse1) {
CommonOperatorBuilder common(zone());
Node* start = graph()->NewNode(common.Start(1));
Node* node = graph()->NewNode(&kMockOpControl, start);
Node* success = graph()->NewNode(common.IfSuccess(), node);
Node* use_control = graph()->NewNode(common.Merge(1), success);
Node* replacement = graph()->NewNode(&kMockOperator);
GraphReducer graph_reducer(zone(), graph(), nullptr);
ReplaceWithValueReducer r(&graph_reducer);
r.ReplaceWithValue(node, replacement);
EXPECT_EQ(start, use_control->InputAt(0));
EXPECT_EQ(0, node->UseCount());
EXPECT_EQ(2, start->UseCount());
EXPECT_EQ(0, replacement->UseCount());
EXPECT_THAT(start->uses(), UnorderedElementsAre(use_control, node));
}
TEST_F(AdvancedReducerTest, ReplaceWithValue_ControlUse2) {
CommonOperatorBuilder common(zone());
Node* start = graph()->NewNode(common.Start(1));
Node* effect = graph()->NewNode(&kMockOperator);
Node* dead = graph()->NewNode(&kMockOperator);
Node* node = graph()->NewNode(&kMockOpControl, start);
Node* success = graph()->NewNode(common.IfSuccess(), node);
Node* exception = graph()->NewNode(common.IfException(), effect, node);
Node* use_control = graph()->NewNode(common.Merge(1), success);
Node* replacement = graph()->NewNode(&kMockOperator);
GraphReducer graph_reducer(zone(), graph(), dead);
ReplaceWithValueReducer r(&graph_reducer);
r.ReplaceWithValue(node, replacement);
EXPECT_EQ(start, use_control->InputAt(0));
EXPECT_EQ(dead, exception->InputAt(1));
EXPECT_EQ(0, node->UseCount());
EXPECT_EQ(2, start->UseCount());
EXPECT_EQ(1, dead->UseCount());
EXPECT_EQ(0, replacement->UseCount());
EXPECT_THAT(start->uses(), UnorderedElementsAre(use_control, node));
EXPECT_THAT(dead->uses(), ElementsAre(exception));
}
TEST_F(AdvancedReducerTest, ReplaceWithValue_ControlUse3) {
CommonOperatorBuilder common(zone());
Node* start = graph()->NewNode(common.Start(1));
Node* effect = graph()->NewNode(&kMockOperator);
Node* dead = graph()->NewNode(&kMockOperator);
Node* node = graph()->NewNode(&kMockOpControl, start);
Node* success = graph()->NewNode(common.IfSuccess(), node);
Node* exception = graph()->NewNode(common.IfException(), effect, node);
Node* use_control = graph()->NewNode(common.Merge(1), success);
Node* replacement = graph()->NewNode(&kMockOperator);
GraphReducer graph_reducer(zone(), graph(), dead);
ReplaceWithValueReducer r(&graph_reducer);
r.ReplaceWithValue(node, replacement);
EXPECT_EQ(start, use_control->InputAt(0));
EXPECT_EQ(dead, exception->InputAt(1));
EXPECT_EQ(0, node->UseCount());
EXPECT_EQ(2, start->UseCount());
EXPECT_EQ(1, dead->UseCount());
EXPECT_EQ(0, replacement->UseCount());
EXPECT_THAT(start->uses(), UnorderedElementsAre(use_control, node));
EXPECT_THAT(dead->uses(), ElementsAre(exception));
}
class GraphReducerTest : public TestWithZone {
public:
GraphReducerTest() : graph_(zone()) {}
static void SetUpTestCase() {
TestWithZone::SetUpTestCase();
DefaultValue<Reduction>::Set(Reducer::NoChange());
}
static void TearDownTestCase() {
DefaultValue<Reduction>::Clear();
TestWithZone::TearDownTestCase();
}
protected:
void ReduceNode(Node* node, Reducer* r) {
GraphReducer reducer(zone(), graph());
reducer.AddReducer(r);
reducer.ReduceNode(node);
}
void ReduceNode(Node* node, Reducer* r1, Reducer* r2) {
GraphReducer reducer(zone(), graph());
reducer.AddReducer(r1);
reducer.AddReducer(r2);
reducer.ReduceNode(node);
}
void ReduceNode(Node* node, Reducer* r1, Reducer* r2, Reducer* r3) {
GraphReducer reducer(zone(), graph());
reducer.AddReducer(r1);
reducer.AddReducer(r2);
reducer.AddReducer(r3);
reducer.ReduceNode(node);
}
void ReduceGraph(Reducer* r1) {
GraphReducer reducer(zone(), graph());
reducer.AddReducer(r1);
reducer.ReduceGraph();
}
void ReduceGraph(Reducer* r1, Reducer* r2) {
GraphReducer reducer(zone(), graph());
reducer.AddReducer(r1);
reducer.AddReducer(r2);
reducer.ReduceGraph();
}
void ReduceGraph(Reducer* r1, Reducer* r2, Reducer* r3) {
GraphReducer reducer(zone(), graph());
reducer.AddReducer(r1);
reducer.AddReducer(r2);
reducer.AddReducer(r3);
reducer.ReduceGraph();
}
Graph* graph() { return &graph_; }
private:
Graph graph_;
};
TEST_F(GraphReducerTest, NodeIsDeadAfterReplace) {
StrictMock<MockReducer> r;
Node* node0 = graph()->NewNode(&kOpA0);
Node* node1 = graph()->NewNode(&kOpA1, node0);
Node* node2 = graph()->NewNode(&kOpA1, node0);
EXPECT_CALL(r, Reduce(node0)).WillOnce(Return(Reducer::NoChange()));
EXPECT_CALL(r, Reduce(node1)).WillOnce(Return(Reducer::Replace(node2)));
ReduceNode(node1, &r);
EXPECT_FALSE(node0->IsDead());
EXPECT_TRUE(node1->IsDead());
EXPECT_FALSE(node2->IsDead());
}
TEST_F(GraphReducerTest, ReduceOnceForEveryReducer) {
StrictMock<MockReducer> r1, r2;
Node* node0 = graph()->NewNode(&kOpA0);
EXPECT_CALL(r1, Reduce(node0));
EXPECT_CALL(r2, Reduce(node0));
ReduceNode(node0, &r1, &r2);
}
TEST_F(GraphReducerTest, ReduceAgainAfterChanged) {
Sequence s1, s2, s3;
StrictMock<MockReducer> r1, r2, r3;
Node* node0 = graph()->NewNode(&kOpA0);
EXPECT_CALL(r1, Reduce(node0));
EXPECT_CALL(r2, Reduce(node0));
EXPECT_CALL(r3, Reduce(node0)).InSequence(s1, s2, s3).WillOnce(
Return(Reducer::Changed(node0)));
EXPECT_CALL(r1, Reduce(node0)).InSequence(s1);
EXPECT_CALL(r2, Reduce(node0)).InSequence(s2);
ReduceNode(node0, &r1, &r2, &r3);
}
TEST_F(GraphReducerTest, ReduceGraphFromEnd1) {
StrictMock<MockReducer> r1;
Node* n = graph()->NewNode(&kOpA0);
Node* end = graph()->NewNode(&kOpA1, n);
graph()->SetEnd(end);
Sequence s;
EXPECT_CALL(r1, Reduce(n));
EXPECT_CALL(r1, Reduce(end));
ReduceGraph(&r1);
}
TEST_F(GraphReducerTest, ReduceGraphFromEnd2) {
StrictMock<MockReducer> r1;
Node* n1 = graph()->NewNode(&kOpA0);
Node* n2 = graph()->NewNode(&kOpA1, n1);
Node* n3 = graph()->NewNode(&kOpA1, n1);
Node* end = graph()->NewNode(&kOpA2, n2, n3);
graph()->SetEnd(end);
Sequence s1, s2;
EXPECT_CALL(r1, Reduce(n1)).InSequence(s1, s2);
EXPECT_CALL(r1, Reduce(n2)).InSequence(s1);
EXPECT_CALL(r1, Reduce(n3)).InSequence(s2);
EXPECT_CALL(r1, Reduce(end)).InSequence(s1, s2);
ReduceGraph(&r1);
}
TEST_F(GraphReducerTest, ReduceInPlace1) {
Node* n1 = graph()->NewNode(&kOpA0);
Node* end = graph()->NewNode(&kOpA1, n1);
graph()->SetEnd(end);
// Tests A* => B* with in-place updates.
InPlaceABReducer r;
for (int i = 0; i < 3; i++) {
size_t before = graph()->NodeCount();
ReduceGraph(&r);
EXPECT_EQ(before, graph()->NodeCount());
EXPECT_EQ(&kOpB0, n1->op());
EXPECT_EQ(&kOpB1, end->op());
EXPECT_EQ(n1, end->InputAt(0));
}
}
TEST_F(GraphReducerTest, ReduceInPlace2) {
Node* n1 = graph()->NewNode(&kOpA0);
Node* n2 = graph()->NewNode(&kOpA1, n1);
Node* n3 = graph()->NewNode(&kOpA1, n1);
Node* end = graph()->NewNode(&kOpA2, n2, n3);
graph()->SetEnd(end);
// Tests A* => B* with in-place updates.
InPlaceABReducer r;
for (int i = 0; i < 3; i++) {
size_t before = graph()->NodeCount();
ReduceGraph(&r);
EXPECT_EQ(before, graph()->NodeCount());
EXPECT_EQ(&kOpB0, n1->op());
EXPECT_EQ(&kOpB1, n2->op());
EXPECT_EQ(n1, n2->InputAt(0));
EXPECT_EQ(&kOpB1, n3->op());
EXPECT_EQ(n1, n3->InputAt(0));
EXPECT_EQ(&kOpB2, end->op());
EXPECT_EQ(n2, end->InputAt(0));
EXPECT_EQ(n3, end->InputAt(1));
}
}
TEST_F(GraphReducerTest, ReduceNew1) {
Node* n1 = graph()->NewNode(&kOpA0);
Node* n2 = graph()->NewNode(&kOpA1, n1);
Node* n3 = graph()->NewNode(&kOpA1, n1);
Node* end = graph()->NewNode(&kOpA2, n2, n3);
graph()->SetEnd(end);
NewABReducer r(graph());
// Tests A* => B* while creating new nodes.
for (int i = 0; i < 3; i++) {
size_t before = graph()->NodeCount();
ReduceGraph(&r);
if (i == 0) {
EXPECT_NE(before, graph()->NodeCount());
} else {
EXPECT_EQ(before, graph()->NodeCount());
}
Node* nend = graph()->end();
EXPECT_NE(end, nend); // end() should be updated too.
Node* nn2 = nend->InputAt(0);
Node* nn3 = nend->InputAt(1);
Node* nn1 = nn2->InputAt(0);
EXPECT_EQ(nn1, nn3->InputAt(0));
EXPECT_EQ(&kOpB0, nn1->op());
EXPECT_EQ(&kOpB1, nn2->op());
EXPECT_EQ(&kOpB1, nn3->op());
EXPECT_EQ(&kOpB2, nend->op());
}
}
TEST_F(GraphReducerTest, Wrapping1) {
Node* end = graph()->NewNode(&kOpA0);
graph()->SetEnd(end);
EXPECT_EQ(1U, graph()->NodeCount());
A0Wrapper r(graph());
ReduceGraph(&r);
EXPECT_EQ(2U, graph()->NodeCount());
Node* nend = graph()->end();
EXPECT_NE(end, nend);
EXPECT_EQ(&kOpB1, nend->op());
EXPECT_EQ(1, nend->InputCount());
EXPECT_EQ(end, nend->InputAt(0));
}
TEST_F(GraphReducerTest, Wrapping2) {
Node* end = graph()->NewNode(&kOpB0);
graph()->SetEnd(end);
EXPECT_EQ(1U, graph()->NodeCount());
B0Wrapper r(graph());
ReduceGraph(&r);
EXPECT_EQ(3U, graph()->NodeCount());
Node* nend = graph()->end();
EXPECT_NE(end, nend);
EXPECT_EQ(&kOpC1, nend->op());
EXPECT_EQ(1, nend->InputCount());
Node* n1 = nend->InputAt(0);
EXPECT_NE(end, n1);
EXPECT_EQ(&kOpC1, n1->op());
EXPECT_EQ(1, n1->InputCount());
EXPECT_EQ(end, n1->InputAt(0));
}
TEST_F(GraphReducerTest, Forwarding1) {
Node* n1 = graph()->NewNode(&kOpA0);
Node* end = graph()->NewNode(&kOpA1, n1);
graph()->SetEnd(end);
A1Forwarder r;
// Tests A1(x) => x
for (int i = 0; i < 3; i++) {
size_t before = graph()->NodeCount();
ReduceGraph(&r);
EXPECT_EQ(before, graph()->NodeCount());
EXPECT_EQ(&kOpA0, n1->op());
EXPECT_EQ(n1, graph()->end());
}
}
TEST_F(GraphReducerTest, Forwarding2) {
Node* n1 = graph()->NewNode(&kOpA0);
Node* n2 = graph()->NewNode(&kOpA1, n1);
Node* n3 = graph()->NewNode(&kOpA1, n1);
Node* end = graph()->NewNode(&kOpA2, n2, n3);
graph()->SetEnd(end);
A1Forwarder r;
// Tests reducing A2(A1(x), A1(y)) => A2(x, y).
for (int i = 0; i < 3; i++) {
size_t before = graph()->NodeCount();
ReduceGraph(&r);
EXPECT_EQ(before, graph()->NodeCount());
EXPECT_EQ(&kOpA0, n1->op());
EXPECT_EQ(n1, end->InputAt(0));
EXPECT_EQ(n1, end->InputAt(1));
EXPECT_EQ(&kOpA2, end->op());
EXPECT_EQ(0, n2->UseCount());
EXPECT_EQ(0, n3->UseCount());
}
}
TEST_F(GraphReducerTest, Forwarding3) {
// Tests reducing a chain of A1(A1(A1(A1(x)))) => x.
for (int i = 0; i < 8; i++) {
Node* n1 = graph()->NewNode(&kOpA0);
Node* end = n1;
for (int j = 0; j < i; j++) {
end = graph()->NewNode(&kOpA1, end);
}
graph()->SetEnd(end);
A1Forwarder r;
for (size_t i = 0; i < 3; i++) {
size_t before = graph()->NodeCount();
ReduceGraph(&r);
EXPECT_EQ(before, graph()->NodeCount());
EXPECT_EQ(&kOpA0, n1->op());
EXPECT_EQ(n1, graph()->end());
}
}
}
TEST_F(GraphReducerTest, ReduceForward1) {
Node* n1 = graph()->NewNode(&kOpA0);
Node* n2 = graph()->NewNode(&kOpA1, n1);
Node* n3 = graph()->NewNode(&kOpA1, n1);
Node* end = graph()->NewNode(&kOpA2, n2, n3);
graph()->SetEnd(end);
InPlaceABReducer r;
B1Forwarder f;
// Tests first reducing A => B, then B1(x) => x.
for (size_t i = 0; i < 3; i++) {
size_t before = graph()->NodeCount();
ReduceGraph(&r, &f);
EXPECT_EQ(before, graph()->NodeCount());
EXPECT_EQ(&kOpB0, n1->op());
EXPECT_TRUE(n2->IsDead());
EXPECT_EQ(n1, end->InputAt(0));
EXPECT_TRUE(n3->IsDead());
EXPECT_EQ(n1, end->InputAt(0));
EXPECT_EQ(&kOpB2, end->op());
EXPECT_EQ(0, n2->UseCount());
EXPECT_EQ(0, n3->UseCount());
}
}
TEST_F(GraphReducerTest, Sorter1) {
AB2Sorter r;
for (int i = 0; i < 6; i++) {
Node* n1 = graph()->NewNode(&kOpA0);
Node* n2 = graph()->NewNode(&kOpA1, n1);
Node* n3 = graph()->NewNode(&kOpA1, n1);
Node* end = NULL; // Initialize to please the compiler.
if (i == 0) end = graph()->NewNode(&kOpA2, n2, n3);
if (i == 1) end = graph()->NewNode(&kOpA2, n3, n2);
if (i == 2) end = graph()->NewNode(&kOpA2, n2, n1);
if (i == 3) end = graph()->NewNode(&kOpA2, n1, n2);
if (i == 4) end = graph()->NewNode(&kOpA2, n3, n1);
if (i == 5) end = graph()->NewNode(&kOpA2, n1, n3);
graph()->SetEnd(end);
size_t before = graph()->NodeCount();
ReduceGraph(&r);
EXPECT_EQ(before, graph()->NodeCount());
EXPECT_EQ(&kOpA0, n1->op());
EXPECT_EQ(&kOpA1, n2->op());
EXPECT_EQ(&kOpA1, n3->op());
EXPECT_EQ(&kOpA2, end->op());
EXPECT_EQ(end, graph()->end());
EXPECT_LE(end->InputAt(0)->id(), end->InputAt(1)->id());
}
}
namespace {
// Generate a node graph with the given permutations.
void GenDAG(Graph* graph, int* p3, int* p2, int* p1) {
Node* level4 = graph->NewNode(&kOpA0);
Node* level3[] = {graph->NewNode(&kOpA1, level4),
graph->NewNode(&kOpA1, level4)};
Node* level2[] = {graph->NewNode(&kOpA1, level3[p3[0]]),
graph->NewNode(&kOpA1, level3[p3[1]]),
graph->NewNode(&kOpA1, level3[p3[0]]),
graph->NewNode(&kOpA1, level3[p3[1]])};
Node* level1[] = {graph->NewNode(&kOpA2, level2[p2[0]], level2[p2[1]]),
graph->NewNode(&kOpA2, level2[p2[2]], level2[p2[3]])};
Node* end = graph->NewNode(&kOpA2, level1[p1[0]], level1[p1[1]]);
graph->SetEnd(end);
}
} // namespace
TEST_F(GraphReducerTest, SortForwardReduce) {
// Tests combined reductions on a series of DAGs.
for (int j = 0; j < 2; j++) {
int p3[] = {j, 1 - j};
for (int m = 0; m < 2; m++) {
int p1[] = {m, 1 - m};
for (int k = 0; k < 24; k++) { // All permutations of 0, 1, 2, 3
int p2[] = {-1, -1, -1, -1};
int n = k;
for (int d = 4; d >= 1; d--) { // Construct permutation.
int p = n % d;
for (int z = 0; z < 4; z++) {
if (p2[z] == -1) {
if (p == 0) p2[z] = d - 1;
p--;
}
}
n = n / d;
}
GenDAG(graph(), p3, p2, p1);
AB2Sorter r1;
A1Forwarder r2;
InPlaceABReducer r3;
ReduceGraph(&r1, &r2, &r3);
Node* end = graph()->end();
EXPECT_EQ(&kOpB2, end->op());
Node* n1 = end->InputAt(0);
Node* n2 = end->InputAt(1);
EXPECT_NE(n1, n2);
EXPECT_LT(n1->id(), n2->id());
EXPECT_EQ(&kOpB2, n1->op());
EXPECT_EQ(&kOpB2, n2->op());
Node* n4 = n1->InputAt(0);
EXPECT_EQ(&kOpB0, n4->op());
EXPECT_EQ(n4, n1->InputAt(1));
EXPECT_EQ(n4, n2->InputAt(0));
EXPECT_EQ(n4, n2->InputAt(1));
}
}
}
}
TEST_F(GraphReducerTest, Order) {
// Test that the order of reducers doesn't matter, as they should be
// rerun for changed nodes.
for (int i = 0; i < 2; i++) {
Node* n1 = graph()->NewNode(&kOpA0);
Node* end = graph()->NewNode(&kOpA1, n1);
graph()->SetEnd(end);
InPlaceABReducer abr;
InPlaceBCReducer bcr;
// Tests A* => C* with in-place updates.
for (size_t j = 0; j < 3; j++) {
size_t before = graph()->NodeCount();
if (i == 0) {
ReduceGraph(&abr, &bcr);
} else {
ReduceGraph(&bcr, &abr);
}
EXPECT_EQ(before, graph()->NodeCount());
EXPECT_EQ(&kOpC0, n1->op());
EXPECT_EQ(&kOpC1, end->op());
EXPECT_EQ(n1, end->InputAt(0));
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8