[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:
Peter Marshall 2018-01-31 13:32:55 +01:00 committed by Commit Bot
parent aa47fd43c4
commit 8622d899d8
4 changed files with 106 additions and 11 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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