v8/test/cctest/compiler/test-osr.cc
mstarzinger 16f133001f Move compiler cctests into v8::internal::compiler namespace.
This moves all cctest files for the compiler to live in the same
namespace as the components they are testing. Hence we can avoid the
forbidden using directives pulling in entire namespaces.

From the Google C++ style guide: "You may not use a using-directive to
make all names from a namespace available". This would be covered by
presubmit linter checks if build/namespaces were not blacklisted.

R=bmeurer@chromium.org

Review URL: https://codereview.chromium.org/1424943004

Cr-Commit-Position: refs/heads/master@{#31671}
2015-10-30 09:16:39 +00:00

574 lines
19 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.
// TODO(jochen): Remove this after the setting is turned on globally.
#define V8_IMMINENT_DEPRECATION_WARNINGS
#include "src/codegen.h"
#include "src/compiler/all-nodes.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/diamond.h"
#include "src/compiler/graph.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/operator.h"
#include "src/compiler/osr.h"
#include "test/cctest/cctest.h"
namespace v8 {
namespace internal {
namespace compiler {
// TODO(titzer): move this method to a common testing place.
static int CheckInputs(Node* node, Node* i0 = NULL, Node* i1 = NULL,
Node* i2 = NULL, Node* i3 = NULL) {
int count = 4;
if (i3 == NULL) count = 3;
if (i2 == NULL) count = 2;
if (i1 == NULL) count = 1;
if (i0 == NULL) count = 0;
CHECK_EQ(count, node->InputCount());
if (i0 != NULL) CHECK_EQ(i0, node->InputAt(0));
if (i1 != NULL) CHECK_EQ(i1, node->InputAt(1));
if (i2 != NULL) CHECK_EQ(i2, node->InputAt(2));
if (i3 != NULL) CHECK_EQ(i3, node->InputAt(3));
return count;
}
static Operator kIntLt(IrOpcode::kInt32LessThan, Operator::kPure,
"Int32LessThan", 2, 0, 0, 1, 0, 0);
static Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0,
0, 1, 0, 0);
static const int kMaxOsrValues = 10;
class OsrDeconstructorTester : public HandleAndZoneScope {
public:
explicit OsrDeconstructorTester(int num_values)
: isolate(main_isolate()),
common(main_zone()),
graph(main_zone()),
jsgraph(main_isolate(), &graph, &common, nullptr, nullptr, nullptr),
start(graph.NewNode(common.Start(1))),
p0(graph.NewNode(common.Parameter(0), start)),
end(graph.NewNode(common.End(1), start)),
osr_normal_entry(graph.NewNode(common.OsrNormalEntry(), start, start)),
osr_loop_entry(graph.NewNode(common.OsrLoopEntry(), start, start)),
self(graph.NewNode(common.Int32Constant(0xaabbccdd))) {
CHECK(num_values <= kMaxOsrValues);
graph.SetStart(start);
for (int i = 0; i < num_values; i++) {
osr_values[i] = graph.NewNode(common.OsrValue(i), osr_loop_entry);
}
}
Isolate* isolate;
CommonOperatorBuilder common;
Graph graph;
JSGraph jsgraph;
Node* start;
Node* p0;
Node* end;
Node* osr_normal_entry;
Node* osr_loop_entry;
Node* self;
Node* osr_values[kMaxOsrValues];
Node* NewOsrPhi(Node* loop, Node* incoming, int osr_value, Node* back1 = NULL,
Node* back2 = NULL, Node* back3 = NULL) {
int count = 5;
if (back3 == NULL) count = 4;
if (back2 == NULL) count = 3;
if (back1 == NULL) count = 2;
CHECK_EQ(loop->InputCount(), count);
CHECK_EQ(osr_loop_entry, loop->InputAt(1));
Node* inputs[6];
inputs[0] = incoming;
inputs[1] = osr_values[osr_value];
if (count > 2) inputs[2] = back1;
if (count > 3) inputs[3] = back2;
if (count > 4) inputs[4] = back3;
inputs[count] = loop;
return graph.NewNode(common.Phi(kMachAnyTagged, count), count + 1, inputs);
}
Node* NewLoop(bool is_osr, int num_backedges, Node* entry = nullptr) {
if (entry == nullptr) entry = osr_normal_entry;
Node* loop = graph.NewNode(common.Loop(1), entry);
if (is_osr) {
loop->AppendInput(graph.zone(), osr_loop_entry);
}
for (int i = 0; i < num_backedges; i++) {
loop->AppendInput(graph.zone(), loop);
}
NodeProperties::ChangeOp(loop, common.Loop(loop->InputCount()));
return loop;
}
Node* NewOsrLoop(int num_backedges, Node* entry = NULL) {
return NewLoop(true, num_backedges, entry);
}
void DeconstructOsr() {
OsrHelper helper(0, 0);
helper.Deconstruct(&jsgraph, &common, main_zone());
AllNodes nodes(main_zone(), &graph);
// Should be edited out.
CHECK(!nodes.IsLive(osr_normal_entry));
CHECK(!nodes.IsLive(osr_loop_entry));
// No dangling nodes should be left over.
for (Node* const node : nodes.live) {
for (Node* const use : node->uses()) {
CHECK(std::find(nodes.live.begin(), nodes.live.end(), use) !=
nodes.live.end());
}
}
}
};
TEST(Deconstruct_osr0) {
OsrDeconstructorTester T(0);
Node* loop = T.NewOsrLoop(1);
T.graph.SetEnd(loop);
T.DeconstructOsr();
CheckInputs(loop, T.start, loop);
}
TEST(Deconstruct_osr1) {
OsrDeconstructorTester T(1);
Node* loop = T.NewOsrLoop(1);
Node* osr_phi =
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, loop);
T.graph.SetEnd(ret);
T.DeconstructOsr();
CheckInputs(loop, T.start, loop);
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
CheckInputs(ret, osr_phi, T.start, loop);
}
TEST(Deconstruct_osr_remove_prologue) {
OsrDeconstructorTester T(1);
Diamond d(&T.graph, &T.common, T.p0);
d.Chain(T.osr_normal_entry);
Node* loop = T.NewOsrLoop(1, d.merge);
Node* osr_phi =
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, loop);
T.graph.SetEnd(ret);
T.DeconstructOsr();
CheckInputs(loop, T.start, loop);
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
CheckInputs(ret, osr_phi, T.start, loop);
// The control before the loop should have been removed.
AllNodes nodes(T.main_zone(), &T.graph);
CHECK(!nodes.IsLive(d.branch));
CHECK(!nodes.IsLive(d.if_true));
CHECK(!nodes.IsLive(d.if_false));
CHECK(!nodes.IsLive(d.merge));
}
TEST(Deconstruct_osr_with_body1) {
OsrDeconstructorTester T(1);
Node* loop = T.NewOsrLoop(1);
Node* branch = T.graph.NewNode(T.common.Branch(), T.p0, loop);
Node* if_true = T.graph.NewNode(T.common.IfTrue(), branch);
Node* if_false = T.graph.NewNode(T.common.IfFalse(), branch);
loop->ReplaceInput(2, if_true);
Node* osr_phi =
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, if_false);
T.graph.SetEnd(ret);
T.DeconstructOsr();
CheckInputs(loop, T.start, if_true);
CheckInputs(branch, T.p0, loop);
CheckInputs(if_true, branch);
CheckInputs(if_false, branch);
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
CheckInputs(ret, osr_phi, T.start, if_false);
}
TEST(Deconstruct_osr_with_body2) {
OsrDeconstructorTester T(1);
Node* loop = T.NewOsrLoop(1);
// Two chained branches in the the body of the loop.
Node* branch1 = T.graph.NewNode(T.common.Branch(), T.p0, loop);
Node* if_true1 = T.graph.NewNode(T.common.IfTrue(), branch1);
Node* if_false1 = T.graph.NewNode(T.common.IfFalse(), branch1);
Node* branch2 = T.graph.NewNode(T.common.Branch(), T.p0, if_true1);
Node* if_true2 = T.graph.NewNode(T.common.IfTrue(), branch2);
Node* if_false2 = T.graph.NewNode(T.common.IfFalse(), branch2);
loop->ReplaceInput(2, if_true2);
Node* osr_phi =
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant());
Node* merge = T.graph.NewNode(T.common.Merge(2), if_false1, if_false2);
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, merge);
T.graph.SetEnd(ret);
T.DeconstructOsr();
CheckInputs(loop, T.start, if_true2);
CheckInputs(branch1, T.p0, loop);
CheckInputs(branch2, T.p0, if_true1);
CheckInputs(if_true1, branch1);
CheckInputs(if_false1, branch1);
CheckInputs(if_true2, branch2);
CheckInputs(if_false2, branch2);
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(), loop);
CheckInputs(ret, osr_phi, T.start, merge);
CheckInputs(merge, if_false1, if_false2);
}
TEST(Deconstruct_osr_with_body3) {
OsrDeconstructorTester T(1);
Node* loop = T.NewOsrLoop(2);
// Two branches that create two different backedges.
Node* branch1 = T.graph.NewNode(T.common.Branch(), T.p0, loop);
Node* if_true1 = T.graph.NewNode(T.common.IfTrue(), branch1);
Node* if_false1 = T.graph.NewNode(T.common.IfFalse(), branch1);
Node* branch2 = T.graph.NewNode(T.common.Branch(), T.p0, if_true1);
Node* if_true2 = T.graph.NewNode(T.common.IfTrue(), branch2);
Node* if_false2 = T.graph.NewNode(T.common.IfFalse(), branch2);
loop->ReplaceInput(2, if_false1);
loop->ReplaceInput(3, if_true2);
Node* osr_phi =
T.NewOsrPhi(loop, T.jsgraph.OneConstant(), 0, T.jsgraph.ZeroConstant(),
T.jsgraph.ZeroConstant());
Node* ret = T.graph.NewNode(T.common.Return(), osr_phi, T.start, if_false2);
T.graph.SetEnd(ret);
T.DeconstructOsr();
CheckInputs(loop, T.start, if_false1, if_true2);
CheckInputs(branch1, T.p0, loop);
CheckInputs(branch2, T.p0, if_true1);
CheckInputs(if_true1, branch1);
CheckInputs(if_false1, branch1);
CheckInputs(if_true2, branch2);
CheckInputs(if_false2, branch2);
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.ZeroConstant(),
T.jsgraph.ZeroConstant(), loop);
CheckInputs(ret, osr_phi, T.start, if_false2);
}
struct While {
OsrDeconstructorTester& t;
Node* branch;
Node* if_true;
Node* exit;
Node* loop;
While(OsrDeconstructorTester& R, Node* cond, bool is_osr, int backedges = 1)
: t(R) {
loop = t.NewLoop(is_osr, backedges);
branch = t.graph.NewNode(t.common.Branch(), cond, loop);
if_true = t.graph.NewNode(t.common.IfTrue(), branch);
exit = t.graph.NewNode(t.common.IfFalse(), branch);
loop->ReplaceInput(loop->InputCount() - 1, if_true);
}
void Nest(While& that) {
that.loop->ReplaceInput(that.loop->InputCount() - 1, exit);
this->loop->ReplaceInput(0, that.if_true);
}
Node* Phi(Node* i1, Node* i2, Node* i3) {
if (loop->InputCount() == 2) {
return t.graph.NewNode(t.common.Phi(kMachAnyTagged, 2), i1, i2, loop);
} else {
return t.graph.NewNode(t.common.Phi(kMachAnyTagged, 3), i1, i2, i3, loop);
}
}
};
static Node* FindSuccessor(Node* node, IrOpcode::Value opcode) {
for (Node* use : node->uses()) {
if (use->opcode() == opcode) return use;
}
UNREACHABLE(); // should have been found.
return nullptr;
}
TEST(Deconstruct_osr_nested1) {
OsrDeconstructorTester T(1);
While outer(T, T.p0, false);
While inner(T, T.p0, true);
inner.Nest(outer);
Node* outer_phi = outer.Phi(T.p0, T.p0, nullptr);
outer.branch->ReplaceInput(0, outer_phi);
Node* osr_phi = inner.Phi(T.jsgraph.TrueConstant(), T.osr_values[0],
T.jsgraph.FalseConstant());
inner.branch->ReplaceInput(0, osr_phi);
outer_phi->ReplaceInput(1, osr_phi);
Node* ret =
T.graph.NewNode(T.common.Return(), outer_phi, T.start, outer.exit);
Node* end = T.graph.NewNode(T.common.End(1), ret);
T.graph.SetEnd(end);
T.DeconstructOsr();
// Check structure of deconstructed graph.
// Check inner OSR loop is directly connected to start.
CheckInputs(inner.loop, T.start, inner.if_true);
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.FalseConstant(), inner.loop);
// Check control transfer to copy of outer loop.
Node* new_outer_loop = FindSuccessor(inner.exit, IrOpcode::kLoop);
Node* new_outer_phi = FindSuccessor(new_outer_loop, IrOpcode::kPhi);
CHECK_NE(new_outer_loop, outer.loop);
CHECK_NE(new_outer_phi, outer_phi);
CheckInputs(new_outer_loop, inner.exit, new_outer_loop->InputAt(1));
// Check structure of outer loop.
Node* new_outer_branch = FindSuccessor(new_outer_loop, IrOpcode::kBranch);
CHECK_NE(new_outer_branch, outer.branch);
CheckInputs(new_outer_branch, new_outer_phi, new_outer_loop);
Node* new_outer_exit = FindSuccessor(new_outer_branch, IrOpcode::kIfFalse);
Node* new_outer_if_true = FindSuccessor(new_outer_branch, IrOpcode::kIfTrue);
// Check structure of return.
end = T.graph.end();
Node* new_ret = end->InputAt(0);
CHECK_EQ(IrOpcode::kReturn, new_ret->opcode());
CheckInputs(new_ret, new_outer_phi, T.start, new_outer_exit);
// Check structure of inner loop.
Node* new_inner_loop = FindSuccessor(new_outer_if_true, IrOpcode::kLoop);
Node* new_inner_phi = FindSuccessor(new_inner_loop, IrOpcode::kPhi);
CheckInputs(new_inner_phi, T.jsgraph.TrueConstant(),
T.jsgraph.FalseConstant(), new_inner_loop);
CheckInputs(new_outer_phi, osr_phi, new_inner_phi, new_outer_loop);
}
TEST(Deconstruct_osr_nested2) {
OsrDeconstructorTester T(1);
// Test multiple backedge outer loop.
While outer(T, T.p0, false, 2);
While inner(T, T.p0, true);
inner.Nest(outer);
Node* outer_phi = outer.Phi(T.p0, T.p0, T.p0);
outer.branch->ReplaceInput(0, outer_phi);
Node* osr_phi = inner.Phi(T.jsgraph.TrueConstant(), T.osr_values[0],
T.jsgraph.FalseConstant());
inner.branch->ReplaceInput(0, osr_phi);
outer_phi->ReplaceInput(1, osr_phi);
outer_phi->ReplaceInput(2, T.jsgraph.FalseConstant());
Node* x_branch = T.graph.NewNode(T.common.Branch(), osr_phi, inner.exit);
Node* x_true = T.graph.NewNode(T.common.IfTrue(), x_branch);
Node* x_false = T.graph.NewNode(T.common.IfFalse(), x_branch);
outer.loop->ReplaceInput(1, x_true);
outer.loop->ReplaceInput(2, x_false);
Node* ret =
T.graph.NewNode(T.common.Return(), outer_phi, T.start, outer.exit);
Node* end = T.graph.NewNode(T.common.End(1), ret);
T.graph.SetEnd(end);
T.DeconstructOsr();
// Check structure of deconstructed graph.
// Check inner OSR loop is directly connected to start.
CheckInputs(inner.loop, T.start, inner.if_true);
CheckInputs(osr_phi, T.osr_values[0], T.jsgraph.FalseConstant(), inner.loop);
// Check control transfer to copy of outer loop.
Node* new_merge = FindSuccessor(x_true, IrOpcode::kMerge);
CHECK_EQ(new_merge, FindSuccessor(x_false, IrOpcode::kMerge));
CheckInputs(new_merge, x_true, x_false);
Node* new_outer_loop = FindSuccessor(new_merge, IrOpcode::kLoop);
Node* new_outer_phi = FindSuccessor(new_outer_loop, IrOpcode::kPhi);
CHECK_NE(new_outer_loop, outer.loop);
CHECK_NE(new_outer_phi, outer_phi);
Node* new_entry_phi = FindSuccessor(new_merge, IrOpcode::kPhi);
CheckInputs(new_entry_phi, osr_phi, T.jsgraph.FalseConstant(), new_merge);
CHECK_EQ(new_merge, new_outer_loop->InputAt(0));
// Check structure of outer loop.
Node* new_outer_branch = FindSuccessor(new_outer_loop, IrOpcode::kBranch);
CHECK_NE(new_outer_branch, outer.branch);
CheckInputs(new_outer_branch, new_outer_phi, new_outer_loop);
Node* new_outer_exit = FindSuccessor(new_outer_branch, IrOpcode::kIfFalse);
Node* new_outer_if_true = FindSuccessor(new_outer_branch, IrOpcode::kIfTrue);
// Check structure of return.
end = T.graph.end();
Node* new_ret = end->InputAt(0);
CHECK_EQ(IrOpcode::kReturn, new_ret->opcode());
CheckInputs(new_ret, new_outer_phi, T.start, new_outer_exit);
// Check structure of inner loop.
Node* new_inner_loop = FindSuccessor(new_outer_if_true, IrOpcode::kLoop);
Node* new_inner_phi = FindSuccessor(new_inner_loop, IrOpcode::kPhi);
CheckInputs(new_inner_phi, T.jsgraph.TrueConstant(),
T.jsgraph.FalseConstant(), new_inner_loop);
CheckInputs(new_outer_phi, new_entry_phi, new_inner_phi,
T.jsgraph.FalseConstant(), new_outer_loop);
}
Node* MakeCounter(JSGraph* jsgraph, Node* start, Node* loop) {
int count = loop->InputCount();
NodeVector tmp_inputs(jsgraph->graph()->zone());
for (int i = 0; i < count; i++) {
tmp_inputs.push_back(start);
}
tmp_inputs.push_back(loop);
Node* phi = jsgraph->graph()->NewNode(
jsgraph->common()->Phi(kMachInt32, count), count + 1, &tmp_inputs[0]);
Node* inc = jsgraph->graph()->NewNode(&kIntAdd, phi, jsgraph->OneConstant());
for (int i = 1; i < count; i++) {
phi->ReplaceInput(i, inc);
}
return phi;
}
TEST(Deconstruct_osr_nested3) {
OsrDeconstructorTester T(1);
// outermost loop.
While loop0(T, T.p0, false, 1);
Node* loop0_cntr = MakeCounter(&T.jsgraph, T.p0, loop0.loop);
loop0.branch->ReplaceInput(0, loop0_cntr);
// middle loop.
Node* loop1 = T.graph.NewNode(T.common.Loop(1), loop0.if_true);
Node* loop1_phi = T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), loop0_cntr,
loop0_cntr, loop1);
// innermost (OSR) loop.
While loop2(T, T.p0, true, 1);
loop2.loop->ReplaceInput(0, loop1);
Node* loop2_cntr = MakeCounter(&T.jsgraph, loop1_phi, loop2.loop);
loop2_cntr->ReplaceInput(1, T.osr_values[0]);
Node* osr_phi = loop2_cntr;
Node* loop2_inc = loop2_cntr->InputAt(2);
loop2.branch->ReplaceInput(0, loop2_cntr);
loop1_phi->ReplaceInput(1, loop2_cntr);
loop0_cntr->ReplaceInput(1, loop2_cntr);
// Branch to either the outer or middle loop.
Node* branch = T.graph.NewNode(T.common.Branch(), loop2_cntr, loop2.exit);
Node* if_true = T.graph.NewNode(T.common.IfTrue(), branch);
Node* if_false = T.graph.NewNode(T.common.IfFalse(), branch);
loop0.loop->ReplaceInput(1, if_true);
loop1->AppendInput(T.graph.zone(), if_false);
NodeProperties::ChangeOp(loop1, T.common.Loop(2));
Node* ret =
T.graph.NewNode(T.common.Return(), loop0_cntr, T.start, loop0.exit);
Node* end = T.graph.NewNode(T.common.End(1), ret);
T.graph.SetEnd(end);
T.DeconstructOsr();
// Check structure of deconstructed graph.
// Check loop2 (OSR loop) is directly connected to start.
CheckInputs(loop2.loop, T.start, loop2.if_true);
CheckInputs(osr_phi, T.osr_values[0], loop2_inc, loop2.loop);
CheckInputs(loop2.branch, osr_phi, loop2.loop);
CheckInputs(loop2.if_true, loop2.branch);
CheckInputs(loop2.exit, loop2.branch);
CheckInputs(branch, osr_phi, loop2.exit);
CheckInputs(if_true, branch);
CheckInputs(if_false, branch);
// Check structure of new_loop1.
Node* new_loop1_loop = FindSuccessor(if_false, IrOpcode::kLoop);
// TODO(titzer): check the internal copy of loop2.
USE(new_loop1_loop);
// Check structure of new_loop0.
Node* new_loop0_loop_entry = FindSuccessor(if_true, IrOpcode::kMerge);
Node* new_loop0_loop = FindSuccessor(new_loop0_loop_entry, IrOpcode::kLoop);
// TODO(titzer): check the internal copies of loop1 and loop2.
Node* new_loop0_branch = FindSuccessor(new_loop0_loop, IrOpcode::kBranch);
Node* new_loop0_if_true = FindSuccessor(new_loop0_branch, IrOpcode::kIfTrue);
Node* new_loop0_exit = FindSuccessor(new_loop0_branch, IrOpcode::kIfFalse);
USE(new_loop0_if_true);
Node* new_ret = T.graph.end()->InputAt(0);
CHECK_EQ(IrOpcode::kReturn, new_ret->opcode());
Node* new_loop0_phi = new_ret->InputAt(0);
CHECK_EQ(IrOpcode::kPhi, new_loop0_phi->opcode());
CHECK_EQ(new_loop0_loop, NodeProperties::GetControlInput(new_loop0_phi));
CHECK_EQ(new_loop0_phi, FindSuccessor(new_loop0_loop, IrOpcode::kPhi));
// Check that the return returns the phi from the OSR loop and control
// depends on the copy of the outer loop0.
CheckInputs(new_ret, new_loop0_phi, T.graph.start(), new_loop0_exit);
}
} // namespace compiler
} // namespace internal
} // namespace v8