fe552636be
This is preparation for using TF to create builtins that handle variable number of arguments and have to remove these arguments dynamically from the stack upon return. The gist of the changes: - Added a second argument to the Return node which specifies the number of stack slots to pop upon return in addition to those specified by the Linkage of the compiled function. - Removed Tail -> Non-Tail fallback in the instruction selector. Since TF now should handles all tail-call cases except where the return value type differs, this fallback was not really useful and in fact caused unexpected behavior with variable sized argument popping, since it wasn't possible to materialize a Return node with the right pop count from the TailCall without additional context. - Modified existing Return generation to pass a constant zero as the additional pop argument since the variable pop functionality LOG=N Review-Url: https://codereview.chromium.org/2446543002 Cr-Commit-Position: refs/heads/master@{#40699}
470 lines
17 KiB
C++
470 lines
17 KiB
C++
// Copyright 2014 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/common-operator.h"
|
|
#include "src/compiler/common-operator-reducer.h"
|
|
#include "src/compiler/machine-operator.h"
|
|
#include "src/compiler/operator.h"
|
|
#include "src/compiler/simplified-operator.h"
|
|
#include "src/machine-type.h"
|
|
#include "test/unittests/compiler/graph-reducer-unittest.h"
|
|
#include "test/unittests/compiler/graph-unittest.h"
|
|
#include "test/unittests/compiler/node-test-utils.h"
|
|
|
|
using testing::StrictMock;
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace compiler {
|
|
|
|
class CommonOperatorReducerTest : public GraphTest {
|
|
public:
|
|
explicit CommonOperatorReducerTest(int num_parameters = 1)
|
|
: GraphTest(num_parameters), machine_(zone()), simplified_(zone()) {}
|
|
~CommonOperatorReducerTest() override {}
|
|
|
|
protected:
|
|
Reduction Reduce(
|
|
AdvancedReducer::Editor* editor, Node* node,
|
|
MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags) {
|
|
MachineOperatorBuilder machine(zone(), MachineType::PointerRepresentation(),
|
|
flags);
|
|
CommonOperatorReducer reducer(editor, graph(), common(), &machine);
|
|
return reducer.Reduce(node);
|
|
}
|
|
|
|
Reduction Reduce(Node* node, MachineOperatorBuilder::Flags flags =
|
|
MachineOperatorBuilder::kNoFlags) {
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
return Reduce(&editor, node, flags);
|
|
}
|
|
|
|
MachineOperatorBuilder* machine() { return &machine_; }
|
|
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
|
|
|
|
private:
|
|
MachineOperatorBuilder machine_;
|
|
SimplifiedOperatorBuilder simplified_;
|
|
};
|
|
|
|
|
|
namespace {
|
|
|
|
const BranchHint kBranchHints[] = {BranchHint::kNone, BranchHint::kFalse,
|
|
BranchHint::kTrue};
|
|
|
|
|
|
const MachineRepresentation kMachineRepresentations[] = {
|
|
MachineRepresentation::kBit, MachineRepresentation::kWord8,
|
|
MachineRepresentation::kWord16, MachineRepresentation::kWord32,
|
|
MachineRepresentation::kWord64, MachineRepresentation::kFloat32,
|
|
MachineRepresentation::kFloat64, MachineRepresentation::kTagged};
|
|
|
|
|
|
const Operator kOp0(0, Operator::kNoProperties, "Op0", 0, 0, 0, 1, 1, 0);
|
|
|
|
} // namespace
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Branch
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, BranchWithInt32ZeroConstant) {
|
|
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
|
|
Node* const control = graph()->start();
|
|
Node* const branch =
|
|
graph()->NewNode(common()->Branch(hint), Int32Constant(0), control);
|
|
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Replace(if_true, IsDead()));
|
|
EXPECT_CALL(editor, Replace(if_false, control));
|
|
Reduction const r = Reduce(&editor, branch);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_THAT(r.replacement(), IsDead());
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, BranchWithInt32OneConstant) {
|
|
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
|
|
Node* const control = graph()->start();
|
|
Node* const branch =
|
|
graph()->NewNode(common()->Branch(hint), Int32Constant(1), control);
|
|
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Replace(if_true, control));
|
|
EXPECT_CALL(editor, Replace(if_false, IsDead()));
|
|
Reduction const r = Reduce(&editor, branch);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_THAT(r.replacement(), IsDead());
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, BranchWithFalseConstant) {
|
|
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
|
|
Node* const control = graph()->start();
|
|
Node* const branch =
|
|
graph()->NewNode(common()->Branch(hint), FalseConstant(), control);
|
|
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Replace(if_true, IsDead()));
|
|
EXPECT_CALL(editor, Replace(if_false, control));
|
|
Reduction const r = Reduce(&editor, branch);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_THAT(r.replacement(), IsDead());
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, BranchWithTrueConstant) {
|
|
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
|
|
Node* const control = graph()->start();
|
|
Node* const branch =
|
|
graph()->NewNode(common()->Branch(hint), TrueConstant(), control);
|
|
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Replace(if_true, control));
|
|
EXPECT_CALL(editor, Replace(if_false, IsDead()));
|
|
Reduction const r = Reduce(&editor, branch);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_THAT(r.replacement(), IsDead());
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, BranchWithBooleanNot) {
|
|
Node* const value = Parameter(0);
|
|
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
|
|
Node* const control = graph()->start();
|
|
Node* const branch = graph()->NewNode(
|
|
common()->Branch(hint),
|
|
graph()->NewNode(simplified()->BooleanNot(), value), control);
|
|
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
Reduction const r = Reduce(branch);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(branch, r.replacement());
|
|
EXPECT_THAT(branch, IsBranch(value, control));
|
|
EXPECT_THAT(if_false, IsIfTrue(branch));
|
|
EXPECT_THAT(if_true, IsIfFalse(branch));
|
|
EXPECT_EQ(NegateBranchHint(hint), BranchHintOf(branch->op()));
|
|
}
|
|
}
|
|
|
|
TEST_F(CommonOperatorReducerTest, BranchWithSelect) {
|
|
Node* const value = Parameter(0);
|
|
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
|
|
Node* const control = graph()->start();
|
|
Node* const branch = graph()->NewNode(
|
|
common()->Branch(hint),
|
|
graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
|
|
value, FalseConstant(), TrueConstant()),
|
|
control);
|
|
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
Reduction const r = Reduce(branch);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(branch, r.replacement());
|
|
EXPECT_THAT(branch, IsBranch(value, control));
|
|
EXPECT_THAT(if_false, IsIfTrue(branch));
|
|
EXPECT_THAT(if_true, IsIfFalse(branch));
|
|
EXPECT_EQ(NegateBranchHint(hint), BranchHintOf(branch->op()));
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Merge
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, MergeOfUnusedDiamond0) {
|
|
Node* const value = Parameter(0);
|
|
Node* const control = graph()->start();
|
|
Node* const branch = graph()->NewNode(common()->Branch(), value, control);
|
|
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
Reduction const r =
|
|
Reduce(graph()->NewNode(common()->Merge(2), if_true, if_false));
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(control, r.replacement());
|
|
EXPECT_THAT(branch, IsDead());
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, MergeOfUnusedDiamond1) {
|
|
Node* const value = Parameter(0);
|
|
Node* const control = graph()->start();
|
|
Node* const branch = graph()->NewNode(common()->Branch(), value, control);
|
|
Node* const if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* const if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
Reduction const r =
|
|
Reduce(graph()->NewNode(common()->Merge(2), if_false, if_true));
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(control, r.replacement());
|
|
EXPECT_THAT(branch, IsDead());
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// EffectPhi
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, EffectPhiWithMerge) {
|
|
const int kMaxInputs = 64;
|
|
Node* inputs[kMaxInputs];
|
|
Node* const input = graph()->NewNode(&kOp0);
|
|
TRACED_FORRANGE(int, input_count, 2, kMaxInputs - 1) {
|
|
int const value_input_count = input_count - 1;
|
|
for (int i = 0; i < value_input_count; ++i) {
|
|
inputs[i] = graph()->start();
|
|
}
|
|
Node* const merge = graph()->NewNode(common()->Merge(value_input_count),
|
|
value_input_count, inputs);
|
|
for (int i = 0; i < value_input_count; ++i) {
|
|
inputs[i] = input;
|
|
}
|
|
inputs[value_input_count] = merge;
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Revisit(merge));
|
|
Reduction r =
|
|
Reduce(&editor, graph()->NewNode(common()->EffectPhi(value_input_count),
|
|
input_count, inputs));
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(input, r.replacement());
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, EffectPhiWithLoop) {
|
|
Node* const e0 = graph()->NewNode(&kOp0);
|
|
Node* const loop =
|
|
graph()->NewNode(common()->Loop(2), graph()->start(), graph()->start());
|
|
loop->ReplaceInput(1, loop);
|
|
Node* const ephi = graph()->NewNode(common()->EffectPhi(2), e0, e0, loop);
|
|
ephi->ReplaceInput(1, ephi);
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Revisit(loop));
|
|
Reduction const r = Reduce(&editor, ephi);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(e0, r.replacement());
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Phi
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, PhiWithMerge) {
|
|
const int kMaxInputs = 64;
|
|
Node* inputs[kMaxInputs];
|
|
Node* const input = graph()->NewNode(&kOp0);
|
|
TRACED_FORRANGE(int, input_count, 2, kMaxInputs - 1) {
|
|
int const value_input_count = input_count - 1;
|
|
TRACED_FOREACH(MachineRepresentation, rep, kMachineRepresentations) {
|
|
for (int i = 0; i < value_input_count; ++i) {
|
|
inputs[i] = graph()->start();
|
|
}
|
|
Node* const merge = graph()->NewNode(common()->Merge(value_input_count),
|
|
value_input_count, inputs);
|
|
for (int i = 0; i < value_input_count; ++i) {
|
|
inputs[i] = input;
|
|
}
|
|
inputs[value_input_count] = merge;
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Revisit(merge));
|
|
Reduction r = Reduce(
|
|
&editor, graph()->NewNode(common()->Phi(rep, value_input_count),
|
|
input_count, inputs));
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(input, r.replacement());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, PhiWithLoop) {
|
|
Node* const p0 = Parameter(0);
|
|
Node* const loop =
|
|
graph()->NewNode(common()->Loop(2), graph()->start(), graph()->start());
|
|
loop->ReplaceInput(1, loop);
|
|
Node* const phi = graph()->NewNode(
|
|
common()->Phi(MachineRepresentation::kTagged, 2), p0, p0, loop);
|
|
phi->ReplaceInput(1, phi);
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Revisit(loop));
|
|
Reduction const r = Reduce(&editor, phi);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(p0, r.replacement());
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, PhiToFloat32Abs) {
|
|
Node* p0 = Parameter(0);
|
|
Node* c0 = Float32Constant(0.0);
|
|
Node* check = graph()->NewNode(machine()->Float32LessThan(), c0, p0);
|
|
Node* branch = graph()->NewNode(common()->Branch(), check, graph()->start());
|
|
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* vtrue = p0;
|
|
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
Node* vfalse = graph()->NewNode(machine()->Float32Sub(), c0, p0);
|
|
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
|
|
Node* phi = graph()->NewNode(
|
|
common()->Phi(MachineRepresentation::kFloat32, 2), vtrue, vfalse, merge);
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Revisit(merge));
|
|
Reduction r = Reduce(&editor, phi);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_THAT(r.replacement(), IsFloat32Abs(p0));
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, PhiToFloat64Abs) {
|
|
Node* p0 = Parameter(0);
|
|
Node* c0 = Float64Constant(0.0);
|
|
Node* check = graph()->NewNode(machine()->Float64LessThan(), c0, p0);
|
|
Node* branch = graph()->NewNode(common()->Branch(), check, graph()->start());
|
|
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* vtrue = p0;
|
|
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
Node* vfalse = graph()->NewNode(machine()->Float64Sub(), c0, p0);
|
|
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
|
|
Node* phi = graph()->NewNode(
|
|
common()->Phi(MachineRepresentation::kFloat64, 2), vtrue, vfalse, merge);
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Revisit(merge));
|
|
Reduction r = Reduce(&editor, phi);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_THAT(r.replacement(), IsFloat64Abs(p0));
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Return
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, ReturnWithPhiAndEffectPhiAndMerge) {
|
|
Node* cond = Parameter(2);
|
|
Node* branch = graph()->NewNode(common()->Branch(), cond, graph()->start());
|
|
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
|
|
Node* etrue = graph()->start();
|
|
Node* vtrue = Parameter(0);
|
|
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
|
Node* efalse = graph()->start();
|
|
Node* vfalse = Parameter(1);
|
|
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
|
|
Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge);
|
|
Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
|
vtrue, vfalse, merge);
|
|
|
|
Node* zero = graph()->NewNode(common()->Int32Constant(0));
|
|
Node* ret = graph()->NewNode(common()->Return(), zero, phi, ephi, merge);
|
|
graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
|
|
StrictMock<MockAdvancedReducerEditor> editor;
|
|
EXPECT_CALL(editor, Replace(merge, IsDead()));
|
|
Reduction const r = Reduce(&editor, ret);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_THAT(r.replacement(), IsDead());
|
|
EXPECT_THAT(graph()->end(), IsEnd(ret, IsReturn(vtrue, etrue, if_true),
|
|
IsReturn(vfalse, efalse, if_false)));
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Select
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, SelectWithSameThenAndElse) {
|
|
Node* const input = graph()->NewNode(&kOp0);
|
|
TRACED_FOREACH(BranchHint, hint, kBranchHints) {
|
|
TRACED_FOREACH(MachineRepresentation, rep, kMachineRepresentations) {
|
|
Reduction r = Reduce(
|
|
graph()->NewNode(common()->Select(rep, hint), input, input, input));
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(input, r.replacement());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, SelectWithInt32ZeroConstant) {
|
|
Node* p0 = Parameter(0);
|
|
Node* p1 = Parameter(1);
|
|
Node* select =
|
|
graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
|
|
Int32Constant(0), p0, p1);
|
|
Reduction r = Reduce(select);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(p1, r.replacement());
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, SelectWithInt32OneConstant) {
|
|
Node* p0 = Parameter(0);
|
|
Node* p1 = Parameter(1);
|
|
Node* select =
|
|
graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
|
|
Int32Constant(1), p0, p1);
|
|
Reduction r = Reduce(select);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(p0, r.replacement());
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, SelectWithFalseConstant) {
|
|
Node* p0 = Parameter(0);
|
|
Node* p1 = Parameter(1);
|
|
Node* select =
|
|
graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
|
|
FalseConstant(), p0, p1);
|
|
Reduction r = Reduce(select);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(p1, r.replacement());
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, SelectWithTrueConstant) {
|
|
Node* p0 = Parameter(0);
|
|
Node* p1 = Parameter(1);
|
|
Node* select = graph()->NewNode(
|
|
common()->Select(MachineRepresentation::kTagged), TrueConstant(), p0, p1);
|
|
Reduction r = Reduce(select);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_EQ(p0, r.replacement());
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, SelectToFloat32Abs) {
|
|
Node* p0 = Parameter(0);
|
|
Node* c0 = Float32Constant(0.0);
|
|
Node* check = graph()->NewNode(machine()->Float32LessThan(), c0, p0);
|
|
Node* select =
|
|
graph()->NewNode(common()->Select(MachineRepresentation::kFloat32), check,
|
|
p0, graph()->NewNode(machine()->Float32Sub(), c0, p0));
|
|
Reduction r = Reduce(select);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_THAT(r.replacement(), IsFloat32Abs(p0));
|
|
}
|
|
|
|
|
|
TEST_F(CommonOperatorReducerTest, SelectToFloat64Abs) {
|
|
Node* p0 = Parameter(0);
|
|
Node* c0 = Float64Constant(0.0);
|
|
Node* check = graph()->NewNode(machine()->Float64LessThan(), c0, p0);
|
|
Node* select =
|
|
graph()->NewNode(common()->Select(MachineRepresentation::kFloat64), check,
|
|
p0, graph()->NewNode(machine()->Float64Sub(), c0, p0));
|
|
Reduction r = Reduce(select);
|
|
ASSERT_TRUE(r.Changed());
|
|
EXPECT_THAT(r.replacement(), IsFloat64Abs(p0));
|
|
}
|
|
|
|
} // namespace compiler
|
|
} // namespace internal
|
|
} // namespace v8
|