v8/test/unittests/compiler/effect-control-linearizer-unittest.cc
Ross McIlroy 12aa090ee6 [Turboprop] Remove ScheduledMachineLowering and replace with inline reductions.
Instead of running a second pass of the scheduled graph after
effect control linearization to do machine lowering, integrate
the machine lowering reducers (MemoryLowering and SelectLowering)
into the graph assembler used by the effect control linearization.
This saves running through the graph and re-maintaining the schedule
for the second time, reducing overhead in Turboprop.

BUG=v8:9684

Change-Id: Ib0fed19089287c8e801a063333cb8404181411db
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2848474
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74178}
2021-04-26 10:02:40 +00:00

514 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_ = zone()->New<SourcePositionTable>(graph());
node_origins_ = zone()->New<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(), PoisoningMitigationLevel::kDontPoison,
broker());
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(), PoisoningMitigationLevel::kDontPoison,
broker());
// 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(), PoisoningMitigationLevel::kDontPoison,
broker());
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(), PoisoningMitigationLevel::kDontPoison,
broker());
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 and machine lowering, maintaining the
// schedule.
LowerToMachineSchedule(jsgraph(), &schedule, zone(), source_positions(),
node_origins(), PoisoningMitigationLevel::kDontPoison,
broker());
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 and machine lowering, maintaining the
// schedule.
LowerToMachineSchedule(jsgraph(), &schedule, zone(), source_positions(),
node_origins(), PoisoningMitigationLevel::kDontPoison,
broker());
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 and machine lowering, maintaining the
// schedule.
LowerToMachineSchedule(jsgraph(), &schedule, zone(), source_positions(),
node_origins(), PoisoningMitigationLevel::kDontPoison,
broker());
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 and machine lowering, maintaining the
// schedule.
LowerToMachineSchedule(jsgraph(), &schedule, zone(), source_positions(),
node_origins(), PoisoningMitigationLevel::kDontPoison,
broker());
ASSERT_THAT(end(), IsEnd(IsThrow()));
}
} // namespace compiler
} // namespace internal
} // namespace v8