Commit 29afe1e5 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[turbofan] Optimize traps after Merge/IfTrue/IfFalse

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/+/3190109Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77181}
parent 9227a8da
......@@ -5,6 +5,7 @@
#include "src/compiler/branch-elimination.h"
#include "src/base/small-vector.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/simplified-operator.h"
......@@ -14,12 +15,15 @@ namespace internal {
namespace compiler {
BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
Zone* zone, Phase phase)
Zone* zone,
SourcePositionTable* source_positions,
Phase phase)
: AdvancedReducer(editor),
jsgraph_(js_graph),
node_conditions_(js_graph->graph()->NodeCount(), zone),
reduced_(js_graph->graph()->NodeCount(), zone),
zone_(zone),
source_positions_(source_positions),
dead_(js_graph->Dead()),
phase_(phase) {}
......@@ -158,6 +162,71 @@ Reduction BranchElimination::ReduceBranch(Node* node) {
return TakeConditionsFromFirstControl(node);
}
// Simplify a trap following a merge.
// Assuming condition is in control1's path conditions, and !condition is in
// control2's path condtions, the following transformation takes place:
//
// control1 control2 condition effect1
// \ / \ / |
// Merge X | control1
// | / \ | /
// effect1 effect2 | | TrapIf control2
// \ | /| ==> | \ /
// EffectPhi | | effect2 Merge
// | / | | /
// condition | / \ | /
// \ | / EffectPhi
// TrapIf
// TODO(manoskouk): We require that the trap's effect input is the Merge's
// EffectPhi, so we can ensure that the new traps' effect inputs are not
// dominated by the Merge. Can we relax this?
bool BranchElimination::TryPullTrapIntoMerge(Node* node) {
DCHECK(node->opcode() == IrOpcode::kTrapIf ||
node->opcode() == IrOpcode::kTrapUnless);
Node* merge = NodeProperties::GetControlInput(node);
DCHECK_EQ(merge->opcode(), IrOpcode::kMerge);
Node* condition = NodeProperties::GetValueInput(node, 0);
Node* effect_input = NodeProperties::GetEffectInput(node);
if (!(effect_input->opcode() == IrOpcode::kEffectPhi &&
NodeProperties::GetControlInput(effect_input) == merge)) {
return false;
}
bool trapping_condition = node->opcode() == IrOpcode::kTrapIf;
base::SmallVector<Node*, 8> new_merge_inputs;
for (Edge edge : merge->input_edges()) {
Node* input = edge.to();
ControlPathConditions from_input = node_conditions_.Get(input);
Node* previous_branch;
bool condition_value;
if (!from_input.LookupCondition(condition, &previous_branch,
&condition_value)) {
return false;
}
if (condition_value == trapping_condition) {
Node* inputs[] = {
condition, NodeProperties::GetEffectInput(effect_input, edge.index()),
input};
Node* trap_clone = graph()->NewNode(node->op(), 3, inputs);
if (source_positions_) {
source_positions_->SetSourcePosition(
trap_clone, source_positions_->GetSourcePosition(node));
}
new_merge_inputs.emplace_back(trap_clone);
} else {
new_merge_inputs.emplace_back(input);
}
}
for (int i = 0; i < merge->InputCount(); i++) {
merge->ReplaceInput(i, new_merge_inputs[i]);
}
ReplaceWithValue(node, dead(), dead(), merge);
Revisit(merge);
return true;
}
Reduction BranchElimination::ReduceTrapConditional(Node* node) {
DCHECK(node->opcode() == IrOpcode::kTrapIf ||
node->opcode() == IrOpcode::kTrapUnless);
......@@ -167,17 +236,55 @@ Reduction BranchElimination::ReduceTrapConditional(Node* node) {
// If we do not know anything about the predecessor, do not propagate just
// yet because we will have to recompute anyway once we compute the
// predecessor.
if (!reduced_.Get(control_input)) {
return NoChange();
if (!reduced_.Get(control_input)) return NoChange();
// If the trap comes directly after a merge, pull it into the merge. This will
// unlock other optimizations later.
if (control_input->opcode() == IrOpcode::kMerge &&
TryPullTrapIntoMerge(node)) {
return Replace(dead());
}
ControlPathConditions from_input = node_conditions_.Get(control_input);
Node* branch;
Node* previous_branch;
bool condition_value;
if (from_input.LookupCondition(condition, &branch, &condition_value)) {
if (from_input.LookupCondition(condition, &previous_branch,
&condition_value)) {
if (condition_value == trapping_condition) {
// This will always trap. Mark its outputs as dead and connect it to
// graph()->end().
// Special case: Trap directly inside a branch. Replace the branch with
// the trap.
// condition control condition control
// | \ / \ /
// | Branch TrapIf
// | / \ ==> |
// | IfTrue IfFalse <subgraph2>
// | / |
// TrapIf <subraph2> Dead
// | |
// <subgraph1> <subgraph1>
// (and symmetrically for TrapUnless.)
if ((control_input->opcode() == IrOpcode::kIfTrue ||
control_input->opcode() == IrOpcode::kIfFalse)) {
Node* branch = NodeProperties::GetControlInput(control_input);
DCHECK_EQ(branch->opcode(), IrOpcode::kBranch);
if (condition == NodeProperties::GetValueInput(branch, 0)) {
Node* other_if_branch = nullptr;
for (Node* use : branch->uses()) {
if (use != control_input) other_if_branch = use;
}
DCHECK_NOT_NULL(other_if_branch);
node->ReplaceInput(NodeProperties::FirstControlIndex(node),
NodeProperties::GetControlInput(branch));
ReplaceWithValue(node, dead(), dead(), dead());
ReplaceWithValue(other_if_branch, node, node, node);
return Changed(node);
}
}
// General case: This will always trap. Mark its outputs as dead and
// connect it to graph()->end().
ReplaceWithValue(node, dead(), dead(), dead());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = graph()->NewNode(common()->Throw(), effect, node);
......
......@@ -19,6 +19,7 @@ namespace compiler {
// Forward declarations.
class CommonOperatorBuilder;
class JSGraph;
class SourcePositionTable;
class V8_EXPORT_PRIVATE BranchElimination final
: public NON_EXPORTED_BASE(AdvancedReducer) {
......@@ -28,7 +29,7 @@ class V8_EXPORT_PRIVATE BranchElimination final
kLATE,
};
BranchElimination(Editor* editor, JSGraph* js_graph, Zone* zone,
Phase phase = kLATE);
SourcePositionTable* sourse_positions, Phase phase = kLATE);
~BranchElimination() final;
const char* reducer_name() const override { return "BranchElimination"; }
......@@ -108,6 +109,7 @@ class V8_EXPORT_PRIVATE BranchElimination final
Reduction ReduceStart(Node* node);
Reduction ReduceOtherControl(Node* node);
void SimplifyBranchCondition(Node* branch);
bool TryPullTrapIntoMerge(Node* node);
Reduction TakeConditionsFromFirstControl(Node* node);
Reduction UpdateConditions(Node* node, ControlPathConditions conditions);
......@@ -131,6 +133,7 @@ class V8_EXPORT_PRIVATE BranchElimination final
node_conditions_;
NodeAuxData<bool> reduced_;
Zone* zone_;
SourcePositionTable* source_positions_;
Node* dead_;
Phase phase_;
};
......
......@@ -1853,9 +1853,9 @@ struct LoadEliminationPhase {
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
BranchElimination branch_condition_elimination(&graph_reducer,
data->jsgraph(), temp_zone,
BranchElimination::kEARLY);
BranchElimination branch_condition_elimination(
&graph_reducer, data->jsgraph(), temp_zone, data->source_positions(),
BranchElimination::kEARLY);
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common(), temp_zone);
RedundancyElimination redundancy_elimination(&graph_reducer, temp_zone);
......@@ -1922,8 +1922,8 @@ struct LateOptimizationPhase {
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
BranchElimination branch_condition_elimination(&graph_reducer,
data->jsgraph(), temp_zone);
BranchElimination branch_condition_elimination(
&graph_reducer, data->jsgraph(), temp_zone, data->source_positions());
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common(), temp_zone);
ValueNumberingReducer value_numbering(temp_zone, data->graph()->zone());
......@@ -2051,7 +2051,7 @@ struct WasmOptimizationPhase {
data->machine(), temp_zone);
ValueNumberingReducer value_numbering(temp_zone, data->graph()->zone());
BranchElimination branch_condition_elimination(
&graph_reducer, data->jsgraph(), temp_zone);
&graph_reducer, data->jsgraph(), temp_zone, data->source_positions());
AddReducer(data, &graph_reducer, &machine_reducer);
AddReducer(data, &graph_reducer, &dead_code_elimination);
AddReducer(data, &graph_reducer, &common_reducer);
......@@ -2106,7 +2106,7 @@ struct CsaEarlyOptimizationPhase {
data->machine(), temp_zone);
ValueNumberingReducer value_numbering(temp_zone, data->graph()->zone());
BranchElimination branch_condition_elimination(
&graph_reducer, data->jsgraph(), temp_zone);
&graph_reducer, data->jsgraph(), temp_zone, data->source_positions());
AddReducer(data, &graph_reducer, &machine_reducer);
AddReducer(data, &graph_reducer, &dead_code_elimination);
AddReducer(data, &graph_reducer, &common_reducer);
......@@ -2124,8 +2124,8 @@ struct CsaOptimizationPhase {
GraphReducer graph_reducer(
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
data->jsgraph()->Dead(), data->observe_node_manager());
BranchElimination branch_condition_elimination(&graph_reducer,
data->jsgraph(), temp_zone);
BranchElimination branch_condition_elimination(
&graph_reducer, data->jsgraph(), temp_zone, data->source_positions());
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
data->common(), temp_zone);
MachineOperatorReducer machine_reducer(&graph_reducer, data->jsgraph(),
......
......@@ -31,7 +31,7 @@ class BranchEliminationTest : public GraphTest {
GraphReducer graph_reducer(zone(), graph(), tick_counter(), broker(),
jsgraph.Dead());
BranchElimination branch_condition_elimination(&graph_reducer, &jsgraph,
zone());
zone(), nullptr);
graph_reducer.AddReducer(&branch_condition_elimination);
graph_reducer.ReduceGraph();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment