"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",
// 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) {
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)
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;
-// 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.
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) {
// 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;
}
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;
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;
&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;
static LoopTree* BuildLoopTree(Graph* graph, Zone* temp_zone);
};
+
} // namespace compiler
} // namespace internal
} // namespace v8
--- /dev/null
+// 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
--- /dev/null
+// 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_
explicit Edge(Node::Input* input) : input_(input) { DCHECK_NOT_NULL(input); }
- Node::Input* const input_;
+ Node::Input* input_;
};
static bool IsConstantOpcode(Value value) {
return kInt32Constant <= value && value <= kHeapConstant;
}
+
+ static bool IsPhiOpcode(Value val) {
+ return val == kPhi || val == kEffectPhi;
+ }
};
} // namespace compiler
// 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 |^-------------------------+
#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"
};
+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"; }
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");
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) {
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) {
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")
~GraphTest() OVERRIDE;
protected:
+ Node* start() { return graph()->start(); }
Node* Parameter(int32_t index = 0);
Node* Float32Constant(volatile float value);
Node* Float64Constant(volatile double value);
--- /dev/null
+// 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
};
-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) {}
};
-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_;
};
};
+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:
};
+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,
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));
}
}
+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>(
}
+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) {
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(
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);
'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',
'../../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',