203438d9bc
This revives the Terminate operator and removes the weird Always operator. As a first step we let the ControlReducer connect non terminating loops via Terminate. The next step will be to change the graph builder to insert Terminate nodes into every loop. Review URL: https://codereview.chromium.org/1123213002 Cr-Commit-Position: refs/heads/master@{#28259}
327 lines
12 KiB
C++
327 lines
12 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/compiler/control-reducer.h"
|
|
#include "src/compiler/diamond.h"
|
|
#include "src/compiler/graph-visualizer.h"
|
|
#include "src/compiler/js-graph.h"
|
|
#include "src/compiler/js-operator.h"
|
|
#include "src/compiler/machine-operator.h"
|
|
#include "src/compiler/node.h"
|
|
#include "test/unittests/compiler/graph-unittest.h"
|
|
#include "test/unittests/compiler/node-test-utils.h"
|
|
#include "testing/gmock-support.h"
|
|
|
|
using testing::_;
|
|
using testing::AllOf;
|
|
using testing::Capture;
|
|
using testing::CaptureEq;
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace compiler {
|
|
|
|
class ControlReducerTest : public TypedGraphTest {
|
|
public:
|
|
ControlReducerTest()
|
|
: TypedGraphTest(1),
|
|
machine_(zone()),
|
|
javascript_(zone()),
|
|
jsgraph_(isolate(), graph(), common(), &javascript_, &machine_) {}
|
|
|
|
protected:
|
|
MachineOperatorBuilder machine_;
|
|
JSOperatorBuilder javascript_;
|
|
JSGraph jsgraph_;
|
|
|
|
void ReduceGraph(int max_phis_for_select = 0) {
|
|
if (FLAG_trace_turbo_graph) {
|
|
OFStream os(stdout);
|
|
os << "-- Graph before control reduction" << std::endl;
|
|
os << AsRPO(*graph());
|
|
}
|
|
ControlReducer::ReduceGraph(zone(), jsgraph(), max_phis_for_select);
|
|
if (FLAG_trace_turbo_graph) {
|
|
OFStream os(stdout);
|
|
os << "-- Graph after control reduction" << std::endl;
|
|
os << AsRPO(*graph());
|
|
}
|
|
}
|
|
|
|
JSGraph* jsgraph() { return &jsgraph_; }
|
|
};
|
|
|
|
|
|
TEST_F(ControlReducerTest, NonTerminatingLoop) {
|
|
Node* loop = graph()->NewNode(common()->Loop(2), graph()->start());
|
|
loop->AppendInput(graph()->zone(), loop);
|
|
ReduceGraph();
|
|
EXPECT_THAT(
|
|
graph()->end(),
|
|
IsEnd(IsMerge(graph()->start(),
|
|
IsTerminate(graph()->start(),
|
|
AllOf(loop, IsLoop(graph()->start(), loop))))));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, NonTerminatingLoopWithEffectPhi) {
|
|
Node* loop = graph()->NewNode(common()->Loop(2), graph()->start());
|
|
loop->AppendInput(graph()->zone(), loop);
|
|
Node* ephi = graph()->NewNode(common()->EffectPhi(2), graph()->start());
|
|
ephi->AppendInput(graph()->zone(), ephi);
|
|
ephi->AppendInput(graph()->zone(), loop);
|
|
ReduceGraph();
|
|
EXPECT_THAT(
|
|
graph()->end(),
|
|
IsEnd(IsMerge(
|
|
graph()->start(),
|
|
IsTerminate(AllOf(ephi, IsEffectPhi(graph()->start(), ephi, loop)),
|
|
AllOf(loop, IsLoop(graph()->start(), loop))))));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, NonTerminatingLoopWithTwoEffectPhis) {
|
|
Node* loop = graph()->NewNode(common()->Loop(2), graph()->start());
|
|
loop->AppendInput(graph()->zone(), loop);
|
|
Node* ephi1 = graph()->NewNode(common()->EffectPhi(2), graph()->start());
|
|
ephi1->AppendInput(graph()->zone(), ephi1);
|
|
ephi1->AppendInput(graph()->zone(), loop);
|
|
Node* ephi2 = graph()->NewNode(common()->EffectPhi(2), graph()->start());
|
|
ephi2->AppendInput(graph()->zone(), ephi2);
|
|
ephi2->AppendInput(graph()->zone(), loop);
|
|
ReduceGraph();
|
|
EXPECT_THAT(
|
|
graph()->end(),
|
|
IsEnd(IsMerge(
|
|
graph()->start(),
|
|
IsTerminate(
|
|
IsEffectSet(
|
|
AllOf(ephi1, IsEffectPhi(graph()->start(), ephi1, loop)),
|
|
AllOf(ephi2, IsEffectPhi(graph()->start(), ephi2, loop))),
|
|
AllOf(loop, IsLoop(graph()->start(), loop))))));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, NonTerminatingLoopWithDeadEnd) {
|
|
Node* loop = graph()->NewNode(common()->Loop(2), graph()->start());
|
|
loop->AppendInput(graph()->zone(), loop);
|
|
graph()->end()->ReplaceInput(0, graph()->NewNode(common()->Dead()));
|
|
ReduceGraph();
|
|
EXPECT_THAT(graph()->end(),
|
|
IsEnd(IsTerminate(graph()->start(),
|
|
AllOf(loop, IsLoop(graph()->start(), loop)))));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, PhiAsInputToBranch_true) {
|
|
Node* p0 = Parameter(0);
|
|
Node* branch1 = graph()->NewNode(common()->Branch(), p0, graph()->start());
|
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
|
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
|
|
Node* merge1 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
|
|
Node* phi1 = graph()->NewNode(common()->Phi(kMachInt32, 2),
|
|
jsgraph()->Int32Constant(1),
|
|
jsgraph()->Int32Constant(2), merge1);
|
|
|
|
Node* branch2 = graph()->NewNode(common()->Branch(), phi1, merge1);
|
|
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
|
|
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
|
|
Node* merge2 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
|
|
Node* result = graph()->NewNode(common()->Phi(kMachInt32, 2),
|
|
jsgraph()->Int32Constant(11),
|
|
jsgraph()->Int32Constant(22), merge2);
|
|
|
|
Node* ret =
|
|
graph()->NewNode(common()->Return(), result, graph()->start(), merge2);
|
|
graph()->end()->ReplaceInput(0, ret);
|
|
|
|
ReduceGraph();
|
|
|
|
// First diamond is not reduced.
|
|
EXPECT_THAT(merge1, IsMerge(IsIfTrue(branch1), IsIfFalse(branch1)));
|
|
|
|
// Second diamond should be folded away.
|
|
EXPECT_THAT(graph()->end(),
|
|
IsEnd(IsReturn(IsInt32Constant(11), graph()->start(), merge1)));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, PhiAsInputToBranch_false) {
|
|
Node* p0 = Parameter(0);
|
|
Node* branch1 = graph()->NewNode(common()->Branch(), p0, graph()->start());
|
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
|
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
|
|
Node* merge1 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
|
|
Node* phi1 = graph()->NewNode(common()->Phi(kMachInt32, 2),
|
|
jsgraph()->Int32Constant(0),
|
|
jsgraph()->BooleanConstant(false), merge1);
|
|
|
|
Node* branch2 = graph()->NewNode(common()->Branch(), phi1, merge1);
|
|
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
|
|
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
|
|
Node* merge2 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
|
|
Node* result = graph()->NewNode(common()->Phi(kMachInt32, 2),
|
|
jsgraph()->Int32Constant(11),
|
|
jsgraph()->Int32Constant(22), merge2);
|
|
|
|
Node* ret =
|
|
graph()->NewNode(common()->Return(), result, graph()->start(), merge2);
|
|
graph()->end()->ReplaceInput(0, ret);
|
|
|
|
ReduceGraph();
|
|
|
|
// First diamond is not reduced.
|
|
EXPECT_THAT(merge1, IsMerge(IsIfTrue(branch1), IsIfFalse(branch1)));
|
|
|
|
// Second diamond should be folded away.
|
|
EXPECT_THAT(graph()->end(),
|
|
IsEnd(IsReturn(IsInt32Constant(22), graph()->start(), merge1)));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, PhiAsInputToBranch_unknown_true) {
|
|
Node* p0 = Parameter(0);
|
|
Node* phi0 = graph()->NewNode(common()->Phi(kMachInt32, 2), p0,
|
|
jsgraph()->Int32Constant(1), graph()->start());
|
|
Node* branch1 = graph()->NewNode(common()->Branch(), phi0, graph()->start());
|
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
|
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
|
|
Node* merge1 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
|
|
Node* phi1 = graph()->NewNode(common()->Phi(kMachInt32, 2),
|
|
jsgraph()->Int32Constant(111),
|
|
jsgraph()->Int32Constant(222), merge1);
|
|
|
|
Node* ret =
|
|
graph()->NewNode(common()->Return(), phi1, graph()->start(), merge1);
|
|
graph()->end()->ReplaceInput(0, ret);
|
|
|
|
ReduceGraph();
|
|
|
|
// Branch should not be folded.
|
|
EXPECT_THAT(phi1,
|
|
IsPhi(kMachInt32, IsInt32Constant(111), IsInt32Constant(222),
|
|
IsMerge(IsIfTrue(branch1), IsIfFalse(branch1))));
|
|
EXPECT_THAT(graph()->end(), IsEnd(IsReturn(phi1, graph()->start(), merge1)));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, RangeAsInputToBranch_true1) {
|
|
Node* p0 = Parameter(Type::Range(1, 2, zone()), 0);
|
|
Node* branch1 = graph()->NewNode(common()->Branch(), p0, graph()->start());
|
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
|
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
|
|
Node* merge1 = graph()->NewNode(common()->Merge(1), if_true1, if_false1);
|
|
Node* result = graph()->NewNode(common()->Phi(kMachInt32, 2),
|
|
jsgraph()->Int32Constant(11),
|
|
jsgraph()->Int32Constant(44), merge1);
|
|
|
|
Node* ret =
|
|
graph()->NewNode(common()->Return(), result, graph()->start(), merge1);
|
|
graph()->end()->ReplaceInput(0, ret);
|
|
|
|
ReduceGraph();
|
|
|
|
// Diamond should be folded away.
|
|
EXPECT_THAT(
|
|
graph()->end(),
|
|
IsEnd(IsReturn(IsInt32Constant(11), graph()->start(), graph()->start())));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, RangeAsInputToBranch_true2) {
|
|
Node* p0 = Parameter(Type::Range(-2, -1, zone()), 0);
|
|
Node* branch1 = graph()->NewNode(common()->Branch(), p0, graph()->start());
|
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
|
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
|
|
Node* merge1 = graph()->NewNode(common()->Merge(1), if_true1, if_false1);
|
|
Node* result = graph()->NewNode(common()->Phi(kMachInt32, 2),
|
|
jsgraph()->Int32Constant(11),
|
|
jsgraph()->Int32Constant(44), merge1);
|
|
|
|
Node* ret =
|
|
graph()->NewNode(common()->Return(), result, graph()->start(), merge1);
|
|
graph()->end()->ReplaceInput(0, ret);
|
|
|
|
ReduceGraph();
|
|
|
|
// Diamond should be folded away.
|
|
EXPECT_THAT(
|
|
graph()->end(),
|
|
IsEnd(IsReturn(IsInt32Constant(11), graph()->start(), graph()->start())));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, SelectPhi) {
|
|
Node* p0 = Parameter(0);
|
|
const MachineType kType = kMachInt32;
|
|
Diamond d(graph(), common(), p0);
|
|
Node* phi =
|
|
d.Phi(kType, jsgraph()->Int32Constant(1), jsgraph()->Int32Constant(2));
|
|
|
|
Node* ret =
|
|
graph()->NewNode(common()->Return(), phi, graph()->start(), d.merge);
|
|
graph()->end()->ReplaceInput(0, ret);
|
|
|
|
ReduceGraph(1);
|
|
|
|
// Phi should be replaced with a select.
|
|
EXPECT_THAT(graph()->end(),
|
|
IsEnd(IsReturn(
|
|
IsSelect(kType, p0, IsInt32Constant(1), IsInt32Constant(2)),
|
|
graph()->start(), graph()->start())));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, SelectPhis_fail) {
|
|
Node* p0 = Parameter(0);
|
|
const MachineType kType = kMachInt32;
|
|
Diamond d(graph(), common(), p0);
|
|
Node* phi =
|
|
d.Phi(kType, jsgraph()->Int32Constant(1), jsgraph()->Int32Constant(2));
|
|
Node* phi2 =
|
|
d.Phi(kType, jsgraph()->Int32Constant(11), jsgraph()->Int32Constant(22));
|
|
USE(phi2);
|
|
Node* ret =
|
|
graph()->NewNode(common()->Return(), phi, graph()->start(), d.merge);
|
|
graph()->end()->ReplaceInput(0, ret);
|
|
|
|
ReduceGraph(1);
|
|
|
|
// Diamond should not be replaced with a select (too many phis).
|
|
EXPECT_THAT(ret, IsReturn(phi, graph()->start(), d.merge));
|
|
EXPECT_THAT(graph()->end(), IsEnd(ret));
|
|
}
|
|
|
|
|
|
TEST_F(ControlReducerTest, SelectTwoPhis) {
|
|
Node* p0 = Parameter(0);
|
|
const MachineType kType = kMachInt32;
|
|
Diamond d(graph(), common(), p0);
|
|
Node* phi1 =
|
|
d.Phi(kType, jsgraph()->Int32Constant(1), jsgraph()->Int32Constant(2));
|
|
Node* phi2 =
|
|
d.Phi(kType, jsgraph()->Int32Constant(2), jsgraph()->Int32Constant(3));
|
|
MachineOperatorBuilder machine(zone());
|
|
Node* add = graph()->NewNode(machine.Int32Add(), phi1, phi2);
|
|
Node* ret =
|
|
graph()->NewNode(common()->Return(), add, graph()->start(), d.merge);
|
|
graph()->end()->ReplaceInput(0, ret);
|
|
|
|
ReduceGraph(2);
|
|
|
|
// Phis should be replaced with two selects.
|
|
EXPECT_THAT(
|
|
ret,
|
|
IsReturn(IsInt32Add(
|
|
IsSelect(kType, p0, IsInt32Constant(1), IsInt32Constant(2)),
|
|
IsSelect(kType, p0, IsInt32Constant(2), IsInt32Constant(3))),
|
|
graph()->start(), graph()->start()));
|
|
EXPECT_THAT(graph()->end(), IsEnd(ret));
|
|
}
|
|
|
|
|
|
} // namespace compiler
|
|
} // namespace internal
|
|
} // namespace v8
|