| Index: src/compiler/loop-peeling.cc
|
| diff --git a/src/compiler/loop-peeling.cc b/src/compiler/loop-peeling.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6a59efc1dce0b00a71f3319b6d06d10e0372e653
|
| --- /dev/null
|
| +++ b/src/compiler/loop-peeling.cc
|
| @@ -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
|
|
|