29afe1e585
We implement two optimizations for trap conditionals for patterns that come up in wasm-gc. In case of a Merge followed by a trap, where the path conditions of all branches of the Merge contain the trap condition, we lift the trap into the branches of the Merge. In case of a Branch whose IfTrue branch is followed by a TrapIf with the same condition, we replace it with the trap followed by the IfFalse branch. Symmetrically for IfFalse and TrapUnless. Bug: v8:7748 Change-Id: I43040aebe60eab7b2230fc3130e3b8250e8b2f45 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3190109 Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#77181}
211 lines
8.6 KiB
C++
211 lines
8.6 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(), broker(),
|
|
jsgraph.Dead());
|
|
BranchElimination branch_condition_elimination(&graph_reducer, &jsgraph,
|
|
zone(), nullptr);
|
|
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; }
|
|
// second branch's condition should be replaced with a phi.
|
|
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);
|
|
// Second branch use the same condition.
|
|
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();
|
|
|
|
// The branch condition for branch2 should be a phi with constants.
|
|
EXPECT_THAT(branch2,
|
|
IsBranch(IsPhi(MachineRepresentation::kWord32, IsInt32Constant(1),
|
|
IsInt32Constant(0), merge1),
|
|
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
|