Commit 44cac16f authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Also push Return into Merge even if there's no EffectPhi.

We already had an optimization in the CommonOperatorReducer that would
duplicate a Return with Phi, EffectPhi and Merge inputs into the
respective branches. But we can also do the same if the effect input of
the Return dominates all branches, i.e. if the Return and Phi nodes are
the only users of the Merge node.

This helps with the awkward code generation that we currently observe
for || and && in return position.

R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2668903002
Cr-Commit-Position: refs/heads/master@{#42839}
parent d52ec9e6
......@@ -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);
......
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