v8/test/unittests/compiler/effect-control-linearizer-unittest.cc
danno 3e2085eba4 [turbofan] Add MachineType to LinkageLocation
By adding MachineType to LinkageLocation, it is possible not only to reason
about the location of a LinkageLocation on the stack, but also about it's
size. This will be useful in follow-on CLs that attempt to merge some of the
parameter passing logic of tail calls and normal (non-tail) calls.

As a nice side-effect, it is no longer necessary to separately keep a
MachineSignature in a CallDescriptor, because the MachineTypes contianed in
LinkageLocation for all of the Descriptor's parameters and return types are
sufficient. This CL therefore removes the MachineSignature from the
CallDescriptor and adjusts all the calling code accordingly, simplifying and
de-duplicating code in a bunch of places.

R=titzer@chromium.org, bmeurer@chromium.org
LOG=N

Review-Url: https://codereview.chromium.org/2124023003
Cr-Commit-Position: refs/heads/master@{#37633}
2016-07-11 10:39:34 +00:00

329 lines
11 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/js-graph.h"
#include "src/compiler/linkage.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/include/gmock/gmock.h"
namespace v8 {
namespace internal {
namespace compiler {
class EffectControlLinearizerTest : public TypedGraphTest {
public:
EffectControlLinearizerTest()
: TypedGraphTest(3),
machine_(zone()),
javascript_(zone()),
simplified_(zone()),
jsgraph_(isolate(), graph(), common(), &javascript_, &simplified_,
&machine_) {}
JSGraph* jsgraph() { return &jsgraph_; }
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
private:
MachineOperatorBuilder machine_;
JSOperatorBuilder javascript_;
SimplifiedOperatorBuilder simplified_;
JSGraph jsgraph_;
};
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* ret = graph()->NewNode(common()->Return(), 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.
EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
introducer.Run();
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* ret =
graph()->NewNode(common()->Return(), 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.
EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
introducer.Run();
// 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, FloatingDiamondsControlWiring) {
Schedule schedule(zone());
// Create the graph and schedule. Roughly (omitting effects and unimportant
// nodes):
//
// BLOCK 0:
// r1: Start
// c1: Call
// b1: Branch(const0, s1)
// |
// +-------+------+
// | |
// BLOCK 1: BLOCK 2:
// t1: IfTrue(b1) f1: IfFalse(b1)
// | |
// +-------+------+
// |
// BLOCK 3:
// m1: Merge(t1, f1)
// c2: IfSuccess(c1)
// b2: Branch(const0 , s1)
// |
// +-------+------+
// | |
// BLOCK 4: BLOCK 5:
// t2: IfTrue(b2) f2:IfFalse(b2)
// | |
// +-------+------+
// |
// BLOCK 6:
// m2: Merge(t2, f2)
// r1: Return(c1, c2)
LinkageLocation kLocationSignature[] = {
LinkageLocation::ForRegister(0, MachineType::Pointer()),
LinkageLocation::ForRegister(1, MachineType::Pointer())};
const CallDescriptor* kCallDescriptor = new (zone()) CallDescriptor(
CallDescriptor::kCallCodeObject, MachineType::AnyTagged(),
LinkageLocation::ForRegister(0, MachineType::Pointer()),
new (zone()) LocationSignature(1, 1, kLocationSignature), 0,
Operator::kNoProperties, 0, 0, CallDescriptor::kNoFlags);
Node* p0 = Parameter(0);
Node* p1 = Parameter(1);
Node* const0 = Int32Constant(0);
Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1,
graph()->start(), graph()->start());
Node* if_success = graph()->NewNode(common()->IfSuccess(), call);
// First Floating diamond.
Node* branch1 =
graph()->NewNode(common()->Branch(), const0, 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);
// Second floating diamond.
Node* branch2 =
graph()->NewNode(common()->Branch(), const0, graph()->start());
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* ret =
graph()->NewNode(common()->Return(), call, graph()->start(), if_success);
// Build the basic block structure.
BasicBlock* start = schedule.start();
schedule.rpo_order()->push_back(start);
start->set_rpo_number(0);
BasicBlock* t1block = AddBlockToSchedule(&schedule);
BasicBlock* f1block = AddBlockToSchedule(&schedule);
BasicBlock* m1block = AddBlockToSchedule(&schedule);
BasicBlock* t2block = AddBlockToSchedule(&schedule);
BasicBlock* f2block = AddBlockToSchedule(&schedule);
BasicBlock* m2block = AddBlockToSchedule(&schedule);
// Populate the basic blocks with nodes.
schedule.AddNode(start, graph()->start());
schedule.AddNode(start, p0);
schedule.AddNode(start, p1);
schedule.AddNode(start, const0);
schedule.AddNode(start, call);
schedule.AddBranch(start, branch1, t1block, f1block);
schedule.AddNode(t1block, if_true1);
schedule.AddGoto(t1block, m1block);
schedule.AddNode(f1block, if_false1);
schedule.AddGoto(f1block, m1block);
schedule.AddNode(m1block, merge1);
// The scheduler does not always put the IfSuccess node to the corresponding
// call's block, simulate that here.
schedule.AddNode(m1block, if_success);
schedule.AddBranch(m1block, branch2, t2block, f2block);
schedule.AddNode(t2block, if_true2);
schedule.AddGoto(t2block, m2block);
schedule.AddNode(f2block, if_false2);
schedule.AddGoto(f2block, m2block);
schedule.AddNode(m2block, merge2);
schedule.AddReturn(m2block, ret);
// Run the state effect introducer.
EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
introducer.Run();
// The effect input to the return should be an effect phi with the
// newly introduced effectful change operators.
ASSERT_THAT(ret, IsReturn(call, call, merge2));
ASSERT_THAT(branch2, IsBranch(const0, merge1));
ASSERT_THAT(branch1, IsBranch(const0, if_success));
ASSERT_THAT(if_success, IsIfSuccess(call));
}
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* ret = graph()->NewNode(common()->Return(), 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.
EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
introducer.Run();
ASSERT_THAT(ret, IsReturn(load, load, if_true));
EXPECT_THAT(load, IsLoadField(AccessBuilder::ForHeapNumberValue(),
heap_number, effect_phi, loop));
}
} // namespace compiler
} // namespace internal
} // namespace v8