// 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(graph()); node_origins_ = zone()->New(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(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(), 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(), 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(), 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(), broker()); Capture 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(), 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(), 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(), 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(), broker()); ASSERT_THAT(end(), IsEnd(IsThrow())); } } // namespace compiler } // namespace internal } // namespace v8