[turbofan] Improve branch folding over phis and ranges.
authortitzer <titzer@chromium.org>
Thu, 2 Apr 2015 15:18:35 +0000 (08:18 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 2 Apr 2015 15:18:40 +0000 (15:18 +0000)
R=mstarzinger@chromium.org
BUG=

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

Cr-Commit-Position: refs/heads/master@{#27591}

src/compiler/control-reducer.cc
test/unittests/compiler/control-reducer-unittest.cc

index 25a680d..238b510 100644 (file)
@@ -433,7 +433,7 @@ class ControlReducerImpl {
   }
 
   // Try to statically fold a condition.
-  Decision DecideCondition(Node* cond) {
+  Decision DecideCondition(Node* cond, bool recurse = true) {
     switch (cond->opcode()) {
       case IrOpcode::kInt32Constant:
         return Int32Matcher(cond).Is(0) ? kFalse : kTrue;
@@ -444,14 +444,29 @@ class ControlReducerImpl {
       case IrOpcode::kHeapConstant: {
         Handle<Object> object =
             HeapObjectMatcher<Object>(cond).Value().handle();
-        if (object->IsTrue()) return kTrue;
-        if (object->IsFalse()) return kFalse;
-        // TODO(turbofan): decide more conditions for heap constants.
-        break;
+        return object->BooleanValue() ? kTrue : kFalse;
+      }
+      case IrOpcode::kPhi: {
+        if (!recurse) return kUnknown;  // Only go one level deep checking phis.
+        Decision result = kUnknown;
+        // Check if all inputs to a phi result in the same decision.
+        for (int i = cond->op()->ValueInputCount() - 1; i >= 0; i--) {
+          // Recurse only one level, since phis can be involved in cycles.
+          Decision decision = DecideCondition(cond->InputAt(i), false);
+          if (decision == kUnknown) return kUnknown;
+          if (result == kUnknown) result = decision;
+          if (result != decision) return kUnknown;
+        }
+        return result;
       }
       default:
         break;
     }
+    if (NodeProperties::IsTyped(cond)) {
+      // If the node has a range type, check whether the range excludes 0.
+      Type* type = NodeProperties::GetBounds(cond).upper;
+      if (type->IsRange() && (type->Min() > 0 || type->Max() < 0)) return kTrue;
+    }
     return kUnknown;
   }
 
index 5e9b0ef..76f2558 100644 (file)
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "src/compiler/control-reducer.h"
+#include "src/compiler/graph-visualizer.h"
 #include "src/compiler/js-graph.h"
 #include "src/compiler/js-operator.h"
 #include "src/compiler/machine-operator.h"
@@ -20,14 +21,34 @@ namespace v8 {
 namespace internal {
 namespace compiler {
 
-class ControlReducerTest : public GraphTest {
+class ControlReducerTest : public TypedGraphTest {
+ public:
+  ControlReducerTest()
+      : TypedGraphTest(1),
+        machine_(zone()),
+        javascript_(zone()),
+        jsgraph_(isolate(), graph(), common(), &javascript_, &machine_) {}
+
  protected:
+  MachineOperatorBuilder machine_;
+  JSOperatorBuilder javascript_;
+  JSGraph jsgraph_;
+
   void ReduceGraph() {
-    JSOperatorBuilder javascript(zone());
-    MachineOperatorBuilder machine(zone());
-    JSGraph jsgraph(isolate(), graph(), common(), &javascript, &machine);
-    ControlReducer::ReduceGraph(zone(), &jsgraph, common());
+    if (FLAG_trace_turbo_graph) {
+      OFStream os(stdout);
+      os << "-- Graph before control reduction" << std::endl;
+      os << AsRPO(*graph());
+    }
+    ControlReducer::ReduceGraph(zone(), jsgraph(), common());
+    if (FLAG_trace_turbo_graph) {
+      OFStream os(stdout);
+      os << "-- Graph after control reduction" << std::endl;
+      os << AsRPO(*graph());
+    }
   }
+
+  JSGraph* jsgraph() { return &jsgraph_; }
 };
 
 
@@ -119,6 +140,145 @@ TEST_F(ControlReducerTest, NonTerminatingLoopWithDeadEnd) {
                                           IsIfTrue(CaptureEq(&branch))))))))));
 }
 
+
+TEST_F(ControlReducerTest, PhiAsInputToBranch_true) {
+  Node* p0 = Parameter(0);
+  Node* branch1 = graph()->NewNode(common()->Branch(), p0, graph()->start());
+  Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
+  Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
+  Node* merge1 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
+  Node* phi1 = graph()->NewNode(common()->Phi(kMachInt32, 2),
+                                jsgraph()->Int32Constant(1),
+                                jsgraph()->Int32Constant(2), merge1);
+
+  Node* branch2 = graph()->NewNode(common()->Branch(), phi1, merge1);
+  Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
+  Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
+  Node* merge2 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
+  Node* result = graph()->NewNode(common()->Phi(kMachInt32, 2),
+                                  jsgraph()->Int32Constant(11),
+                                  jsgraph()->Int32Constant(22), merge2);
+
+  Node* ret =
+      graph()->NewNode(common()->Return(), result, graph()->start(), merge2);
+  graph()->end()->ReplaceInput(0, ret);
+
+  ReduceGraph();
+
+  // First diamond is not reduced.
+  EXPECT_THAT(merge1, IsMerge(IsIfTrue(branch1), IsIfFalse(branch1)));
+
+  // Second diamond should be folded away.
+  EXPECT_THAT(graph()->end(),
+              IsEnd(IsReturn(IsInt32Constant(11), graph()->start(), merge1)));
+}
+
+
+TEST_F(ControlReducerTest, PhiAsInputToBranch_false) {
+  Node* p0 = Parameter(0);
+  Node* branch1 = graph()->NewNode(common()->Branch(), p0, graph()->start());
+  Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
+  Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
+  Node* merge1 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
+  Node* phi1 = graph()->NewNode(common()->Phi(kMachInt32, 2),
+                                jsgraph()->Int32Constant(0),
+                                jsgraph()->BooleanConstant(false), merge1);
+
+  Node* branch2 = graph()->NewNode(common()->Branch(), phi1, merge1);
+  Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
+  Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
+  Node* merge2 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
+  Node* result = graph()->NewNode(common()->Phi(kMachInt32, 2),
+                                  jsgraph()->Int32Constant(11),
+                                  jsgraph()->Int32Constant(22), merge2);
+
+  Node* ret =
+      graph()->NewNode(common()->Return(), result, graph()->start(), merge2);
+  graph()->end()->ReplaceInput(0, ret);
+
+  ReduceGraph();
+
+  // First diamond is not reduced.
+  EXPECT_THAT(merge1, IsMerge(IsIfTrue(branch1), IsIfFalse(branch1)));
+
+  // Second diamond should be folded away.
+  EXPECT_THAT(graph()->end(),
+              IsEnd(IsReturn(IsInt32Constant(22), graph()->start(), merge1)));
+}
+
+
+TEST_F(ControlReducerTest, PhiAsInputToBranch_unknown_true) {
+  Node* p0 = Parameter(0);
+  Node* phi0 = graph()->NewNode(common()->Phi(kMachInt32, 2), p0,
+                                jsgraph()->Int32Constant(1), graph()->start());
+  Node* branch1 = graph()->NewNode(common()->Branch(), phi0, graph()->start());
+  Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
+  Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
+  Node* merge1 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
+  Node* phi1 = graph()->NewNode(common()->Phi(kMachInt32, 2),
+                                jsgraph()->Int32Constant(111),
+                                jsgraph()->Int32Constant(222), merge1);
+
+  Node* ret =
+      graph()->NewNode(common()->Return(), phi1, graph()->start(), merge1);
+  graph()->end()->ReplaceInput(0, ret);
+
+  ReduceGraph();
+
+  // Branch should not be folded.
+  EXPECT_THAT(phi1,
+              IsPhi(kMachInt32, IsInt32Constant(111), IsInt32Constant(222),
+                    IsMerge(IsIfTrue(branch1), IsIfFalse(branch1))));
+  EXPECT_THAT(graph()->end(), IsEnd(IsReturn(phi1, graph()->start(), merge1)));
+}
+
+
+TEST_F(ControlReducerTest, RangeAsInputToBranch_true1) {
+  Node* p0 = Parameter(Type::Range(1, 2, zone()), 0);
+  Node* branch1 = graph()->NewNode(common()->Branch(), p0, graph()->start());
+  Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
+  Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
+  Node* merge1 = graph()->NewNode(common()->Merge(1), if_true1, if_false1);
+  Node* result = graph()->NewNode(common()->Phi(kMachInt32, 2),
+                                  jsgraph()->Int32Constant(11),
+                                  jsgraph()->Int32Constant(44), merge1);
+
+  Node* ret =
+      graph()->NewNode(common()->Return(), result, graph()->start(), merge1);
+  graph()->end()->ReplaceInput(0, ret);
+
+  ReduceGraph();
+
+  // Diamond should be folded away.
+  EXPECT_THAT(
+      graph()->end(),
+      IsEnd(IsReturn(IsInt32Constant(11), graph()->start(), graph()->start())));
+}
+
+
+TEST_F(ControlReducerTest, RangeAsInputToBranch_true2) {
+  Node* p0 = Parameter(Type::Range(-2, -1, zone()), 0);
+  Node* branch1 = graph()->NewNode(common()->Branch(), p0, graph()->start());
+  Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
+  Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
+  Node* merge1 = graph()->NewNode(common()->Merge(1), if_true1, if_false1);
+  Node* result = graph()->NewNode(common()->Phi(kMachInt32, 2),
+                                  jsgraph()->Int32Constant(11),
+                                  jsgraph()->Int32Constant(44), merge1);
+
+  Node* ret =
+      graph()->NewNode(common()->Return(), result, graph()->start(), merge1);
+  graph()->end()->ReplaceInput(0, ret);
+
+  ReduceGraph();
+
+  // Diamond should be folded away.
+  EXPECT_THAT(
+      graph()->end(),
+      IsEnd(IsReturn(IsInt32Constant(11), graph()->start(), graph()->start())));
+}
+
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8