Index: src/compiler/common-operator-reducer.cc |
diff --git a/src/compiler/common-operator-reducer.cc b/src/compiler/common-operator-reducer.cc |
index 85d49b7ae61be8508039a5160edd13b1fe080b33..2b62e9c684e592fc479621a4fb5a2f273047e9a1 100644 |
--- a/src/compiler/common-operator-reducer.cc |
+++ b/src/compiler/common-operator-reducer.cc |
@@ -284,55 +284,91 @@ Reduction CommonOperatorReducer::ReducePhi(Node* node) { |
return Replace(value); |
} |
- |
Reduction CommonOperatorReducer::ReduceReturn(Node* node) { |
DCHECK_EQ(IrOpcode::kReturn, node->opcode()); |
Node* effect = NodeProperties::GetEffectInput(node); |
- Node* const control = NodeProperties::GetControlInput(node); |
- bool changed = false; |
if (effect->opcode() == IrOpcode::kCheckpoint) { |
// Any {Return} node can never be used to insert a deoptimization point, |
// hence checkpoints can be cut out of the effect chain flowing into it. |
effect = NodeProperties::GetEffectInput(effect); |
NodeProperties::ReplaceEffectInput(node, effect); |
- changed = true; |
+ Reduction const reduction = ReduceReturn(node); |
+ return reduction.Changed() ? reduction : Changed(node); |
} |
// TODO(ahaas): Extend the reduction below to multiple return values. |
if (ValueInputCountOfReturn(node->op()) != 1) { |
return NoChange(); |
} |
- Node* const value = node->InputAt(1); |
+ Node* pop_count = NodeProperties::GetValueInput(node, 0); |
+ Node* value = NodeProperties::GetValueInput(node, 1); |
+ Node* control = NodeProperties::GetControlInput(node); |
if (value->opcode() == IrOpcode::kPhi && |
NodeProperties::GetControlInput(value) == control && |
- effect->opcode() == IrOpcode::kEffectPhi && |
- NodeProperties::GetControlInput(effect) == control && |
control->opcode() == IrOpcode::kMerge) { |
+ // This optimization pushes {Return} nodes through merges. It checks that |
+ // the return value is actually a {Phi} and the return control dependency |
+ // is the {Merge} to which the {Phi} belongs. |
+ |
+ // Value1 ... ValueN Control1 ... ControlN |
+ // ^ ^ ^ ^ |
+ // | | | | |
+ // +----+-----+ +------+-----+ |
+ // | | |
+ // Phi --------------> Merge |
+ // ^ ^ |
+ // | | |
+ // | +-----------------+ |
+ // | | |
+ // Return -----> Effect |
+ // ^ |
+ // | |
+ // End |
+ |
+ // Now the effect input to the {Return} node can be either an {EffectPhi} |
+ // hanging off the same {Merge}, or the {Merge} node is only connected to |
+ // the {Return} and the {Phi}, in which case we know that the effect input |
+ // must somehow dominate all merged branches. |
+ |
Node::Inputs control_inputs = control->inputs(); |
Node::Inputs value_inputs = value->inputs(); |
- Node::Inputs effect_inputs = effect->inputs(); |
DCHECK_NE(0, control_inputs.count()); |
DCHECK_EQ(control_inputs.count(), value_inputs.count() - 1); |
- DCHECK_EQ(control_inputs.count(), effect_inputs.count() - 1); |
DCHECK_EQ(IrOpcode::kEnd, graph()->end()->opcode()); |
DCHECK_NE(0, graph()->end()->InputCount()); |
- for (int i = 0; i < control_inputs.count(); ++i) { |
- // Create a new {Return} and connect it to {end}. We don't need to mark |
- // {end} as revisit, because we mark {node} as {Dead} below, which was |
- // previously connected to {end}, so we know for sure that at some point |
- // the reducer logic will visit {end} again. |
- Node* ret = graph()->NewNode(common()->Return(), node->InputAt(0), |
- value_inputs[i], effect_inputs[i], |
- control_inputs[i]); |
- NodeProperties::MergeControlToEnd(graph(), common(), ret); |
+ if (control->OwnedBy(node, value)) { |
+ for (int i = 0; i < control_inputs.count(); ++i) { |
+ // Create a new {Return} and connect it to {end}. We don't need to mark |
+ // {end} as revisit, because we mark {node} as {Dead} below, which was |
+ // previously connected to {end}, so we know for sure that at some point |
+ // the reducer logic will visit {end} again. |
+ Node* ret = graph()->NewNode(node->op(), pop_count, value_inputs[i], |
+ effect, control_inputs[i]); |
+ NodeProperties::MergeControlToEnd(graph(), common(), ret); |
+ } |
+ // Mark the Merge {control} and Return {node} as {dead}. |
+ Replace(control, dead()); |
+ return Replace(dead()); |
+ } else if (effect->opcode() == IrOpcode::kEffectPhi && |
+ NodeProperties::GetControlInput(effect) == control) { |
+ Node::Inputs effect_inputs = effect->inputs(); |
+ DCHECK_EQ(control_inputs.count(), effect_inputs.count() - 1); |
+ for (int i = 0; i < control_inputs.count(); ++i) { |
+ // Create a new {Return} and connect it to {end}. We don't need to mark |
+ // {end} as revisit, because we mark {node} as {Dead} below, which was |
+ // previously connected to {end}, so we know for sure that at some point |
+ // the reducer logic will visit {end} again. |
+ Node* ret = graph()->NewNode(node->op(), pop_count, value_inputs[i], |
+ effect_inputs[i], control_inputs[i]); |
+ NodeProperties::MergeControlToEnd(graph(), common(), ret); |
+ } |
+ // Mark the Merge {control} and Return {node} as {dead}. |
+ Replace(control, dead()); |
+ return Replace(dead()); |
} |
- // Mark the merge {control} and return {node} as {dead}. |
- Replace(control, dead()); |
- return Replace(dead()); |
} |
- return changed ? Changed(node) : NoChange(); |
+ return NoChange(); |
} |
- |
Reduction CommonOperatorReducer::ReduceSelect(Node* node) { |
DCHECK_EQ(IrOpcode::kSelect, node->opcode()); |
Node* const cond = node->InputAt(0); |