v8/test/unittests/compiler/effect-control-linearizer-unittest.cc
Ross McIlroy f34771f79a [TurboProp] Don't try to rewire unreachable blocks to end.
We can't consistently rewire the successor blocks of an unreachable node to
disconnect them from the graph when we are trying to maintain the schedule.
Instead simply leave the code there. As a future optimization we could add a
proper scheduled dead code elimination phase which can deal with this.

As a side-effect, one of the tests sees a int64 DeadValue, so add support for that
in the instruction selector.

BUG=chromium:1083272,chromium:1083763,chromium:1084953,v8:9684

Change-Id: I69a6feaeef4eae62110392e27ea848b28bccf787
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2209061
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Auto-Submit: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67953}
2020-05-25 10:42:52 +00:00

510 lines
18 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/effect-control-linearizer.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-origin-table.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/schedule.h"
#include "src/compiler/simplified-operator.h"
#include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h"
#include "test/unittests/test-utils.h"
#include "testing/gmock-support.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace v8 {
namespace internal {
namespace compiler {
using testing::Capture;
class EffectControlLinearizerTest : public GraphTest {
public:
EffectControlLinearizerTest()
: GraphTest(3),
machine_(zone()),
javascript_(zone()),
simplified_(zone()),
jsgraph_(isolate(), graph(), common(), &javascript_, &simplified_,
&machine_) {
source_positions_ = new (zone()) SourcePositionTable(graph());
node_origins_ = new (zone()) NodeOriginTable(graph());
}
JSGraph* jsgraph() { return &jsgraph_; }
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
SourcePositionTable* source_positions() { return source_positions_; }
NodeOriginTable* node_origins() { return node_origins_; }
private:
MachineOperatorBuilder machine_;
JSOperatorBuilder javascript_;
SimplifiedOperatorBuilder simplified_;
JSGraph jsgraph_;
SourcePositionTable* source_positions_;
NodeOriginTable* node_origins_;
};
namespace {
BasicBlock* AddBlockToSchedule(Schedule* schedule) {
BasicBlock* block = schedule->NewBasicBlock();
block->set_rpo_number(static_cast<int32_t>(schedule->rpo_order()->size()));
schedule->rpo_order()->push_back(block);
return block;
}
} // namespace
TEST_F(EffectControlLinearizerTest, SimpleLoad) {
Schedule schedule(zone());
// Create the graph.
Node* heap_number = NumberConstant(0.5);
Node* load = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), heap_number,
graph()->start(), graph()->start());
Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret = graph()->NewNode(common()->Return(), zero, load, graph()->start(),
graph()->start());
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddNode(start, heap_number);
schedule.AddNode(start, load);
schedule.AddReturn(start, ret);
// Run the state effect introducer.
LinearizeEffectControl(
jsgraph(), &schedule, zone(), source_positions(), node_origins(),
MaskArrayIndexEnable::kDoNotMaskArrayIndex, MaintainSchedule::kDiscard);
EXPECT_THAT(load,
IsLoadField(AccessBuilder::ForHeapNumberValue(), heap_number,
graph()->start(), graph()->start()));
// The return should have reconnected effect edge to the load.
EXPECT_THAT(ret, IsReturn(load, load, graph()->start()));
}
TEST_F(EffectControlLinearizerTest, DiamondLoad) {
Schedule schedule(zone());
// Create the graph.
Node* branch =
graph()->NewNode(common()->Branch(), Int32Constant(0), graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* heap_number = NumberConstant(0.5);
Node* vtrue = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), heap_number,
graph()->start(), if_true);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = Float64Constant(2);
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kFloat64, 2), vtrue, vfalse, merge);
Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret =
graph()->NewNode(common()->Return(), zero, phi, graph()->start(), merge);
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* tblock = AddBlockToSchedule(&schedule);
BasicBlock* fblock = AddBlockToSchedule(&schedule);
BasicBlock* mblock = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddBranch(start, branch, tblock, fblock);
schedule.AddNode(tblock, if_true);
schedule.AddNode(tblock, heap_number);
schedule.AddNode(tblock, vtrue);
schedule.AddGoto(tblock, mblock);
schedule.AddNode(fblock, if_false);
schedule.AddNode(fblock, vfalse);
schedule.AddGoto(fblock, mblock);
schedule.AddNode(mblock, merge);
schedule.AddNode(mblock, phi);
schedule.AddReturn(mblock, ret);
// Run the state effect introducer.
LinearizeEffectControl(
jsgraph(), &schedule, zone(), source_positions(), node_origins(),
MaskArrayIndexEnable::kDoNotMaskArrayIndex, MaintainSchedule::kDiscard);
// The effect input to the return should be an effect phi with the
// newly introduced effectful change operators.
ASSERT_THAT(
ret, IsReturn(phi, IsEffectPhi(vtrue, graph()->start(), merge), merge));
}
TEST_F(EffectControlLinearizerTest, LoopLoad) {
Schedule schedule(zone());
// Create the graph.
Node* loop = graph()->NewNode(common()->Loop(1), graph()->start());
Node* effect_phi =
graph()->NewNode(common()->EffectPhi(1), graph()->start(), loop);
Node* cond = Int32Constant(0);
Node* branch = graph()->NewNode(common()->Branch(), cond, loop);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
loop->AppendInput(zone(), if_false);
NodeProperties::ChangeOp(loop, common()->Loop(2));
effect_phi->InsertInput(zone(), 1, effect_phi);
NodeProperties::ChangeOp(effect_phi, common()->EffectPhi(2));
Node* heap_number = NumberConstant(0.5);
Node* load = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), heap_number,
graph()->start(), loop);
Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret =
graph()->NewNode(common()->Return(), zero, load, effect_phi, if_true);
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* lblock = AddBlockToSchedule(&schedule);
BasicBlock* fblock = AddBlockToSchedule(&schedule);
BasicBlock* rblock = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddGoto(start, lblock);
schedule.AddNode(lblock, loop);
schedule.AddNode(lblock, effect_phi);
schedule.AddNode(lblock, heap_number);
schedule.AddNode(lblock, load);
schedule.AddNode(lblock, cond);
schedule.AddBranch(lblock, branch, rblock, fblock);
schedule.AddNode(fblock, if_false);
schedule.AddGoto(fblock, lblock);
schedule.AddNode(rblock, if_true);
schedule.AddReturn(rblock, ret);
// Run the state effect introducer.
LinearizeEffectControl(
jsgraph(), &schedule, zone(), source_positions(), node_origins(),
MaskArrayIndexEnable::kDoNotMaskArrayIndex, MaintainSchedule::kDiscard);
ASSERT_THAT(ret, IsReturn(load, load, if_true));
EXPECT_THAT(load, IsLoadField(AccessBuilder::ForHeapNumberValue(),
heap_number, effect_phi, loop));
}
TEST_F(EffectControlLinearizerTest, CloneBranch) {
Schedule schedule(zone());
Node* cond0 = Parameter(0);
Node* cond1 = Parameter(1);
Node* cond2 = Parameter(2);
Node* branch0 = graph()->NewNode(common()->Branch(), cond0, start());
Node* control1 = graph()->NewNode(common()->IfTrue(), branch0);
Node* control2 = graph()->NewNode(common()->IfFalse(), branch0);
Node* merge0 = graph()->NewNode(common()->Merge(2), control1, control2);
Node* phi0 = graph()->NewNode(common()->Phi(MachineRepresentation::kBit, 2),
cond1, cond2, merge0);
Node* branch = graph()->NewNode(common()->Branch(), phi0, merge0);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
graph()->SetEnd(graph()->NewNode(common()->End(1), merge));
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* f1block = AddBlockToSchedule(&schedule);
BasicBlock* t1block = AddBlockToSchedule(&schedule);
BasicBlock* bblock = AddBlockToSchedule(&schedule);
BasicBlock* f2block = AddBlockToSchedule(&schedule);
BasicBlock* t2block = AddBlockToSchedule(&schedule);
BasicBlock* mblock = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddBranch(start, branch0, t1block, f1block);
schedule.AddNode(t1block, control1);
schedule.AddGoto(t1block, bblock);
schedule.AddNode(f1block, control2);
schedule.AddGoto(f1block, bblock);
schedule.AddNode(bblock, merge0);
schedule.AddNode(bblock, phi0);
schedule.AddBranch(bblock, branch, t2block, f2block);
schedule.AddNode(t2block, if_true);
schedule.AddGoto(t2block, mblock);
schedule.AddNode(f2block, if_false);
schedule.AddGoto(f2block, mblock);
schedule.AddNode(mblock, merge);
schedule.AddNode(mblock, graph()->end());
LinearizeEffectControl(
jsgraph(), &schedule, zone(), source_positions(), node_origins(),
MaskArrayIndexEnable::kDoNotMaskArrayIndex, MaintainSchedule::kDiscard);
Capture<Node *> branch1_capture, branch2_capture;
EXPECT_THAT(
end(),
IsEnd(IsMerge(IsMerge(IsIfTrue(CaptureEq(&branch1_capture)),
IsIfTrue(CaptureEq(&branch2_capture))),
IsMerge(IsIfFalse(AllOf(CaptureEq(&branch1_capture),
IsBranch(cond1, control1))),
IsIfFalse(AllOf(CaptureEq(&branch2_capture),
IsBranch(cond2, control2)))))));
}
TEST_F(EffectControlLinearizerTest, UnreachableThenBranch) {
Schedule schedule(zone());
// Create the graph.
Node* unreachable = graph()->NewNode(common()->Unreachable(),
graph()->start(), graph()->start());
Node* branch =
graph()->NewNode(common()->Branch(), Int32Constant(0), graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* true_throw = graph()->NewNode(common()->Throw(), unreachable, if_true);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* false_throw =
graph()->NewNode(common()->Throw(), unreachable, if_false);
graph()->SetEnd(graph()->NewNode(common()->End(0)));
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* tblock = AddBlockToSchedule(&schedule);
BasicBlock* fblock = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddNode(start, unreachable);
schedule.AddBranch(start, branch, tblock, fblock);
schedule.AddNode(tblock, if_true);
schedule.AddThrow(tblock, true_throw);
NodeProperties::MergeControlToEnd(graph(), common(), true_throw);
schedule.AddNode(fblock, if_false);
schedule.AddThrow(fblock, false_throw);
NodeProperties::MergeControlToEnd(graph(), common(), false_throw);
ASSERT_THAT(end(), IsEnd(IsThrow(), IsThrow()));
ASSERT_THAT(end()->op()->ControlInputCount(), 2);
// Run the state effect linearizer, maintaining the schedule.
LinearizeEffectControl(
jsgraph(), &schedule, zone(), source_positions(), node_origins(),
MaskArrayIndexEnable::kDoNotMaskArrayIndex, MaintainSchedule::kMaintain);
ASSERT_THAT(end(), IsEnd(IsThrow()));
}
TEST_F(EffectControlLinearizerTest, UnreachableThenDiamond) {
Schedule schedule(zone());
// Create the graph.
Node* unreachable = graph()->NewNode(common()->Unreachable(),
graph()->start(), graph()->start());
Node* branch =
graph()->NewNode(common()->Branch(), Int32Constant(0), graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* throw_node = graph()->NewNode(common()->Throw(), unreachable, if_false);
graph()->SetEnd(graph()->NewNode(common()->End(0)));
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* tblock = AddBlockToSchedule(&schedule);
BasicBlock* fblock = AddBlockToSchedule(&schedule);
BasicBlock* mblock = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddNode(start, unreachable);
schedule.AddBranch(start, branch, tblock, fblock);
schedule.AddNode(tblock, if_true);
schedule.AddGoto(tblock, mblock);
schedule.AddNode(fblock, if_false);
schedule.AddGoto(fblock, mblock);
schedule.AddNode(mblock, merge);
schedule.AddThrow(mblock, throw_node);
NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
ASSERT_THAT(end(), IsEnd(IsThrow()));
ASSERT_THAT(end()->op()->ControlInputCount(), 1);
// Run the state effect linearizer, maintaining the schedule.
LinearizeEffectControl(
jsgraph(), &schedule, zone(), source_positions(), node_origins(),
MaskArrayIndexEnable::kDoNotMaskArrayIndex, MaintainSchedule::kMaintain);
ASSERT_THAT(end(), IsEnd(IsThrow()));
}
TEST_F(EffectControlLinearizerTest, UnreachableThenLoop) {
Schedule schedule(zone());
// Create the graph.
Node* unreachable = graph()->NewNode(common()->Unreachable(),
graph()->start(), graph()->start());
Node* loop = graph()->NewNode(common()->Loop(1), graph()->start());
Node* cond = Int32Constant(0);
Node* branch = graph()->NewNode(common()->Branch(), cond, loop);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
loop->AppendInput(zone(), if_false);
NodeProperties::ChangeOp(loop, common()->Loop(2));
Node* throw_node = graph()->NewNode(common()->Throw(), unreachable, if_false);
graph()->SetEnd(graph()->NewNode(common()->End(0)));
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* lblock = AddBlockToSchedule(&schedule);
BasicBlock* fblock = AddBlockToSchedule(&schedule);
BasicBlock* tblock = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddNode(start, unreachable);
schedule.AddGoto(start, lblock);
schedule.AddNode(lblock, loop);
schedule.AddNode(lblock, cond);
schedule.AddBranch(lblock, branch, tblock, fblock);
schedule.AddNode(fblock, if_false);
schedule.AddGoto(fblock, lblock);
schedule.AddNode(tblock, if_true);
schedule.AddThrow(tblock, throw_node);
NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
ASSERT_THAT(end(), IsEnd(IsThrow()));
ASSERT_THAT(end()->op()->ControlInputCount(), 1);
// Run the state effect linearizer, maintaining the schedule.
LinearizeEffectControl(
jsgraph(), &schedule, zone(), source_positions(), node_origins(),
MaskArrayIndexEnable::kDoNotMaskArrayIndex, MaintainSchedule::kMaintain);
ASSERT_THAT(end(), IsEnd(IsThrow()));
}
TEST_F(EffectControlLinearizerTest, UnreachableInChangedBlockThenBranch) {
Schedule schedule(zone());
// Create the graph.
Node* truncate = graph()->NewNode(simplified()->TruncateTaggedToWord32(),
NumberConstant(1.1));
Node* unreachable = graph()->NewNode(common()->Unreachable(),
graph()->start(), graph()->start());
Node* branch =
graph()->NewNode(common()->Branch(), Int32Constant(0), graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* true_throw = graph()->NewNode(common()->Throw(), unreachable, if_true);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* false_throw =
graph()->NewNode(common()->Throw(), unreachable, if_false);
graph()->SetEnd(graph()->NewNode(common()->End(0)));
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* tblock = AddBlockToSchedule(&schedule);
BasicBlock* fblock = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddNode(start, truncate);
schedule.AddNode(start, unreachable);
schedule.AddBranch(start, branch, tblock, fblock);
schedule.AddNode(tblock, if_true);
schedule.AddThrow(tblock, true_throw);
NodeProperties::MergeControlToEnd(graph(), common(), true_throw);
schedule.AddNode(fblock, if_false);
schedule.AddThrow(fblock, false_throw);
NodeProperties::MergeControlToEnd(graph(), common(), false_throw);
ASSERT_THAT(end(), IsEnd(IsThrow(), IsThrow()));
ASSERT_THAT(end()->op()->ControlInputCount(), 2);
// Run the state effect linearizer, maintaining the schedule.
LinearizeEffectControl(
jsgraph(), &schedule, zone(), source_positions(), node_origins(),
MaskArrayIndexEnable::kDoNotMaskArrayIndex, MaintainSchedule::kMaintain);
ASSERT_THAT(end(), IsEnd(IsThrow()));
}
} // namespace compiler
} // namespace internal
} // namespace v8