[turbofan] Check node input/use consistency for changed operators and new nodes.
authorjarin <jarin@chromium.org>
Fri, 25 Sep 2015 08:42:51 +0000 (01:42 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 25 Sep 2015 08:43:11 +0000 (08:43 +0000)
Verifies consistency of node inputs and uses:
- node inputs should agree with the input count computed from the node's operator.
- effect inputs should have effect outputs (or be a sentinel).
- control inputs should have control outputs (or be a sentinel).
- frame state inputs should be frame states (or be a sentinel).
- if the node has control uses, it should produce control.
- if the node has effect uses, it should produce effect.
- if the node has frame state uses, it must be a frame state.

I also removed some tests, either because they did not seem to be useful (scheduler) or they tested dead functionality (diamond effect phi).

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

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

15 files changed:
src/compiler/diamond.h
src/compiler/graph.cc
src/compiler/js-intrinsic-lowering.cc
src/compiler/js-typed-lowering.cc
src/compiler/node-properties.cc
src/compiler/node-properties.h
src/compiler/select-lowering.cc
test/cctest/compiler/test-loop-analysis.cc
test/cctest/compiler/test-machine-operator-reducer.cc
test/cctest/compiler/test-osr.cc
test/unittests/compiler/control-equivalence-unittest.cc
test/unittests/compiler/diamond-unittest.cc
test/unittests/compiler/loop-peeling-unittest.cc
test/unittests/compiler/scheduler-unittest.cc
test/unittests/compiler/value-numbering-reducer-unittest.cc

index e7e0a2c32d584a80ad688ccd9e74d39316065b2a..f562092a8a4395f7acadb89df68dc84e0a2d4882 100644 (file)
@@ -52,10 +52,6 @@ struct Diamond {
   Node* Phi(MachineType machine_type, Node* tv, Node* fv) {
     return graph->NewNode(common->Phi(machine_type, 2), tv, fv, merge);
   }
-
-  Node* EffectPhi(Node* tv, Node* fv) {
-    return graph->NewNode(common->EffectPhi(2), tv, fv, merge);
-  }
 };
 
 }  // namespace compiler
index ac7c80c568a94fdf378dbfc2b2e5cc2fdd54234c..7956df473d42faae93bb9003cd1c605d3adef1b0 100644 (file)
@@ -8,7 +8,7 @@
 
 #include "src/base/bits.h"
 #include "src/compiler/node.h"
-#include "src/compiler/operator-properties.h"
+#include "src/compiler/node-properties.h"
 
 namespace v8 {
 namespace internal {
@@ -44,8 +44,11 @@ void Graph::RemoveDecorator(GraphDecorator* decorator) {
 
 Node* Graph::NewNode(const Operator* op, int input_count, Node** inputs,
                      bool incomplete) {
-  DCHECK_EQ(OperatorProperties::GetTotalInputCount(op), input_count);
-  return NewNodeUnchecked(op, input_count, inputs, incomplete);
+  Node* node = NewNodeUnchecked(op, input_count, inputs, incomplete);
+#ifdef DEBUG
+  NodeProperties::Verify(node);
+#endif  // DEBUG
+  return node;
 }
 
 
index 324c42e9de2a0564102c22d1f10369fe9b1cb350..219a452a7d66adc38770fe1bedd64739ea9e08bf 100644 (file)
@@ -283,13 +283,13 @@ Reduction JSIntrinsicLowering::ReduceSeqStringGetChar(
     Node* node, String::Encoding encoding) {
   Node* effect = NodeProperties::GetEffectInput(node);
   Node* control = NodeProperties::GetControlInput(node);
+  RelaxControls(node);
   node->ReplaceInput(2, effect);
   node->ReplaceInput(3, control);
   node->TrimInputCount(4);
   NodeProperties::ChangeOp(
       node,
       simplified()->LoadElement(AccessBuilder::ForSeqStringChar(encoding)));
-  RelaxControls(node);
   return Changed(node);
 }
 
@@ -302,6 +302,8 @@ Reduction JSIntrinsicLowering::ReduceSeqStringSetChar(
   Node* string = NodeProperties::GetValueInput(node, 2);
   Node* effect = NodeProperties::GetEffectInput(node);
   Node* control = NodeProperties::GetControlInput(node);
+  ReplaceWithValue(node, string, node);
+  NodeProperties::RemoveType(node);
   node->ReplaceInput(0, string);
   node->ReplaceInput(1, index);
   node->ReplaceInput(2, chr);
@@ -311,8 +313,6 @@ Reduction JSIntrinsicLowering::ReduceSeqStringSetChar(
   NodeProperties::ChangeOp(
       node,
       simplified()->StoreElement(AccessBuilder::ForSeqStringChar(encoding)));
-  NodeProperties::RemoveType(node);
-  ReplaceWithValue(node, string, node);
   return Changed(node);
 }
 
@@ -552,36 +552,36 @@ Reduction JSIntrinsicLowering::ReduceCallFunction(Node* node) {
 
 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
                                       Node* b) {
+  RelaxControls(node);
   node->ReplaceInput(0, a);
   node->ReplaceInput(1, b);
   node->TrimInputCount(2);
   NodeProperties::ChangeOp(node, op);
-  RelaxControls(node);
   return Changed(node);
 }
 
 
 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
                                       Node* b, Node* c) {
+  RelaxControls(node);
   node->ReplaceInput(0, a);
   node->ReplaceInput(1, b);
   node->ReplaceInput(2, c);
   node->TrimInputCount(3);
   NodeProperties::ChangeOp(node, op);
-  RelaxControls(node);
   return Changed(node);
 }
 
 
 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
                                       Node* b, Node* c, Node* d) {
+  RelaxControls(node);
   node->ReplaceInput(0, a);
   node->ReplaceInput(1, b);
   node->ReplaceInput(2, c);
   node->ReplaceInput(3, d);
   node->TrimInputCount(4);
   NodeProperties::ChangeOp(node, op);
-  RelaxControls(node);
   return Changed(node);
 }
 
index f74f0a2baa1649f6a60d1da866b1ad4ca01272dc..5a0a000176da0200ea77278a691718a1ae9bde96 100644 (file)
@@ -644,10 +644,10 @@ Reduction JSTypedLowering::ReduceJSUnaryNot(Node* node) {
     // chain) because we assume String::length to be immutable.
     Node* length = graph()->NewNode(simplified()->LoadField(access), input,
                                     graph()->start(), graph()->start());
+    ReplaceWithValue(node, node, length);
     node->ReplaceInput(0, length);
     node->ReplaceInput(1, jsgraph()->ZeroConstant());
     NodeProperties::ChangeOp(node, simplified()->NumberEqual());
-    ReplaceWithValue(node, node, length);
     return Changed(node);
   }
   return NoChange();
@@ -905,6 +905,7 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
         }
         // Check if we can avoid the bounds check.
         if (key_type->Min() >= 0 && key_type->Max() < array->length_value()) {
+          RelaxControls(node);
           node->ReplaceInput(0, buffer);
           DCHECK_EQ(key, node->InputAt(1));
           node->ReplaceInput(2, value);
@@ -915,12 +916,12 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
               node,
               simplified()->StoreElement(
                   AccessBuilder::ForTypedArrayElement(array->type(), true)));
-          RelaxControls(node);
           return Changed(node);
         }
         // Compute byte offset.
         Node* offset = Word32Shl(key, static_cast<int>(k));
         // Turn into a StoreBuffer operation.
+        RelaxControls(node);
         node->ReplaceInput(0, buffer);
         node->ReplaceInput(1, offset);
         node->ReplaceInput(2, length);
@@ -929,7 +930,6 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
         node->ReplaceInput(5, control);
         node->TrimInputCount(6);
         NodeProperties::ChangeOp(node, simplified()->StoreBuffer(access));
-        RelaxControls(node);
         return Changed(node);
       }
     }
index 80031eb2dcb32a163e3fb501e7e2fa7c2b64fb45..fffd4da6a4df123385647c5b01443e65fdc4a941 100644 (file)
@@ -197,8 +197,11 @@ void NodeProperties::ReplaceUses(Node* node, Node* value, Node* effect,
 
 // static
 void NodeProperties::ChangeOp(Node* node, const Operator* new_op) {
-  DCHECK_EQ(OperatorProperties::GetTotalInputCount(new_op), node->InputCount());
   node->set_op(new_op);
+
+#ifdef DEBUG
+  Verify(node);
+#endif  // DEBUG
 }
 
 
@@ -267,6 +270,51 @@ void NodeProperties::CollectControlProjections(Node* node, Node** projections,
 }
 
 
+// static
+void NodeProperties::Verify(Node* node) {
+  CHECK_EQ(OperatorProperties::GetTotalInputCount(node->op()),
+           node->InputCount());
+  // If this node has no effect or no control outputs,
+  // we check that no its uses are effect or control inputs.
+  bool check_no_control = node->op()->ControlOutputCount() == 0;
+  bool check_no_effect = node->op()->EffectOutputCount() == 0;
+  bool check_no_frame_state = node->opcode() != IrOpcode::kFrameState;
+  if (check_no_effect || check_no_control) {
+    for (Edge edge : node->use_edges()) {
+      Node* const user = edge.from();
+      CHECK(!user->IsDead());
+      if (NodeProperties::IsControlEdge(edge)) {
+        CHECK(!check_no_control);
+      } else if (NodeProperties::IsEffectEdge(edge)) {
+        CHECK(!check_no_effect);
+      } else if (NodeProperties::IsFrameStateEdge(edge)) {
+        CHECK(!check_no_frame_state);
+      }
+    }
+  }
+  // Frame state inputs should be frame states (or sentinels).
+  for (int i = 0; i < OperatorProperties::GetFrameStateInputCount(node->op());
+       i++) {
+    Node* input = NodeProperties::GetFrameStateInput(node, i);
+    CHECK(input->opcode() == IrOpcode::kFrameState ||
+          input->opcode() == IrOpcode::kStart ||
+          input->opcode() == IrOpcode::kDead);
+  }
+  // Effect inputs should be effect-producing nodes (or sentinels).
+  for (int i = 0; i < node->op()->EffectInputCount(); i++) {
+    Node* input = NodeProperties::GetEffectInput(node, i);
+    CHECK(input->op()->EffectOutputCount() > 0 ||
+          input->opcode() == IrOpcode::kDead);
+  }
+  // Control inputs should be control-producing nodes (or sentinels).
+  for (int i = 0; i < node->op()->ControlInputCount(); i++) {
+    Node* input = NodeProperties::GetControlInput(node, i);
+    CHECK(input->op()->ControlOutputCount() > 0 ||
+          input->opcode() == IrOpcode::kDead);
+  }
+}
+
+
 // static
 bool NodeProperties::AllValueInputsAreTyped(Node* node) {
   int input_count = node->op()->ValueInputCount();
index 44f9b7974c8936fc6ddfe5729182ea65976e4f34..18c63678d56f4773601b812db77b5ca717be6b04 100644 (file)
@@ -110,6 +110,16 @@ class NodeProperties final {
   //  - Switch: [ IfValue, ..., IfDefault ]
   static void CollectControlProjections(Node* node, Node** proj, size_t count);
 
+  // Verifies consistency of node inputs and uses:
+  // - node inputs should agree with the input count computed from
+  //   the node's operator.
+  // - effect inputs should have effect outputs.
+  // - control inputs should have control outputs.
+  // - frame state inputs should be frame states.
+  // - if the node has control uses, it should produce control.
+  // - if the node has effect uses, it should produce effect.
+  // - if the node has frame state uses, it must be a frame state.
+  static void Verify(Node* node);
 
   // ---------------------------------------------------------------------------
   // Type.
index ee648c6c82fafe76675ece85d879e0975e8db26a..28a5d922b799c8a3116c9c0c01f18cdbef5b50f5 100644 (file)
@@ -52,10 +52,10 @@ Reduction SelectLowering::Reduce(Node* node) {
   }
 
   // Create a Phi hanging off the previously determined merge.
-  NodeProperties::ChangeOp(node, common()->Phi(p.type(), 2));
   node->ReplaceInput(0, vthen);
   node->ReplaceInput(1, velse);
   node->ReplaceInput(2, merge);
+  NodeProperties::ChangeOp(node, common()->Phi(p.type(), 2));
   return Changed(node);
 }
 
index 6560ae337b6f6d5c465ba51ea380c41fede92022..60e7657f2bb4a53315c0cb240dd4f6801dc12da6 100644 (file)
@@ -27,7 +27,7 @@ static Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0,
                         0, 1, 0, 0);
 static Operator kIntLt(IrOpcode::kInt32LessThan, Operator::kPure,
                        "Int32LessThan", 2, 0, 0, 1, 0, 0);
-static Operator kStore(IrOpcode::kStore, Operator::kNoProperties, "Store", 0, 2,
+static Operator kStore(IrOpcode::kStore, Operator::kNoProperties, "Store", 1, 1,
                        1, 0, 1, 0);
 
 static const int kNumLeafs = 4;
@@ -234,8 +234,7 @@ struct StoreLoop {
   Node* store;
 
   explicit StoreLoop(While& w)
-      : base(w.t.jsgraph.Int32Constant(12)),
-        val(w.t.jsgraph.Int32Constant(13)) {
+      : base(w.t.graph.start()), val(w.t.jsgraph.Int32Constant(13)) {
     Build(w);
   }
 
@@ -243,7 +242,7 @@ struct StoreLoop {
 
   void Build(While& w) {
     phi = w.t.graph.NewNode(w.t.op(2, true), base, base, w.loop);
-    store = w.t.graph.NewNode(&kStore, phi, val, w.loop);
+    store = w.t.graph.NewNode(&kStore, val, phi, w.loop);
     phi->ReplaceInput(1, store);
   }
 };
@@ -489,7 +488,7 @@ TEST(LaNestedLoop1x) {
   p2a->ReplaceInput(1, p2b);
   p2b->ReplaceInput(1, p2a);
 
-  t.Return(t.p0, p1a, w1.exit);
+  t.Return(t.p0, t.start, w1.exit);
 
   Node* chain[] = {w1.loop, w2.loop};
   t.CheckNestedLoops(chain, 2);
index 592c95ca05f9d71fe71ce172d26790757fc44a96..c02e7e5751b3037e88a3d95ea98c40d69b5fad53 100644 (file)
@@ -715,7 +715,7 @@ TEST(ReduceLoadStore) {
   {
     Node* store = R.graph.NewNode(
         R.machine.Store(StoreRepresentation(kMachInt32, kNoWriteBarrier)), base,
-        index, load, load, load);
+        index, load, load, R.graph.start());
     MachineOperatorReducer reducer(&R.jsgraph);
     Reduction reduction = reducer.Reduce(store);
     CHECK(!reduction.Changed());  // stores should not be reduced.
index 80dbccc633f7191759505a8c6e9e97dd19abd1b3..356cbd24a7f7527fc75f8fd7143966d2e595fc9d 100644 (file)
@@ -93,22 +93,16 @@ class OsrDeconstructorTester : public HandleAndZoneScope {
     return graph.NewNode(common.Phi(kMachAnyTagged, count), count + 1, inputs);
   }
 
-  Node* NewLoop(bool is_osr, int num_backedges, Node* entry = NULL) {
-    CHECK_LT(num_backedges, 4);
-    CHECK_GE(num_backedges, 0);
-    int count = 1 + num_backedges;
-    if (entry == NULL) entry = osr_normal_entry;
-    Node* inputs[5] = {entry, self, self, self, self};
+  Node* NewLoop(bool is_osr, int num_backedges, Node* entry = nullptr) {
+    if (entry == nullptr) entry = osr_normal_entry;
+    Node* loop = graph.NewNode(common.Loop(1), entry);
     if (is_osr) {
-      count = 2 + num_backedges;
-      inputs[1] = osr_loop_entry;
+      loop->AppendInput(graph.zone(), osr_loop_entry);
     }
-
-    Node* loop = graph.NewNode(common.Loop(count), count, inputs);
-    for (int i = 0; i < loop->InputCount(); i++) {
-      if (loop->InputAt(i) == self) loop->ReplaceInput(i, loop);
+    for (int i = 0; i < num_backedges; i++) {
+      loop->AppendInput(graph.zone(), loop);
     }
-
+    NodeProperties::ChangeOp(loop, common.Loop(loop->InputCount()));
     return loop;
   }
 
@@ -497,8 +491,7 @@ TEST(Deconstruct_osr_nested3) {
   loop0.branch->ReplaceInput(0, loop0_cntr);
 
   // middle loop.
-  Node* loop1 = T.graph.NewNode(T.common.Loop(2), loop0.if_true, T.self);
-  loop1->ReplaceInput(0, loop0.if_true);
+  Node* loop1 = T.graph.NewNode(T.common.Loop(1), loop0.if_true);
   Node* loop1_phi = T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), loop0_cntr,
                                     loop0_cntr, loop1);
 
@@ -521,7 +514,8 @@ TEST(Deconstruct_osr_nested3) {
   Node* if_false = T.graph.NewNode(T.common.IfFalse(), branch);
 
   loop0.loop->ReplaceInput(1, if_true);
-  loop1->ReplaceInput(1, if_false);
+  loop1->AppendInput(T.graph.zone(), if_false);
+  NodeProperties::ChangeOp(loop1, T.common.Loop(2));
 
   Node* ret =
       T.graph.NewNode(T.common.Return(), loop0_cntr, T.start, loop0.exit);
index 47be5407f720cd0d209973dc6239d3c6c639b22e..d383bf7c432a08afe7427898cd122eb7b57394ce 100644 (file)
@@ -70,6 +70,10 @@ class ControlEquivalenceTest : public GraphTest {
     return Store(graph()->NewNode(common()->IfFalse(), control));
   }
 
+  Node* Merge1(Node* control) {
+    return Store(graph()->NewNode(common()->Merge(1), control));
+  }
+
   Node* Merge2(Node* control1, Node* control2) {
     return Store(graph()->NewNode(common()->Merge(2), control1, control2));
   }
@@ -107,10 +111,10 @@ TEST_F(ControlEquivalenceTest, Empty1) {
 
 TEST_F(ControlEquivalenceTest, Empty2) {
   Node* start = graph()->start();
-  Node* end = End(start);
-  ComputeEquivalence(end);
+  Node* merge1 = Merge1(start);
+  ComputeEquivalence(merge1);
 
-  ASSERT_EQUIVALENCE(start, end);
+  ASSERT_EQUIVALENCE(start, merge1);
 }
 
 
index c14886fbb7b7e3ed7528ee820b35136607dac5a7..50c50d4e696093f93997f2dd7672b2721a46d383 100644 (file)
@@ -128,22 +128,6 @@ TEST_F(DiamondTest, DiamondPhis) {
 }
 
 
-TEST_F(DiamondTest, DiamondEffectPhis) {
-  Node* p0 = Parameter(0);
-  Node* p1 = Parameter(1);
-  Node* p2 = Parameter(2);
-  Diamond d(graph(), common(), p0);
-
-  Node* phi = d.EffectPhi(p1, p2);
-
-  EXPECT_THAT(d.branch, IsBranch(p0, graph()->start()));
-  EXPECT_THAT(d.if_true, IsIfTrue(d.branch));
-  EXPECT_THAT(d.if_false, IsIfFalse(d.branch));
-  EXPECT_THAT(d.merge, IsMerge(d.if_true, d.if_false));
-  EXPECT_THAT(phi, IsEffectPhi(p1, p2, d.merge));
-}
-
-
 TEST_F(DiamondTest, BranchHint) {
   Diamond dn(graph(), common(), Parameter(0));
   CHECK(BranchHint::kNone == BranchHintOf(dn.branch->op()));
index b90e68d3b8263de29ee3ae3a54ff2be7d58a5090..f1dac8bb64c22478b95f8ff8a27793dd9693838b 100644 (file)
@@ -468,7 +468,7 @@ TEST_F(LoopPeelingTest, TwoExitLoop_nope) {
 
 
 const Operator kMockCall(IrOpcode::kCall, Operator::kNoProperties, "MockCall",
-                         0, 0, 1, 1, 0, 2);
+                         0, 0, 1, 1, 1, 2);
 
 
 TEST_F(LoopPeelingTest, TwoExitLoopWithCall_nope) {
index 80998adc2ee83debaba0e74e4a87266995a0f4a7..45d555d7a143697e841d4799316a7f656a4171cc 100644 (file)
@@ -137,7 +137,7 @@ const Operator kHeapConstant(IrOpcode::kHeapConstant, Operator::kPure,
 const Operator kIntAdd(IrOpcode::kInt32Add, Operator::kPure, "Int32Add", 2, 0,
                        0, 1, 0, 0);
 const Operator kMockCall(IrOpcode::kCall, Operator::kNoProperties, "MockCall",
-                         0, 0, 1, 1, 0, 2);
+                         0, 0, 1, 1, 1, 2);
 const Operator kMockTailCall(IrOpcode::kTailCall, Operator::kNoProperties,
                              "MockTailCall", 1, 1, 1, 0, 0, 1);
 
@@ -649,31 +649,6 @@ TEST_F(SchedulerTest, BuildScheduleOneParameter) {
 }
 
 
-TEST_F(SchedulerTest, BuildScheduleIfSplit) {
-  graph()->SetStart(graph()->NewNode(common()->Start(5)));
-
-  Node* p1 = graph()->NewNode(common()->Parameter(0), graph()->start());
-  Node* p2 = graph()->NewNode(common()->Parameter(1), graph()->start());
-  Node* p3 = graph()->NewNode(common()->Parameter(2), graph()->start());
-  Node* p4 = graph()->NewNode(common()->Parameter(3), graph()->start());
-  Node* p5 = graph()->NewNode(common()->Parameter(4), graph()->start());
-  Node* cmp =
-      graph()->NewNode(js()->LessThanOrEqual(LanguageMode::SLOPPY), p1, p2, p3,
-                       p4, p5, graph()->start(), graph()->start());
-  Node* branch = graph()->NewNode(common()->Branch(), cmp, graph()->start());
-  Node* true_branch = graph()->NewNode(common()->IfTrue(), branch);
-  Node* false_branch = graph()->NewNode(common()->IfFalse(), branch);
-
-  Node* ret1 =
-      graph()->NewNode(common()->Return(), p4, graph()->start(), true_branch);
-  Node* ret2 =
-      graph()->NewNode(common()->Return(), p5, graph()->start(), false_branch);
-  graph()->SetEnd(graph()->NewNode(common()->End(2), ret1, ret2));
-
-  ComputeAndVerifySchedule(13);
-}
-
-
 namespace {
 
 Node* CreateDiamond(Graph* graph, CommonOperatorBuilder* common, Node* cond) {
index a97c4aa664afba30c6c7e951c17b78498b298b7b..c003033940024d8ec500e4b9b9fac2e464d979f5 100644 (file)
@@ -73,8 +73,7 @@ TEST_F(ValueNumberingReducerTest, OperatorEqualityNotIdentity) {
   static const size_t kMaxInputCount = 16;
   Node* inputs[kMaxInputCount];
   for (size_t i = 0; i < arraysize(inputs); ++i) {
-    Operator::Opcode opcode = static_cast<Operator::Opcode>(
-        std::numeric_limits<Operator::Opcode>::max() - i);
+    Operator::Opcode opcode = static_cast<Operator::Opcode>(kMaxInputCount + i);
     inputs[i] = graph()->NewNode(
         new (zone()) TestOperator(opcode, Operator::kIdempotent, 0, 1));
   }
@@ -99,8 +98,7 @@ TEST_F(ValueNumberingReducerTest, SubsequentReductionsYieldTheSameNode) {
   static const size_t kMaxInputCount = 16;
   Node* inputs[kMaxInputCount];
   for (size_t i = 0; i < arraysize(inputs); ++i) {
-    Operator::Opcode opcode = static_cast<Operator::Opcode>(
-        std::numeric_limits<Operator::Opcode>::max() - i);
+    Operator::Opcode opcode = static_cast<Operator::Opcode>(2 + i);
     inputs[i] = graph()->NewNode(
         new (zone()) TestOperator(opcode, Operator::kIdempotent, 0, 1));
   }