v8/test/unittests/compiler/branch-elimination-unittest.cc
Tobias Tebbi 4f48d04f97 [turbofan] introduce a deterministic tick measurement and assert optimization doesn't take too long
This adds a simple counter to Turbofan that's incremented throughout the compilation, hopefully
frequently enough so we can use it to detect divergence and performance bugs.
In addition, we assert that this counter never gets too high. That's the equivalent of a simple
timeout, just more deterministic. The limitations on Turbofan input size should guarantee that
we never exceed this limit. Since we probably do exceed it rarely, this check is only a DCHECK and
intended to detect performance and divergence issues, but not supposed to be performed in release
builds.

In addition, this CL adds UMA stats to observe the real world distribution of the tick measurement.

Bug: v8:9444

Change-Id: I182dac6ecac64715e3f5885ff5c7c17549351cd0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1695475
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Reviewed-by: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62754}
2019-07-17 07:00:00 +00:00

215 lines
8.4 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/branch-elimination.h"
#include "src/codegen/tick-counter.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-properties.h"
#include "test/unittests/compiler/compiler-test-utils.h"
#include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h"
#include "testing/gmock-support.h"
namespace v8 {
namespace internal {
namespace compiler {
class BranchEliminationTest : public GraphTest {
public:
BranchEliminationTest()
: machine_(zone(), MachineType::PointerRepresentation(),
MachineOperatorBuilder::kNoFlags) {}
MachineOperatorBuilder* machine() { return &machine_; }
void Reduce() {
JSOperatorBuilder javascript(zone());
JSGraph jsgraph(isolate(), graph(), common(), &javascript, nullptr,
machine());
GraphReducer graph_reducer(zone(), graph(), tick_counter(), jsgraph.Dead());
BranchElimination branch_condition_elimination(&graph_reducer, &jsgraph,
zone());
graph_reducer.AddReducer(&branch_condition_elimination);
graph_reducer.ReduceGraph();
}
private:
MachineOperatorBuilder machine_;
};
TEST_F(BranchEliminationTest, NestedBranchSameTrue) {
// { return (x ? (x ? 1 : 2) : 3; }
// should be reduced to
// { return (x ? 1 : 3; }
Node* condition = Parameter(0);
Node* outer_branch =
graph()->NewNode(common()->Branch(), condition, graph()->start());
Node* outer_if_true = graph()->NewNode(common()->IfTrue(), outer_branch);
Node* inner_branch =
graph()->NewNode(common()->Branch(), condition, outer_if_true);
Node* inner_if_true = graph()->NewNode(common()->IfTrue(), inner_branch);
Node* inner_if_false = graph()->NewNode(common()->IfFalse(), inner_branch);
Node* inner_merge =
graph()->NewNode(common()->Merge(2), inner_if_true, inner_if_false);
Node* inner_phi =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
Int32Constant(1), Int32Constant(2), inner_merge);
Node* outer_if_false = graph()->NewNode(common()->IfFalse(), outer_branch);
Node* outer_merge =
graph()->NewNode(common()->Merge(2), inner_merge, outer_if_false);
Node* outer_phi =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
inner_phi, Int32Constant(3), outer_merge);
Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret = graph()->NewNode(common()->Return(), zero, outer_phi,
graph()->start(), outer_merge);
graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
Reduce();
// Outer branch should not be rewritten, the inner branch should be discarded.
EXPECT_THAT(outer_branch, IsBranch(condition, graph()->start()));
EXPECT_THAT(inner_phi,
IsPhi(MachineRepresentation::kWord32, IsInt32Constant(1),
IsInt32Constant(2), IsMerge(outer_if_true, IsDead())));
}
TEST_F(BranchEliminationTest, NestedBranchSameFalse) {
// { return (x ? 1 : (x ? 2 : 3); }
// should be reduced to
// { return (x ? 1 : 3; }
Node* condition = Parameter(0);
Node* outer_branch =
graph()->NewNode(common()->Branch(), condition, graph()->start());
Node* outer_if_true = graph()->NewNode(common()->IfTrue(), outer_branch);
Node* outer_if_false = graph()->NewNode(common()->IfFalse(), outer_branch);
Node* inner_branch =
graph()->NewNode(common()->Branch(), condition, outer_if_false);
Node* inner_if_true = graph()->NewNode(common()->IfTrue(), inner_branch);
Node* inner_if_false = graph()->NewNode(common()->IfFalse(), inner_branch);
Node* inner_merge =
graph()->NewNode(common()->Merge(2), inner_if_true, inner_if_false);
Node* inner_phi =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
Int32Constant(2), Int32Constant(3), inner_merge);
Node* outer_merge =
graph()->NewNode(common()->Merge(2), outer_if_true, inner_merge);
Node* outer_phi =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
Int32Constant(1), inner_phi, outer_merge);
Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret = graph()->NewNode(common()->Return(), zero, outer_phi,
graph()->start(), outer_merge);
graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
Reduce();
// Outer branch should not be rewritten, the inner branch should be discarded.
EXPECT_THAT(outer_branch, IsBranch(condition, graph()->start()));
EXPECT_THAT(inner_phi,
IsPhi(MachineRepresentation::kWord32, IsInt32Constant(2),
IsInt32Constant(3), IsMerge(IsDead(), outer_if_false)));
}
TEST_F(BranchEliminationTest, BranchAfterDiamond) {
// { var y = x ? 1 : 2; return y + x ? 3 : 4; }
// should not be reduced.
Node* condition = Parameter(0);
Node* branch1 =
graph()->NewNode(common()->Branch(), condition, 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);
Node* phi1 =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
Int32Constant(1), Int32Constant(2), merge1);
Node* branch2 = graph()->NewNode(common()->Branch(), condition, merge1);
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* phi2 =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
Int32Constant(3), Int32Constant(4), merge1);
Node* add = graph()->NewNode(machine()->Int32Add(), phi1, phi2);
Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret =
graph()->NewNode(common()->Return(), zero, add, graph()->start(), merge2);
graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
Reduce();
// Outer branch should not be rewritten, the inner branch condition should
// be true.
EXPECT_THAT(branch1, IsBranch(condition, graph()->start()));
EXPECT_THAT(branch2, IsBranch(condition, merge1));
}
TEST_F(BranchEliminationTest, BranchInsideLoopSame) {
// if (x) while (x) { return 2; } else { return 1; }
// should be rewritten to
// if (x) while (true) { return 2; } else { return 1; }
Node* condition = Parameter(0);
Node* outer_branch =
graph()->NewNode(common()->Branch(), condition, graph()->start());
Node* outer_if_true = graph()->NewNode(common()->IfTrue(), outer_branch);
Node* loop = graph()->NewNode(common()->Loop(1), outer_if_true);
Node* effect =
graph()->NewNode(common()->EffectPhi(1), graph()->start(), loop);
Node* inner_branch = graph()->NewNode(common()->Branch(), condition, loop);
Node* inner_if_true = graph()->NewNode(common()->IfTrue(), inner_branch);
Node* zero = graph()->NewNode(common()->Int32Constant(0));
Node* ret1 = graph()->NewNode(common()->Return(), zero, Int32Constant(2),
effect, inner_if_true);
Node* inner_if_false = graph()->NewNode(common()->IfFalse(), inner_branch);
loop->AppendInput(zone(), inner_if_false);
NodeProperties::ChangeOp(loop, common()->Loop(2));
effect->InsertInput(zone(), 1, effect);
NodeProperties::ChangeOp(effect, common()->EffectPhi(2));
Node* outer_if_false = graph()->NewNode(common()->IfFalse(), outer_branch);
Node* outer_merge =
graph()->NewNode(common()->Merge(2), loop, outer_if_false);
Node* outer_ephi = graph()->NewNode(common()->EffectPhi(2), effect,
graph()->start(), outer_merge);
Node* ret2 = graph()->NewNode(common()->Return(), zero, Int32Constant(1),
outer_ephi, outer_merge);
Node* terminate = graph()->NewNode(common()->Terminate(), effect, loop);
graph()->SetEnd(graph()->NewNode(common()->End(3), ret1, ret2, terminate));
Reduce();
// Outer branch should not be rewritten, the inner branch should be discarded.
EXPECT_THAT(outer_branch, IsBranch(condition, graph()->start()));
EXPECT_THAT(ret1, IsReturn(IsInt32Constant(2), effect, loop));
}
} // namespace compiler
} // namespace internal
} // namespace v8