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