[turbofan] Add support for pushing returns into merges.
authorbmeurer <bmeurer@chromium.org>
Fri, 26 Jun 2015 08:20:53 +0000 (01:20 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 26 Jun 2015 08:21:01 +0000 (08:21 +0000)
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}

src/compiler/common-operator-reducer.cc
src/compiler/common-operator-reducer.h
test/unittests/compiler/common-operator-reducer-unittest.cc
test/unittests/compiler/node-test-utils.cc
test/unittests/compiler/node-test-utils.h

index 694c36e..1ed05f1 100644 (file)
@@ -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);
index 3c43bda..8582d6b 100644 (file)
@@ -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
index 05a4937..c33d107 100644 (file)
@@ -425,6 +425,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
 
 
index 27d8ed3..542d795 100644 (file)
@@ -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));
index 76d4296..5607f0a 100644 (file)
@@ -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,