Commit 7337021e authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add support for pushing returns into merges.

This will enable tail call optimization even across inlining. Plus it
might enable some other interesting optimizations as well. In order to
avoid blowing up the generated code, we can still canonicalize the
epilogue in the CodeGenerator, similar to what fullcodegen does.

R=jarin@chromium.org

Review URL: https://codereview.chromium.org/1215623002

Cr-Commit-Position: refs/heads/master@{#29311}
parent 8a3cf4ec
......@@ -11,6 +11,7 @@
#include "src/compiler/machine-operator.h"
#include "src/compiler/node.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
namespace v8 {
namespace internal {
......@@ -43,6 +44,16 @@ Decision DecideCondition(Node* const cond) {
} // namespace
CommonOperatorReducer::CommonOperatorReducer(Editor* editor, Graph* graph,
CommonOperatorBuilder* common,
MachineOperatorBuilder* machine)
: AdvancedReducer(editor),
graph_(graph),
common_(common),
machine_(machine),
dead_(graph->NewNode(common->Dead())) {}
Reduction CommonOperatorReducer::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kBranch:
......@@ -53,6 +64,8 @@ Reduction CommonOperatorReducer::Reduce(Node* node) {
return ReduceEffectPhi(node);
case IrOpcode::kPhi:
return ReducePhi(node);
case IrOpcode::kReturn:
return ReduceReturn(node);
case IrOpcode::kSelect:
return ReduceSelect(node);
default:
......@@ -91,20 +104,19 @@ Reduction CommonOperatorReducer::ReduceBranch(Node* node) {
Decision const decision = DecideCondition(cond);
if (decision == Decision::kUnknown) return NoChange();
Node* const control = node->InputAt(1);
if (!dead_.is_set()) dead_.set(graph()->NewNode(common()->Dead()));
for (Node* const use : node->uses()) {
switch (use->opcode()) {
case IrOpcode::kIfTrue:
Replace(use, (decision == Decision::kTrue) ? control : dead_.get());
Replace(use, (decision == Decision::kTrue) ? control : dead());
break;
case IrOpcode::kIfFalse:
Replace(use, (decision == Decision::kFalse) ? control : dead_.get());
Replace(use, (decision == Decision::kFalse) ? control : dead());
break;
default:
UNREACHABLE();
}
}
return Replace(dead_.get());
return Replace(dead());
}
......@@ -253,6 +265,41 @@ Reduction CommonOperatorReducer::ReducePhi(Node* node) {
}
Reduction CommonOperatorReducer::ReduceReturn(Node* node) {
DCHECK_EQ(IrOpcode::kReturn, node->opcode());
Node* const value = node->InputAt(0);
Node* const effect = node->InputAt(1);
Node* const control = node->InputAt(2);
if (value->opcode() == IrOpcode::kPhi &&
NodeProperties::GetControlInput(value) == control &&
effect->opcode() == IrOpcode::kEffectPhi &&
NodeProperties::GetControlInput(effect) == control &&
control->opcode() == IrOpcode::kMerge) {
int const control_input_count = control->InputCount();
DCHECK_NE(0, control_input_count);
DCHECK_EQ(control_input_count, value->InputCount() - 1);
DCHECK_EQ(control_input_count, effect->InputCount() - 1);
Node* const end = graph()->end();
DCHECK_EQ(IrOpcode::kEnd, end->opcode());
DCHECK_NE(0, end->InputCount());
for (int i = 0; i < control_input_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(), value->InputAt(i),
effect->InputAt(i), control->InputAt(i));
end->set_op(common()->End(end->InputCount() + 1));
end->AppendInput(graph()->zone(), ret);
}
// Mark the merge {control} and return {node} as {dead}.
Replace(control, dead());
return Replace(dead());
}
return NoChange();
}
Reduction CommonOperatorReducer::ReduceSelect(Node* node) {
DCHECK_EQ(IrOpcode::kSelect, node->opcode());
Node* const cond = node->InputAt(0);
......
......@@ -23,11 +23,7 @@ class CommonOperatorReducer final : public AdvancedReducer {
public:
CommonOperatorReducer(Editor* editor, Graph* graph,
CommonOperatorBuilder* common,
MachineOperatorBuilder* machine)
: AdvancedReducer(editor),
graph_(graph),
common_(common),
machine_(machine) {}
MachineOperatorBuilder* machine);
~CommonOperatorReducer() final {}
Reduction Reduce(Node* node) final;
......@@ -37,6 +33,7 @@ class CommonOperatorReducer final : public AdvancedReducer {
Reduction ReduceMerge(Node* node);
Reduction ReduceEffectPhi(Node* node);
Reduction ReducePhi(Node* node);
Reduction ReduceReturn(Node* node);
Reduction ReduceSelect(Node* node);
Reduction Change(Node* node, Operator const* op, Node* a);
......@@ -45,11 +42,12 @@ class CommonOperatorReducer final : public AdvancedReducer {
Graph* graph() const { return graph_; }
CommonOperatorBuilder* common() const { return common_; }
MachineOperatorBuilder* machine() const { return machine_; }
Node* dead() const { return dead_; }
Graph* const graph_;
CommonOperatorBuilder* const common_;
MachineOperatorBuilder* const machine_;
SetOncePointer<Node> dead_;
Node* const dead_;
};
} // namespace compiler
......
......@@ -424,6 +424,35 @@ TEST_F(CommonOperatorReducerTest, PhiToFloat64Min) {
}
// -----------------------------------------------------------------------------
// Return
TEST_F(CommonOperatorReducerTest, ReturnWithPhiAndEffectPhiAndMerge) {
Node* cond = Parameter(2);
Node* branch = graph()->NewNode(common()->Branch(), cond, graph()->start());
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* etrue = graph()->start();
Node* vtrue = Parameter(0);
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* efalse = graph()->start();
Node* vfalse = Parameter(1);
Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge);
Node* phi =
graph()->NewNode(common()->Phi(kMachAnyTagged, 2), vtrue, vfalse, merge);
Node* ret = graph()->NewNode(common()->Return(), phi, ephi, merge);
graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, Replace(merge, IsDead()));
Reduction const r = Reduce(&editor, ret);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsDead());
EXPECT_THAT(graph()->end(), IsEnd(ret, IsReturn(vtrue, etrue, if_true),
IsReturn(vfalse, efalse, if_false)));
}
// -----------------------------------------------------------------------------
// Select
......
......@@ -1395,6 +1395,14 @@ Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher,
}
Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher,
const Matcher<Node*>& control1_matcher,
const Matcher<Node*>& control2_matcher) {
return MakeMatcher(new IsControl3Matcher(IrOpcode::kEnd, control0_matcher,
control1_matcher, control2_matcher));
}
Matcher<Node*> IsBranch(const Matcher<Node*>& value_matcher,
const Matcher<Node*>& control_matcher) {
return MakeMatcher(new IsBranchMatcher(value_matcher, control_matcher));
......
......@@ -40,6 +40,9 @@ Matcher<Node*> IsDead();
Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher);
Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher,
const Matcher<Node*>& control1_matcher);
Matcher<Node*> IsEnd(const Matcher<Node*>& control0_matcher,
const Matcher<Node*>& control1_matcher,
const Matcher<Node*>& control2_matcher);
Matcher<Node*> IsBranch(const Matcher<Node*>& value_matcher,
const Matcher<Node*>& control_matcher);
Matcher<Node*> IsMerge(const Matcher<Node*>& control0_matcher,
......
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