[turbofan] Move graph trimming functionality to dedicated GraphTrimmer.
authorbmeurer <bmeurer@chromium.org>
Wed, 17 Jun 2015 10:56:27 +0000 (03:56 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 17 Jun 2015 10:56:37 +0000 (10:56 +0000)
Up until now that was still mixed with control reduction in the
ControlReducer. This separation allows us to remove the horrible
Reducer::Finish hack and also do graph trimming at more appropriate
places in the pipeline (i.e. trim dead nodes after generic lowering,
which can also make nodes dead).

R=jarin@chromium.org,mstarzinger@chromium.org

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

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

15 files changed:
BUILD.gn
src/compiler/control-reducer.cc
src/compiler/control-reducer.h
src/compiler/graph-reducer.cc
src/compiler/graph-reducer.h
src/compiler/graph-trimmer.cc [new file with mode: 0644]
src/compiler/graph-trimmer.h [new file with mode: 0644]
src/compiler/node-marker.cc
src/compiler/node-marker.h
src/compiler/osr.cc
src/compiler/pipeline.cc
test/cctest/compiler/test-control-reducer.cc
test/unittests/compiler/graph-trimmer-unittest.cc [new file with mode: 0644]
test/unittests/unittests.gyp
tools/gyp/v8.gyp

index 3e652cf..f48e276 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -642,6 +642,8 @@ source_set("v8_base") {
     "src/compiler/graph-reducer.h",
     "src/compiler/graph-replay.cc",
     "src/compiler/graph-replay.h",
+    "src/compiler/graph-trimmer.cc",
+    "src/compiler/graph-trimmer.h",
     "src/compiler/graph-visualizer.cc",
     "src/compiler/graph-visualizer.h",
     "src/compiler/graph.cc",
index 05cf5ec..4644440 100644 (file)
@@ -21,27 +21,8 @@ namespace compiler {
     if (FLAG_trace_turbo_reduction) PrintF(__VA_ARGS__); \
   } while (false)
 
-enum VisitState { kUnvisited = 0, kOnStack = 1, kRevisit = 2, kVisited = 3 };
 enum Decision { kFalse, kUnknown, kTrue };
 
-class ReachabilityMarker : public NodeMarker<uint8_t> {
- public:
-  explicit ReachabilityMarker(Graph* graph) : NodeMarker<uint8_t>(graph, 8) {}
-  bool SetReachableFromEnd(Node* node) {
-    uint8_t before = Get(node);
-    Set(node, before | kFromEnd);
-    return before & kFromEnd;
-  }
-  bool IsReachableFromEnd(Node* node) { return Get(node) & kFromEnd; }
-  void Push(Node* node) { Set(node, Get(node) | kFwStack); }
-  void Pop(Node* node) { Set(node, Get(node) & ~kFwStack); }
-  bool IsOnStack(Node* node) { return Get(node) & kFwStack; }
-
- private:
-  enum Bit { kFromEnd = 1, kFwStack = 2 };
-};
-
-
 class ControlReducerImpl final : public AdvancedReducer {
  public:
   Zone* zone_;
@@ -58,83 +39,6 @@ class ControlReducerImpl final : public AdvancedReducer {
   CommonOperatorBuilder* common() { return jsgraph_->common(); }
   Node* dead() { return jsgraph_->DeadControl(); }
 
-  // Finish reducing the graph by trimming nodes.
-  bool Finish() final {
-    // TODO(bmeurer): Move this to the GraphReducer.
-    Trim();
-    return true;
-  }
-
-  void AddNodesReachableFromRoots(ReachabilityMarker& marked,
-                                  NodeVector& nodes) {
-    jsgraph_->GetCachedNodes(&nodes);  // Consider cached nodes roots.
-    Node* end = graph()->end();
-    marked.SetReachableFromEnd(end);
-    if (!end->IsDead()) nodes.push_back(end);  // Consider end to be a root.
-    for (Node* node : nodes) marked.SetReachableFromEnd(node);
-    AddBackwardsReachableNodes(marked, nodes, 0);
-  }
-
-  void AddBackwardsReachableNodes(ReachabilityMarker& marked, NodeVector& nodes,
-                                  size_t cursor) {
-    while (cursor < nodes.size()) {
-      Node* node = nodes[cursor++];
-      for (Node* const input : node->inputs()) {
-        if (!marked.SetReachableFromEnd(input)) {
-          nodes.push_back(input);
-        }
-      }
-    }
-  }
-
-  void Trim() {
-    // Gather all nodes backwards-reachable from end through inputs.
-    ReachabilityMarker marked(graph());
-    NodeVector nodes(zone_);
-    jsgraph_->GetCachedNodes(&nodes);
-    AddNodesReachableFromRoots(marked, nodes);
-    TrimNodes(marked, nodes);
-  }
-
-  void TrimNodes(ReachabilityMarker& marked, NodeVector& nodes) {
-    // Remove dead->live edges.
-    for (size_t j = 0; j < nodes.size(); j++) {
-      Node* node = nodes[j];
-      for (Edge edge : node->use_edges()) {
-        Node* use = edge.from();
-        if (!marked.IsReachableFromEnd(use)) {
-          TRACE("DeadLink: #%d:%s(%d) -> #%d:%s\n", use->id(),
-                use->op()->mnemonic(), edge.index(), node->id(),
-                node->op()->mnemonic());
-          edge.UpdateTo(NULL);
-        }
-      }
-    }
-#if DEBUG
-    // Verify that no inputs to live nodes are NULL.
-    for (Node* node : nodes) {
-      for (int index = 0; index < node->InputCount(); index++) {
-        Node* input = node->InputAt(index);
-        if (input == nullptr) {
-          std::ostringstream str;
-          str << "GraphError: node #" << node->id() << ":" << *node->op()
-              << "(input @" << index << ") == null";
-          FATAL(str.str().c_str());
-        }
-        if (input->opcode() == IrOpcode::kDead) {
-          std::ostringstream str;
-          str << "GraphError: node #" << node->id() << ":" << *node->op()
-              << "(input @" << index << ") == dead";
-          FATAL(str.str().c_str());
-        }
-      }
-      for (Node* use : node->uses()) {
-        CHECK(marked.IsReachableFromEnd(use));
-      }
-    }
-#endif
-  }
-
   //===========================================================================
   // Reducer implementation: perform reductions on a node.
   //===========================================================================
@@ -443,13 +347,6 @@ class DummyEditor final : public AdvancedReducer::Editor {
 }  // namespace
 
 
-void ControlReducer::TrimGraph(Zone* zone, JSGraph* jsgraph) {
-  DummyEditor editor;
-  ControlReducerImpl impl(&editor, zone, jsgraph);
-  impl.Trim();
-}
-
-
 Node* ControlReducer::ReduceMerge(JSGraph* jsgraph, Node* node,
                                   int max_phis_for_select) {
   Zone zone;
index 06fb9e5..0fce0fb 100644 (file)
@@ -25,9 +25,6 @@ class ControlReducer {
   static void ReduceGraph(Zone* zone, JSGraph* graph,
                           int max_phis_for_select = 0);
 
-  // Trim nodes in the graph that are not reachable from end.
-  static void TrimGraph(Zone* zone, JSGraph* graph);
-
   // Reduces a single merge node and attached phis.
   static Node* ReduceMerge(JSGraph* graph, Node* node,
                            int max_phis_for_select = 0);
index 5435fb0..33d400b 100644 (file)
@@ -14,9 +14,6 @@ namespace v8 {
 namespace internal {
 namespace compiler {
 
-bool Reducer::Finish() { return true; }
-
-
 enum class GraphReducer::State : uint8_t {
   kUnvisited,
   kRevisit,
@@ -70,23 +67,7 @@ void GraphReducer::ReduceNode(Node* node) {
 }
 
 
-void GraphReducer::ReduceGraph() {
-  for (;;) {
-    ReduceNode(graph()->end());
-    // TODO(turbofan): Remove this once the dead node trimming is in the
-    // GraphReducer.
-    bool done = true;
-    for (Reducer* const reducer : reducers_) {
-      if (!reducer->Finish()) {
-        done = false;
-        break;
-      }
-    }
-    if (done) break;
-    // Reset all marks on the graph in preparation to re-reduce the graph.
-    state_.Reset(graph());
-  }
-}
+void GraphReducer::ReduceGraph() { ReduceNode(graph()->end()); }
 
 
 Reduction GraphReducer::Reduce(Node* const node) {
index a8a907f..b4e9ef8 100644 (file)
@@ -47,13 +47,6 @@ class Reducer {
   // Try to reduce a node if possible.
   virtual Reduction Reduce(Node* node) = 0;
 
-  // Ask this reducer to finish operation, returns {true} if the reducer is
-  // done, while {false} indicates that the graph might need to be reduced
-  // again.
-  // TODO(turbofan): Remove this once the dead node trimming is in the
-  // GraphReducer.
-  virtual bool Finish();
-
   // Helper functions for subclasses to produce reductions for a node.
   static Reduction NoChange() { return Reduction(); }
   static Reduction Replace(Node* node) { return Reduction(node); }
diff --git a/src/compiler/graph-trimmer.cc b/src/compiler/graph-trimmer.cc
new file mode 100644 (file)
index 0000000..ff2c036
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/compiler/graph-trimmer.h"
+
+#include "src/compiler/graph.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+GraphTrimmer::GraphTrimmer(Zone* zone, Graph* graph)
+    : graph_(graph), is_live_(graph, 2), live_(zone) {
+  live_.reserve(graph->NodeCount());
+}
+
+
+GraphTrimmer::~GraphTrimmer() {}
+
+
+void GraphTrimmer::TrimGraph() {
+  // Mark end node as live.
+  MarkAsLive(graph()->end());
+  // Compute transitive closure of live nodes.
+  for (size_t i = 0; i < live_.size(); ++i) {
+    for (Node* const input : live_[i]->inputs()) {
+      DCHECK_EQ(IsLive(input),
+                std::find(live_.begin(), live_.end(), input) != live_.end());
+      MarkAsLive(input);
+    }
+  }
+  // Remove dead->live edges.
+  for (Node* const live : live_) {
+    DCHECK(IsLive(live));
+    for (Edge edge : live->use_edges()) {
+      Node* const user = edge.from();
+      DCHECK_EQ(IsLive(user),
+                std::find(live_.begin(), live_.end(), user) != live_.end());
+      if (!IsLive(user)) {
+        if (FLAG_trace_turbo_reduction) {
+          OFStream os(stdout);
+          os << "DeadLink: " << *user << "(" << edge.index() << ") -> " << *live
+             << std::endl;
+        }
+        edge.UpdateTo(nullptr);
+      }
+    }
+  }
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
diff --git a/src/compiler/graph-trimmer.h b/src/compiler/graph-trimmer.h
new file mode 100644 (file)
index 0000000..d8258be
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_COMPILER_GRAPH_TRIMMER_H_
+#define V8_COMPILER_GRAPH_TRIMMER_H_
+
+#include "src/compiler/node-marker.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// Forward declarations.
+class Graph;
+
+
+// Trims dead nodes from the node graph.
+class GraphTrimmer final {
+ public:
+  GraphTrimmer(Zone* zone, Graph* graph);
+  ~GraphTrimmer();
+
+  // Trim nodes in the {graph} that are not reachable from {graph->end()}.
+  void TrimGraph();
+
+  // Trim nodes in the {graph} that are not reachable from either {graph->end()}
+  // or any of the roots in the sequence [{begin},{end}[.
+  template <typename ForwardIterator>
+  void TrimGraph(ForwardIterator begin, ForwardIterator end) {
+    while (begin != end) MarkAsLive(*begin++);
+    TrimGraph();
+  }
+
+ private:
+  V8_INLINE bool IsLive(Node* const node) { return is_live_.Get(node); }
+  V8_INLINE void MarkAsLive(Node* const node) {
+    if (!node->IsDead() && !IsLive(node)) {
+      is_live_.Set(node, true);
+      live_.push_back(node);
+    }
+  }
+
+  Graph* graph() const { return graph_; }
+
+  Graph* const graph_;
+  NodeMarker<bool> is_live_;
+  NodeVector live_;
+
+  DISALLOW_COPY_AND_ASSIGN(GraphTrimmer);
+};
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_COMPILER_GRAPH_TRIMMER_H_
index 3e56a11..fdfb22b 100644 (file)
@@ -16,14 +16,6 @@ NodeMarkerBase::NodeMarkerBase(Graph* graph, uint32_t num_states)
   DCHECK_LT(mark_min_, mark_max_);  // check for wraparound.
 }
 
-
-void NodeMarkerBase::Reset(Graph* graph) {
-  uint32_t const num_states = mark_max_ - mark_min_;
-  mark_min_ = graph->mark_max_;
-  mark_max_ = graph->mark_max_ += num_states;
-  DCHECK_LT(mark_min_, mark_max_);  // check for wraparound.
-}
-
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
index 9ce03b0..5ef2063 100644 (file)
@@ -34,11 +34,10 @@ class NodeMarkerBase {
     DCHECK_LT(node->mark(), mark_max_);
     node->set_mark(mark + mark_min_);
   }
-  void Reset(Graph* graph);
 
  private:
-  Mark mark_min_;
-  Mark mark_max_;
+  Mark const mark_min_;
+  Mark const mark_max_;
 
   DISALLOW_COPY_AND_ASSIGN(NodeMarkerBase);
 };
index 4d8583b..fcd0476 100644 (file)
@@ -8,6 +8,7 @@
 #include "src/compiler/control-reducer.h"
 #include "src/compiler/frame.h"
 #include "src/compiler/graph.h"
+#include "src/compiler/graph-trimmer.h"
 #include "src/compiler/graph-visualizer.h"
 #include "src/compiler/js-graph.h"
 #include "src/compiler/loop-analysis.h"
@@ -337,9 +338,12 @@ void OsrHelper::Deconstruct(JSGraph* jsgraph, CommonOperatorBuilder* common,
   Node* node = ControlReducer::ReduceMerge(jsgraph, osr_loop);
   if (node != osr_loop) osr_loop->ReplaceUses(node);
 
-  // Run the normal control reduction, which naturally trims away the dead
-  // parts of the graph.
+  // Run control reduction and graph trimming.
   ControlReducer::ReduceGraph(tmp_zone, jsgraph);
+  GraphTrimmer trimmer(tmp_zone, jsgraph->graph());
+  NodeVector roots(tmp_zone);
+  jsgraph->GetCachedNodes(&roots);
+  trimmer.TrimGraph(roots.begin(), roots.end());
 }
 
 
@@ -351,7 +355,6 @@ void OsrHelper::SetupFrame(Frame* frame) {
   frame->SetOsrStackSlotCount(static_cast<int>(UnoptimizedFrameSlots()));
 }
 
-
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
index 1dee5ae..7b74592 100644 (file)
@@ -19,6 +19,7 @@
 #include "src/compiler/control-reducer.h"
 #include "src/compiler/frame-elider.h"
 #include "src/compiler/graph-replay.h"
+#include "src/compiler/graph-trimmer.h"
 #include "src/compiler/graph-visualizer.h"
 #include "src/compiler/greedy-allocator.h"
 #include "src/compiler/instruction.h"
@@ -653,6 +654,28 @@ struct LateControlReductionPhase {
 };
 
 
+struct EarlyGraphTrimmingPhase {
+  static const char* phase_name() { return "early graph trimming"; }
+  void Run(PipelineData* data, Zone* temp_zone) {
+    GraphTrimmer trimmer(temp_zone, data->graph());
+    NodeVector roots(temp_zone);
+    data->jsgraph()->GetCachedNodes(&roots);
+    trimmer.TrimGraph(roots.begin(), roots.end());
+  }
+};
+
+
+struct LateGraphTrimmingPhase {
+  static const char* phase_name() { return "late graph trimming"; }
+  void Run(PipelineData* data, Zone* temp_zone) {
+    GraphTrimmer trimmer(temp_zone, data->graph());
+    NodeVector roots(temp_zone);
+    data->jsgraph()->GetCachedNodes(&roots);
+    trimmer.TrimGraph(roots.begin(), roots.end());
+  }
+};
+
+
 struct StressLoopPeelingPhase {
   static const char* phase_name() { return "stress loop peeling"; }
 
@@ -1024,6 +1047,9 @@ Handle<Code> Pipeline::GenerateCode() {
   Run<InliningPhase>();
   RunPrintAndVerify("Inlined", true);
 
+  Run<EarlyGraphTrimmingPhase>();
+  RunPrintAndVerify("Early trimmed", true);
+
   if (FLAG_print_turbo_replay) {
     // Print a replay of the initial graph.
     GraphReplayPrinter::PrintReplay(data.graph());
@@ -1090,6 +1116,10 @@ Handle<Code> Pipeline::GenerateCode() {
   // TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
   RunPrintAndVerify("Lowered generic", true);
 
+  Run<LateGraphTrimmingPhase>();
+  // TODO(jarin, rossberg): Remove UNTYPED once machine typing works.
+  RunPrintAndVerify("Late trimmed", true);
+
   BeginPhaseKind("block building");
 
   data.source_positions()->RemoveDecorator();
index 81cb77a..713090f 100644 (file)
@@ -141,8 +141,6 @@ class ControlReducerTester : HandleAndZoneScope {
     return effect ? common.EffectPhi(count) : common.Phi(kMachAnyTagged, count);
   }
 
-  void Trim() { ControlReducer::TrimGraph(main_zone(), &jsgraph); }
-
   void ReduceGraph() { ControlReducer::ReduceGraph(main_zone(), &jsgraph); }
 
   // Checks one-step reduction of a phi.
@@ -311,213 +309,6 @@ struct DeadChecker {
 };
 
 
-TEST(Trim1_live) {
-  ControlReducerTester T;
-  CHECK(IsUsedBy(T.start, T.p0));
-  T.graph.SetEnd(T.p0);
-  T.Trim();
-  CHECK(IsUsedBy(T.start, T.p0));
-  CheckInputs(T.p0, T.start);
-}
-
-
-TEST(Trim1_dead) {
-  ControlReducerTester T;
-  CHECK(IsUsedBy(T.start, T.p0));
-  T.Trim();
-  CHECK(!IsUsedBy(T.start, T.p0));
-  CHECK(!T.p0->InputAt(0));
-}
-
-
-TEST(Trim2_live) {
-  ControlReducerTester T;
-  Node* phi =
-      T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, T.start);
-  CHECK(IsUsedBy(T.one, phi));
-  CHECK(IsUsedBy(T.half, phi));
-  CHECK(IsUsedBy(T.start, phi));
-  T.graph.SetEnd(phi);
-  T.Trim();
-  CHECK(IsUsedBy(T.one, phi));
-  CHECK(IsUsedBy(T.half, phi));
-  CHECK(IsUsedBy(T.start, phi));
-  CheckInputs(phi, T.one, T.half, T.start);
-}
-
-
-TEST(Trim2_dead) {
-  ControlReducerTester T;
-  Node* phi =
-      T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, T.start);
-  CHECK(IsUsedBy(T.one, phi));
-  CHECK(IsUsedBy(T.half, phi));
-  CHECK(IsUsedBy(T.start, phi));
-  T.Trim();
-  CHECK(!IsUsedBy(T.one, phi));
-  CHECK(!IsUsedBy(T.half, phi));
-  CHECK(!IsUsedBy(T.start, phi));
-  CHECK(!phi->InputAt(0));
-  CHECK(!phi->InputAt(1));
-  CHECK(!phi->InputAt(2));
-}
-
-
-TEST(Trim_chain1) {
-  ControlReducerTester T;
-  const int kDepth = 15;
-  Node* live[kDepth];
-  Node* dead[kDepth];
-  Node* end = T.start;
-  for (int i = 0; i < kDepth; i++) {
-    live[i] = end = T.graph.NewNode(T.common.Merge(1), end);
-    dead[i] = T.graph.NewNode(T.common.Merge(1), end);
-  }
-  // end         -> live[last] ->  live[last-1] -> ... -> start
-  //     dead[last] ^ dead[last-1] ^ ...                  ^
-  T.graph.SetEnd(end);
-  T.Trim();
-  for (int i = 0; i < kDepth; i++) {
-    CHECK(!IsUsedBy(live[i], dead[i]));
-    CHECK(!dead[i]->InputAt(0));
-    CHECK_EQ(i == 0 ? T.start : live[i - 1], live[i]->InputAt(0));
-  }
-}
-
-
-TEST(Trim_chain2) {
-  ControlReducerTester T;
-  const int kDepth = 15;
-  Node* live[kDepth];
-  Node* dead[kDepth];
-  Node* l = T.start;
-  Node* d = T.start;
-  for (int i = 0; i < kDepth; i++) {
-    live[i] = l = T.graph.NewNode(T.common.Merge(1), l);
-    dead[i] = d = T.graph.NewNode(T.common.Merge(1), d);
-  }
-  // end -> live[last] -> live[last-1] -> ... -> start
-  //        dead[last] -> dead[last-1] -> ... -> start
-  T.graph.SetEnd(l);
-  T.Trim();
-  CHECK(!IsUsedBy(T.start, dead[0]));
-  for (int i = 0; i < kDepth; i++) {
-    CHECK_EQ(i == 0 ? NULL : dead[i - 1], dead[i]->InputAt(0));
-    CHECK_EQ(i == 0 ? T.start : live[i - 1], live[i]->InputAt(0));
-  }
-}
-
-
-TEST(Trim_cycle1) {
-  ControlReducerTester T;
-  Node* loop = T.graph.NewNode(T.common.Loop(1), T.start, T.start);
-  loop->ReplaceInput(1, loop);
-  Node* end = T.graph.NewNode(T.common.End(1), loop);
-  T.graph.SetEnd(end);
-
-  CHECK(IsUsedBy(T.start, loop));
-  CHECK(IsUsedBy(loop, end));
-  CHECK(IsUsedBy(loop, loop));
-
-  T.Trim();
-
-  // nothing should have happened to the loop itself.
-  CHECK(IsUsedBy(T.start, loop));
-  CHECK(IsUsedBy(loop, end));
-  CHECK(IsUsedBy(loop, loop));
-  CheckInputs(loop, T.start, loop);
-  CheckInputs(end, loop);
-}
-
-
-TEST(Trim_cycle2) {
-  ControlReducerTester T;
-  Node* loop = T.graph.NewNode(T.common.Loop(2), T.start, T.start);
-  loop->ReplaceInput(1, loop);
-  Node* end = T.graph.NewNode(T.common.End(1), loop);
-  Node* phi =
-      T.graph.NewNode(T.common.Phi(kMachAnyTagged, 2), T.one, T.half, loop);
-  T.graph.SetEnd(end);
-
-  CHECK(IsUsedBy(T.start, loop));
-  CHECK(IsUsedBy(loop, end));
-  CHECK(IsUsedBy(loop, loop));
-  CHECK(IsUsedBy(loop, phi));
-  CHECK(IsUsedBy(T.one, phi));
-  CHECK(IsUsedBy(T.half, phi));
-
-  T.Trim();
-
-  // nothing should have happened to the loop itself.
-  CHECK(IsUsedBy(T.start, loop));
-  CHECK(IsUsedBy(loop, end));
-  CHECK(IsUsedBy(loop, loop));
-  CheckInputs(loop, T.start, loop);
-  CheckInputs(end, loop);
-
-  // phi should have been trimmed away.
-  CHECK(!IsUsedBy(loop, phi));
-  CHECK(!IsUsedBy(T.one, phi));
-  CHECK(!IsUsedBy(T.half, phi));
-  CHECK(!phi->InputAt(0));
-  CHECK(!phi->InputAt(1));
-  CHECK(!phi->InputAt(2));
-}
-
-
-void CheckTrimConstant(ControlReducerTester* T, Node* k) {
-  Node* phi = T->graph.NewNode(T->common.Phi(kMachInt32, 1), k, T->start);
-  CHECK(IsUsedBy(k, phi));
-  T->Trim();
-  CHECK(!IsUsedBy(k, phi));
-  CHECK(!phi->InputAt(0));
-  CHECK(!phi->InputAt(1));
-}
-
-
-TEST(Trim_constants) {
-  ControlReducerTester T;
-  int32_t int32_constants[] = {
-      0, -1,  -2,  2,  2,  3,  3,  4,  4,  5,  5,  4,  5,  6, 6, 7, 8, 7, 8, 9,
-      0, -11, -12, 12, 12, 13, 13, 14, 14, 15, 15, 14, 15, 6, 6, 7, 8, 7, 8, 9};
-
-  for (size_t i = 0; i < arraysize(int32_constants); i++) {
-    CheckTrimConstant(&T, T.jsgraph.Int32Constant(int32_constants[i]));
-    CheckTrimConstant(&T, T.jsgraph.Float64Constant(int32_constants[i]));
-    CheckTrimConstant(&T, T.jsgraph.Constant(int32_constants[i]));
-  }
-
-  Node* other_constants[] = {
-      T.jsgraph.UndefinedConstant(), T.jsgraph.TheHoleConstant(),
-      T.jsgraph.TrueConstant(),      T.jsgraph.FalseConstant(),
-      T.jsgraph.NullConstant(),      T.jsgraph.ZeroConstant(),
-      T.jsgraph.OneConstant(),       T.jsgraph.NaNConstant(),
-      T.jsgraph.Constant(21),        T.jsgraph.Constant(22.2)};
-
-  for (size_t i = 0; i < arraysize(other_constants); i++) {
-    CheckTrimConstant(&T, other_constants[i]);
-  }
-}
-
-
-TEST(Trim_EmptyFrameState1) {
-  ControlReducerTester T;
-
-  Node* node = T.jsgraph.EmptyFrameState();
-  T.Trim();
-
-  for (Node* input : node->inputs()) {
-    CHECK_NOT_NULL(input);
-  }
-}
-
-
-TEST(Trim_EmptyFrameState2) {
-  ControlReducerTester T;
-  CheckTrimConstant(&T, T.jsgraph.EmptyFrameState());
-}
-
-
 TEST(CReducePhi1) {
   ControlReducerTester R;
 
diff --git a/test/unittests/compiler/graph-trimmer-unittest.cc b/test/unittests/compiler/graph-trimmer-unittest.cc
new file mode 100644 (file)
index 0000000..36892e6
--- /dev/null
@@ -0,0 +1,85 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/compiler/graph-trimmer.h"
+#include "test/unittests/compiler/graph-unittest.h"
+#include "testing/gmock-support.h"
+
+using testing::ElementsAre;
+using testing::UnorderedElementsAre;
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+class GraphTrimmerTest : public GraphTest {
+ public:
+  GraphTrimmerTest() : GraphTest(1) {}
+
+ protected:
+  void TrimGraph(Node* root) {
+    Node* const roots[1] = {root};
+    GraphTrimmer trimmer(zone(), graph());
+    trimmer.TrimGraph(&roots[0], &roots[arraysize(roots)]);
+  }
+  void TrimGraph() {
+    GraphTrimmer trimmer(zone(), graph());
+    trimmer.TrimGraph();
+  }
+};
+
+
+namespace {
+
+const Operator kDead0(IrOpcode::kDead, Operator::kNoProperties, "Dead0", 0, 0,
+                      1, 0, 0, 0);
+const Operator kLive0(IrOpcode::kDead, Operator::kNoProperties, "Live0", 0, 0,
+                      1, 0, 0, 1);
+
+}  // namespace
+
+
+TEST_F(GraphTrimmerTest, Empty) {
+  Node* const start = graph()->NewNode(common()->Start(0));
+  Node* const end = graph()->NewNode(common()->End(1), start);
+  graph()->SetStart(start);
+  graph()->SetEnd(end);
+  TrimGraph();
+  EXPECT_EQ(end, graph()->end());
+  EXPECT_EQ(start, graph()->start());
+  EXPECT_EQ(start, end->InputAt(0));
+}
+
+
+TEST_F(GraphTrimmerTest, DeadUseOfStart) {
+  Node* const dead0 = graph()->NewNode(&kDead0, graph()->start());
+  graph()->SetEnd(graph()->NewNode(common()->End(1), graph()->start()));
+  TrimGraph();
+  EXPECT_THAT(dead0->inputs(), ElementsAre(nullptr));
+  EXPECT_THAT(graph()->start()->uses(), ElementsAre(graph()->end()));
+}
+
+
+TEST_F(GraphTrimmerTest, DeadAndLiveUsesOfStart) {
+  Node* const dead0 = graph()->NewNode(&kDead0, graph()->start());
+  Node* const live0 = graph()->NewNode(&kLive0, graph()->start());
+  graph()->SetEnd(graph()->NewNode(common()->End(1), live0));
+  TrimGraph();
+  EXPECT_THAT(dead0->inputs(), ElementsAre(nullptr));
+  EXPECT_THAT(graph()->start()->uses(), ElementsAre(live0));
+  EXPECT_THAT(live0->uses(), ElementsAre(graph()->end()));
+}
+
+
+TEST_F(GraphTrimmerTest, Roots) {
+  Node* const live0 = graph()->NewNode(&kLive0, graph()->start());
+  Node* const live1 = graph()->NewNode(&kLive0, graph()->start());
+  graph()->SetEnd(graph()->NewNode(common()->End(1), live0));
+  TrimGraph(live1);
+  EXPECT_THAT(graph()->start()->uses(), UnorderedElementsAre(live0, live1));
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
index a401833..440b947 100644 (file)
@@ -48,6 +48,7 @@
         'compiler/diamond-unittest.cc',
         'compiler/graph-reducer-unittest.cc',
         'compiler/graph-reducer-unittest.h',
+        'compiler/graph-trimmer-unittest.cc',
         'compiler/graph-unittest.cc',
         'compiler/graph-unittest.h',
         'compiler/instruction-selector-unittest.cc',
index 51c1ae4..6646838 100644 (file)
         '../../src/compiler/graph-reducer.h',
         '../../src/compiler/graph-replay.cc',
         '../../src/compiler/graph-replay.h',
+        '../../src/compiler/graph-trimmer.cc',
+        '../../src/compiler/graph-trimmer.h',
         '../../src/compiler/graph-visualizer.cc',
         '../../src/compiler/graph-visualizer.h',
         '../../src/compiler/graph.cc',