[turbofan] First version of loop peeling.
authortitzer <titzer@chromium.org>
Tue, 20 Jan 2015 09:45:02 +0000 (01:45 -0800)
committerCommit bot <commit-bot@chromium.org>
Tue, 20 Jan 2015 09:45:17 +0000 (09:45 +0000)
BUG=

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

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

19 files changed:
BUILD.gn
src/compiler/control-reducer.cc
src/compiler/graph-visualizer.cc
src/compiler/loop-analysis.cc
src/compiler/loop-analysis.h
src/compiler/loop-peeling.cc [new file with mode: 0644]
src/compiler/loop-peeling.h [new file with mode: 0644]
src/compiler/node.h
src/compiler/opcodes.h
src/compiler/osr.h
src/compiler/pipeline.cc
src/compiler/scheduler.cc
src/flag-definitions.h
test/unittests/compiler/graph-unittest.h
test/unittests/compiler/loop-peeling-unittest.cc [new file with mode: 0644]
test/unittests/compiler/node-test-utils.cc
test/unittests/compiler/node-test-utils.h
test/unittests/unittests.gyp
tools/gyp/v8.gyp

index bb0818c..1be045c 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -529,6 +529,7 @@ source_set("v8_base") {
     "src/compiler/linkage.h",
     "src/compiler/load-elimination.cc",
     "src/compiler/load-elimination.h",
+    "src/compiler/loop-peeling.cc",
     "src/compiler/loop-analysis.cc",
     "src/compiler/loop-analysis.h",
     "src/compiler/machine-operator-reducer.cc",
index 877d1dc..ebddfc0 100644 (file)
@@ -457,10 +457,7 @@ class ControlReducerImpl {
     // Gather phis and effect phis to be edited.
     ZoneVector<Node*> phis(zone_);
     for (Node* const use : node->uses()) {
-      if (use->opcode() == IrOpcode::kPhi ||
-          use->opcode() == IrOpcode::kEffectPhi) {
-        phis.push_back(use);
-      }
+      if (IrOpcode::IsPhiOpcode(use->opcode())) phis.push_back(use);
     }
 
     if (live == 1) {
index fe9c28e..0bad333 100644 (file)
@@ -129,7 +129,7 @@ class JSONGraphNodeWriter {
     os_ << "{\"id\":" << SafeId(node) << ",\"label\":\"" << Escaped(label, "\"")
         << "\"";
     IrOpcode::Value opcode = node->opcode();
-    if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) {
+    if (IrOpcode::IsPhiOpcode(opcode)) {
       os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node)
           << "]";
       os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node)
@@ -325,8 +325,7 @@ void GraphVisualizer::PrintNode(Node* node, bool gray) {
 
 
 static bool IsLikelyBackEdge(Node* from, int index, Node* to) {
-  if (from->opcode() == IrOpcode::kPhi ||
-      from->opcode() == IrOpcode::kEffectPhi) {
+  if (IrOpcode::IsPhiOpcode(from->opcode())) {
     Node* control = NodeProperties::GetControlInput(from, 0);
     return control != NULL && control->opcode() != IrOpcode::kMerge &&
            control != to && index != 0;
index 8a9698b..6b6f022 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2013 the V8 project authors. All rights reserved.
+// Copyright 2014 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.
 
@@ -196,8 +196,7 @@ class LoopFinderImpl {
       if (node->opcode() == IrOpcode::kLoop) {
         // found the loop node first.
         loop_num = CreateLoopInfo(node);
-      } else if (node->opcode() == IrOpcode::kPhi ||
-                 node->opcode() == IrOpcode::kEffectPhi) {
+      } else if (IrOpcode::IsPhiOpcode(node->opcode())) {
         // found a phi first.
         Node* merge = node->InputAt(node->InputCount() - 1);
         if (merge->opcode() == IrOpcode::kLoop) {
@@ -235,8 +234,7 @@ class LoopFinderImpl {
 
     // Setup loop mark for phis attached to loop header.
     for (Node* use : node->uses()) {
-      if (use->opcode() == IrOpcode::kPhi ||
-          use->opcode() == IrOpcode::kEffectPhi) {
+      if (IrOpcode::IsPhiOpcode(use->opcode())) {
         SetBackwardMark(use, loop_num);
         loop_tree_->node_to_loop_num_[use->id()] = loop_num;
       }
@@ -291,8 +289,7 @@ class LoopFinderImpl {
   bool IsBackedge(Node* use, Edge& edge) {
     if (LoopNum(use) <= 0) return false;
     if (edge.index() == kAssumedLoopEntryIndex) return false;
-    if (use->opcode() == IrOpcode::kPhi ||
-        use->opcode() == IrOpcode::kEffectPhi) {
+    if (IrOpcode::IsPhiOpcode(use->opcode())) {
       return !NodeProperties::IsControlEdge(edge);
     }
     return true;
index 030c93d..cd6cd98 100644 (file)
@@ -37,6 +37,7 @@ class LoopTree : public ZoneObject {
     size_t HeaderSize() const { return body_start_ - header_start_; }
     size_t BodySize() const { return body_end_ - body_start_; }
     size_t TotalSize() const { return body_end_ - header_start_; }
+    size_t depth() const { return static_cast<size_t>(depth_); }
 
    private:
     friend class LoopTree;
@@ -94,6 +95,22 @@ class LoopTree : public ZoneObject {
                      &loop_nodes_[0] + loop->body_end_);
   }
 
+  // Return a range which can iterate over the nodes of {loop}.
+  NodeRange LoopNodes(Loop* loop) {
+    return NodeRange(&loop_nodes_[0] + loop->header_start_,
+                     &loop_nodes_[0] + loop->body_end_);
+  }
+
+  // Return the node that represents the control, i.e. the loop node itself.
+  Node* GetLoopControl(Loop* loop) {
+    // TODO(turbofan): make the loop control node always first?
+    for (Node* node : HeaderNodes(loop)) {
+      if (node->opcode() == IrOpcode::kLoop) return node;
+    }
+    UNREACHABLE();
+    return NULL;
+  }
+
  private:
   friend class LoopFinderImpl;
 
@@ -127,6 +144,7 @@ class LoopFinder {
   static LoopTree* BuildLoopTree(Graph* graph, Zone* temp_zone);
 };
 
+
 }  // namespace compiler
 }  // namespace internal
 }  // namespace v8
diff --git a/src/compiler/loop-peeling.cc b/src/compiler/loop-peeling.cc
new file mode 100644 (file)
index 0000000..6a59efc
--- /dev/null
@@ -0,0 +1,356 @@
+// 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/common-operator.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/loop-peeling.h"
+#include "src/compiler/node.h"
+#include "src/compiler/node-marker.h"
+#include "src/compiler/node-properties-inl.h"
+#include "src/zone.h"
+
+// Loop peeling is an optimization that copies the body of a loop, creating
+// a new copy of the body called the "peeled iteration" that represents the
+// first iteration. Beginning with a loop as follows:
+
+//             E
+//             |                 A
+//             |                 |                     (backedges)
+//             | +---------------|---------------------------------+
+//             | | +-------------|-------------------------------+ |
+//             | | |             | +--------+                    | |
+//             | | |             | | +----+ |                    | |
+//             | | |             | | |    | |                    | |
+//           ( Loop )<-------- ( phiA )   | |                    | |
+//              |                 |       | |                    | |
+//      ((======P=================U=======|=|=====))             | |
+//      ((                                | |     ))             | |
+//      ((        X <---------------------+ |     ))             | |
+//      ((                                  |     ))             | |
+//      ((     body                         |     ))             | |
+//      ((                                  |     ))             | |
+//      ((        Y <-----------------------+     ))             | |
+//      ((                                        ))             | |
+//      ((===K====L====M==========================))             | |
+//           |    |    |                                         | |
+//           |    |    +-----------------------------------------+ |
+//           |    +------------------------------------------------+
+//           |
+//          exit
+
+// The body of the loop is duplicated so that all nodes considered "inside"
+// the loop (e.g. {P, U, X, Y, K, L, M}) have a corresponding copies in the
+// peeled iteration (e.g. {P', U', X', Y', K', L', M'}). What were considered
+// backedges of the loop correspond to edges from the peeled iteration to
+// the main loop body, with multiple backedges requiring a merge.
+
+// Similarly, any exits from the loop body need to be merged with "exits"
+// from the peeled iteration, resulting in the graph as follows:
+
+//             E
+//             |                 A
+//             |                 |
+//      ((=====P'================U'===============))
+//      ((                                        ))
+//      ((        X'<-------------+               ))
+//      ((                        |               ))
+//      ((   peeled iteration     |               ))
+//      ((                        |               ))
+//      ((        Y'<-----------+ |               ))
+//      ((                      | |               ))
+//      ((===K'===L'====M'======|=|===============))
+//           |    |     |       | |
+//  +--------+    +-+ +-+       | |
+//  |               | |         | |
+//  |              Merge <------phi
+//  |                |           |
+//  |          +-----+           |
+//  |          |                 |                     (backedges)
+//  |          | +---------------|---------------------------------+
+//  |          | | +-------------|-------------------------------+ |
+//  |          | | |             | +--------+                    | |
+//  |          | | |             | | +----+ |                    | |
+//  |          | | |             | | |    | |                    | |
+//  |        ( Loop )<-------- ( phiA )   | |                    | |
+//  |           |                 |       | |                    | |
+//  |   ((======P=================U=======|=|=====))             | |
+//  |   ((                                | |     ))             | |
+//  |   ((        X <---------------------+ |     ))             | |
+//  |   ((                                  |     ))             | |
+//  |   ((     body                         |     ))             | |
+//  |   ((                                  |     ))             | |
+//  |   ((        Y <-----------------------+     ))             | |
+//  |   ((                                        ))             | |
+//  |   ((===K====L====M==========================))             | |
+//  |        |    |    |                                         | |
+//  |        |    |    +-----------------------------------------+ |
+//  |        |    +------------------------------------------------+
+//  |        |
+//  |        |
+//  +----+ +-+
+//       | |
+//      Merge
+//        |
+//      exit
+
+// Note that the boxes ((===)) above are not explicitly represented in the
+// graph, but are instead computed by the {LoopFinder}.
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+struct Peeling {
+  // Maps a node to its index in the {pairs} vector.
+  NodeMarker<size_t> node_map;
+  // The vector which contains the mapped nodes.
+  NodeVector* pairs;
+
+  Peeling(Graph* graph, Zone* tmp_zone, size_t max, NodeVector* p)
+      : node_map(graph, static_cast<uint32_t>(max)), pairs(p) {}
+
+  Node* map(Node* node) {
+    if (node_map.Get(node) == 0) return node;
+    return pairs->at(node_map.Get(node));
+  }
+
+  void Insert(Node* original, Node* copy) {
+    node_map.Set(original, 1 + pairs->size());
+    pairs->push_back(original);
+    pairs->push_back(copy);
+  }
+
+  void CopyNodes(Graph* graph, Zone* tmp_zone, Node* dead, NodeRange nodes) {
+    NodeVector inputs(tmp_zone);
+    // Copy all the nodes first.
+    for (Node* node : nodes) {
+      inputs.clear();
+      for (Node* input : node->inputs()) inputs.push_back(map(input));
+      Insert(node, graph->NewNode(node->op(), node->InputCount(), &inputs[0]));
+    }
+
+    // Fix remaining inputs of the copies.
+    for (Node* original : nodes) {
+      Node* copy = pairs->at(node_map.Get(original));
+      for (int i = 0; i < copy->InputCount(); i++) {
+        copy->ReplaceInput(i, map(original->InputAt(i)));
+      }
+    }
+  }
+
+  bool Marked(Node* node) { return node_map.Get(node) > 0; }
+};
+
+
+class PeeledIterationImpl : public PeeledIteration {
+ public:
+  NodeVector node_pairs_;
+  explicit PeeledIterationImpl(Zone* zone) : node_pairs_(zone) {}
+};
+
+
+Node* PeeledIteration::map(Node* node) {
+  // TODO(turbofan): we use a simple linear search, since the peeled iteration
+  // is really only used in testing.
+  PeeledIterationImpl* impl = static_cast<PeeledIterationImpl*>(this);
+  for (size_t i = 0; i < impl->node_pairs_.size(); i += 2) {
+    if (impl->node_pairs_[i] == node) return impl->node_pairs_[i + 1];
+  }
+  return node;
+}
+
+
+static const Operator* ResizeMergeOrPhi(CommonOperatorBuilder* common,
+                                        const Operator* op, int size) {
+  if (op->opcode() == IrOpcode::kPhi) {
+    return common->Phi(OpParameter<MachineType>(op), size);
+  } else if (op->opcode() == IrOpcode::kEffectPhi) {
+    return common->EffectPhi(size);
+  } else if (op->opcode() == IrOpcode::kMerge) {
+    return common->Merge(size);
+  } else if (op->opcode() == IrOpcode::kLoop) {
+    return common->Loop(size);
+  } else {
+    UNREACHABLE();
+    return nullptr;
+  }
+}
+
+
+PeeledIteration* LoopPeeler::Peel(Graph* graph, CommonOperatorBuilder* common,
+                                  LoopTree* loop_tree, LoopTree::Loop* loop,
+                                  Zone* tmp_zone) {
+  PeeledIterationImpl* iter = new (tmp_zone) PeeledIterationImpl(tmp_zone);
+  Peeling peeling(graph, tmp_zone, loop->TotalSize() * 2 + 2,
+                  &iter->node_pairs_);
+
+  //============================================================================
+  // Construct the peeled iteration.
+  //============================================================================
+  Node* dead = graph->NewNode(common->Dead());
+
+  // Map the loop header nodes to their entry values.
+  for (Node* node : loop_tree->HeaderNodes(loop)) {
+    // TODO(titzer): assuming loop entry at index 0.
+    peeling.Insert(node, node->InputAt(0));
+  }
+
+  // Copy all the nodes of loop body for the peeled iteration.
+  peeling.CopyNodes(graph, tmp_zone, dead, loop_tree->BodyNodes(loop));
+
+  //============================================================================
+  // Replace the entry to the loop with the output of the peeled iteration.
+  //============================================================================
+  Node* loop_node = loop_tree->GetLoopControl(loop);
+  Node* new_entry;
+  int backedges = loop_node->InputCount() - 1;
+  if (backedges > 1) {
+    // Multiple backedges from original loop, therefore multiple output edges
+    // from the peeled iteration.
+    NodeVector inputs(tmp_zone);
+    for (int i = 1; i < loop_node->InputCount(); i++) {
+      inputs.push_back(peeling.map(loop_node->InputAt(i)));
+    }
+    Node* merge =
+        graph->NewNode(common->Merge(backedges), backedges, &inputs[0]);
+
+    // Merge values from the multiple output edges of the peeled iteration.
+    for (Node* node : loop_tree->HeaderNodes(loop)) {
+      if (node->opcode() == IrOpcode::kLoop) continue;  // already done.
+      inputs.clear();
+      for (int i = 0; i < backedges; i++) {
+        inputs.push_back(peeling.map(node->InputAt(1 + i)));
+      }
+      for (Node* input : inputs) {
+        if (input != inputs[0]) {  // Non-redundant phi.
+          inputs.push_back(merge);
+          const Operator* op = ResizeMergeOrPhi(common, node->op(), backedges);
+          Node* phi = graph->NewNode(op, backedges + 1, &inputs[0]);
+          node->ReplaceInput(0, phi);
+          break;
+        }
+      }
+    }
+    new_entry = merge;
+  } else {
+    // Only one backedge, simply replace the input to loop with output of
+    // peeling.
+    for (Node* node : loop_tree->HeaderNodes(loop)) {
+      node->ReplaceInput(0, peeling.map(node->InputAt(0)));
+    }
+    new_entry = peeling.map(loop_node->InputAt(1));
+  }
+  loop_node->ReplaceInput(0, new_entry);
+
+  //============================================================================
+  // Find the loop exit region.
+  //============================================================================
+  NodeVector exits(tmp_zone);
+  Node* end = NULL;
+  for (Node* node : loop_tree->LoopNodes(loop)) {
+    for (Node* use : node->uses()) {
+      if (!loop_tree->Contains(loop, use)) {
+        if (node->opcode() == IrOpcode::kBranch &&
+            (use->opcode() == IrOpcode::kIfTrue ||
+             use->opcode() == IrOpcode::kIfFalse)) {
+          // This is a branch from inside the loop to outside the loop.
+          exits.push_back(use);
+        }
+      }
+    }
+  }
+
+  if (exits.size() == 0) return iter;  // no exits => NTL
+
+  if (exits.size() == 1) {
+    // Only one exit, so {end} is that exit.
+    end = exits[0];
+  } else {
+    // {end} should be the common merge from the exits.
+    NodeVector rets(tmp_zone);
+    for (Node* exit : exits) {
+      Node* found = NULL;
+      for (Node* use : exit->uses()) {
+        if (use->opcode() == IrOpcode::kMerge) {
+          found = use;
+          if (end == NULL) {
+            end = found;
+          } else {
+            CHECK_EQ(end, found);  // it should be unique!
+          }
+        } else if (use->opcode() == IrOpcode::kReturn) {
+          found = use;
+          rets.push_back(found);
+        }
+      }
+      // There should be a merge or a return for each exit.
+      CHECK_NE(NULL, found);
+    }
+    // Return nodes, the end merge, and the phis associated with the end merge
+    // must be duplicated as well.
+    for (Node* node : rets) exits.push_back(node);
+    if (end != NULL) {
+      exits.push_back(end);
+      for (Node* use : end->uses()) {
+        if (IrOpcode::IsPhiOpcode(use->opcode())) exits.push_back(use);
+      }
+    }
+  }
+
+  //============================================================================
+  // Duplicate the loop exit region and add a merge.
+  //============================================================================
+  NodeRange exit_range(&exits[0], &exits[0] + exits.size());
+  peeling.CopyNodes(graph, tmp_zone, dead, exit_range);
+
+  Node* merge = graph->NewNode(common->Merge(2), end, peeling.map(end));
+  end->ReplaceUses(merge);
+  merge->ReplaceInput(0, end);  // HULK SMASH!!
+
+  // Find and update all the edges into either the loop or exit region.
+  for (int i = 0; i < 2; i++) {
+    NodeRange range = i == 0 ? loop_tree->LoopNodes(loop) : exit_range;
+    ZoneVector<Edge> value_edges(tmp_zone);
+    ZoneVector<Edge> effect_edges(tmp_zone);
+
+    for (Node* node : range) {
+      // Gather value and effect edges from outside the region.
+      for (Edge edge : node->use_edges()) {
+        if (!peeling.Marked(edge.from())) {
+          // Edge from outside the loop into the region.
+          if (NodeProperties::IsValueEdge(edge) ||
+              NodeProperties::IsContextEdge(edge)) {
+            value_edges.push_back(edge);
+          } else if (NodeProperties::IsEffectEdge(edge)) {
+            effect_edges.push_back(edge);
+          } else {
+            // don't do anything for control edges.
+            // TODO(titzer): should update control edges to peeled?
+          }
+        }
+      }
+
+      // Update all the value and effect edges at once.
+      if (!value_edges.empty()) {
+        // TODO(titzer): machine type is wrong here.
+        Node* phi = graph->NewNode(common->Phi(kMachAnyTagged, 2), node,
+                                   peeling.map(node), merge);
+        for (Edge edge : value_edges) edge.UpdateTo(phi);
+        value_edges.clear();
+      }
+      if (!effect_edges.empty()) {
+        Node* effect_phi = graph->NewNode(common->EffectPhi(2), node,
+                                          peeling.map(node), merge);
+        for (Edge edge : effect_edges) edge.UpdateTo(effect_phi);
+        effect_edges.clear();
+      }
+    }
+  }
+
+  return iter;
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
diff --git a/src/compiler/loop-peeling.h b/src/compiler/loop-peeling.h
new file mode 100644 (file)
index 0000000..3cbca78
--- /dev/null
@@ -0,0 +1,42 @@
+// 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_LOOP_PEELING_H_
+#define V8_COMPILER_LOOP_PEELING_H_
+
+#include "src/compiler/loop-analysis.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// Represents the output of peeling a loop, which is basically the mapping
+// from the body of the loop to the corresponding nodes in the peeled
+// iteration.
+class PeeledIteration : public ZoneObject {
+ public:
+  // Maps {node} to its corresponding copy in the peeled iteration, if
+  // the node was part of the body of the loop. Returns {node} otherwise.
+  Node* map(Node* node);
+
+ protected:
+  PeeledIteration() {}
+};
+
+class CommonOperatorBuilder;
+
+// Implements loop peeling.
+class LoopPeeler {
+ public:
+  static PeeledIteration* Peel(Graph* graph, CommonOperatorBuilder* common,
+                               LoopTree* loop_tree, LoopTree::Loop* loop,
+                               Zone* tmp_zone);
+};
+
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_COMPILER_LOOP_PEELING_H_
index ed9c01d..57a0ebb 100644 (file)
@@ -284,7 +284,7 @@ class Edge FINAL {
 
   explicit Edge(Node::Input* input) : input_(input) { DCHECK_NOT_NULL(input); }
 
-  Node::Input* const input_;
+  Node::Input* input_;
 };
 
 
index 651c37b..fd886b2 100644 (file)
@@ -294,6 +294,10 @@ class IrOpcode {
   static bool IsConstantOpcode(Value value) {
     return kInt32Constant <= value && value <= kHeapConstant;
   }
+
+  static bool IsPhiOpcode(Value val) {
+    return val == kPhi || val == kEffectPhi;
+  }
 };
 
 }  // namespace compiler
index fb22670..89773f0 100644 (file)
@@ -9,12 +9,10 @@
 
 // TurboFan structures OSR graphs in a way that separates almost all phases of
 // compilation from OSR implementation details. This is accomplished with
-// special
-// control nodes that are added at graph building time. In particular, the graph
-// is built in such a way that typing still computes the best types and
-// optimizations and lowering work unchanged. All that remains is to deconstruct
-// the OSR artifacts before scheduling. The helper class below performs the
-// necessary graph rewriting.
+// special control nodes that are added at graph building time. In particular,
+// the graph is built in such a way that typing still computes the best types
+// and optimizations and lowering work unchanged. All that remains is to
+// deconstruct the OSR artifacts before scheduling and code generation.
 
 // Graphs built for OSR from the AstGraphBuilder are structured as follows:
 //                             Start
 //         end
 
 // The control structure expresses the relationship that the loop has a separate
-// entrypoint which corresponds to entering the loop directly from start.
+// entrypoint which corresponds to entering the loop directly from the middle
+// of unoptimized code.
 // Similarly, the values that come in from unoptimized code are represented with
 // {OsrValue} nodes that merge into any phis associated with the OSR loop.
-// The nodes {A} and {B} represent values in the "normal" graph that correspond
-// to the values of those phis before the loop and on any backedges,
-// respectively.
+// In the above diagram, nodes {A} and {B} represent values in the "normal"
+// graph that correspond to the values of those phis before the loop and on any
+// backedges, respectively.
 
 // To deconstruct OSR, we simply replace the uses of the {OsrNormalEntry}
-// control
-// node with {Dead} and {OsrLoopEntry} with start and run the {ControlReducer}.
-// Control reduction propagates the dead control forward, essentially "killing"
-// all the code before the OSR loop. The entrypoint to the loop corresponding
-// to the "normal" entry path will also be removed, as well as the inputs to
-// the loop phis, resulting in the reduced graph:
+// control node with {Dead} and {OsrLoopEntry} with start and run the
+// {ControlReducer}. Control reduction propagates the dead control forward,
+// essentially "killing" all the code before the OSR loop. The entrypoint to the
+// loop corresponding to the "normal" entry path will also be removed, as well
+// as the inputs to the loop phis, resulting in the reduced graph:
 
 //                             Start
 //         Dead                  |^-------------------------+
index eb9ed4c..73aa962 100644 (file)
@@ -27,6 +27,8 @@
 #include "src/compiler/js-typed-lowering.h"
 #include "src/compiler/jump-threading.h"
 #include "src/compiler/load-elimination.h"
+#include "src/compiler/loop-analysis.h"
+#include "src/compiler/loop-peeling.h"
 #include "src/compiler/machine-operator-reducer.h"
 #include "src/compiler/move-optimizer.h"
 #include "src/compiler/osr.h"
@@ -504,6 +506,23 @@ struct LateControlReductionPhase : ControlReductionPhase {
 };
 
 
+struct StressLoopPeelingPhase {
+  static const char* phase_name() { return "stress loop peeling"; }
+
+  void Run(PipelineData* data, Zone* temp_zone) {
+    SourcePositionTable::Scope pos(data->source_positions(),
+                                   SourcePosition::Unknown());
+    // Peel the first outer loop for testing.
+    // TODO(titzer): peel all loops? the N'th loop? Innermost loops?
+    LoopTree* loop_tree = LoopFinder::BuildLoopTree(data->graph(), temp_zone);
+    if (loop_tree != NULL && loop_tree->outer_loops().size() > 0) {
+      LoopPeeler::Peel(data->graph(), data->common(), loop_tree,
+                       loop_tree->outer_loops()[0], temp_zone);
+    }
+  }
+};
+
+
 struct GenericLoweringPhase {
   static const char* phase_name() { return "generic lowering"; }
 
@@ -828,6 +847,11 @@ Handle<Code> Pipeline::GenerateCode() {
     Run<TypedLoweringPhase>();
     RunPrintAndVerify("Lowered typed");
 
+    if (FLAG_turbo_stress_loop_peeling) {
+      Run<StressLoopPeelingPhase>();
+      RunPrintAndVerify("Loop peeled", true);
+    }
+
     if (info()->is_osr()) {
       Run<OsrDeconstructionPhase>();
       RunPrintAndVerify("OSR deconstruction");
index cb11bec..1b514a6 100644 (file)
@@ -1328,7 +1328,7 @@ class ScheduleLateNodeVisitor {
   BasicBlock* GetBlockForUse(Edge edge) {
     Node* use = edge.from();
     IrOpcode::Value opcode = use->opcode();
-    if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) {
+    if (IrOpcode::IsPhiOpcode(opcode)) {
       // If the use is from a coupled (i.e. floating) phi, compute the common
       // dominator of its uses. This will not recurse more than one level.
       if (scheduler_->GetPlacement(use) == Scheduler::kCoupled) {
@@ -1438,10 +1438,8 @@ void Scheduler::FuseFloatingControl(BasicBlock* block, Node* node) {
   NodeVector propagation_roots(control_flow_builder_->control_);
   for (Node* node : control_flow_builder_->control_) {
     for (Node* use : node->uses()) {
-      if (use->opcode() == IrOpcode::kPhi ||
-          use->opcode() == IrOpcode::kEffectPhi) {
+      if (IrOpcode::IsPhiOpcode(use->opcode()))
         propagation_roots.push_back(use);
-      }
     }
   }
   if (FLAG_trace_turbo_scheduler) {
index 08ebad8..b9794f4 100644 (file)
@@ -414,6 +414,8 @@ DEFINE_BOOL(turbo_verify_allocation, DEBUG_BOOL,
 DEFINE_BOOL(turbo_move_optimization, true, "optimize gap moves in TurboFan")
 DEFINE_BOOL(turbo_jt, true, "enable jump threading in TurboFan")
 DEFINE_BOOL(turbo_osr, false, "enable OSR in TurboFan")
+DEFINE_BOOL(turbo_stress_loop_peeling, false,
+            "stress loop peeling optimization")
 
 DEFINE_INT(typed_array_max_size_in_heap, 64,
            "threshold for in-heap typed array")
index 7c75161..0ab81a7 100644 (file)
@@ -32,6 +32,7 @@ class GraphTest : public TestWithContext, public TestWithZone {
   ~GraphTest() OVERRIDE;
 
  protected:
+  Node* start() { return graph()->start(); }
   Node* Parameter(int32_t index = 0);
   Node* Float32Constant(volatile float value);
   Node* Float64Constant(volatile double value);
diff --git a/test/unittests/compiler/loop-peeling-unittest.cc b/test/unittests/compiler/loop-peeling-unittest.cc
new file mode 100644 (file)
index 0000000..5a0d0c5
--- /dev/null
@@ -0,0 +1,451 @@
+// 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/access-builder.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/js-graph.h"
+#include "src/compiler/loop-peeling.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/node.h"
+#include "src/compiler/node-properties-inl.h"
+#include "test/unittests/compiler/compiler-test-utils.h"
+#include "test/unittests/compiler/graph-unittest.h"
+#include "test/unittests/compiler/node-test-utils.h"
+#include "testing/gmock-support.h"
+
+using testing::AllOf;
+using testing::BitEq;
+using testing::Capture;
+using testing::CaptureEq;
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+struct While {
+  Node* loop;
+  Node* branch;
+  Node* if_true;
+  Node* exit;
+};
+
+
+// A helper for building branches.
+struct Branch {
+  Node* branch;
+  Node* if_true;
+  Node* if_false;
+};
+
+
+// A helper for building counters attached to loops.
+struct Counter {
+  Node* base;
+  Node* inc;
+  Node* phi;
+  Node* add;
+};
+
+
+class LoopPeelingTest : public GraphTest {
+ public:
+  LoopPeelingTest() : GraphTest(1), machine_(zone()) {}
+  ~LoopPeelingTest() OVERRIDE {}
+
+ protected:
+  MachineOperatorBuilder machine_;
+
+  MachineOperatorBuilder* machine() { return &machine_; }
+
+  LoopTree* GetLoopTree() {
+    if (FLAG_trace_turbo_graph) {
+      OFStream os(stdout);
+      os << AsRPO(*graph());
+    }
+    Zone zone(isolate());
+    return LoopFinder::BuildLoopTree(graph(), &zone);
+  }
+
+
+  PeeledIteration* PeelOne() {
+    LoopTree* loop_tree = GetLoopTree();
+    return Peel(loop_tree, loop_tree->outer_loops()[0]);
+  }
+
+  PeeledIteration* Peel(LoopTree* loop_tree, LoopTree::Loop* loop) {
+    PeeledIteration* peeled =
+        LoopPeeler::Peel(graph(), common(), loop_tree, loop, zone());
+    if (FLAG_trace_turbo_graph) {
+      OFStream os(stdout);
+      os << AsRPO(*graph());
+    }
+    return peeled;
+  }
+
+  Node* InsertReturn(Node* val, Node* effect, Node* control) {
+    Node* r = graph()->NewNode(common()->Return(), val, effect, control);
+    graph()->SetEnd(r);
+    return r;
+  }
+
+  Node* ExpectPeeled(Node* node, PeeledIteration* iter) {
+    Node* p = iter->map(node);
+    EXPECT_NE(node, p);
+    return p;
+  }
+
+  void ExpectNotPeeled(Node* node, PeeledIteration* iter) {
+    EXPECT_EQ(node, iter->map(node));
+  }
+
+  While NewWhile(Node* cond, Node* control = nullptr) {
+    if (control == nullptr) control = start();
+    Node* loop = graph()->NewNode(common()->Loop(2), control, control);
+    Node* branch = graph()->NewNode(common()->Branch(), cond, loop);
+    Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+    Node* exit = graph()->NewNode(common()->IfFalse(), branch);
+    loop->ReplaceInput(1, if_true);
+    return {loop, branch, if_true, exit};
+  }
+
+  void Chain(While* a, Node* control) { a->loop->ReplaceInput(0, control); }
+  void Nest(While* a, While* b) {
+    b->loop->ReplaceInput(1, a->exit);
+    a->loop->ReplaceInput(0, b->if_true);
+  }
+  Node* NewPhi(While* w, Node* a, Node* b) {
+    return graph()->NewNode(common()->Phi(kMachAnyTagged, 2), a, b, w->loop);
+  }
+
+  Branch NewBranch(Node* cond, Node* control = nullptr) {
+    if (control == nullptr) control = start();
+    Node* branch = graph()->NewNode(common()->Branch(), cond, control);
+    Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+    Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+    return {branch, if_true, if_false};
+  }
+
+  Counter NewCounter(While* w, int32_t b, int32_t k) {
+    Node* base = Int32Constant(b);
+    Node* inc = Int32Constant(k);
+    Node* phi =
+        graph()->NewNode(common()->Phi(kMachAnyTagged, 2), base, base, w->loop);
+    Node* add = graph()->NewNode(machine()->Int32Add(), phi, inc);
+    phi->ReplaceInput(1, add);
+    return {base, inc, phi, add};
+  }
+};
+
+
+TEST_F(LoopPeelingTest, SimpleLoop) {
+  Node* p0 = Parameter(0);
+  While w = NewWhile(p0);
+  Node* r = InsertReturn(p0, start(), w.exit);
+
+  PeeledIteration* peeled = PeelOne();
+
+  Node* br1 = ExpectPeeled(w.branch, peeled);
+  Node* if_true1 = ExpectPeeled(w.if_true, peeled);
+  Node* if_false1 = ExpectPeeled(w.exit, peeled);
+
+  EXPECT_THAT(br1, IsBranch(p0, start()));
+  EXPECT_THAT(if_true1, IsIfTrue(br1));
+  EXPECT_THAT(if_false1, IsIfFalse(br1));
+
+  EXPECT_THAT(w.loop, IsLoop(if_true1, w.if_true));
+  EXPECT_THAT(r, IsReturn(p0, start(), IsMerge(w.exit, if_false1)));
+}
+
+
+TEST_F(LoopPeelingTest, SimpleLoopWithCounter) {
+  Node* p0 = Parameter(0);
+  While w = NewWhile(p0);
+  Counter c = NewCounter(&w, 0, 1);
+  Node* r = InsertReturn(c.phi, start(), w.exit);
+
+  PeeledIteration* peeled = PeelOne();
+
+  Node* br1 = ExpectPeeled(w.branch, peeled);
+  Node* if_true1 = ExpectPeeled(w.if_true, peeled);
+  Node* if_false1 = ExpectPeeled(w.exit, peeled);
+
+  EXPECT_THAT(br1, IsBranch(p0, start()));
+  EXPECT_THAT(if_true1, IsIfTrue(br1));
+  EXPECT_THAT(if_false1, IsIfFalse(br1));
+  EXPECT_THAT(w.loop, IsLoop(if_true1, w.if_true));
+
+  EXPECT_THAT(peeled->map(c.add), IsInt32Add(c.base, c.inc));
+
+  Capture<Node*> merge;
+  EXPECT_THAT(
+      r, IsReturn(IsPhi(kMachAnyTagged, c.phi, c.base,
+                        AllOf(CaptureEq(&merge), IsMerge(w.exit, if_false1))),
+                  start(), CaptureEq(&merge)));
+}
+
+
+TEST_F(LoopPeelingTest, SimpleNestedLoopWithCounter_peel_outer) {
+  Node* p0 = Parameter(0);
+  While outer = NewWhile(p0);
+  While inner = NewWhile(p0);
+  Nest(&inner, &outer);
+
+  Counter c = NewCounter(&outer, 0, 1);
+  Node* r = InsertReturn(c.phi, start(), outer.exit);
+
+  PeeledIteration* peeled = PeelOne();
+
+  Node* bro = ExpectPeeled(outer.branch, peeled);
+  Node* if_trueo = ExpectPeeled(outer.if_true, peeled);
+  Node* if_falseo = ExpectPeeled(outer.exit, peeled);
+
+  EXPECT_THAT(bro, IsBranch(p0, start()));
+  EXPECT_THAT(if_trueo, IsIfTrue(bro));
+  EXPECT_THAT(if_falseo, IsIfFalse(bro));
+
+  Node* bri = ExpectPeeled(inner.branch, peeled);
+  Node* if_truei = ExpectPeeled(inner.if_true, peeled);
+  Node* if_falsei = ExpectPeeled(inner.exit, peeled);
+
+  EXPECT_THAT(bri, IsBranch(p0, ExpectPeeled(inner.loop, peeled)));
+  EXPECT_THAT(if_truei, IsIfTrue(bri));
+  EXPECT_THAT(if_falsei, IsIfFalse(bri));
+
+  EXPECT_THAT(outer.loop, IsLoop(if_falsei, inner.exit));
+  EXPECT_THAT(peeled->map(c.add), IsInt32Add(c.base, c.inc));
+
+  Capture<Node*> merge;
+  EXPECT_THAT(
+      r,
+      IsReturn(IsPhi(kMachAnyTagged, c.phi, c.base,
+                     AllOf(CaptureEq(&merge), IsMerge(outer.exit, if_falseo))),
+               start(), CaptureEq(&merge)));
+}
+
+
+TEST_F(LoopPeelingTest, SimpleNestedLoopWithCounter_peel_inner) {
+  Node* p0 = Parameter(0);
+  While outer = NewWhile(p0);
+  While inner = NewWhile(p0);
+  Nest(&inner, &outer);
+
+  Counter c = NewCounter(&outer, 0, 1);
+  Node* r = InsertReturn(c.phi, start(), outer.exit);
+
+  LoopTree* loop_tree = GetLoopTree();
+  LoopTree::Loop* loop = loop_tree->ContainingLoop(inner.loop);
+  EXPECT_NE(nullptr, loop);
+  EXPECT_EQ(1u, loop->depth());
+
+  PeeledIteration* peeled = Peel(loop_tree, loop);
+
+  ExpectNotPeeled(outer.loop, peeled);
+  ExpectNotPeeled(outer.branch, peeled);
+  ExpectNotPeeled(outer.if_true, peeled);
+  ExpectNotPeeled(outer.exit, peeled);
+
+  Node* bri = ExpectPeeled(inner.branch, peeled);
+  Node* if_truei = ExpectPeeled(inner.if_true, peeled);
+  Node* if_falsei = ExpectPeeled(inner.exit, peeled);
+
+  EXPECT_THAT(bri, IsBranch(p0, ExpectPeeled(inner.loop, peeled)));
+  EXPECT_THAT(if_truei, IsIfTrue(bri));
+  EXPECT_THAT(if_falsei, IsIfFalse(bri));
+
+  EXPECT_THAT(outer.loop, IsLoop(start(), IsMerge(inner.exit, if_falsei)));
+  ExpectNotPeeled(c.add, peeled);
+
+  EXPECT_THAT(r, IsReturn(c.phi, start(), outer.exit));
+}
+
+
+TEST_F(LoopPeelingTest, SimpleInnerCounter_peel_inner) {
+  Node* p0 = Parameter(0);
+  While outer = NewWhile(p0);
+  While inner = NewWhile(p0);
+  Nest(&inner, &outer);
+  Counter c = NewCounter(&inner, 0, 1);
+  Node* phi = NewPhi(&outer, Int32Constant(11), c.phi);
+
+  Node* r = InsertReturn(phi, start(), outer.exit);
+
+  LoopTree* loop_tree = GetLoopTree();
+  LoopTree::Loop* loop = loop_tree->ContainingLoop(inner.loop);
+  EXPECT_NE(nullptr, loop);
+  EXPECT_EQ(1u, loop->depth());
+
+  PeeledIteration* peeled = Peel(loop_tree, loop);
+
+  ExpectNotPeeled(outer.loop, peeled);
+  ExpectNotPeeled(outer.branch, peeled);
+  ExpectNotPeeled(outer.if_true, peeled);
+  ExpectNotPeeled(outer.exit, peeled);
+
+  Node* bri = ExpectPeeled(inner.branch, peeled);
+  Node* if_truei = ExpectPeeled(inner.if_true, peeled);
+  Node* if_falsei = ExpectPeeled(inner.exit, peeled);
+
+  EXPECT_THAT(bri, IsBranch(p0, ExpectPeeled(inner.loop, peeled)));
+  EXPECT_THAT(if_truei, IsIfTrue(bri));
+  EXPECT_THAT(if_falsei, IsIfFalse(bri));
+
+  EXPECT_THAT(outer.loop, IsLoop(start(), IsMerge(inner.exit, if_falsei)));
+  EXPECT_THAT(peeled->map(c.add), IsInt32Add(c.base, c.inc));
+
+  Node* back = phi->InputAt(1);
+  EXPECT_THAT(back, IsPhi(kMachAnyTagged, c.phi, c.base,
+                          IsMerge(inner.exit, if_falsei)));
+
+  EXPECT_THAT(phi,
+              IsPhi(kMachAnyTagged, IsInt32Constant(11), back, outer.loop));
+
+  EXPECT_THAT(r, IsReturn(phi, start(), outer.exit));
+}
+
+
+TEST_F(LoopPeelingTest, TwoBackedgeLoop) {
+  Node* p0 = Parameter(0);
+  Node* loop = graph()->NewNode(common()->Loop(3), start(), start(), start());
+  Branch b1 = NewBranch(p0, loop);
+  Branch b2 = NewBranch(p0, b1.if_true);
+
+  loop->ReplaceInput(1, b2.if_true);
+  loop->ReplaceInput(2, b2.if_false);
+
+  Node* r = InsertReturn(p0, start(), b1.if_false);
+
+  PeeledIteration* peeled = PeelOne();
+
+  Node* b1b = ExpectPeeled(b1.branch, peeled);
+  Node* b1t = ExpectPeeled(b1.if_true, peeled);
+  Node* b1f = ExpectPeeled(b1.if_false, peeled);
+
+  EXPECT_THAT(b1b, IsBranch(p0, start()));
+  EXPECT_THAT(ExpectPeeled(b1.if_true, peeled), IsIfTrue(b1b));
+  EXPECT_THAT(b1f, IsIfFalse(b1b));
+
+  Node* b2b = ExpectPeeled(b2.branch, peeled);
+  Node* b2t = ExpectPeeled(b2.if_true, peeled);
+  Node* b2f = ExpectPeeled(b2.if_false, peeled);
+
+  EXPECT_THAT(b2b, IsBranch(p0, b1t));
+  EXPECT_THAT(b2t, IsIfTrue(b2b));
+  EXPECT_THAT(b2f, IsIfFalse(b2b));
+
+  EXPECT_THAT(loop, IsLoop(IsMerge(b2t, b2f), b2.if_true, b2.if_false));
+  EXPECT_THAT(r, IsReturn(p0, start(), IsMerge(b1.if_false, b1f)));
+}
+
+
+TEST_F(LoopPeelingTest, TwoBackedgeLoopWithPhi) {
+  Node* p0 = Parameter(0);
+  Node* loop = graph()->NewNode(common()->Loop(3), start(), start(), start());
+  Branch b1 = NewBranch(p0, loop);
+  Branch b2 = NewBranch(p0, b1.if_true);
+  Node* phi =
+      graph()->NewNode(common()->Phi(kMachAnyTagged, 3), Int32Constant(0),
+                       Int32Constant(1), Int32Constant(2), loop);
+
+  loop->ReplaceInput(1, b2.if_true);
+  loop->ReplaceInput(2, b2.if_false);
+
+  Node* r = InsertReturn(phi, start(), b1.if_false);
+
+  PeeledIteration* peeled = PeelOne();
+
+  Node* b1b = ExpectPeeled(b1.branch, peeled);
+  Node* b1t = ExpectPeeled(b1.if_true, peeled);
+  Node* b1f = ExpectPeeled(b1.if_false, peeled);
+
+  EXPECT_THAT(b1b, IsBranch(p0, start()));
+  EXPECT_THAT(ExpectPeeled(b1.if_true, peeled), IsIfTrue(b1b));
+  EXPECT_THAT(b1f, IsIfFalse(b1b));
+
+  Node* b2b = ExpectPeeled(b2.branch, peeled);
+  Node* b2t = ExpectPeeled(b2.if_true, peeled);
+  Node* b2f = ExpectPeeled(b2.if_false, peeled);
+
+  EXPECT_THAT(b2b, IsBranch(p0, b1t));
+  EXPECT_THAT(b2t, IsIfTrue(b2b));
+  EXPECT_THAT(b2f, IsIfFalse(b2b));
+
+  EXPECT_THAT(loop, IsLoop(IsMerge(b2t, b2f), b2.if_true, b2.if_false));
+
+  EXPECT_THAT(
+      phi, IsPhi(kMachAnyTagged, IsPhi(kMachAnyTagged, IsInt32Constant(1),
+                                       IsInt32Constant(2), IsMerge(b2t, b2f)),
+                 IsInt32Constant(1), IsInt32Constant(2), loop));
+
+  Capture<Node*> merge;
+  EXPECT_THAT(
+      r, IsReturn(IsPhi(kMachAnyTagged, phi, IsInt32Constant(0),
+                        AllOf(CaptureEq(&merge), IsMerge(b1.if_false, b1f))),
+                  start(), CaptureEq(&merge)));
+}
+
+
+TEST_F(LoopPeelingTest, TwoBackedgeLoopWithCounter) {
+  Node* p0 = Parameter(0);
+  Node* loop = graph()->NewNode(common()->Loop(3), start(), start(), start());
+  Branch b1 = NewBranch(p0, loop);
+  Branch b2 = NewBranch(p0, b1.if_true);
+  Node* phi =
+      graph()->NewNode(common()->Phi(kMachAnyTagged, 3), Int32Constant(0),
+                       Int32Constant(1), Int32Constant(2), loop);
+
+  phi->ReplaceInput(
+      1, graph()->NewNode(machine()->Int32Add(), phi, Int32Constant(1)));
+  phi->ReplaceInput(
+      2, graph()->NewNode(machine()->Int32Add(), phi, Int32Constant(2)));
+
+  loop->ReplaceInput(1, b2.if_true);
+  loop->ReplaceInput(2, b2.if_false);
+
+  Node* r = InsertReturn(phi, start(), b1.if_false);
+
+  PeeledIteration* peeled = PeelOne();
+
+  Node* b1b = ExpectPeeled(b1.branch, peeled);
+  Node* b1t = ExpectPeeled(b1.if_true, peeled);
+  Node* b1f = ExpectPeeled(b1.if_false, peeled);
+
+  EXPECT_THAT(b1b, IsBranch(p0, start()));
+  EXPECT_THAT(ExpectPeeled(b1.if_true, peeled), IsIfTrue(b1b));
+  EXPECT_THAT(b1f, IsIfFalse(b1b));
+
+  Node* b2b = ExpectPeeled(b2.branch, peeled);
+  Node* b2t = ExpectPeeled(b2.if_true, peeled);
+  Node* b2f = ExpectPeeled(b2.if_false, peeled);
+
+  EXPECT_THAT(b2b, IsBranch(p0, b1t));
+  EXPECT_THAT(b2t, IsIfTrue(b2b));
+  EXPECT_THAT(b2f, IsIfFalse(b2b));
+
+  Capture<Node*> entry;
+  EXPECT_THAT(loop, IsLoop(AllOf(CaptureEq(&entry), IsMerge(b2t, b2f)),
+                           b2.if_true, b2.if_false));
+
+  Node* eval = phi->InputAt(0);
+
+  EXPECT_THAT(eval, IsPhi(kMachAnyTagged,
+                          IsInt32Add(IsInt32Constant(0), IsInt32Constant(1)),
+                          IsInt32Add(IsInt32Constant(0), IsInt32Constant(2)),
+                          CaptureEq(&entry)));
+
+  EXPECT_THAT(phi,
+              IsPhi(kMachAnyTagged, eval, IsInt32Add(phi, IsInt32Constant(1)),
+                    IsInt32Add(phi, IsInt32Constant(2)), loop));
+
+  Capture<Node*> merge;
+  EXPECT_THAT(
+      r, IsReturn(IsPhi(kMachAnyTagged, phi, IsInt32Constant(0),
+                        AllOf(CaptureEq(&merge), IsMerge(b1.if_false, b1f))),
+                  start(), CaptureEq(&merge)));
+}
+
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
index 74afda9..5f38b72 100644 (file)
@@ -94,11 +94,36 @@ class IsBranchMatcher FINAL : public NodeMatcher {
 };
 
 
-class IsMergeMatcher FINAL : public NodeMatcher {
+class IsControl1Matcher FINAL : public NodeMatcher {
+ public:
+  IsControl1Matcher(IrOpcode::Value opcode,
+                    const Matcher<Node*>& control_matcher)
+      : NodeMatcher(opcode), control_matcher_(control_matcher) {}
+
+  void DescribeTo(std::ostream* os) const FINAL {
+    NodeMatcher::DescribeTo(os);
+    *os << " whose control (";
+    control_matcher_.DescribeTo(os);
+    *os << ")";
+  }
+
+  bool MatchAndExplain(Node* node, MatchResultListener* listener) const FINAL {
+    return (NodeMatcher::MatchAndExplain(node, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetControlInput(node),
+                                 "control", control_matcher_, listener));
+  }
+
+ private:
+  const Matcher<Node*> control_matcher_;
+};
+
+
+class IsControl2Matcher FINAL : public NodeMatcher {
  public:
-  IsMergeMatcher(const Matcher<Node*>& control0_matcher,
-                 const Matcher<Node*>& control1_matcher)
-      : NodeMatcher(IrOpcode::kMerge),
+  IsControl2Matcher(IrOpcode::Value opcode,
+                    const Matcher<Node*>& control0_matcher,
+                    const Matcher<Node*>& control1_matcher)
+      : NodeMatcher(opcode),
         control0_matcher_(control0_matcher),
         control1_matcher_(control1_matcher) {}
 
@@ -125,27 +150,42 @@ class IsMergeMatcher FINAL : public NodeMatcher {
 };
 
 
-class IsControl1Matcher FINAL : public NodeMatcher {
+class IsControl3Matcher FINAL : public NodeMatcher {
  public:
-  IsControl1Matcher(IrOpcode::Value opcode,
-                    const Matcher<Node*>& control_matcher)
-      : NodeMatcher(opcode), control_matcher_(control_matcher) {}
+  IsControl3Matcher(IrOpcode::Value opcode,
+                    const Matcher<Node*>& control0_matcher,
+                    const Matcher<Node*>& control1_matcher,
+                    const Matcher<Node*>& control2_matcher)
+      : NodeMatcher(opcode),
+        control0_matcher_(control0_matcher),
+        control1_matcher_(control1_matcher),
+        control2_matcher_(control2_matcher) {}
 
   void DescribeTo(std::ostream* os) const FINAL {
     NodeMatcher::DescribeTo(os);
-    *os << " whose control (";
-    control_matcher_.DescribeTo(os);
+    *os << " whose control0 (";
+    control0_matcher_.DescribeTo(os);
+    *os << ") and control1 (";
+    control1_matcher_.DescribeTo(os);
+    *os << ") and control2 (";
+    control2_matcher_.DescribeTo(os);
     *os << ")";
   }
 
   bool MatchAndExplain(Node* node, MatchResultListener* listener) const FINAL {
     return (NodeMatcher::MatchAndExplain(node, listener) &&
-            PrintMatchAndExplain(NodeProperties::GetControlInput(node),
-                                 "control", control_matcher_, listener));
+            PrintMatchAndExplain(NodeProperties::GetControlInput(node, 0),
+                                 "control0", control0_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetControlInput(node, 1),
+                                 "control1", control1_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetControlInput(node, 2),
+                                 "control2", control2_matcher_, listener));
   }
 
  private:
-  const Matcher<Node*> control_matcher_;
+  const Matcher<Node*> control0_matcher_;
+  const Matcher<Node*> control1_matcher_;
+  const Matcher<Node*> control2_matcher_;
 };
 
 
@@ -180,6 +220,44 @@ class IsFinishMatcher FINAL : public NodeMatcher {
 };
 
 
+class IsReturnMatcher FINAL : public NodeMatcher {
+ public:
+  IsReturnMatcher(const Matcher<Node*>& value_matcher,
+                  const Matcher<Node*>& effect_matcher,
+                  const Matcher<Node*>& control_matcher)
+      : NodeMatcher(IrOpcode::kReturn),
+        value_matcher_(value_matcher),
+        effect_matcher_(effect_matcher),
+        control_matcher_(control_matcher) {}
+
+  void DescribeTo(std::ostream* os) const FINAL {
+    NodeMatcher::DescribeTo(os);
+    *os << " whose value (";
+    value_matcher_.DescribeTo(os);
+    *os << ") and effect (";
+    effect_matcher_.DescribeTo(os);
+    *os << ") and control (";
+    control_matcher_.DescribeTo(os);
+    *os << ")";
+  }
+
+  bool MatchAndExplain(Node* node, MatchResultListener* listener) const FINAL {
+    return (NodeMatcher::MatchAndExplain(node, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0),
+                                 "value", value_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
+                                 effect_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetControlInput(node),
+                                 "control", control_matcher_, listener));
+  }
+
+ private:
+  const Matcher<Node*> value_matcher_;
+  const Matcher<Node*> effect_matcher_;
+  const Matcher<Node*> control_matcher_;
+};
+
+
 template <typename T>
 class IsConstantMatcher FINAL : public NodeMatcher {
  public:
@@ -294,6 +372,58 @@ class IsPhiMatcher FINAL : public NodeMatcher {
 };
 
 
+class IsPhi2Matcher FINAL : public NodeMatcher {
+ public:
+  IsPhi2Matcher(const Matcher<MachineType>& type_matcher,
+                const Matcher<Node*>& value0_matcher,
+                const Matcher<Node*>& value1_matcher,
+                const Matcher<Node*>& value2_matcher,
+                const Matcher<Node*>& control_matcher)
+      : NodeMatcher(IrOpcode::kPhi),
+        type_matcher_(type_matcher),
+        value0_matcher_(value0_matcher),
+        value1_matcher_(value1_matcher),
+        value2_matcher_(value2_matcher),
+        control_matcher_(control_matcher) {}
+
+  void DescribeTo(std::ostream* os) const FINAL {
+    NodeMatcher::DescribeTo(os);
+    *os << " whose type (";
+    type_matcher_.DescribeTo(os);
+    *os << "), value0 (";
+    value0_matcher_.DescribeTo(os);
+    *os << "), value1 (";
+    value1_matcher_.DescribeTo(os);
+    *os << "), value2 (";
+    value2_matcher_.DescribeTo(os);
+    *os << ") and control (";
+    control_matcher_.DescribeTo(os);
+    *os << ")";
+  }
+
+  bool MatchAndExplain(Node* node, MatchResultListener* listener) const FINAL {
+    return (NodeMatcher::MatchAndExplain(node, listener) &&
+            PrintMatchAndExplain(OpParameter<MachineType>(node), "type",
+                                 type_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0),
+                                 "value0", value0_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1),
+                                 "value1", value1_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetValueInput(node, 2),
+                                 "value2", value2_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetControlInput(node),
+                                 "control", control_matcher_, listener));
+  }
+
+ private:
+  const Matcher<MachineType> type_matcher_;
+  const Matcher<Node*> value0_matcher_;
+  const Matcher<Node*> value1_matcher_;
+  const Matcher<Node*> value2_matcher_;
+  const Matcher<Node*> control_matcher_;
+};
+
+
 class IsEffectPhiMatcher FINAL : public NodeMatcher {
  public:
   IsEffectPhiMatcher(const Matcher<Node*>& effect0_matcher,
@@ -1029,7 +1159,23 @@ Matcher<Node*> IsBranch(const Matcher<Node*>& value_matcher,
 
 Matcher<Node*> IsMerge(const Matcher<Node*>& control0_matcher,
                        const Matcher<Node*>& control1_matcher) {
-  return MakeMatcher(new IsMergeMatcher(control0_matcher, control1_matcher));
+  return MakeMatcher(new IsControl2Matcher(IrOpcode::kMerge, control0_matcher,
+                                           control1_matcher));
+}
+
+
+Matcher<Node*> IsLoop(const Matcher<Node*>& control0_matcher,
+                      const Matcher<Node*>& control1_matcher) {
+  return MakeMatcher(new IsControl2Matcher(IrOpcode::kLoop, control0_matcher,
+                                           control1_matcher));
+}
+
+
+Matcher<Node*> IsLoop(const Matcher<Node*>& control0_matcher,
+                      const Matcher<Node*>& control1_matcher,
+                      const Matcher<Node*>& control2_matcher) {
+  return MakeMatcher(new IsControl3Matcher(IrOpcode::kLoop, control0_matcher,
+                                           control1_matcher, control2_matcher));
 }
 
 
@@ -1055,6 +1201,14 @@ Matcher<Node*> IsFinish(const Matcher<Node*>& value_matcher,
 }
 
 
+Matcher<Node*> IsReturn(const Matcher<Node*>& value_matcher,
+                        const Matcher<Node*>& effect_matcher,
+                        const Matcher<Node*>& control_matcher) {
+  return MakeMatcher(
+      new IsReturnMatcher(value_matcher, effect_matcher, control_matcher));
+}
+
+
 Matcher<Node*> IsExternalConstant(
     const Matcher<ExternalReference>& value_matcher) {
   return MakeMatcher(new IsConstantMatcher<ExternalReference>(
@@ -1117,6 +1271,17 @@ Matcher<Node*> IsPhi(const Matcher<MachineType>& type_matcher,
 }
 
 
+Matcher<Node*> IsPhi(const Matcher<MachineType>& type_matcher,
+                     const Matcher<Node*>& value0_matcher,
+                     const Matcher<Node*>& value1_matcher,
+                     const Matcher<Node*>& value2_matcher,
+                     const Matcher<Node*>& merge_matcher) {
+  return MakeMatcher(new IsPhi2Matcher(type_matcher, value0_matcher,
+                                       value1_matcher, value2_matcher,
+                                       merge_matcher));
+}
+
+
 Matcher<Node*> IsEffectPhi(const Matcher<Node*>& effect0_matcher,
                            const Matcher<Node*>& effect1_matcher,
                            const Matcher<Node*>& merge_matcher) {
index 02b6e43..736cf0f 100644 (file)
@@ -35,11 +35,19 @@ Matcher<Node*> IsBranch(const Matcher<Node*>& value_matcher,
                         const Matcher<Node*>& control_matcher);
 Matcher<Node*> IsMerge(const Matcher<Node*>& control0_matcher,
                        const Matcher<Node*>& control1_matcher);
+Matcher<Node*> IsLoop(const Matcher<Node*>& control0_matcher,
+                      const Matcher<Node*>& control1_matcher);
+Matcher<Node*> IsLoop(const Matcher<Node*>& control0_matcher,
+                      const Matcher<Node*>& control1_matcher,
+                      const Matcher<Node*>& control2_matcher);
 Matcher<Node*> IsIfTrue(const Matcher<Node*>& control_matcher);
 Matcher<Node*> IsIfFalse(const Matcher<Node*>& control_matcher);
 Matcher<Node*> IsValueEffect(const Matcher<Node*>& value_matcher);
 Matcher<Node*> IsFinish(const Matcher<Node*>& value_matcher,
                         const Matcher<Node*>& effect_matcher);
+Matcher<Node*> IsReturn(const Matcher<Node*>& value_matcher,
+                        const Matcher<Node*>& effect_matcher,
+                        const Matcher<Node*>& control_matcher);
 Matcher<Node*> IsExternalConstant(
     const Matcher<ExternalReference>& value_matcher);
 Matcher<Node*> IsHeapConstant(
@@ -57,6 +65,11 @@ Matcher<Node*> IsPhi(const Matcher<MachineType>& type_matcher,
                      const Matcher<Node*>& value0_matcher,
                      const Matcher<Node*>& value1_matcher,
                      const Matcher<Node*>& merge_matcher);
+Matcher<Node*> IsPhi(const Matcher<MachineType>& type_matcher,
+                     const Matcher<Node*>& value0_matcher,
+                     const Matcher<Node*>& value1_matcher,
+                     const Matcher<Node*>& value2_matcher,
+                     const Matcher<Node*>& merge_matcher);
 Matcher<Node*> IsEffectPhi(const Matcher<Node*>& effect0_matcher,
                            const Matcher<Node*>& effect1_matcher,
                            const Matcher<Node*>& merge_matcher);
index ee66eb7..7f8e71c 100644 (file)
@@ -54,6 +54,7 @@
         'compiler/js-operator-unittest.cc',
         'compiler/js-typed-lowering-unittest.cc',
         'compiler/load-elimination-unittest.cc',
+        'compiler/loop-peeling-unittest.cc',
         'compiler/machine-operator-reducer-unittest.cc',
         'compiler/machine-operator-unittest.cc',
         'compiler/move-optimizer-unittest.cc',
index e217654..9443911 100644 (file)
         '../../src/compiler/load-elimination.h',
         '../../src/compiler/loop-analysis.cc',
         '../../src/compiler/loop-analysis.h',
+        '../../src/compiler/loop-peeling.cc',
+        '../../src/compiler/loop-peeling.h',
         '../../src/compiler/machine-operator-reducer.cc',
         '../../src/compiler/machine-operator-reducer.h',
         '../../src/compiler/machine-operator.cc',