[turbofan] Ensure that NTLs are always properly connected to the end.

Up until now we used a special Terminate node to artifically connect non
terminating loops to the End node, but this was kind of adhoc and didn't
work for the CFG. So without all kinds of weird hacks, the end block in
the CFG will not be connected to NTLs, which makes it impossible to
compute post dominance / control dependence in the current setting.

So instead of Terminate, we add a special Branch to NTLs, whose
condition is the special Always node, which corresponds to True, except
that it cannot be folded away. This way we don't need any special
machinery in the scheduler, since it's just a regular Branch.

R=titzer@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#26294}
This commit is contained in:
bmeurer 2015-01-27 06:02:21 -08:00 committed by Commit bot
parent f605f1c223
commit 59a02ebdbe
20 changed files with 333 additions and 255 deletions

View File

@ -110,6 +110,7 @@ size_t ProjectionIndexOf(const Operator* const op) {
#define CACHED_OP_LIST(V) \
V(Always, Operator::kPure, 0, 0, 0, 1, 0, 0) \
V(Dead, Operator::kFoldable, 0, 0, 0, 0, 0, 1) \
V(End, Operator::kFoldable, 0, 0, 1, 0, 0, 0) \
V(IfTrue, Operator::kFoldable, 0, 0, 1, 0, 0, 1) \
@ -294,14 +295,6 @@ const Operator* CommonOperatorBuilder::Merge(int control_input_count) {
}
const Operator* CommonOperatorBuilder::Terminate(int effects) {
return new (zone()) Operator( // --
IrOpcode::kTerminate, Operator::kPure, // opcode
"Terminate", // name
0, effects, 1, 0, 0, 1); // counts
}
const Operator* CommonOperatorBuilder::Parameter(int index) {
switch (index) {
#define CACHED_PARAMETER(index) \
@ -427,6 +420,15 @@ const Operator* CommonOperatorBuilder::EffectPhi(int arguments) {
}
const Operator* CommonOperatorBuilder::EffectSet(int arguments) {
DCHECK(arguments > 1); // Disallow empty/singleton sets.
return new (zone()) Operator( // --
IrOpcode::kEffectSet, Operator::kPure, // opcode
"EffectSet", // name
0, arguments, 0, 0, 1, 0); // counts
}
const Operator* CommonOperatorBuilder::ValueEffect(int arguments) {
DCHECK(arguments > 0); // Disallow empty value effects.
return new (zone()) Operator( // --

View File

@ -162,13 +162,17 @@ class CommonOperatorBuilder FINAL : public ZoneObject {
public:
explicit CommonOperatorBuilder(Zone* zone);
// Special operator used only in Branches to mark them as always taken, but
// still unfoldable. This is required to properly connect non terminating
// loops to end (in both the sea of nodes and the CFG).
const Operator* Always();
const Operator* Dead();
const Operator* End();
const Operator* Branch(BranchHint = BranchHint::kNone);
const Operator* IfTrue();
const Operator* IfFalse();
const Operator* Throw();
const Operator* Terminate(int effects);
const Operator* Return();
const Operator* Start(int num_formal_parameters);
@ -191,6 +195,7 @@ class CommonOperatorBuilder FINAL : public ZoneObject {
const Operator* Select(MachineType, BranchHint = BranchHint::kNone);
const Operator* Phi(MachineType type, int arguments);
const Operator* EffectPhi(int arguments);
const Operator* EffectSet(int arguments);
const Operator* ValueEffect(int arguments);
const Operator* Finish(int arguments);
const Operator* StateValues(int arguments);

View File

@ -169,44 +169,80 @@ class ControlReducerImpl {
Node* ConnectNTL(Node* loop) {
TRACE(("ConnectNTL: #%d:%s\n", loop->id(), loop->op()->mnemonic()));
if (loop->opcode() != IrOpcode::kTerminate) {
// Insert a {Terminate} node if the loop has effects.
ZoneDeque<Node*> effects(zone_);
for (Node* const use : loop->uses()) {
if (use->opcode() == IrOpcode::kEffectPhi) effects.push_back(use);
}
int count = static_cast<int>(effects.size());
if (count > 0) {
Node** inputs = zone_->NewArray<Node*>(1 + count);
for (int i = 0; i < count; i++) inputs[i] = effects[i];
inputs[count] = loop;
loop = graph()->NewNode(common_->Terminate(count), 1 + count, inputs);
TRACE(("AddTerminate: #%d:%s[%d]\n", loop->id(), loop->op()->mnemonic(),
count));
Node* always = graph()->NewNode(common_->Always());
// Mark the node as visited so that we can revisit later.
MarkAsVisited(always);
Node* branch = graph()->NewNode(common_->Branch(), always, loop);
// Mark the node as visited so that we can revisit later.
MarkAsVisited(branch);
Node* if_true = graph()->NewNode(common_->IfTrue(), branch);
// Mark the node as visited so that we can revisit later.
MarkAsVisited(if_true);
Node* if_false = graph()->NewNode(common_->IfFalse(), branch);
// Mark the node as visited so that we can revisit later.
MarkAsVisited(if_false);
// Hook up the branch into the loop and collect all loop effects.
NodeVector effects(zone_);
for (auto edge : loop->use_edges()) {
DCHECK_EQ(loop, edge.to());
DCHECK(NodeProperties::IsControlEdge(edge));
if (edge.from() == branch) continue;
switch (edge.from()->opcode()) {
#define CASE(Opcode) case IrOpcode::k##Opcode:
CONTROL_OP_LIST(CASE)
#undef CASE
// Update all control nodes (except {branch}) pointing to the {loop}.
edge.UpdateTo(if_true);
break;
case IrOpcode::kEffectPhi:
effects.push_back(edge.from());
break;
default:
break;
}
}
Node* to_add = loop;
// Compute effects for the Return.
Node* effect = graph()->start();
int const effects_count = static_cast<int>(effects.size());
if (effects_count == 1) {
effect = effects[0];
} else if (effects_count > 1) {
effect = graph()->NewNode(common_->EffectSet(effects_count),
effects_count, &effects.front());
// Mark the node as visited so that we can revisit later.
MarkAsVisited(effect);
}
// Add a return to connect the NTL to the end.
Node* ret = graph()->NewNode(
common_->Return(), jsgraph_->UndefinedConstant(), effect, if_false);
// Mark the node as visited so that we can revisit later.
MarkAsVisited(ret);
Node* end = graph()->end();
CHECK_EQ(IrOpcode::kEnd, end->opcode());
Node* merge = end->InputAt(0);
if (merge == NULL || merge->opcode() == IrOpcode::kDead) {
// The end node died; just connect end to {loop}.
end->ReplaceInput(0, loop);
// The end node died; just connect end to {ret}.
end->ReplaceInput(0, ret);
} else if (merge->opcode() != IrOpcode::kMerge) {
// Introduce a final merge node for {end->InputAt(0)} and {loop}.
merge = graph()->NewNode(common_->Merge(2), merge, loop);
// Introduce a final merge node for {end->InputAt(0)} and {ret}.
merge = graph()->NewNode(common_->Merge(2), merge, ret);
end->ReplaceInput(0, merge);
to_add = merge;
ret = merge;
// Mark the node as visited so that we can revisit later.
EnsureStateSize(merge->id());
state_[merge->id()] = kVisited;
MarkAsVisited(merge);
} else {
// Append a new input to the final merge at the end.
merge->AppendInput(graph()->zone(), loop);
merge->AppendInput(graph()->zone(), ret);
merge->set_op(common_->Merge(merge->InputCount()));
}
return to_add;
return ret;
}
void AddNodesReachableFromEnd(ReachabilityMarker& marked, NodeVector& nodes) {
@ -337,6 +373,13 @@ class ControlReducerImpl {
}
}
// Mark {node} as visited.
void MarkAsVisited(Node* node) {
size_t id = static_cast<size_t>(node->id());
EnsureStateSize(id);
state_[id] = kVisited;
}
Node* dead() {
if (dead_ == NULL) dead_ = graph()->NewNode(common_->Dead());
return dead_;

View File

@ -7,8 +7,14 @@
namespace v8 {
namespace internal {
// Forward declarations.
class Zone;
namespace compiler {
// Forward declarations.
class JSGraph;
class CommonOperatorBuilder;
class Node;

View File

@ -508,6 +508,9 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
CheckNoPhis(tbranch);
CheckNoPhis(fbranch);
if (tbranch == fbranch) return VisitGoto(tbranch);
// Treat special Branch(Always, IfTrue, IfFalse) as Goto(IfTrue).
Node* const condition = input->InputAt(0);
if (condition->opcode() == IrOpcode::kAlways) return VisitGoto(tbranch);
return VisitBranch(input, tbranch, fbranch);
}
case BasicBlock::kReturn: {
@ -541,8 +544,8 @@ MachineType InstructionSelector::GetMachineType(Node* node) {
case IrOpcode::kIfTrue:
case IrOpcode::kIfFalse:
case IrOpcode::kEffectPhi:
case IrOpcode::kEffectSet:
case IrOpcode::kMerge:
case IrOpcode::kTerminate:
// No code needed for these graph artifacts.
return kMachNone;
case IrOpcode::kFinish:

View File

@ -45,9 +45,12 @@ Reduction JSGenericLowering::Reduce(Node* node) {
// have inserted the correct ChangeBoolToBit, otherwise we need to perform
// poor-man's representation inference here and insert manual change.
if (!info()->is_typing_enabled()) {
Node* test = graph()->NewNode(machine()->WordEqual(), node->InputAt(0),
jsgraph()->TrueConstant());
node->ReplaceInput(0, test);
Node* condition = node->InputAt(0);
if (condition->opcode() != IrOpcode::kAlways) {
Node* test = graph()->NewNode(machine()->WordEqual(), condition,
jsgraph()->TrueConstant());
node->ReplaceInput(0, test);
}
break;
}
// Fall-through.

View File

@ -14,7 +14,6 @@
V(IfFalse) \
V(Merge) \
V(Return) \
V(Terminate) \
V(OsrNormalEntry) \
V(OsrLoopEntry) \
V(Throw)
@ -37,6 +36,7 @@
#define INNER_OP_LIST(V) \
V(Select) \
V(Phi) \
V(EffectSet) \
V(EffectPhi) \
V(ValueEffect) \
V(Finish) \
@ -49,7 +49,8 @@
#define COMMON_OP_LIST(V) \
CONSTANT_OP_LIST(V) \
INNER_OP_LIST(V)
INNER_OP_LIST(V) \
V(Always)
// Opcodes for JavaScript operators.
#define JS_COMPARE_BINOP_LIST(V) \
@ -277,7 +278,7 @@ class IrOpcode {
// Returns true if opcode for common operator.
static bool IsCommonOpcode(Value value) {
return kDead <= value && value <= kProjection;
return kDead <= value && value <= kAlways;
}
// Returns true if opcode for control operator.

View File

@ -316,13 +316,6 @@ class CFGBuilder : public ZoneObject {
case IrOpcode::kMerge:
BuildBlockForNode(node);
break;
case IrOpcode::kTerminate: {
// Put Terminate in the loop to which it refers.
Node* loop = NodeProperties::GetControlInput(node);
BasicBlock* block = BuildBlockForNode(loop);
FixNode(block, node);
break;
}
case IrOpcode::kBranch:
BuildBlocksForSuccessors(node, IrOpcode::kIfTrue, IrOpcode::kIfFalse);
break;

View File

@ -473,6 +473,8 @@ class RepresentationSelector {
SetOutput(node, kRepTagged | changer_->TypeFromUpperBound(upper));
return;
}
case IrOpcode::kAlways:
return VisitLeaf(node, kRepBit);
case IrOpcode::kInt32Constant:
return VisitLeaf(node, kRepWord32);
case IrOpcode::kInt64Constant:

View File

@ -589,6 +589,11 @@ Bounds Typer::Visitor::TypeStart(Node* node) {
// Common operators.
Bounds Typer::Visitor::TypeAlways(Node* node) {
return Bounds(Type::None(zone()), Type::Boolean(zone()));
}
Bounds Typer::Visitor::TypeParameter(Node* node) {
return Bounds::Unbounded(zone());
}
@ -670,6 +675,12 @@ Bounds Typer::Visitor::TypeEffectPhi(Node* node) {
}
Bounds Typer::Visitor::TypeEffectSet(Node* node) {
UNREACHABLE();
return Bounds();
}
Bounds Typer::Visitor::TypeValueEffect(Node* node) {
UNREACHABLE();
return Bounds();

View File

@ -178,6 +178,16 @@ void Verifier::Visitor::Check(Node* node) {
}
switch (node->opcode()) {
case IrOpcode::kAlways:
// Always has no inputs.
CHECK_EQ(0, input_count);
// Always uses are Branch.
for (auto use : node->uses()) {
CHECK(use->opcode() == IrOpcode::kBranch);
}
// Type is boolean.
CheckUpperIs(node, Type::Boolean());
break;
case IrOpcode::kStart:
// Start has no inputs.
CHECK_EQ(0, input_count);
@ -233,12 +243,6 @@ void Verifier::Visitor::Check(Node* node) {
// Type is empty.
CheckNotTyped(node);
break;
case IrOpcode::kTerminate:
// Type is empty.
CheckNotTyped(node);
CHECK_EQ(1, control_count);
CHECK_EQ(input_count, 1 + effect_count);
break;
case IrOpcode::kOsrNormalEntry:
case IrOpcode::kOsrLoopEntry:
// Osr entries have
@ -348,6 +352,12 @@ void Verifier::Visitor::Check(Node* node) {
CHECK_EQ(input_count, 1 + effect_count);
break;
}
case IrOpcode::kEffectSet: {
CHECK_EQ(0, value_count);
CHECK_EQ(0, control_count);
CHECK_LT(1, effect_count);
break;
}
case IrOpcode::kValueEffect:
// TODO(rossberg): what are the constraints on these?
break;

View File

@ -1214,172 +1214,6 @@ TEST(CDeadLoop2) {
}
TEST(CNonTermLoop1) {
ControlReducerTester R;
Node* loop =
R.SetSelfReferences(R.graph.NewNode(R.common.Loop(2), R.start, R.self));
R.ReduceGraph();
Node* end = R.graph.end();
CheckLoop(loop, R.start, loop);
Node* merge = end->InputAt(0);
CheckMerge(merge, R.start, loop);
}
TEST(CNonTermLoop2) {
ControlReducerTester R;
Diamond d(R, R.p0);
Node* loop = R.SetSelfReferences(
R.graph.NewNode(R.common.Loop(2), d.if_false, R.self));
d.merge->ReplaceInput(1, R.dead);
Node* end = R.graph.end();
end->ReplaceInput(0, d.merge);
R.ReduceGraph();
CHECK_EQ(end, R.graph.end());
CheckLoop(loop, d.if_false, loop);
Node* merge = end->InputAt(0);
CheckMerge(merge, d.if_true, loop);
}
TEST(NonTermLoop3) {
ControlReducerTester R;
Node* loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
Branch b(R, R.one, loop);
loop->ReplaceInput(1, b.if_true);
Node* end = R.graph.end();
end->ReplaceInput(0, b.if_false);
R.ReduceGraph();
CHECK_EQ(end, R.graph.end());
CheckInputs(end, loop);
CheckInputs(loop, R.start, loop);
}
TEST(CNonTermLoop_terminate1) {
ControlReducerTester R;
Node* loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
Node* effect = R.SetSelfReferences(
R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
Branch b(R, R.one, loop);
loop->ReplaceInput(1, b.if_true);
Node* end = R.graph.end();
end->ReplaceInput(0, b.if_false);
R.ReduceGraph();
CHECK_EQ(end, R.graph.end());
CheckLoop(loop, R.start, loop);
Node* terminate = end->InputAt(0);
CHECK_EQ(IrOpcode::kTerminate, terminate->opcode());
CHECK_EQ(2, terminate->InputCount());
CHECK_EQ(1, terminate->op()->EffectInputCount());
CHECK_EQ(1, terminate->op()->ControlInputCount());
CheckInputs(terminate, effect, loop);
}
TEST(CNonTermLoop_terminate2) {
ControlReducerTester R;
Node* loop = R.graph.NewNode(R.common.Loop(2), R.start, R.start);
Node* effect1 = R.SetSelfReferences(
R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
Node* effect2 = R.SetSelfReferences(
R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
Branch b(R, R.one, loop);
loop->ReplaceInput(1, b.if_true);
Node* end = R.graph.end();
end->ReplaceInput(0, b.if_false);
R.ReduceGraph();
CheckLoop(loop, R.start, loop);
CHECK_EQ(end, R.graph.end());
Node* terminate = end->InputAt(0);
CHECK_EQ(IrOpcode::kTerminate, terminate->opcode());
CHECK_EQ(3, terminate->InputCount());
CHECK_EQ(2, terminate->op()->EffectInputCount());
CHECK_EQ(1, terminate->op()->ControlInputCount());
Node* e0 = terminate->InputAt(0);
Node* e1 = terminate->InputAt(1);
CHECK(e0 == effect1 || e1 == effect1);
CHECK(e0 == effect2 || e1 == effect2);
CHECK_EQ(loop, terminate->InputAt(2));
}
TEST(CNonTermLoop_terminate_m1) {
ControlReducerTester R;
Node* loop =
R.SetSelfReferences(R.graph.NewNode(R.common.Loop(2), R.start, R.self));
Node* effect = R.SetSelfReferences(
R.graph.NewNode(R.common.EffectPhi(2), R.start, R.self, loop));
R.ReduceGraph();
Node* end = R.graph.end();
CHECK_EQ(R.start, loop->InputAt(0));
CHECK_EQ(loop, loop->InputAt(1));
Node* merge = end->InputAt(0);
CHECK_EQ(IrOpcode::kMerge, merge->opcode());
CHECK_EQ(2, merge->InputCount());
CHECK_EQ(2, merge->op()->ControlInputCount());
CHECK_EQ(R.start, merge->InputAt(0));
Node* terminate = merge->InputAt(1);
CHECK_EQ(IrOpcode::kTerminate, terminate->opcode());
CHECK_EQ(2, terminate->InputCount());
CHECK_EQ(1, terminate->op()->EffectInputCount());
CHECK_EQ(1, terminate->op()->ControlInputCount());
CHECK_EQ(effect, terminate->InputAt(0));
CHECK_EQ(loop, terminate->InputAt(1));
}
TEST(CNonTermLoop_big1) {
ControlReducerTester R;
Branch b1(R, R.p0);
Node* rt = R.graph.NewNode(R.common.Return(), R.one, R.start, b1.if_true);
Branch b2(R, R.p0, b1.if_false);
Node* rf = R.graph.NewNode(R.common.Return(), R.zero, R.start, b2.if_true);
Node* loop = R.SetSelfReferences(
R.graph.NewNode(R.common.Loop(2), b2.if_false, R.self));
Node* merge = R.graph.NewNode(R.common.Merge(2), rt, rf);
R.end->ReplaceInput(0, merge);
R.ReduceGraph();
CheckInputs(R.end, merge);
CheckInputs(merge, rt, rf, loop);
CheckInputs(loop, b2.if_false, loop);
}
TEST(CNonTermLoop_big2) {
ControlReducerTester R;
Branch b1(R, R.p0);
Node* rt = R.graph.NewNode(R.common.Return(), R.one, R.start, b1.if_true);
Node* loop = R.graph.NewNode(R.common.Loop(2), b1.if_false, R.start);
Branch b2(R, R.zero, loop);
loop->ReplaceInput(1, b2.if_false);
Node* rf = R.graph.NewNode(R.common.Return(), R.zero, R.start, b2.if_true);
Node* merge = R.graph.NewNode(R.common.Merge(2), rt, rf);
R.end->ReplaceInput(0, merge);
R.ReduceGraph();
Node* new_merge = R.end->InputAt(0); // old merge was reduced.
CHECK_NE(merge, new_merge);
CheckInputs(new_merge, rt, loop);
CheckInputs(loop, b1.if_false, loop);
CHECK(merge->IsDead());
CHECK(rf->IsDead());
CHECK(b2.if_true->IsDead());
}
TEST(Return1) {
ControlReducerTester R;
Node* ret = R.Return(R.one, R.start, R.start);

View File

@ -28,6 +28,7 @@ struct SharedOperator {
int value_input_count;
int effect_input_count;
int control_input_count;
int value_output_count;
int effect_output_count;
int control_output_count;
};
@ -39,19 +40,21 @@ std::ostream& operator<<(std::ostream& os, const SharedOperator& fop) {
const SharedOperator kSharedOperators[] = {
#define SHARED(Name, properties, value_input_count, effect_input_count, \
control_input_count, effect_output_count, control_output_count) \
{ \
&CommonOperatorBuilder::Name, IrOpcode::k##Name, properties, \
value_input_count, effect_input_count, control_input_count, \
effect_output_count, control_output_count \
#define SHARED(Name, properties, value_input_count, effect_input_count, \
control_input_count, value_output_count, effect_output_count, \
control_output_count) \
{ \
&CommonOperatorBuilder::Name, IrOpcode::k##Name, properties, \
value_input_count, effect_input_count, control_input_count, \
value_output_count, effect_output_count, control_output_count \
}
SHARED(Dead, Operator::kFoldable, 0, 0, 0, 0, 1),
SHARED(End, Operator::kFoldable, 0, 0, 1, 0, 0),
SHARED(IfTrue, Operator::kFoldable, 0, 0, 1, 0, 1),
SHARED(IfFalse, Operator::kFoldable, 0, 0, 1, 0, 1),
SHARED(Throw, Operator::kFoldable, 1, 1, 1, 0, 1),
SHARED(Return, Operator::kNoProperties, 1, 1, 1, 0, 1)
SHARED(Always, Operator::kPure, 0, 0, 0, 1, 0, 0),
SHARED(Dead, Operator::kFoldable, 0, 0, 0, 0, 0, 1),
SHARED(End, Operator::kFoldable, 0, 0, 1, 0, 0, 0),
SHARED(IfTrue, Operator::kFoldable, 0, 0, 1, 0, 0, 1),
SHARED(IfFalse, Operator::kFoldable, 0, 0, 1, 0, 0, 1),
SHARED(Throw, Operator::kFoldable, 1, 1, 1, 0, 0, 1),
SHARED(Return, Operator::kNoProperties, 1, 1, 1, 0, 0, 1)
#undef SHARED
};
@ -83,7 +86,7 @@ TEST_P(CommonSharedOperatorTest, NumberOfInputsAndOutputs) {
sop.value_input_count + sop.effect_input_count + sop.control_input_count,
OperatorProperties::GetTotalInputCount(op));
EXPECT_EQ(0, op->ValueOutputCount());
EXPECT_EQ(sop.value_output_count, op->ValueOutputCount());
EXPECT_EQ(sop.effect_output_count, op->EffectOutputCount());
EXPECT_EQ(sop.control_output_count, op->ControlOutputCount());
}

View File

@ -0,0 +1,124 @@
// 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/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 GraphTest {
protected:
void ReduceGraph() {
JSOperatorBuilder javascript(zone());
MachineOperatorBuilder machine(zone());
JSGraph jsgraph(isolate(), graph(), common(), &javascript, &machine);
ControlReducer::ReduceGraph(zone(), &jsgraph, common());
}
};
TEST_F(ControlReducerTest, NonTerminatingLoop) {
Node* loop = graph()->NewNode(common()->Loop(2), graph()->start());
loop->AppendInput(graph()->zone(), loop);
ReduceGraph();
Capture<Node*> branch;
EXPECT_THAT(
graph()->end(),
IsEnd(IsMerge(
graph()->start(),
IsReturn(IsUndefinedConstant(), graph()->start(),
IsIfFalse(
AllOf(CaptureEq(&branch),
IsBranch(IsAlways(),
AllOf(loop, IsLoop(graph()->start(),
IsIfTrue(CaptureEq(
&branch)))))))))));
}
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();
Capture<Node*> branch;
EXPECT_THAT(
graph()->end(),
IsEnd(IsMerge(
graph()->start(),
IsReturn(IsUndefinedConstant(),
AllOf(ephi, IsEffectPhi(graph()->start(), ephi, loop)),
IsIfFalse(
AllOf(CaptureEq(&branch),
IsBranch(IsAlways(),
AllOf(loop, IsLoop(graph()->start(),
IsIfTrue(CaptureEq(
&branch)))))))))));
}
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();
Capture<Node*> branch;
EXPECT_THAT(
graph()->end(),
IsEnd(IsMerge(
graph()->start(),
IsReturn(
IsUndefinedConstant(),
IsEffectSet(
AllOf(ephi1, IsEffectPhi(graph()->start(), ephi1, loop)),
AllOf(ephi2, IsEffectPhi(graph()->start(), ephi2, loop))),
IsIfFalse(AllOf(
CaptureEq(&branch),
IsBranch(
IsAlways(),
AllOf(loop, IsLoop(graph()->start(),
IsIfTrue(CaptureEq(&branch)))))))))));
}
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();
Capture<Node*> branch;
EXPECT_THAT(
graph()->end(),
IsEnd(IsReturn(
IsUndefinedConstant(), graph()->start(),
IsIfFalse(AllOf(
CaptureEq(&branch),
IsBranch(IsAlways(),
AllOf(loop, IsLoop(graph()->start(),
IsIfTrue(CaptureEq(&branch))))))))));
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -13,6 +13,7 @@ namespace compiler {
GraphTest::GraphTest(int num_parameters) : common_(zone()), graph_(zone()) {
graph()->SetStart(graph()->NewNode(common()->Start(num_parameters)));
graph()->SetEnd(graph()->NewNode(common()->End(), graph()->start()));
}
@ -92,6 +93,12 @@ Matcher<Node*> GraphTest::IsTrueConstant() {
}
Matcher<Node*> GraphTest::IsUndefinedConstant() {
return IsHeapConstant(
Unique<HeapObject>::CreateImmovable(factory()->undefined_value()));
}
TypedGraphTest::TypedGraphTest(int num_parameters)
: GraphTest(num_parameters),
typer_(isolate(), graph(), MaybeHandle<Context>()) {}

View File

@ -50,6 +50,7 @@ class GraphTest : public TestWithContext, public TestWithIsolateAndZone {
Matcher<Node*> IsFalseConstant();
Matcher<Node*> IsTrueConstant();
Matcher<Node*> IsUndefinedConstant();
CommonOperatorBuilder* common() { return &common_; }
Graph* graph() { return &graph_; }

View File

@ -462,6 +462,37 @@ class IsEffectPhiMatcher FINAL : public NodeMatcher {
};
class IsEffectSetMatcher FINAL : public NodeMatcher {
public:
IsEffectSetMatcher(const Matcher<Node*>& effect0_matcher,
const Matcher<Node*>& effect1_matcher)
: NodeMatcher(IrOpcode::kEffectSet),
effect0_matcher_(effect0_matcher),
effect1_matcher_(effect1_matcher) {}
void DescribeTo(std::ostream* os) const FINAL {
NodeMatcher::DescribeTo(os);
*os << "), effect0 (";
effect0_matcher_.DescribeTo(os);
*os << ") and effect1 (";
effect1_matcher_.DescribeTo(os);
*os << ")";
}
bool MatchAndExplain(Node* node, MatchResultListener* listener) const FINAL {
return (NodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node, 0),
"effect0", effect0_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetEffectInput(node, 1),
"effect1", effect1_matcher_, listener));
}
private:
const Matcher<Node*> effect0_matcher_;
const Matcher<Node*> effect1_matcher_;
};
class IsProjectionMatcher FINAL : public NodeMatcher {
public:
IsProjectionMatcher(const Matcher<size_t>& index_matcher,
@ -1148,6 +1179,17 @@ class IsUnopMatcher FINAL : public NodeMatcher {
private:
const Matcher<Node*> input_matcher_;
};
} // namespace
Matcher<Node*> IsAlways() {
return MakeMatcher(new NodeMatcher(IrOpcode::kAlways));
}
Matcher<Node*> IsEnd(const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsControl1Matcher(IrOpcode::kEnd, control_matcher));
}
@ -1290,6 +1332,12 @@ Matcher<Node*> IsEffectPhi(const Matcher<Node*>& effect0_matcher,
}
Matcher<Node*> IsEffectSet(const Matcher<Node*>& effect0_matcher,
const Matcher<Node*>& effect1_matcher) {
return MakeMatcher(new IsEffectSetMatcher(effect0_matcher, effect1_matcher));
}
Matcher<Node*> IsProjection(const Matcher<size_t>& index_matcher,
const Matcher<Node*>& base_matcher) {
return MakeMatcher(new IsProjectionMatcher(index_matcher, base_matcher));

View File

@ -31,6 +31,8 @@ class Node;
using ::testing::Matcher;
Matcher<Node*> IsAlways();
Matcher<Node*> IsEnd(const Matcher<Node*>& control_matcher);
Matcher<Node*> IsBranch(const Matcher<Node*>& value_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsMerge(const Matcher<Node*>& control0_matcher,
@ -73,6 +75,8 @@ Matcher<Node*> IsPhi(const Matcher<MachineType>& type_matcher,
Matcher<Node*> IsEffectPhi(const Matcher<Node*>& effect0_matcher,
const Matcher<Node*>& effect1_matcher,
const Matcher<Node*>& merge_matcher);
Matcher<Node*> IsEffectSet(const Matcher<Node*>& effect0_matcher,
const Matcher<Node*>& effect1_matcher);
Matcher<Node*> IsProjection(const Matcher<size_t>& index_matcher,
const Matcher<Node*>& base_matcher);
Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,

View File

@ -1966,29 +1966,6 @@ TARGET_TEST_F(SchedulerTest, BranchHintFalse) {
CHECK(!schedule->block(f)->deferred());
}
TARGET_TEST_F(SchedulerTest, ScheduleTerminate) {
Node* start = graph()->NewNode(common()->Start(1));
graph()->SetStart(start);
Node* loop = graph()->NewNode(common()->Loop(2), start, start);
loop->ReplaceInput(1, loop); // self loop, NTL.
Node* effect = graph()->NewNode(common()->EffectPhi(1), start, loop);
effect->ReplaceInput(0, effect);
Node* terminate = graph()->NewNode(common()->Terminate(1), effect, loop);
Node* end = graph()->NewNode(common()->End(), terminate);
graph()->SetEnd(end);
Schedule* schedule = ComputeAndVerifySchedule(6, graph());
BasicBlock* block = schedule->block(loop);
CHECK_NE(NULL, loop);
CHECK_EQ(block, schedule->block(effect));
CHECK_GE(block->rpo_number(), 0);
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -42,6 +42,7 @@
'compiler/common-operator-unittest.cc',
'compiler/compiler-test-utils.h',
'compiler/control-equivalence-unittest.cc',
'compiler/control-reducer-unittest.cc',
'compiler/diamond-unittest.cc',
'compiler/graph-reducer-unittest.cc',
'compiler/graph-unittest.cc',