[turbofan] Eliminate switch operations with constant input.
Where the value we are switching on is a constant, we can just look through each IfValue case and replace the switch and go straight to the appropriate case. If no case matches, expect and go to the IfDefault. For the (unrealistic) example in the linked bug, this improves performance ~1.5x. Bug: v8:7389 Change-Id: I7ffe209bda9ed22571ea106396b18e0bcf9a1e22 Reviewed-on: https://chromium-review.googlesource.com/893141 Commit-Queue: Peter Marshall <petermarshall@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/master@{#51029}
This commit is contained in:
parent
aa47fd43c4
commit
8622d899d8
@ -38,12 +38,14 @@ Decision DecideCondition(Node* const cond) {
|
||||
|
||||
CommonOperatorReducer::CommonOperatorReducer(Editor* editor, Graph* graph,
|
||||
CommonOperatorBuilder* common,
|
||||
MachineOperatorBuilder* machine)
|
||||
MachineOperatorBuilder* machine,
|
||||
Zone* temp_zone)
|
||||
: AdvancedReducer(editor),
|
||||
graph_(graph),
|
||||
common_(common),
|
||||
machine_(machine),
|
||||
dead_(graph->NewNode(common->Dead())) {
|
||||
dead_(graph->NewNode(common->Dead())),
|
||||
zone_(temp_zone) {
|
||||
NodeProperties::SetType(dead_, Type::None());
|
||||
}
|
||||
|
||||
@ -64,6 +66,8 @@ Reduction CommonOperatorReducer::Reduce(Node* node) {
|
||||
return ReduceReturn(node);
|
||||
case IrOpcode::kSelect:
|
||||
return ReduceSelect(node);
|
||||
case IrOpcode::kSwitch:
|
||||
return ReduceSwitch(node);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -414,6 +418,42 @@ Reduction CommonOperatorReducer::ReduceSelect(Node* node) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
Reduction CommonOperatorReducer::ReduceSwitch(Node* node) {
|
||||
DCHECK_EQ(IrOpcode::kSwitch, node->opcode());
|
||||
Node* const switched_value = node->InputAt(0);
|
||||
Node* const control = node->InputAt(1);
|
||||
|
||||
// Attempt to constant match the switched value against the IfValue cases. If
|
||||
// no case matches, then use the IfDefault. We don't bother marking
|
||||
// non-matching cases as dead code (same for an unused IfDefault), because the
|
||||
// Switch itself will be marked as dead code.
|
||||
Int32Matcher mswitched(switched_value);
|
||||
if (mswitched.HasValue()) {
|
||||
bool matched = false;
|
||||
|
||||
size_t const projection_count = node->op()->ControlOutputCount();
|
||||
Node** projections = zone_->NewArray<Node*>(projection_count);
|
||||
NodeProperties::CollectControlProjections(node, projections,
|
||||
projection_count);
|
||||
for (size_t i = 0; i < projection_count - 1; i++) {
|
||||
Node* if_value = projections[i];
|
||||
DCHECK_EQ(IrOpcode::kIfValue, if_value->opcode());
|
||||
int32_t value_index = OpParameter<int32_t>(if_value->op());
|
||||
if (value_index == mswitched.Value()) {
|
||||
matched = true;
|
||||
Replace(if_value, control);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
Node* if_default = projections[projection_count - 1];
|
||||
DCHECK_EQ(IrOpcode::kIfDefault, if_default->opcode());
|
||||
Replace(if_default, control);
|
||||
}
|
||||
return Replace(dead());
|
||||
}
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
Reduction CommonOperatorReducer::Change(Node* node, Operator const* op,
|
||||
Node* a) {
|
||||
|
@ -26,7 +26,7 @@ class V8_EXPORT_PRIVATE CommonOperatorReducer final
|
||||
public:
|
||||
CommonOperatorReducer(Editor* editor, Graph* graph,
|
||||
CommonOperatorBuilder* common,
|
||||
MachineOperatorBuilder* machine);
|
||||
MachineOperatorBuilder* machine, Zone* temp_zone);
|
||||
~CommonOperatorReducer() final {}
|
||||
|
||||
const char* reducer_name() const override { return "CommonOperatorReducer"; }
|
||||
@ -41,6 +41,7 @@ class V8_EXPORT_PRIVATE CommonOperatorReducer final
|
||||
Reduction ReducePhi(Node* node);
|
||||
Reduction ReduceReturn(Node* node);
|
||||
Reduction ReduceSelect(Node* node);
|
||||
Reduction ReduceSwitch(Node* node);
|
||||
|
||||
Reduction Change(Node* node, Operator const* op, Node* a);
|
||||
Reduction Change(Node* node, Operator const* op, Node* a, Node* b);
|
||||
@ -54,6 +55,7 @@ class V8_EXPORT_PRIVATE CommonOperatorReducer final
|
||||
CommonOperatorBuilder* const common_;
|
||||
MachineOperatorBuilder* const machine_;
|
||||
Node* const dead_;
|
||||
Zone* zone_;
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
|
@ -955,7 +955,8 @@ PipelineWasmCompilationJob::ExecuteJobImpl() {
|
||||
ValueNumberingReducer value_numbering(scope.zone(), data->graph()->zone());
|
||||
MachineOperatorReducer machine_reducer(data->jsgraph(), asmjs_origin_);
|
||||
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
|
||||
data->common(), data->machine());
|
||||
data->common(), data->machine(),
|
||||
scope.zone());
|
||||
AddReducer(data, &graph_reducer, &dead_code_elimination);
|
||||
AddReducer(data, &graph_reducer, &machine_reducer);
|
||||
AddReducer(data, &graph_reducer, &common_reducer);
|
||||
@ -1113,7 +1114,8 @@ struct InliningPhase {
|
||||
data->common(), temp_zone);
|
||||
CheckpointElimination checkpoint_elimination(&graph_reducer);
|
||||
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
|
||||
data->common(), data->machine());
|
||||
data->common(), data->machine(),
|
||||
temp_zone);
|
||||
JSCallReducer call_reducer(&graph_reducer, data->jsgraph(),
|
||||
data->info()->is_bailout_on_uninitialized()
|
||||
? JSCallReducer::kBailoutOnUninitialized
|
||||
@ -1217,7 +1219,8 @@ struct TypedLoweringPhase {
|
||||
SimplifiedOperatorReducer simple_reducer(&graph_reducer, data->jsgraph());
|
||||
CheckpointElimination checkpoint_elimination(&graph_reducer);
|
||||
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
|
||||
data->common(), data->machine());
|
||||
data->common(), data->machine(),
|
||||
temp_zone);
|
||||
AddReducer(data, &graph_reducer, &dead_code_elimination);
|
||||
AddReducer(data, &graph_reducer, &builtin_reducer);
|
||||
AddReducer(data, &graph_reducer, &create_lowering);
|
||||
@ -1324,7 +1327,8 @@ struct EarlyOptimizationPhase {
|
||||
ValueNumberingReducer value_numbering(temp_zone, data->graph()->zone());
|
||||
MachineOperatorReducer machine_reducer(data->jsgraph());
|
||||
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
|
||||
data->common(), data->machine());
|
||||
data->common(), data->machine(),
|
||||
temp_zone);
|
||||
AddReducer(data, &graph_reducer, &dead_code_elimination);
|
||||
AddReducer(data, &graph_reducer, &simple_reducer);
|
||||
AddReducer(data, &graph_reducer, &redundancy_elimination);
|
||||
@ -1391,7 +1395,8 @@ struct EffectControlLinearizationPhase {
|
||||
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
|
||||
data->common(), temp_zone);
|
||||
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
|
||||
data->common(), data->machine());
|
||||
data->common(), data->machine(),
|
||||
temp_zone);
|
||||
AddReducer(data, &graph_reducer, &dead_code_elimination);
|
||||
AddReducer(data, &graph_reducer, &common_reducer);
|
||||
graph_reducer.ReduceGraph();
|
||||
@ -1427,7 +1432,8 @@ struct LoadEliminationPhase {
|
||||
CheckpointElimination checkpoint_elimination(&graph_reducer);
|
||||
ValueNumberingReducer value_numbering(temp_zone, data->graph()->zone());
|
||||
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
|
||||
data->common(), data->machine());
|
||||
data->common(), data->machine(),
|
||||
temp_zone);
|
||||
AddReducer(data, &graph_reducer, &branch_condition_elimination);
|
||||
AddReducer(data, &graph_reducer, &dead_code_elimination);
|
||||
AddReducer(data, &graph_reducer, &redundancy_elimination);
|
||||
@ -1467,7 +1473,8 @@ struct LateOptimizationPhase {
|
||||
ValueNumberingReducer value_numbering(temp_zone, data->graph()->zone());
|
||||
MachineOperatorReducer machine_reducer(data->jsgraph());
|
||||
CommonOperatorReducer common_reducer(&graph_reducer, data->graph(),
|
||||
data->common(), data->machine());
|
||||
data->common(), data->machine(),
|
||||
temp_zone);
|
||||
SelectLowering select_lowering(data->jsgraph()->graph(),
|
||||
data->jsgraph()->common());
|
||||
AddReducer(data, &graph_reducer, &branch_condition_elimination);
|
||||
|
@ -31,7 +31,7 @@ class CommonOperatorReducerTest : public GraphTest {
|
||||
MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags) {
|
||||
MachineOperatorBuilder machine(zone(), MachineType::PointerRepresentation(),
|
||||
flags);
|
||||
CommonOperatorReducer reducer(editor, graph(), common(), &machine);
|
||||
CommonOperatorReducer reducer(editor, graph(), common(), &machine, zone());
|
||||
return reducer.Reduce(node);
|
||||
}
|
||||
|
||||
@ -492,6 +492,52 @@ TEST_F(CommonOperatorReducerTest, SelectToFloat64Abs) {
|
||||
EXPECT_THAT(r.replacement(), IsFloat64Abs(p0));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Switch
|
||||
|
||||
TEST_F(CommonOperatorReducerTest, SwitchInputMatchesCaseWithDefault) {
|
||||
Node* const control = graph()->start();
|
||||
|
||||
Node* sw = graph()->NewNode(common()->Switch(2), Int32Constant(1), control);
|
||||
Node* const if_1 = graph()->NewNode(common()->IfValue(1), sw);
|
||||
graph()->NewNode(common()->IfDefault(), sw);
|
||||
|
||||
StrictMock<MockAdvancedReducerEditor> editor;
|
||||
EXPECT_CALL(editor, Replace(if_1, control));
|
||||
Reduction r = Reduce(&editor, sw);
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(), IsDead());
|
||||
}
|
||||
|
||||
TEST_F(CommonOperatorReducerTest, SwitchInputMatchesDefaultWithCase) {
|
||||
Node* const control = graph()->start();
|
||||
|
||||
Node* sw = graph()->NewNode(common()->Switch(2), Int32Constant(0), control);
|
||||
graph()->NewNode(common()->IfValue(1), sw);
|
||||
Node* const if_default = graph()->NewNode(common()->IfDefault(), sw);
|
||||
|
||||
StrictMock<MockAdvancedReducerEditor> editor;
|
||||
EXPECT_CALL(editor, Replace(if_default, control));
|
||||
Reduction r = Reduce(&editor, sw);
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(), IsDead());
|
||||
}
|
||||
|
||||
TEST_F(CommonOperatorReducerTest, SwitchInputMatchesCaseExtraCaseWithDefault) {
|
||||
Node* const control = graph()->start();
|
||||
|
||||
Node* sw = graph()->NewNode(common()->Switch(3), Int32Constant(0), control);
|
||||
Node* const if_0 = graph()->NewNode(common()->IfValue(0), sw);
|
||||
graph()->NewNode(common()->IfValue(1), sw);
|
||||
graph()->NewNode(common()->IfDefault(), sw);
|
||||
|
||||
StrictMock<MockAdvancedReducerEditor> editor;
|
||||
EXPECT_CALL(editor, Replace(if_0, control));
|
||||
Reduction r = Reduce(&editor, sw);
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(), IsDead());
|
||||
}
|
||||
|
||||
} // namespace common_operator_reducer_unittest
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
|
Loading…
Reference in New Issue
Block a user