Home | History | Annotate | Download | only in compiler
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/compiler/loop-peeling.h"
      6 #include "src/compiler/common-operator.h"
      7 #include "src/compiler/graph.h"
      8 #include "src/compiler/node-marker.h"
      9 #include "src/compiler/node-properties.h"
     10 #include "src/compiler/node.h"
     11 #include "src/zone/zone.h"
     12 
     13 // Loop peeling is an optimization that copies the body of a loop, creating
     14 // a new copy of the body called the "peeled iteration" that represents the
     15 // first iteration. Beginning with a loop as follows:
     16 
     17 //             E
     18 //             |                 A
     19 //             |                 |                     (backedges)
     20 //             | +---------------|---------------------------------+
     21 //             | | +-------------|-------------------------------+ |
     22 //             | | |             | +--------+                    | |
     23 //             | | |             | | +----+ |                    | |
     24 //             | | |             | | |    | |                    | |
     25 //           ( Loop )<-------- ( phiA )   | |                    | |
     26 //              |                 |       | |                    | |
     27 //      ((======P=================U=======|=|=====))             | |
     28 //      ((                                | |     ))             | |
     29 //      ((        X <---------------------+ |     ))             | |
     30 //      ((                                  |     ))             | |
     31 //      ((     body                         |     ))             | |
     32 //      ((                                  |     ))             | |
     33 //      ((        Y <-----------------------+     ))             | |
     34 //      ((                                        ))             | |
     35 //      ((===K====L====M==========================))             | |
     36 //           |    |    |                                         | |
     37 //           |    |    +-----------------------------------------+ |
     38 //           |    +------------------------------------------------+
     39 //           |
     40 //          exit
     41 
     42 // The body of the loop is duplicated so that all nodes considered "inside"
     43 // the loop (e.g. {P, U, X, Y, K, L, M}) have a corresponding copies in the
     44 // peeled iteration (e.g. {P', U', X', Y', K', L', M'}). What were considered
     45 // backedges of the loop correspond to edges from the peeled iteration to
     46 // the main loop body, with multiple backedges requiring a merge.
     47 
     48 // Similarly, any exits from the loop body need to be merged with "exits"
     49 // from the peeled iteration, resulting in the graph as follows:
     50 
     51 //             E
     52 //             |                 A
     53 //             |                 |
     54 //      ((=====P'================U'===============))
     55 //      ((                                        ))
     56 //      ((        X'<-------------+               ))
     57 //      ((                        |               ))
     58 //      ((   peeled iteration     |               ))
     59 //      ((                        |               ))
     60 //      ((        Y'<-----------+ |               ))
     61 //      ((                      | |               ))
     62 //      ((===K'===L'====M'======|=|===============))
     63 //           |    |     |       | |
     64 //  +--------+    +-+ +-+       | |
     65 //  |               | |         | |
     66 //  |              Merge <------phi
     67 //  |                |           |
     68 //  |          +-----+           |
     69 //  |          |                 |                     (backedges)
     70 //  |          | +---------------|---------------------------------+
     71 //  |          | | +-------------|-------------------------------+ |
     72 //  |          | | |             | +--------+                    | |
     73 //  |          | | |             | | +----+ |                    | |
     74 //  |          | | |             | | |    | |                    | |
     75 //  |        ( Loop )<-------- ( phiA )   | |                    | |
     76 //  |           |                 |       | |                    | |
     77 //  |   ((======P=================U=======|=|=====))             | |
     78 //  |   ((                                | |     ))             | |
     79 //  |   ((        X <---------------------+ |     ))             | |
     80 //  |   ((                                  |     ))             | |
     81 //  |   ((     body                         |     ))             | |
     82 //  |   ((                                  |     ))             | |
     83 //  |   ((        Y <-----------------------+     ))             | |
     84 //  |   ((                                        ))             | |
     85 //  |   ((===K====L====M==========================))             | |
     86 //  |        |    |    |                                         | |
     87 //  |        |    |    +-----------------------------------------+ |
     88 //  |        |    +------------------------------------------------+
     89 //  |        |
     90 //  |        |
     91 //  +----+ +-+
     92 //       | |
     93 //      Merge
     94 //        |
     95 //      exit
     96 
     97 // Note that the boxes ((===)) above are not explicitly represented in the
     98 // graph, but are instead computed by the {LoopFinder}.
     99 
    100 namespace v8 {
    101 namespace internal {
    102 namespace compiler {
    103 
    104 struct Peeling {
    105   // Maps a node to its index in the {pairs} vector.
    106   NodeMarker<size_t> node_map;
    107   // The vector which contains the mapped nodes.
    108   NodeVector* pairs;
    109 
    110   Peeling(Graph* graph, Zone* tmp_zone, size_t max, NodeVector* p)
    111       : node_map(graph, static_cast<uint32_t>(max)), pairs(p) {}
    112 
    113   Node* map(Node* node) {
    114     if (node_map.Get(node) == 0) return node;
    115     return pairs->at(node_map.Get(node));
    116   }
    117 
    118   void Insert(Node* original, Node* copy) {
    119     node_map.Set(original, 1 + pairs->size());
    120     pairs->push_back(original);
    121     pairs->push_back(copy);
    122   }
    123 
    124   void CopyNodes(Graph* graph, Zone* tmp_zone, Node* dead, NodeRange nodes) {
    125     NodeVector inputs(tmp_zone);
    126     // Copy all the nodes first.
    127     for (Node* node : nodes) {
    128       inputs.clear();
    129       for (Node* input : node->inputs()) {
    130         inputs.push_back(map(input));
    131       }
    132       Node* copy = graph->NewNode(node->op(), node->InputCount(), &inputs[0]);
    133       if (NodeProperties::IsTyped(node)) {
    134         NodeProperties::SetType(copy, NodeProperties::GetType(node));
    135       }
    136       Insert(node, copy);
    137     }
    138 
    139     // Fix remaining inputs of the copies.
    140     for (Node* original : nodes) {
    141       Node* copy = pairs->at(node_map.Get(original));
    142       for (int i = 0; i < copy->InputCount(); i++) {
    143         copy->ReplaceInput(i, map(original->InputAt(i)));
    144       }
    145     }
    146   }
    147 
    148   bool Marked(Node* node) { return node_map.Get(node) > 0; }
    149 };
    150 
    151 
    152 class PeeledIterationImpl : public PeeledIteration {
    153  public:
    154   NodeVector node_pairs_;
    155   explicit PeeledIterationImpl(Zone* zone) : node_pairs_(zone) {}
    156 };
    157 
    158 
    159 Node* PeeledIteration::map(Node* node) {
    160   // TODO(turbofan): we use a simple linear search, since the peeled iteration
    161   // is really only used in testing.
    162   PeeledIterationImpl* impl = static_cast<PeeledIterationImpl*>(this);
    163   for (size_t i = 0; i < impl->node_pairs_.size(); i += 2) {
    164     if (impl->node_pairs_[i] == node) return impl->node_pairs_[i + 1];
    165   }
    166   return node;
    167 }
    168 
    169 bool LoopPeeler::CanPeel(LoopTree* loop_tree, LoopTree::Loop* loop) {
    170   // Look for returns and if projections that are outside the loop but whose
    171   // control input is inside the loop.
    172   Node* loop_node = loop_tree->GetLoopControl(loop);
    173   for (Node* node : loop_tree->LoopNodes(loop)) {
    174     for (Node* use : node->uses()) {
    175       if (!loop_tree->Contains(loop, use)) {
    176         bool unmarked_exit;
    177         switch (node->opcode()) {
    178           case IrOpcode::kLoopExit:
    179             unmarked_exit = (node->InputAt(1) != loop_node);
    180             break;
    181           case IrOpcode::kLoopExitValue:
    182           case IrOpcode::kLoopExitEffect:
    183             unmarked_exit = (node->InputAt(1)->InputAt(1) != loop_node);
    184             break;
    185           default:
    186             unmarked_exit = (use->opcode() != IrOpcode::kTerminate);
    187         }
    188         if (unmarked_exit) {
    189           if (FLAG_trace_turbo_loop) {
    190             Node* loop_node = loop_tree->GetLoopControl(loop);
    191             PrintF(
    192                 "Cannot peel loop %i. Loop exit without explicit mark: Node %i "
    193                 "(%s) is inside "
    194                 "loop, but its use %i (%s) is outside.\n",
    195                 loop_node->id(), node->id(), node->op()->mnemonic(), use->id(),
    196                 use->op()->mnemonic());
    197           }
    198           return false;
    199         }
    200       }
    201     }
    202   }
    203   return true;
    204 }
    205 
    206 
    207 PeeledIteration* LoopPeeler::Peel(Graph* graph, CommonOperatorBuilder* common,
    208                                   LoopTree* loop_tree, LoopTree::Loop* loop,
    209                                   Zone* tmp_zone) {
    210   if (!CanPeel(loop_tree, loop)) return nullptr;
    211 
    212   //============================================================================
    213   // Construct the peeled iteration.
    214   //============================================================================
    215   PeeledIterationImpl* iter = new (tmp_zone) PeeledIterationImpl(tmp_zone);
    216   size_t estimated_peeled_size = 5 + (loop->TotalSize()) * 2;
    217   Peeling peeling(graph, tmp_zone, estimated_peeled_size, &iter->node_pairs_);
    218 
    219   Node* dead = graph->NewNode(common->Dead());
    220 
    221   // Map the loop header nodes to their entry values.
    222   for (Node* node : loop_tree->HeaderNodes(loop)) {
    223     peeling.Insert(node, node->InputAt(kAssumedLoopEntryIndex));
    224   }
    225 
    226   // Copy all the nodes of loop body for the peeled iteration.
    227   peeling.CopyNodes(graph, tmp_zone, dead, loop_tree->BodyNodes(loop));
    228 
    229   //============================================================================
    230   // Replace the entry to the loop with the output of the peeled iteration.
    231   //============================================================================
    232   Node* loop_node = loop_tree->GetLoopControl(loop);
    233   Node* new_entry;
    234   int backedges = loop_node->InputCount() - 1;
    235   if (backedges > 1) {
    236     // Multiple backedges from original loop, therefore multiple output edges
    237     // from the peeled iteration.
    238     NodeVector inputs(tmp_zone);
    239     for (int i = 1; i < loop_node->InputCount(); i++) {
    240       inputs.push_back(peeling.map(loop_node->InputAt(i)));
    241     }
    242     Node* merge =
    243         graph->NewNode(common->Merge(backedges), backedges, &inputs[0]);
    244 
    245     // Merge values from the multiple output edges of the peeled iteration.
    246     for (Node* node : loop_tree->HeaderNodes(loop)) {
    247       if (node->opcode() == IrOpcode::kLoop) continue;  // already done.
    248       inputs.clear();
    249       for (int i = 0; i < backedges; i++) {
    250         inputs.push_back(peeling.map(node->InputAt(1 + i)));
    251       }
    252       for (Node* input : inputs) {
    253         if (input != inputs[0]) {  // Non-redundant phi.
    254           inputs.push_back(merge);
    255           const Operator* op = common->ResizeMergeOrPhi(node->op(), backedges);
    256           Node* phi = graph->NewNode(op, backedges + 1, &inputs[0]);
    257           node->ReplaceInput(0, phi);
    258           break;
    259         }
    260       }
    261     }
    262     new_entry = merge;
    263   } else {
    264     // Only one backedge, simply replace the input to loop with output of
    265     // peeling.
    266     for (Node* node : loop_tree->HeaderNodes(loop)) {
    267       node->ReplaceInput(0, peeling.map(node->InputAt(1)));
    268     }
    269     new_entry = peeling.map(loop_node->InputAt(1));
    270   }
    271   loop_node->ReplaceInput(0, new_entry);
    272 
    273   //============================================================================
    274   // Change the exit and exit markers to merge/phi/effect-phi.
    275   //============================================================================
    276   for (Node* exit : loop_tree->ExitNodes(loop)) {
    277     switch (exit->opcode()) {
    278       case IrOpcode::kLoopExit:
    279         // Change the loop exit node to a merge node.
    280         exit->ReplaceInput(1, peeling.map(exit->InputAt(0)));
    281         NodeProperties::ChangeOp(exit, common->Merge(2));
    282         break;
    283       case IrOpcode::kLoopExitValue:
    284         // Change exit marker to phi.
    285         exit->InsertInput(graph->zone(), 1, peeling.map(exit->InputAt(0)));
    286         NodeProperties::ChangeOp(
    287             exit, common->Phi(MachineRepresentation::kTagged, 2));
    288         break;
    289       case IrOpcode::kLoopExitEffect:
    290         // Change effect exit marker to effect phi.
    291         exit->InsertInput(graph->zone(), 1, peeling.map(exit->InputAt(0)));
    292         NodeProperties::ChangeOp(exit, common->EffectPhi(2));
    293         break;
    294       default:
    295         break;
    296     }
    297   }
    298   return iter;
    299 }
    300 
    301 namespace {
    302 
    303 void PeelInnerLoops(Graph* graph, CommonOperatorBuilder* common,
    304                     LoopTree* loop_tree, LoopTree::Loop* loop,
    305                     Zone* temp_zone) {
    306   // If the loop has nested loops, peel inside those.
    307   if (!loop->children().empty()) {
    308     for (LoopTree::Loop* inner_loop : loop->children()) {
    309       PeelInnerLoops(graph, common, loop_tree, inner_loop, temp_zone);
    310     }
    311     return;
    312   }
    313   // Only peel small-enough loops.
    314   if (loop->TotalSize() > LoopPeeler::kMaxPeeledNodes) return;
    315   if (FLAG_trace_turbo_loop) {
    316     PrintF("Peeling loop with header: ");
    317     for (Node* node : loop_tree->HeaderNodes(loop)) {
    318       PrintF("%i ", node->id());
    319     }
    320     PrintF("\n");
    321   }
    322 
    323   LoopPeeler::Peel(graph, common, loop_tree, loop, temp_zone);
    324 }
    325 
    326 void EliminateLoopExit(Node* node) {
    327   DCHECK_EQ(IrOpcode::kLoopExit, node->opcode());
    328   // The exit markers take the loop exit as input. We iterate over uses
    329   // and remove all the markers from the graph.
    330   for (Edge edge : node->use_edges()) {
    331     if (NodeProperties::IsControlEdge(edge)) {
    332       Node* marker = edge.from();
    333       if (marker->opcode() == IrOpcode::kLoopExitValue) {
    334         NodeProperties::ReplaceUses(marker, marker->InputAt(0));
    335         marker->Kill();
    336       } else if (marker->opcode() == IrOpcode::kLoopExitEffect) {
    337         NodeProperties::ReplaceUses(marker, nullptr,
    338                                     NodeProperties::GetEffectInput(marker));
    339         marker->Kill();
    340       }
    341     }
    342   }
    343   NodeProperties::ReplaceUses(node, nullptr, nullptr,
    344                               NodeProperties::GetControlInput(node, 0));
    345   node->Kill();
    346 }
    347 
    348 }  // namespace
    349 
    350 // static
    351 void LoopPeeler::PeelInnerLoopsOfTree(Graph* graph,
    352                                       CommonOperatorBuilder* common,
    353                                       LoopTree* loop_tree, Zone* temp_zone) {
    354   for (LoopTree::Loop* loop : loop_tree->outer_loops()) {
    355     PeelInnerLoops(graph, common, loop_tree, loop, temp_zone);
    356   }
    357 
    358   EliminateLoopExits(graph, temp_zone);
    359 }
    360 
    361 // static
    362 void LoopPeeler::EliminateLoopExits(Graph* graph, Zone* temp_zone) {
    363   ZoneQueue<Node*> queue(temp_zone);
    364   ZoneVector<bool> visited(graph->NodeCount(), false, temp_zone);
    365   queue.push(graph->end());
    366   while (!queue.empty()) {
    367     Node* node = queue.front();
    368     queue.pop();
    369 
    370     if (node->opcode() == IrOpcode::kLoopExit) {
    371       Node* control = NodeProperties::GetControlInput(node);
    372       EliminateLoopExit(node);
    373       if (!visited[control->id()]) {
    374         visited[control->id()] = true;
    375         queue.push(control);
    376       }
    377     } else {
    378       for (int i = 0; i < node->op()->ControlInputCount(); i++) {
    379         Node* control = NodeProperties::GetControlInput(node, i);
    380         if (!visited[control->id()]) {
    381           visited[control->id()] = true;
    382           queue.push(control);
    383         }
    384       }
    385     }
    386   }
    387 }
    388 
    389 }  // namespace compiler
    390 }  // namespace internal
    391 }  // namespace v8
    392